Skip to content

10. Entwicklung verteilter Anwendungen CORBA

10.1. Einführung

Im vorigen Kapitel haben wir gesehen, wie man mit dem Paket RMI verteilte Anwendungen in Java erstellt. Hier befassen wir uns mit demselben Problem, diesmal jedoch mit der CORBA-Architektur. CORBA (Common Object Request Broker Architecture) ist eine Spezifikation, die von der OMG (Object Management Group) definiert wurde, einem Zusammenschluss zahlreicher Unternehmen aus der IT-Branche. CORBA definiert einen „Software-Bus“, auf den Anwendungen zugreifen können, die in verschiedenen Sprachen geschrieben sind:

Image

Wir werden sehen, dass die Erstellung einer verteilten Anwendung mit CORBA der bei Java RMI verwendeten Methode sehr ähnlich ist: Die Konzepte sind vergleichbar. CORBA bietet den Vorteil der Interoperabilität mit Anwendungen, die in anderen Sprachen geschrieben sind.

10.2. Entwicklungsprozess einer CORBA-Anwendung

10.2.1. Einleitung

Um eine Client-Server-Anwendung CORBA zu entwickeln, gehen wir wie folgt vor:

  1. Erstellung der Server-Schnittstelle mit IDL (Interface Definition Language)
  2. Generierung der „Skelett“- und „Stub“-Klassen des Servers
  3. Erstellung des Servers
  4. Erstellung des Clients
  5. Kompilierung aller Klassen
  6. Starten eines Verzeichnisses der Dienste CORBA
  7. Starten des Servers
  8. Starten des Clients

Als erstes Beispiel nehmen wir den Echo-Server, der bereits im Kontext RMI verwendet wurde. So kann der Leser die Unterschiede zwischen den beiden Methoden erkennen.

Die Anwendung wurde mit jdk1.2 getestet.

10.2.2. Erstellung der Server-Schnittstelle

Wie bei Java RMI wird der Server gegenüber dem Client durch seine Schnittstelle definiert. Auch wenn die Klassen, die den Server implementieren, für den Client nicht erforderlich sind, so sind doch die Klassen seiner Schnittstelle notwendig. Während Java RMI eine Java-Schnittstelle verwendete, aus der die „Skelett“- und „Stub“-Klassen des Servers hervorgingen, erfordert die Architektur von Java CORBA die Beschreibung der Schnittstelle in einer anderen Sprache als Java. Aus dieser Schnittstelle entstehen mehrere Klassen, von denen einige vom Client, andere vom Server verwendet werden.

Die Beschreibung der Echo-Schnittstelle lautet wie folgt:

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

Die Beschreibung der Schnittstelle wird in einer Datei namens echo.idl gespeichert. Sie ist in der Sprache IDL (Interface Definition Language) des OMG verfasst. Um verwertbar zu sein, muss sie von einem Programm analysiert werden, das Quelldateien in der Sprache erstellt, die zur Entwicklung der Anwendung CORBA verwendet wird. Hier verwenden wir das Programm idltojava.exe, das ausgehend von der vorangegangenen Schnittstelle die für die Anwendung erforderlichen Quelldateien .java erstellt. Das Programm idltojava.exe ist nicht im Lieferumfang von JDK enthalten. Es kann auf der Website von Sun unter http://java.sun.com heruntergeladen werden.

Betrachten wir die wenigen Zeilen der oben gezeigten Schnittstelle idl:

module echo

entspricht dem Java-Paket „echo“. Durch die Kompilierung der Schnittstelle entsteht das Java-Paket echo c.a.d – ein Verzeichnis, das Java-Klassen enthält.

interface iSrvEcho

entspricht der Java-Schnittstelle „iSrvEcho“. Daraus entsteht eine Java-Schnittstelle.

string echo(in string msg)

entspricht der Java-Anweisung **String echo(String msg)**. Die Typen der Sprache IDL stimmen nicht genau mit denen der Sprache Java überein. Die Entsprechungen finden Sie etwas weiter unten in diesem Kapitel. In der Sprache IDL können die Parameter einer Funktion Eingabeparameter (in), Ausgabeparameter (out) oder Ein- und Ausgabeparameter (inout) sein. Hier erhält die Methode echo einen Eingabeparameter msg, bei dem es sich um eine Zeichenkette handelt, und gibt als Ergebnis eine Zeichenkette zurück.

Die vorstehende Schnittstelle ist die unseres Echo-Servers. Zur Erinnerung: Eine Remote-Schnittstelle beschreibt die Methoden des Serverobjekts, auf die Clients zugreifen können. Hier steht den Clients nur die Methode echo zur Verfügung.

10.2.3. Kompilierung der Server-Schnittstelle IDL

Sobald die Server-Schnittstelle definiert ist, werden die entsprechenden Java-Dateien generiert.

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

Die Option -fno-cpp dient dazu, anzugeben, dass kein Präprozessor verwendet werden soll (wird meist bei C/C++ verwendet). Die Kompilierung der Datei echo.idl erzeugt ein Unterverzeichnis echo, das die folgenden Dateien enthält:

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

Die Datei iSrvEcho.java ist die Java-Datei, die die Server-Schnittstelle beschreibt:

/*  * Datei: ./ECHO/ISRVECHO.JAVA  * Von: ECHO.IDL  * Datum: Mo, 15. März 13:56:08 1999  *   Von: D:\JAVAIDL\IDLTOJ~1.EXE Java IDL 1.2 18. Aug. 1998 16:25:34  */

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

Man sieht, dass es sich fast um eine wörtliche Übersetzung der Schnittstelle IDL handelt. Wenn man neugierig ist und sich den Inhalt der anderen Dateien .java ansieht, findet man komplexere Dinge. Hier ist, was die Dokumentation über die Rolle dieser verschiedenen Dateien sagt:

iSrvEcho.java

die Server-Schnittstelle

_iSrvEchoImplbase.java

implementiert die oben genannte Schnittstelle iSrvEcho. Es handelt sich um eine abstrakte Klasse, das „Gerüst“ des Servers, das dem Server die für die verteilte Anwendung erforderlichen Funktionen CORBA bereitstellt.

_iSrvEchoStub.java

Dies ist das „Stub“-Image des Servers, das vom Client verwendet wird. Es stellt dem Client die Funktionen von CORBA zur Verfügung, um den Server zu erreichen.

iSrvEchoHelper.java

Stellt die für die Verwaltung von Objektverweisen erforderlichen Methoden bereit: CORBA

iSrvEchoHolder.java

Stellt die erforderlichen Methoden zur Verwaltung der Ein- und Ausgabeparameter der Methoden der Schnittstelle bereit.

10.2.4. Kompilierung der aus der Schnittstelle IDL generierten Klassen

Es empfiehlt sich, die oben genannten Klassen zu kompilieren. In einem anderen Beispiel werden wir sehen, dass hier Fehler aufgrund einer fehlerhaften Funktionsweise des Generators idltojava entdeckt werden können. Hier läuft alles reibungslos, und nach der Kompilierung befinden sich im Verzeichnis des Pakets echo die folgenden Dateien:

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. Server-Eintrag

10.2.5.1. Implementierung der Schnittstelle iSrvEcho

Wir haben oben die Schnittstelle iSrvEcho definiert. Nun schreiben wir die Klasse, die diese Schnittstelle implementiert. Sie wird von der Klasse _iSrvEchoImplbase.java abgeleitet, die, wie oben angegeben, bereits die Schnittstelle iSrvEcho implementiert.

// importierte Pakete
import echo.*;

// Klasse, die das Remote-Echo implementiert
public class srvEcho extends _iSrvEchoImplBase{
    // Methode zur Umsetzung des Echos
    public String echo(String msg){
        return  "["  + msg + "]";
    }// Ende des Echos
}// Ende der Klasse

Der Code ist selbsterklärend. Diese Klasse wird in der Datei srvEcho.java im übergeordneten Verzeichnis des Pakets der Schnittstelle iSrvEcho gespeichert.

Zur Überprüfung kann man kompilieren:

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. Eintrag der Server-Erstellungsklasse

Wie bei einer Client-Server-Anwendung RMI muss ein Server CORBA in einem Verzeichnis registriert werden, damit er für Clients zugänglich ist. Dieser Registrierungsvorgang unterscheidet sich auf der Entwicklungsebene je nachdem, ob es sich um eine CORBA- oder eine RMI-Anwendung handelt. Hier ist das Echo des Servers CORBA, das in der Datei serveurEcho.java gespeichert ist:

// importierte Pakete
import echo.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;

//----------- Klasse serveurEcho
public class serveurEcho{
    // ------- main: startet den Echo-Server
    // PG-Syntax machineAnnuaire portAnnuaire nomService
    // Rechner: Rechner, der das Verzeichnis unterstützt CORBA
    // Port: Port des Verzeichnisses CORBA
    // nomService: Name des zu registrierenden Dienstes

  public static void main(String arg[]){
    // Sind die Argumente vorhanden?
    if(arg.length!=3){
        System.err.println("Syntaxe : pg machineAnnuaire portAnnuaire nomService");
        System.exit(1);
    }
    // Die Argumente werden abgerufen
    String machine=arg[0];
    String port=arg[1];
    String nomService=arg[2];

    try{

    // Wir benötigen ein Objekt CORBA, um weiterarbeiten zu können
    String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
      ORB orb=ORB.init(initORB,null);
    // Der Dienst wird in das Dienstverzeichnis aufgenommen
    // Er wird srvEcho heißen
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
    // Wir erstellen den Server und ordnen ihn dem Dienst srvEcho zu
    srvEcho serveurEcho=new srvEcho();
      ncRef.rebind(path,serveurEcho);
    orb.connect(serveurEcho);
    // Weiterverfolgung
    System.out.println("Serveur d'écho prêt");
    // Warten auf Client-Anfragen
      java.lang.Object sync=new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
    } catch(Exception e){
    // Es ist ein Fehler aufgetreten
      System.err.println("Erreur " + e);
      e.printStackTrace(System.err);
    }
  }// Hauptprozess
}// serveurEcho

Im Folgenden erläutern wir die Grundzüge der Inbetriebnahme des Servers, ohne auf Details einzugehen, die auf den ersten Blick komplex erscheinen. Behalten Sie die Grundzüge des vorangegangenen Beispiels im Hinterkopf, da diese bei jedem CORBA-Server wiederkehren.

10.2.5.2.1. Die Serverparameter

Ein CORBA-Server muss sich bei einem Verzeichnisdienst registrieren, der auf einem bestimmten Rechner und einem bestimmten Port läuft. Unsere Anwendung erhält diese beiden Daten als Parameter. Der so registrierte Dienst muss einen Namen tragen; dies ist der dritte Parameter.

10.2.5.2.2. Erstellen des Zugriffsobjekts auf den Verzeichnisdienst CORBA

Um den Verzeichnisdienst zu erreichen und unseren Echo-Server zu registrieren, benötigen wir ein Objekt namens ORB (Object Request Broker), das mit der folgenden Klassenmethode abgerufen wird:

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

Das Beispiel verwendet die folgende Sequenz, um das Objekt ORB abzurufen:


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

Die verwendeten (Parameter, Wert)-Paare lauten wie folgt:


("-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.

Der zweite Parameter der Methode init wird auf null gesetzt. Hätte man auch den ersten Parameter auf null gesetzt, wäre das verwendete Paar (Rechner, Port) standardmäßig (localhost,900) gewesen.

10.2.5.2.3. Den Server im Dienstverzeichnis registrieren CORBA

Die Registrierung des Servers im Verzeichnis erfolgt mit den folgenden Schritten:

    // Der Dienst wird in das Dienstverzeichnis aufgenommen
    // Er wird srvEcho heißen
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
    // Der Server wird erstellt und dem Dienst srvEcho zugeordnet
    srvEcho serveurEcho=new srvEcho();
          ncRef.rebind(path,serveurEcho);
            orb.connect(serveurEcho);

Der erste Teil des Codes dient der Vorbereitung des Dienstnamens. Dieser Name wird im Code durch die Variable „path“ symbolisiert. Der Name eines Dienstes besteht aus mehreren Komponenten:

  • eine Ausgangskomponente objRef, ein generisches Objekt, das auf einen Typ NamingContext reduziert werden muss, in diesem Fall ncRef.
  • den Namen des Dienstes, hier nomService, der als Parameter an den Server übergeben wurde

Diese Bestandteile des Namens (NameComponent) werden in einem Array zusammengefasst, hier path. Dieses Array „benennt“ den erstellten Dienst eindeutig. Sobald der Name erstellt ist, muss er noch

  • ihn einer Instanz des Servers zuzuordnen (die zuvor erstellte Klasse srvEcho)

srvEcho serveurEcho=new srvEcho();
  • und im Verzeichnis zu speichern
    ncRef.rebind(path,serveurEcho);
    orb.connect(serveurEcho);

10.2.5.3. Kompilierung der Server-Startklasse

Die vorherige Klasse wird kompiliert:

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. Kundeneintrag

10.2.6.1. Der Code

Wir schreiben einen Client, um unseren Echo-Dienst zu testen. Wir übergeben dem Client dieselben drei Parameter wie dem Server:

Rechner: Rechner, auf dem sich das Dienstverzeichnis CORBA befindet

Port: Port, über den dieses Verzeichnis läuft

nomService: Name des Echo-Dienstes

Der Client stellt eine Verbindung zum Echo-Dienst her und fordert den Benutzer anschließend auf, Nachrichten über die Tastatur einzugeben. Diese werden an den Echo-Server gesendet, der sie zurücksendet. Der Verlauf dieses Dialogs wird auf dem Bildschirm angezeigt.

Der Echo-Dienst-Client CORBA ähnelt stark dem bereits geschriebenen Client RMI. Auch hier muss sich der Client mit einem Verzeichnisdienst verbinden, um eine Referenz auf das Serverobjekt zu erhalten, mit dem er sich verbinden möchte. Der Unterschied zwischen den beiden Clients liegt genau darin und nur darin. Hier ist der Code des Echo-Clients CORBA:


    

10.2.6.2. Die Verbindung des Clients zum Server

Der oben genannte Client CORBA stellt die Verbindung zum Server mit folgendem Befehl her:

        // Man stellt die Verbindung zum Echo-Server her
        iSrvEcho serveurEcho=getServeurEcho(machine,port,nomService);

Nach Abschluss dieses Vorgangs verfügt der Client über eine Referenz auf den Echo-Server. Anschließend unterscheidet sich ein Client CORBA nicht von einem Client RMI. Die private Methode, die die Verbindung zum Server herstellt, lautet wie folgt:

// importierte Pakete
import java.io.*;
import echo.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;

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

    public static void main(String arg[]){
         // Syntax: cltEcho machineAnnuaire portAnnuaire nameservice
         // Rechner: Rechner, auf dem das Dienstverzeichnis läuft CORBA
         // Port: Port, auf dem das Dienstverzeichnis läuft
         // nomService: Name des Echo-Dienstes

         // Überprüfung der Argumente
        if(arg.length!=3){
            System.err.println("Syntaxe : pg machineAnnuaire portAnnuaire nomservice");
            System.exit(1);
        }

         // Die Parameter werden abgerufen
        String machine=arg[0];
        String port=arg[1];
        String nomService=arg[2];

         // Verbindung zum Echo-Server herstellen
        iSrvEcho serveurEcho=getServeurEcho(machine,port,nomService);

         // Client-Server-Dialog
        BufferedReader in=null;
        String msg=null;
        String reponse=null;
        iSrvEcho serveur=null;

        try{
             // Tastatur-Stream öffnen
            in=new BufferedReader(new InputStreamReader(System.in));
             // Schleife zum Einlesen der an den Echo-Server zu sendenden Nachrichten
            System.out.print("Message : ");
            msg=in.readLine().toLowerCase().trim();
            while(! msg.equals("fin")){
                 // Senden der Nachricht an den Server und Empfang der Antwort
                reponse=serveurEcho.echo(msg);
                 // Weiterverfolgung
                System.out.println("Réponse serveur : " + reponse);
                 // nächste Nachricht
                System.out.print("Message : ");                
                msg=in.readLine().toLowerCase().trim();
            }// while
             // Fertig
            System.exit(0);
         // Fehlerbehandlung         
        } catch (Exception e){
            System.err.println("Erreur : " + e);
            System.exit(2);
        }// try
    }// main

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

         // Anfrage einer Referenz vom Echo-Server
         // Sendungsverfolgung
        System.out.println("--> Connexion au serveur CORBA en cours...");
         // die Referenz des Echo-Servers
        iSrvEcho serveurEcho=null;
        try{
             // Es wird ein Objekt CORBA für die Bearbeitung angefordert
            String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};    
          ORB orb=ORB.init(initORB,null);
             // Der Verzeichnisdienst wird verwendet, um den Echo-Server zu lokalisieren
          org.omg.CORBA.Object objRef=
                orb.resolve_initial_references("NameService");
          NamingContext ncRef=NamingContextHelper.narrow(objRef);
             // Der gesuchte Dienst heißt srvEcho – er wird angefordert
          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
         // Die Referenz zum Server wird zurückgegeben
        return serveurEcho;
    }// getServeurEcho

}// Klasse

Es handelt sich um dieselben Code-Sequenzen wie auf dem Server:

  • Wir erstellen ein Objekt ORB, mit dem wir das Verzeichnis der Dienste CORBA ansprechen können
String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};    
      ORB orb=ORB.init(initORB,null);
  • Wir definieren die verschiedenen Bestandteile des Namens des Echo-Dienstes
        org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
  • Wir fordern vom Verzeichnisdienst eine Referenz des Echo-Dienstes an (hier unterscheiden wir uns vom Server)
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. Tests

10.2.7.1. Starten des Verzeichnisdienstes

Auf einem Windows-Rechner starten wir den Verzeichnisdienst wie folgt:

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

Dadurch wird der Verzeichnisdienst auf Port 1000 des Rechners gestartet.

Der Verzeichnisdienst „tnameserv“ erzeugt eine Bildschirmausgabe, die in etwa wie folgt aussieht:

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

Das ist zwar schwer lesbar, aber wir behalten die letzte Zeile im Hinterkopf: Der Dienst läuft auf Port 1000.

10.2.7.2. Start des Echo-Servers

Der Echo-Dienst wird mit drei Parametern gestartet:


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

Der Server gibt Folgendes aus:

    Serveur d’écho prêt

10.2.7.3. Starten des Clients auf demselben Rechner wie der Server


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. Start des Clients auf einem Windows-Rechner, der nicht der Server ist

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. Beispiel 2: Ein Server SQL

10.3.1. Einleitung

Wir greifen hier die Beschreibung des Servers SQL wieder auf, der bereits im Zusammenhang mit Java RMI behandelt wurde, um erneut die Gemeinsamkeiten und Unterschiede zwischen den beiden Methoden hervorzuheben. Zur Erinnerung: Die Aufgabe dieses Servers SQL besteht darin, auf einem Windows-Rechner zu laufen und es Remote-Clients zu ermöglichen, die öffentlichen Datenbanken ODBC dieses Windows-Rechners zu nutzen.

Image

Der Client CORBA könnte drei Vorgänge ausführen:

  • eine Verbindung zu einer Datenbank seiner Wahl herstellen
  • SQL-Abfragen senden
  • die Verbindung schließen

Der Server führt die Abfragen SQL des Clients aus und sendet ihm die Ergebnisse zurück. Das ist seine wesentliche Aufgabe, weshalb wir ihn als SQL-Server bezeichnen. Wir wenden die verschiedenen zuvor beim Echo-Server behandelten Schritte an.

10.3.2. Implementierung der Server-Schnittstelle IDL

Zur Erinnerung: Hier ist die Schnittstelle RMI, die wir für den Server verwendet hatten:

import java.rmi.*;

// die Remote-Schnittstelle
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;
}

Die verschiedenen Methoden hatten folgende Funktionen:

Connect: Der Client stellt eine Verbindung zu einer entfernten Datenbank her, wobei er den Treiber, die URL JDBC sowie seine ID und sein Passwort angibt, um auf diese Datenbank zuzugreifen. Der Server gibt ihm eine Zeichenkette zurück, die das Ergebnis der Verbindung angibt:

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

executeSQL: Der Client fordert die Ausführung einer Abfrage SQL in der Datenbank an, mit der er verbunden ist. Er gibt das Trennzeichen an, das die Felder in den zurückgegebenen Ergebnissen trennen soll. Der Server gibt ein Array von Zeichenfolgen zurück:

    100 n

bei einer Abfrage zur Aktualisierung der Datenbank, wobei n die Anzahl der aktualisierten Zeilen ist

    500 msg d’erreur

, wenn die Abfrage einen Fehler verursacht hat

    501 Pas de résultats

wenn die Abfrage kein Ergebnis geliefert hat

    101 ligne1
    101 ligne2
    101 ...

wenn die Abfrage Ergebnisse geliefert hat. Die vom Server zurückgegebenen Zeilen sind die Ergebniszeilen der Abfrage.

Close: Der Client schließt seine Verbindung zur entfernten Datenbank. Der Server gibt eine Zeichenkette zurück, die das Ergebnis dieses Schließvorgangs angibt:

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

Die Schnittstelle IDL des Servers sieht wie folgt aus:

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();
    };// Schnittstelle
};// Modul

Die einzige Neuerung gegenüber der Schnittstelle IDL des Echo-Servers ist die Verwendung des Schlüsselworts „sequence“. Mit diesem Schlüsselwort lässt sich ein eindimensionales Array definieren. Die Definition erfolgt in zwei Schritten:

  • Definition eines Typs zur Bezeichnung des Arrays, hier „resultats“:
typedef sequence<string> resultats;

Das Schlüsselwort typedef ist C/C++-Programmierern wohlbekannt: Es ermöglicht die Definition eines neuen Typs. Hier wird der Typ resultats als Äquivalent zum Typ sequence<string>, c.a.d, definiert – ein dynamisches (nicht dimensioniertes) Array von Zeichenketten.

  • Verwendung des neuen Typs dort, wo er benötigt wird
resultats executeSQL(in string requete, in string separateur);

Die Methode executeSQL gibt somit ein Array von Zeichenfolgen zurück.

10.3.3. Kompilierung der Server-Schnittstelle IDL

Die vorherige Schnittstelle IDL wird in die Datei srvSQL.idl übernommen. Diese Datei wird kompiliert:

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

Es ist festzustellen, dass durch die Kompilierung ein Verzeichnis mit dem Namen des Schnittstellenmoduls IDL (srvSQL) erstellt wurde. Sehen wir uns den Inhalt dieses Verzeichnisses an:

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

Zur Erinnerung: Die Dateien Helper und Holder sind Klassen, die mit den Ein- und Ausgabeparametern sowie den Ergebnissen der Methoden der Remote-Schnittstelle in Verbindung stehen. Das Verzeichnis srvSQL enthält alle .java-Dateien, die mit der in der .idl-Datei definierten Schnittstelle interSQL zusammenhängen. Es enthält außerdem Dateien, die mit dem Typ resultats zusammenhängen, der in der Schnittstelle IDL definiert wurde.

Die Datei interSQL.java ist die Java-Datei der Schnittstelle unseres Servers. Es ist wichtig zu überprüfen, ob das automatisch Erzeugte unseren Erwartungen entspricht. Die generierte Datei interSQL.java sieht wie folgt aus:

/*  * Datei: ./SRVSQL/INTERSQL.JAVA  * Von: SRVSQL.IDL  * Datum: Fr, 19. März 09:59:48 1999  *   Von: D:\JAVAIDL\IDLTOJ~1.EXE Java IDL 1.2 18. Aug. 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()
;
}

Wir stellen fest, dass wir dieselbe Benutzeroberfläche haben wie die, die für den Client-Server RMI verwendet wird. Wir können also fortfahren. Kompilieren wir alle diese .java-Dateien:

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. Schreiben des Servers SQL

Wir schreiben nun den Code für den Server SQL. Zur Erinnerung: Diese Klasse muss von der abstrakten Klasse _nomInterfaceImplBase abgeleitet sein, die durch die Kompilierung der Datei IDL erzeugt wurde. Abgesehen von dieser Besonderheit und mit Ausnahme der Codeabschnitte, die sich auf die Registrierung des Dienstes in einem Verzeichnis beziehen, ist der Code des Servers CORBA identisch mit dem des Servers RMI:

// importierte Pakete
import java.sql.*;
import java.util.*;
import srvSQL.*;

// Klasse SQLServant
public class SQLServant extends _interSQLImplBase{

    // globale Daten der Klasse
    private Connection DB;

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

        // Verbindung zur Datenbank-URL über den Treiber
        // Authentifizierung mit ID und Passwort

        String resultat=null;            // Ergebnis der Methode
        try{
            // Laden des Treibers
            Class.forName(pilote);
            // Verbindungsanfrage
            DB=DriverManager.getConnection(url,id,mdp);
            // OK
            resultat="200 Connexion réussie";
        } catch (Exception e){
            // Fehler
            resultat="500 Echec de la connexion (" + e + ")";
        }
        // Ende
        return resultat;
    }            

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

        // führt eine Abfrage SQL in der Datenbank DB
        // und speichert die Ergebnisse in einem String-Array

        // Daten, die für die Ausführung der Abfrage erforderlich sind
        Statement S=null;
        ResultSet RS=null;
        String[] lignes=null;
        Vector resultats=new Vector();
        String ligne=null;

        try{
            // Erstellung des Abfragecontainers
            S=DB.createStatement();
            // Abfrage ausführen
            if (! S.execute(requete)){
                // Aktualisierungsabfrage
                // Die Anzahl der aktualisierten Zeilen wird zurückgegeben
                lignes=new String[1];
                lignes[0]="100 "+S.getUpdateCount();
                return lignes;
            }
            // Es handelte sich um eine Abfrage
            // Die Ergebnisse werden abgerufen
            RS=S.getResultSet();
            // Anzahl der Felder im Resultset
            int nbChamps=RS.getMetaData().getColumnCount();
            // Auswertung der Ergebnisse
            while(RS.next()){
                // Erstellung der Ergebniszeile
                ligne="101 ";
                for (int i=1;i<nbChamps;i++)
                    ligne+=RS.getString(i)+separateur;
                ligne+=RS.getString(nbChamps);
                // Hinzufügen zum Ergebnisvektor
                resultats.addElement(ligne);
            }// while
            // Ende der Auswertung der Ergebnisse
            // Ressourcen werden freigegeben
            RS.close();
            S.close();
            // Ergebnisse werden zurückgegeben
            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){
            // Fehler
            lignes=new String[1];
            lignes[0]="500 " + e;
            return lignes;
        }// try-catch
    }// executeSQL

    // --------------- schließen
    public String close(){
        // schließt die Verbindung zur Datenbank
        String resultat=null;
        try{
            DB.close();
            resultat="200 Base fermée";
        } catch (Exception e){
            resultat="500 Erreur à la fermeture de la base ("+e+")";
        }
        // Rückgabe des Ergebnisses
        return resultat;
    }
}// Klasse SQLServant

Diese Klasse befindet sich in der Datei SQLServant.java, die wir kompilieren:

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. Erstellung des Startprogramms für den Server SQL

Die vorstehende Klasse stellt den Server SQL nach dem Start dar. Zuvor muss er in einem Verzeichnis der Dienste CORBA registriert werden. Wie beim Echo-Dienst werden wir dies mit einer speziellen Klasse tun, an die wir zur Laufzeit drei Parameter übergeben:

Rechner: Rechner, auf dem sich das Dienstverzeichnis CORBA befindet

Port: Port, über den dieses Verzeichnis läuft

nomService: Name des Dienstes SQL

Der Code dieser Klasse ist nahezu identisch mit dem der Klasse, die dasselbe für den Echo-Dienst erledigt hat. Wir haben die Zeile, die sich zwischen den beiden Klassen unterscheidet, fett hervorgehoben: Sie erstellt nicht dasselbe Serverobjekt. Man sieht also, dass wir immer noch denselben Mechanismus zum Starten des Servers haben. Wenn man diesen Mechanismus in eine Klasse auslagert, wie es hier geschehen ist, wird er für den Entwickler nahezu transparent.

// importierte Pakete
import srvSQL.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;

//----------- Klasse serveurSQL
public class serveurSQL{
    // ------- main: startet den Server SQL
  public static void main(String arg[]){
        // serveurSQL Port-Dienst der Maschine

        //– Ist die Anzahl der Argumente korrekt?
        if(arg.length!=3){
            System.err.println("Syntaxe : pg machineAnnuaireCorba portAnnuaireCorba nomService");
            System.exit(1);
        }
        // Die Argumente werden abgerufen
        String machine=arg[0];
        String port=arg[1];
        String nomService=arg[2];
        String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
    try{
            // Wir benötigen ein Objekt CORBA, um arbeiten zu können
      ORB orb=ORB.init(initORB,null);
            // Der Dienst wird in das Dienstverzeichnis aufgenommen
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
        // Der Server wird erstellt und dem Dienst zugeordnet srvSQL
        SQLServant serveurSQL=new SQLServant();
      ncRef.rebind(path,serveurSQL);
        orb.connect(serveurSQL);
        // Weiterverfolgung
        System.out.println("Serveur SQL prêt");
        // Warten auf Client-Anfragen
      java.lang.Object sync=new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
    } catch(Exception e){
            // Es ist ein Fehler aufgetreten
      System.err.println("Erreur " + e);
      e.printStackTrace(System.err);
    }
  }// Hauptprozess
}// srvSQL

Wir kompilieren diese neue Klasse:

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. Client-Schreibvorgang

Der Client des Servers CORBA wird mit folgenden Parametern aufgerufen:

    machine port nomServiceAnnuaire pilote urlBase id mdp separateur

Rechner: Rechner, auf dem sich das Verzeichnis der Dienste befindet CORBA

Port: Port, über den dieses Verzeichnis läuft

nomService: Name des Dienstes SQL

Treiber: Treiber, den der Server SQL zur Verwaltung der gewünschten Datenbank verwenden soll

urlBase: URL JDBC der zu verwaltenden Datenbank

id: ID des Kunden oder null, falls keine ID vorhanden ist

mdp: Passwort des Kunden oder null, falls kein Passwort vorhanden ist

Trennzeichen: Zeichen, das der Server SQL verwenden muss, um die Felder in den Ergebniszeilen einer Abfrage voneinander zu trennen

Hier ein Beispiel für mögliche Parameter:

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

wobei hier:

Rechner: Rechner, auf dem sich das Dienstverzeichnis CORBA befindet

Port: Port, über den dieses Verzeichnis läuft

srvSQL: srvSQL, Name CORBA des Servers SQL

Treiber: sun.jdbc.odbc.JdbcOdbcDriver, der übliche Treiber für Datenbanken mit ODBC-Schnittstelle

urlBase: jdbc:odbc:articles, zur Verwendung einer Datenbank „articles“, die in der Liste der öffentlichen Datenbanken ODBC des Windows-Rechners deklariert ist

ID: null, keine Identität

Passwort: null, kein Passwort

Trennzeichen: , die Felder der Ergebnisse werden durch ein Komma getrennt

Nach dem Start mit den oben genannten Parametern führt der Client die folgenden Schritte aus:

  • Er stellt eine Verbindung zum Rechner „machine“ über den Port „port“ her, um den Dienst CORBA srvSQL anzufordern
  • Er fordert die Verbindung zur Artikeldatenbank an
connect(‘’sun.jdbc.odbc.JdbcOdbcDriver’’, ‘‘jdbc:odbc:articles’’, ’’’’, ’’’’)
  • Er fordert den Benutzer auf, eine Abfrage SQL über die Tastatur einzugeben
  • sie wird an den Server gesendet SQL
executeSQL(requete, ’’,’’);
  • Er zeigt die vom Server zurückgegebenen Ergebnisse auf dem Bildschirm an
  • Er fordert den Benutzer erneut auf, eine Abfrage SQL über die Tastatur einzugeben. Er beendet den Vorgang, sobald die Abfrage beendet ist.

Der Java-Code des Clients folgt. Die Kommentare sollten zum Verständnis ausreichen. Man wird feststellen, dass:

  • Der Code ist identisch mit dem des bereits behandelten Clients RMI. Er unterscheidet sich davon durch den Prozess der Abfrage des Verzeichnisdienstes, der in der Methode getServeurSQL isoliert ist.
  • Die Methode getServeurSQL entspricht der für den Echo-Client geschriebenen Methode

Daraus folgt:

  • sich ein Client CORBA von einem Client RMI lediglich in der Art und Weise unterscheidet, wie er den Server kontaktiert
  • Diese Art der Kontaktaufnahme ist für alle CORBA-Clients identisch
import srvSQL.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
import java.io.*;

public class clientSQL {

    // globale Daten der Klasse
    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 Rechner Port Trennzeichen Treiber URL ID Passwort
        // Rechner, Port: Rechner und Port des zu kontaktierenden Dienstverzeichnisses CORBA
        // Dienst: Name des Dienstes
        // Treiber: Zu verwendender Treiber für die zu nutzende Datenbank
        // urlBase: JDBC-URL der zu verwendenden Datenbank
        // ID: Benutzer-ID
        // mdp: sein Passwort
        // Trennzeichen: Zeichenfolge, die die Felder in den Ergebnissen einer Abfrage trennt

        // Überprüfung der Anzahl der Argumente
        if(arg.length!=8)
            erreur(syntaxe,1);

        // Initialisierung der Parameter für die Datenbankverbindung
        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];        

        // Parameter des Verzeichnisdienstes CORBA
        String[] initORB={"-ORBInitialHost",arg[0],"-ORBInitialPort",arg[1]};
        // Client CORBA – eine Referenz vom Server wird angefordert SQL
        interSQL serveurSQL=getServeurSQL(machine,port,service);

        // Client-Server-Dialog
        String requete=null;
        String reponse=null;
        String[] lignes=null;
        String codeErreur=null;

        try{
            // Öffnen des Tastaturstroms
            in=new BufferedReader(new InputStreamReader(System.in));
            // Nachverfolgung
            System.out.println("--> Connexion à la base de données en cours");
            // erster Verbindungsversuch zur Datenbank
            reponse=serveurSQL.connect(pilote,urlBase,id,mdp);
            // Weiterverfolgung
            System.out.println("<-- "+reponse);
            // Antwortanalyse
            codeErreur=reponse.substring(0,3);
            if(codeErreur.equals("500")) 
                erreur("Abandon sur erreur de connexion à la base",3);
            // Schleife zum Einlesen der an den Server SQL zu sendenden Anfragen
            System.out.print("--> Requête : ");
            requete=in.readLine().toLowerCase().trim();
            while(! requete.equals("fin")){
                // Senden der Anfrage an den Server und Empfang der Antwort
                lignes=serveurSQL.executeSQL(requete,separateur);
                // Weiterverfolgung
                afficheLignes(lignes);
                // nächste Anfrage
                System.out.print("--> Requête : ");                
                requete=in.readLine().toLowerCase().trim();
            }// while
            // Weiterverfolgung
            System.out.println("--> Fermeture de la connexion à la base de données distante");
            // Verbindung wird beendet
            reponse=serveurSQL.close();
            // Weiter
            System.out.println("<-- " + reponse);
            // Ende
            System.exit(0);
        // Fehlerbehandlung        
        } catch (Exception e){
            erreur("Abandon sur erreur : " + e,2);
        }// try
    }// main

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

    // ------------ Fehler
    private static void erreur(String msg, int exitCode){
        // Anzeige einer Fehlermeldung
        System.err.println(msg);
        // Mögliche Freigabe der Ressourcen
        try{
            in.close();
            serveurSQL.close();
        } catch(Exception e){}
        // Beenden
        System.exit(exitCode);
    }// Fehler

    // ---------------------- getServeurSQL
    private static interSQL getServeurSQL(String machine, String port, String service){
        // Anfrage einer Referenz vom Server SQL
        // Rechner: Rechner aus dem Dienstverzeichnis CORBA
        // Port: Port des Dienstverzeichnisses CORBA
        // Dienst: Name des anzufragenden Dienstes CORBA

        // Verfolgung
        System.out.println("--> Connexion au serveur CORBA en cours...");
        // Server-ID SQL
        interSQL serveurSQL=null;
        // Parameter des Verzeichnisdienstes CORBA
        String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
                try{
            // Ein Objekt CORBA wird für die Arbeit angefordert – dazu wird der Port 
            // des Dienstverzeichnisses CORBA
      ORB orb=ORB.init(initORB,null);
            // Man nutzt das Verzeichnis, um den Server SQL zu finden
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
            // Der gesuchte Dienst heißt srvSQL – er wird angefordert
      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
        // Die Referenz zum Server wird zurückgegeben
        return serveurSQL;
    }// getServeurSQL

}// Klasse

Kompilieren wir die Client-Klasse:

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

Wir sind bereit für die Tests.

10.3.7. Tests

10.3.7.1. Pré-requis

Es wird angenommen, dass eine Datenbank namens „Articles“ (ACCESS) auf dem Windows-Server (SQL) öffentlich verfügbar ist:

Image

Diese Datenbank hat folgende Struktur:

Name
Typ
code
4-stelliger Artikelcode
nom
Name (Zeichenkette)
prix
Preis (tatsächlich)
stock_actu
sein aktueller Lagerbestand (Ganzzahl)
stock_mini
der Mindestbestand (ganze Zahl), bei Unterschreitung dessen der Artikel nachbestellt werden muss

10.3.7.2. Start des Verzeichnisdienstes

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. Start des Servers SQL

Der SQL-Server wird gestartet:

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

In einem Fenster wird Folgendes angezeigt: DOS:

    Serveur SQL prêt

10.3.7.4. Starten eines Clients auf demselben Rechner wie der Server

Hier sind die Ergebnisse, die mit einem Client auf demselben Rechner wie der Server erzielt wurden:

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
--> Abfrage: 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
--> Abfrage: delete from articles where stock_mini<7
<-- 100 1
--> Abfrage: 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. Starten eines Clients auf einem anderen Rechner als dem Server

Hier sind die Ergebnisse, die mit einem Client auf einem anderen Rechner als dem Server erzielt wurden:

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
--> Abfrage: 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. Zuordnungen IDL – JAVA

Hier geben wir die Zuordnungen zwischen den einfachen Typen IDL und JAVA an:

Typ IDL
Java-Typ
boolean
boolean
char
Zeichen
wchar
char
Byte
Byte
Zeichenkette
java.lang.String
wstring
java.lang.String
short
short
unsigned short
short
long
int
unsigned long
int
long long
long
unsigned long long
long
float
float
double
double

Zur Erinnerung: Um ein Array mit Elementen vom Typ T in der Schnittstelle IDL zu definieren, verwendet man die Anweisung:

    typedef   sequence<T> nomType;

und dass anschließend nomType verwendet wird, um auf den Typ des Arrays zu verweisen. So wurde in der Server-Schnittstelle SQL folgende Deklaration verwendet:

    typedef sequence<string> resultats;

verwendet, damit resultats in Java auf das Array String[] verweist.