Projektstudium SS98 - distributed computing


vorherige SeiteInhaltnächste Seite

Ein Client-Server Beispiel eines Whiteboard mit CORBA

CORBA unterstützt eine Menge von flexieblen Möglichkeiten für verteilte Anwendungen. In diesen Beispiel wird CORBA verwendet, um Java Objekte zwischen einen Applet whiteboard Client und einen Java Server zu transüportieren. Im Folgenden werden wir alle grundlegenden Schritte durchführen, die nötig sind CORBA als eine integrierte Technologie für verteilte Anwendungen zu nutzen.

Wo nach wir suchen, ist eine direkte Objekt-zu-Objekt Kommunikation, die es Objekten erlaubt die Methoden von anderen direkt aufzurufen. RMI unterstützt dies, und verbirgt die Mechanismen vor den Entwicklern. Aber was ist, wenn wir den Server in C++ implementieren wollen, oder in einer anderen nicht Javasprache. Wir währen ohne Glück, da RMI nur JVM-zu-JVM Kommunikation unterstützt. Das läßt uns allein mit einer Alternative: CORBA.

CORBA bietet einen generellen Weg Objekte zusammen zu bringen, auch wenn sie in verschiedenen Sprachen und / oder physischen System implementiert sind. In diesen Beispiel werden wir nur die Oberfläche von CORBA ankratzen. Dies wird uns am Ende ermöglichen CORBA für verteilte Anwendungen mit Java zu nutzen.

Auf die Architektur von CORBA wird hier nicht eingegangen. Es sei auf die vorheriegen Abschnitte verwiesen.

CORBA Entwicklungsprozeß
Hier wird der generelle Prozeß beschieben, der bei der Erstellung einer CORBA / Java Anwendung zu vollziehen ist:

  1. Installiere einen ORB auf dem Web-Server. Diese Installation wird vom CORBA-Server genutzt. Mach dessen Klassen erreichbar unterhalb des Netzbaumes, so daß der Client sie laden kann. Dies ist nicht nötig, wenn man plant den in Netscape eingebauten Visigenic ORB zu nutzen (was zu empfehlen ist, wenn für alle Zugriffe der Clients Netscape genutzt wird.
  2. Definiere die Serverschnittstellen für die Anwendung in einer IDL-Datei .
  3. Compilire die Schnittstelle für das Serverobjekt von IDL zu (in diesen Fall) Java unter der Verwendung von dem idl2java Compiler. Dieses Tool generiert eine Menge von Hilfsklassen. Diese beinhalten die Javaschnittstellen selbst.
  4. Schreib die Serverklassen -- als Beispeil, CorbaListServerImpl. Der Server beerbt die Klasse _server interface nameImplBase. Er implementiert die exacten  Methodensignaturen, die in den Java Server Schnittstellenklassen zu finden sind, die von idl2java aus der IDL Definition generiert wurden. In unseren Fall, wird die Schnittstellenklasse CorbaListServer genannt.
  5. Der Server wird zudem eine eigene main () Method besitzen, so daß er sich selbst in die Exportieren kann, wenn er gestartet wird. Im Generellen muß das Serverobjekt keine eigenen main () method besitzen und sich selbst Exportieren. Das Objekt kann auch von einen anderen Objekt Exportiert  werden, wenn dieses dafür einen Thread bereit stellt.

  6. Schreibe die Clientklassen. Der Client sollte sich beim lokalen ORB registrieren und eine Referenze zu anderen verteilten Objekten erzeugen. Er kann dann diese Referenz nutzen, um Aufrufe direkt an das remote server object zu richten.
  7. Compiliere den gesamten Code.
  8. Starten des "listening" Service von der Serverseite. Visibroker's osagent und gatekeeper sind die relevanten  Services für unseren Zweck. Osagent's standard Port ist TCP 14000 und gatekeeper's ist TCP 15000, so wenn mann Zugriff durch eine Firewall hindurch haben will, Löcher in diese an diesen Ports stoßen muß.
  9. Wenn der Client ein Applet ist, dann erweieter den HTMLtag um die benötigten Parameter.
  10. Start das Serverobjekt mit java CorbaListServerImpl. Der Client kann nun Verbindungen zu den Server herstellen!

Nun ist es an der Zeit die Theorie in die Praxis umzusetzen und diese Schritte bei unserem Beispiel durchzuführen.

Installation des ORBs
Das erste, was man machen muß, wenn man mit CORBA arbeiten will, ist es einen ORB zu installieren. In diesen Beispiel werden wir den Visibroker benutzen, weil dieser ab Netscape 4.0 in diesen eingebettet ist.

Nach dem VisiBroker installiert ist, ist sicher zu stellen, daß JDK1.1.3 oder höher verwendet wird und das bin Verzeichnis im PATH und im CLASSPATH eingetragen wurde. Der ClassPath muß die Visibroker die Visigenic classes beinhalten, welche bei Windowssystemen normalerwiese unter c:\visigenic\vbroker\lib\ zu finden sind. Es ist für jede einzelne JAR Datei ein extra Eintrag zu versehen. Bei der Benutzung von dem Visigenic Java Compiler (vjc) ist das Eintragen der Klassen nicht nötig.

Nun sind wird soweit die Schnittstellen des Servers zu definieren:
 

  CORBA Whiteboard Systemarchitektur

Definieren der IDL-Schnittstellen
IDL is keine Programmiersprache, es spezifiziert die Schnittstellen für CORBA Objekte.

Der folgende Abschnitt befaßt sich mit der Definition der Serverschnittstellen.

module corbawb {



  typedef sequence SerializedObject;



  interface CorbaListServer {

    SerializedObject addElement (in SerializedObject element);

    SerializedObject replaceElement (in SerializedObject id, in serializedObject element);

    SerializedObject getUpdate (in long updateCount);

  };



};

Das erste, was dieses IDL Datei macht, ist das Definieren des CORBA Modul, welches dem Java Packet entspricht. Die Javaversion der Schnittstellendefinitionen des Moduls und allen mit idl2java generierten Klassen werden in das PAcket corbawb gespeichert.

Der Server
Der Server besteht auf der Java-Serverschnittstelle erzeugt  aus IDL und eine Serverimplementation. Die Serverimplementation wird erweitert durch eine Skeletonklasse und einide andere Helfer, die vom IDL Compiler erzeugt wurden. Die Skeletonklasse weiß, wie die Netzwerkfunktionnen von CORBA umzusetzen sind.

Class CorbaListServer
Dies ist der von idl2java erstellte Code, und ist die Schnittstelle die implement werden muß (CorbaListServerImpl)

package corbawb;

public interface CorbaListServer extends org.omg.CORBA.Object {

  public byte[] addElement (byte[] element);

  public byte[] replaceElement (byte[] id, byte[] element);

  public byte[] getUpdate (int updateCount);

}

Class CorbaListServerImpl
Hier sehen wir die Iimporte für den ORB und die BOA Klassen, welche zusammen den Prozeß verwalten, um den Server für Clients erreichbar zu machen.

 

package corbawb;



import org.omg.CORBA.ORB;

import org.omg.CORBA.BOA;

import org.merlin.step.dec.IDList;



public class CorbaListServerImpl extends _CorbaListServerImplBase {

Es ist extrem wichtig zu errinnern, das unsere Serverimplementation von _CorbaListServerImplBase abgeleitet ist, welche vom IDL Compiler erzeugt wird. _CorbaListServerImplBase selbst ist deklariert um die CorbaListServer Schnittstelle zu implementieren, was bedeutet, daß wir die Implementation von diesen Methods in CorbaListServerImpl bereitstellen müssen.

 

  protected IDList list = new IDList ();



  public CorbaListServerImpl (String name) {

    super (name);

  }



  // CorbaListServer Schnittstellenmethoden Implementation

  public byte [] addElement (byte [] elementBytes) {

    Object element = CorbaWBUtil.deserialize (elementBytes);

    return CorbaWBUtil.serialize (list.addElement (element));

  }

  

  public byte [] replaceElement (byte [] idBytes, byte [] elementBytes) {

    Object id = CorbaWBUtil.deserialize (idBytes);

    Object element = CorbaWBUtil.deserialize (elementBytes);

    return CorbaWBUtil.serialize (list.replaceElement (id, element));

  }

  

  public byte [] getUpdate (int updateCount) {

    return CorbaWBUtil.serialize ((list.getUpdateCount () == updateCount) ? null : list.clone ());

  }

Hie sehen wir Details vom Konstruktor und CorbaListServer Schnittstellen Method Implementation. Es ist zu beachten, daß wir byte arrays benutzen. Dies ist die  Schnittstelle, die der IDL Compiler erzeugt hat.

Jede Benutzung eines Objekts ist ein einen Methodenaufruf von CorbaWBUtil eingepackt, welches das Objekt in einen byte array umsetzt oder ein byte array wieder zu ein Objekt zurücksetzt wenn nötig.

  

  public static void main (String[] args) {

    ORB orb = ORB.init (args, null);

    BOA boa = orb.BOA_init ();

    boa.obj_is_ready (new CorbaListServerImpl ("CorbaListServer"));

    System.out.println ("CorbaListServerImpl is ready.");

    boa.impl_is_ready ();

  }

}

Die main () Methode  unterstüzt den Code, der das Serverobjekt für Clients erreichbar macht. Der Aufruf ist :boa.impl_is_ready ().

Der Client
Das Whiteboard benutzt eine verteilte Datenstrukture ObservableList um Elemente abzuspeichern. Verteilte Iimplementationen die Sockets und RMI benutzen, beerben einfach  ObservableList und Überschreiben die Methoden um die Art des Networking zu wechseln. In beiden Fällen unterstützt der Server eine zentrale IDList um die Übersicht über alle geteilten Elemente zu behalten.

Wird weden ObservableList beerben um eine CORBA-basierte Implementation zu unterstützen, die mit den zuvor entwickelten Server arbeitet.

Class CorbaList

 

package corbawb;



import java.io.*;

import java.util.*;

import java.net.*;

import java.applet.*;



import org.merlin.step.dec.IDList;

import org.merlin.step.dec.ObservableList;



public class CorbaList extends ObservableList implements Runnable {

  

  public static final int UPDATE_DELAY = 10 * 1000;

  Applet parent;

  IDList list;

  Thread processor;

  CorbaListAdapter adapter;



  public CorbaList (Applet parent) {

    list = new IDList ();

    adapter = new CorbaListAdapter (parent);

    update ();

    processor = new Thread (this);

    processor.start ();

  }

Dieser Code beerbt ObservableList und kreiert einen Thread um Updateaufrufe im Hintergrund durchzuführen. Er instanziet zudem eine Kopie vom CorbaListAdapter, welche der aktuelle CORBA Client ist. CorbaList macht Aufrufe zu dem Adapter, welcher diese in CORBA Format übersetzt und den CORBA Server aufruft.

Die Adapter Klasse erlaubt uns CORBA's byte arrays zu benutzen um Objekte zu transferieren. Die Adapter Klass arbeitet mit den Machanismus der byte arrays und gibt ein reguläres Objekts zu CorbaList zurück.

  public Enumeration elements () {

    return list.elements ();

  }



  public synchronized void addElement (Object element) {

    Object id = adapter.addElement (element);

    list.addElementWithID (id, element);

  }



  public synchronized void replaceElementAtEnd (Object oldElement, Object element) {

    Object oldID = list.getIDOfElement (oldElement);

    if (oldID != null) {

      Object id = adapter.replaceElement (oldID, element);

      if (id != null)

        list.replaceElementWithID (oldID, id, element);

      else

        System.out.println ("Unknown id:" + oldID);

    }

  }



  protected synchronized void update () {

    IDList newList = adapter.getUpdate (list.getUpdateCount ());

    if (newList != null) {

      list = newList;

      fireUpdate ();

    }

  }

Diese Methods spezialisieren ObservableList für die Benutzung als eine verteilte Datenstruktur. Beachten, daß wir adapter's Methods aufrufen um auf den Server zuzugreifen. Alle Methoden sind synchronized um eine fehlerhafte Liste zu vermeiden.

  public void run () {

    while (processor == Thread.currentThread ()) {

      try {

        Thread.sleep (UPDATE_DELAY);

        update ();

      }

      catch (Exception ignored) {

        ignored.printStackTrace ();

      }


    }

  }



  public void stop () {

    processor = null;

  }

}

Dieser Abschnitt zeigt den Update-Thread, welcher den Server für periodische Updates pollt.

Class CORBAListAdapter
Der CorbaListAdapter it der aktuelle CORBA Client. Sein constructor initialisiert die Clientseite des ORBs.

package corbawb;



import java.applet.*;



import org.omg.CORBA.SystemException;

import org.omg.CORBA.ORB;

import org.merlin.step.dec.IDList;



public class CorbaListAdapter {

  

  CorbaListServer server;



  public CorbaListAdapter (Applet parent) {

    ORB orb = ORB.init (parent, null);

    server = CorbaListServerHelper.bind (orb, "CorbaListServer");

    System.out.println ("Client ORB initialized.");

  }

Beachte den Aufruf von CorbaListServerHelper.bind (). Diese Klasse wurde durch idl2java erstellt.

  public Object addElement (Object element) {

    try {

      byte [] elementBytes = CorbaWBUtil.serialize (element);

      byte [] idBytes = server.addElement (elementBytes);

      return CorbaWBUtil.deserialize (idBytes);

    } catch (SystemException ignored) {

      ignored.printStackTrace ();

      return null;

    }

  }



  public Object replaceElement (Object oldID, Object element) {

    try {

      byte [] oldIDbytes = CorbaWBUtil.serialize (oldID);

      byte [] elementBytes = CorbaWBUtil.serialize (element);

      byte [] newIDbytes = server.replaceElement (oldIDbytes, elementBytes);

      return CorbaWBUtil.deserialize (newIDbytes);

    } catch (SystemException ignored) {

      ignored.printStackTrace ();

      return null;

    }

  }



  public IDList getUpdate (int updateCount) {

    try {

      byte [] newIDListBytes = server.getUpdate (updateCount);

      Object newIDList = CorbaWBUtil.deserialize (newIDListBytes);

      return (newIDList != null) ? (IDList) newIDList : null;      

    } catch (SystemException ignored) {

      ignored.printStackTrace ();

      return null;

    }

  }

}

Es existieren Einpackmethoden die Javaobjekte mit der CORBA Datastruktur verbinden. Diesee Methods benutzen die CorbaWBUtil Methods, um Objete in byte arrays umzuwandeln und umgekehrt.

Der HTML applet tag

<html><head><title>CORBA-based Whiteboard</title></head>

<body bgcolor="#ffffff">

<applet code="corbawb.CorbaWBLauncher" width=96 height=96>

<param name=org.omg.CORBA.ORBClass value=com.visigenic.vbroker.orb.ORB>

<PARAM name=ORBgatekeeperIOR value="http://yourserver:15000/gatekeeper.ior">

<PARAM name=USE_ORB_LOCATOR value=true>

You gotta be Java.

</applet>

</body></html>

Dies ist der Applet Tag der uns ermöglicht den Client in einen Webbrowser zu nutzen. Die meisten der Parameters sind Visigenic-specifische Anweisungen. <yourserver> sollte durch den Namen der Maschiene ersetzt werden, die vom CORBA/Web Server gebrach macht.

Class CorbaWBUtil
Diese Klasse beinhaltet die statischen  Methoden zum umwandeln der Objekte in byte arrays und zurück. Diese Methoden werden vom Client und vom Server benutzt.

package corbawb;



import java.io.*;



public class CorbaWBUtil {



  public static Object deserialize (byte b []) {

    try {

      ByteArrayInputStream bais = new ByteArrayInputStream (b);

      ObjectInputStream ois = new ObjectInputStream (bais);

      return ois.readObject ();

    } catch (IOException ex) {

      ex.printStackTrace ();

      return null;

    } catch (ClassNotFoundException ex) {

      ex.printStackTrace ();

      return null;

    }

  }

Die deserialize () Methode gibt ein Objekt, erstellt aus ein array of bytes zurück. Die Absicht dieser Methode ist, die Adaption des CorbaList Objektes zu den aktuellen Daten, die mittels CORBA gesendet wurden.

  public static byte [] serialize (Object o) {

    try {

      ByteArrayOutputStream baos = new ByteArrayOutputStream ();

      ObjectOutputStream oos = new ObjectOutputStream (baos);

      oos.writeObject (o);

      oos.flush ();

      return baos.toByteArray ();

    } catch (IOException ex) {

      ex.printStackTrace ();

      return null;

    }

  }

}

Die serialize () method gibt ein byte array zurück, welcher aus einen Objekt erstellt wurde. Wie die deserialize () Methode, wird serialize () benutzt um CorbaList Objekte mit den aktuell mittels CORBA veschickten Daten zu verbinden.

Fazit
CORBA ist eine nützliche Alternative zu RMI, sockets, und servlets, wenn die Integration von Client/Servern verschiedener Platformen nötig ist. Zudem wird CORBA kommerziell relevant durch seine Skalierbarkeit weite Unterstützung. Man sollte es für Projekte nutzen, wenn seine Stärken wirklich genutzt werden.

vorherige SeiteInhaltnach obennächste Seite

 
© Copyright 1998 André Möller, Oliver Mentz & Magnus Wiencke