Anwendungsbeispiel

Spiel-Client

Der Spiel-Client kann fast vollständig aus Kapitel 7 übernommen werden. Wurde allerdings festgestellt, dass ein Spiel gewonnen oder verloren ist, so muss der Spiel-Client (Klasse GUISVUser) den RMI-Client aufrufen. Hierzu wurde eine Variable gewonnen und eine innere Thread-Klasse StartRMIClient entwickelt. Diese Klasse ruft dann den RMI-Client server2.Klient2 auf. Zunächst müssen die Variablendefinitionen wie folgt verändert werden:

code 

// Daten für RMI-Client
public boolean gewonnen = false;
StartRMIClient srmi;
public String[]  mainString0 = {"localhost","1234","0"};

// Spiel ist verloren
public String[]  mainString1 = {"localhost","1234","1"};

// Spiel ist gewonnen
Thread myThread;

Die Methode actionPerformed muss nun um den Aufruf des RMI-Clients erweitert werden. Die veränderte Implementierung ist im Folgenden angegeben.

code 

    public void actionPerformed(ActionEvent e) {

      Daten daten;
      Point p = null;
      daten = new Daten();
      if (val == 0){ //Schiffe setzen

        spieler.initialisieren();
        computer.initialisieren();
        try {

          netz = new SVClient((GUISVUser) c);

        } catch (IOException ioe){

          ausgabeFenster("Probleme beim Netzaufbau");
          return;

        }

        if (netz.SVSocket == null)

          return;

        SchiffFenster fenster = new SchiffFenster(spieler, c);

        fenster.setTitle("Schiffe setzen");
        fenster.pack();fenster.setVisible(true);
        fenster.setSize(600,350);
        shoot.setEnabled(true);
        repaint();

Die oben angegebenen Code-Zeilen sind noch identisch mit der bereits vorgestellten Implementierung. Nun muss aber der RMI-Thread gestoppt werden, falls dieser noch läuft:

code 

        // Thread der Datenbank mittels RMI-Client stoppen
        if (myThread != null){

          myThread.stop();
          myThread = null;

        }

Die nun folgenden Teile sind wiederum identisch mit der bisherigen Implementierung.

code 

        } else{

          //Schiessen
          getSchussKoordinaten();

          //Koordinaten bereits markiert?
          if (sc1.sa1.tmp.x == -1)

            return;

          daten.status=daten.SCHUSS;
          daten.p.x=sc1.sa1.tmp.x;
          daten.p.y=sc1.sa1.tmp.y;
          netz.sendData(daten);
          shoot.setEnabled(false);

          try {

            daten = netz.getData();

          }catch (IOException ioe) {}

          if (daten.status == daten.TREFFER) {

            //TREFFER
            System.out.println("Wir haben getroffen");

            //Einfaerben
            computer.spiel[sc1.sa1.tmp.x][sc1.sa1.tmp.y]=compu ter.VERSENKT;

            computer.schiffZahl--;

            if (computer.schiffZahl==0){

Nachdem der Rechner verloren hat, muss die Datenbank aktualisiert werden. Hierzu muss der folgende Aufruf verwendet werden:

code 

              // Anbinden an die Datenbank mittels RMI-Client, um
              //Ergebnis zu schreiben
              gewonnen = true;

              if (myThread == null){

                myThread = new StartRMIClient();
                myThread.start();

              }

Die restliche Implementierung der Methode ist wiederum identisch mit der bereits bekannten Version.

code 

              //Gewinnmeldung
              ausgabeFenster("Herzlichen Glückwunsch, Sie haben gewonnen!");

              try {

                netz.beenden();

              } catch (IOException ioe) {}

              //Neues Spiel anbieten
              shoot.setEnabled(false);

              return;

            }
            sc1.sa1.tmp.x = sc1.sa1.tmp.y = -1;
            sc1.sa1.repaint();
            repaint();
            shoot.setEnabled(true);

          } else {
          System.out.println("Wasser");
          computer.spiel[sc1.sa1.tmp.x][sc1.sa1.tmp.y]=computer .WASSER;

          sc1.sa1.tmp.x = sc1.sa1.tmp.y = -1;
          sc1.sa1.repaint();
          rechnerTrifft(daten);
          if (spieler.schiffZahl!=0)

            shoot.setEnabled(true);

          sc2.repaint();
          repaint();

        }

      }
      validate();

    }

Auch die Methode rechnerTrifft muss um einen derartigen Aufruf erweitert werden, der aber mit dem oben beschriebenen Aufruf identisch ist. Hierbei wird allerdings die Variable gewonnen auf den Wert false gesetzt. Im Folgenden ist die Realisierung der inneren Klasse StartRMIClient angegeben, die den RMI-Client startet.

code 

    // Thread zum Starten des RMI-Clients, um den
    //Datenbankeintrag zu aktualisieren
    public class StartRMIClient extends Thread{

      public StartRMIClient(){

        super();

      }
      public void run() {

        try {

          server2.Klient2 kl2 = new server2.Klient2();
          if (gewonnen) {

            System.out.println("YAHOO Gewonnen!");
            kl2.main(mainString1);

          } else{

            System.out.println("Schade Verloren!");
            kl2.main(mainString0);

          }
          myThread.sleep(1000);

        }catch (Exception ex) {}

      }

    }

RMI-Client

Die Steueranwendung des RMI-Clients wurde ebenfalls bereits erläutert. Sie sieht - inklusive einiger Erweiterungen - wie folgt aus:

code 

package server2;
import java.rmi.*;
public class Klient2   {

    static int spielAntwort;
    public Klient2(){ }

    public static void main (String args[]) {

      DatabaseUpdate2 du = null; Passwort2 pwd;
      if (args.length != 3) {

        System.err.println("Eingabe: java server.Klient2 <Server> <Port> <0 oder 1>");
        System.exit(1);

      }
      String server = args[0];
      int port = Integer.parseInt(args[1]);
      int spielStatus = Integer.parseInt(args[2]);

      //Neuer Security Manager
      System.setSecurityManager(new RMISecurityManager());

      try {

        //Binden der Objektinstanz an entfernte Registry
        String url="//"+server+":"+port+"/DatabaseUpdate2";
        du = (DatabaseUpdate2)Naming.lookup(url);
        System.out.println("lookup erfolgsreich");

      } catch (Exception e) {

        System.err.println("Keine Verbindung"+e);

      }

      try {

        pwd = new PasswortImpl2();
        if (spielStatus == 1) {

          //Schicke 1, also Spieler hat gewonnen
          spielAntwort = du.updateDatenbank(1,pwd);

        } else {

          //Schicke 0, also Spieler hat verloren
          spielAntwort = du.updateDatenbank(0,pwd);

        }
        if (spielAntwort == 1)

          System.out.println("Eintrag erfolgsreich abgeschlossen.");

        else

          System.out.println("Eintrag wurde nicht bearbeitet.");

      }catch (Exception e4) {

        System.err.println("Problem in Remote-Methode des Clients ");

      }

    }// Main

} // Hauptklasse

Die Definition des Interfaces Passwort wurde dahingehend verändert, dass nun auch die User-ID eingegeben werden kann.

code 

package server2;
import java.rmi.*;
public interface
Passwort2 extends Remote {

    //Anfordern des Passworts und des User IDs
    public String passwort() throws RemoteException;
    public String uid() throws RemoteException;

}

Die Implementierungsklasse ruft ein Fenster beim Client auf, wenn der Server dies initiiert. Hierzu wurde eine neue Klasse definiert, die im Folgenden beschrieben ist.

code 

package server2;
import java.rmi.*;
import java.rmi.server.*;
import java.io.Serializable;
public class PasswortImpl2 extends UnicastRemoteObject implements Passwort2, Serializable {

    private String uid = "";
    private String pwd = "";
    public PasswortImpl2 () throws RemoteException {

      System.out.println("Innerhalb PWD-Konstruktor");

    }
    public void setUid(String u){

      this.uid = u;

    }
    public void setPwd(String p){

      this.pwd = p;

    }
    public String passwort() throws RemoteException{

      System.out.println("Aufruf der Passwort-Methode");
      return pwd;

    }
    public String uid() throws RemoteException{

      System.out.println("Aufruf der uid-Methode");
      EingabeFenster ef = new EingabeFenster(new java.awt.Frame(), "Eingabefenster!!");
      ef.setVisible(true);
      ef.setSize(300,300);
      ef.setModal(true);
      setUid( ef.getUid() );
      setPwd( ef.getPasswort() );
      return uid;

    }

}

Zur Eingabe des Passworts wurde die folgende Klasse entwickelt. Die Definition mag zwar lang erscheinen, beinhaltet allerdings fast ausschließlich die Funktionalität zur Erzeugung des Eingabefensters. Hierbei wird eine innere Klasse verwendet, die das Event-Handling übernimmt, sowie eine innere Klasse, die das Fenster wieder schließt.

code 

package server2;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
public class EingabeFenster extends Dialog{

    String uid = "";
    String passwort ="";
    EFWindowListener windowListener;
    public EingabeFenster(Frame dw, String title) {

      super(dw, title, true);

      //{{INIT_CONTROLS
      setLayout(null);
      setVisible(false);
      setSize(300,200);

      label1 = new java.awt.Label("User ID:",Label.RIGHT);
      label1.setBounds(36,48,70,30);
      label1.setBackground(new Color(16777215));
      add(label1);

      textField4uid = new java.awt.TextField();
      textField4uid.setBounds(122,48,120,30);
      textField4uid.setBackground(new Color(16777215));
      add(textField4uid);

      label2 = new java.awt.Label("Passwort:",Label.RIGHT);
      label2.setBounds(36,91,70,30);
      label2.setBackground(new Color(16777215));
      add(label2);

      textField4pwd = new java.awt.TextField();
      textField4pwd.setEchoChar('*');
      textField4pwd.setBounds(122,91,120,30);
      textField4pwd.setBackground(new Color(16777215));
      add(textField4pwd);

      OKButton = new java.awt.Button();
      OKButton.setLabel("OK");
      OKButton.setBounds(75,135,100,40);
      OKButton.setBackground(new Color(12632256));
      add(OKButton);

      setTitle("Eingabefenster");

      //}}
      //{{INIT_MENUS
      //}}

      //{{REGISTER_LISTENERS

      windowListener = new EFWindowListener();
      addWindowListener(windowListener);
      SymAction lSymAction = new SymAction();
      OKButton.addActionListener(lSymAction);

      //}}

    }

    //{{DECLARE_CONTROLS

    java.awt.Label label1;
    java.awt.TextField textField4uid;
    java.awt.Label label2;
    java.awt.TextField textField4pwd;
    java.awt.Button OKButton;

    //}}

    public void setUid(java.lang.String uid){

      this.uid = uid;
      System.out.println("UID " +uid);

    }
    public java.lang.String getUid(){

      return this.uid;

    }
    public void setPasswort(java.lang.String passwort){

      this.passwort = passwort;
      System.out.println("PWD " +passwort);

    }
    public java.lang.String getPasswort(){

      return this.passwort;

    }

Die folgende Klasse fungiert als Windows-Adapter, um das Fenster wieder zu schließen.

code 

    class EFWindowListener extends WindowAdapter {

      public void windowClosing(WindowEvent e){

        dispose();
        System.exit(0);

      }
      public void WindowClosed(WindowEvent e){

        dispose();
        System.exit(0);

      }

    }

Die folgende Klasse fungiert als Event-Handler.

code 

    class SymAction implements java.awt.event.ActionListener{

      public void actionPerformed(java.awt.event.ActionEvent event){

        Object object = event.getSource();
        if (object == OKButton)

          button1_ActionPerformed(event);

      }

    }
    void button1_ActionPerformed(java.awt.event.ActionEvent event){

      setUid(textField4uid.getText());
      setPasswort(textField4pwd.getText());
      dispose();

    }

}

Spiel-Server

Ein großer Vorteil dieser Anwendung ist, dass der Spiel-Server nicht modifiziert werden muss. Der Aufbau dieser Klassen bleibt daher unverändert. Der Leser sei hierzu auf die Beschreibung in Kapitel 7 verwiesen.

RMI-Server

Aufgabe des RMI-Servers ist es, ein Objekt anzulegen, dessen Methoden der Client zur Datenbankaktualisierung aufrufen kann. Hierzu wird das in Kapitel 11.4 beschriebene Programm verwendet. Der Vollständigkeit halber ist im Folgenden die Realisierung der main-Methode angegeben.

code 

package server2;
import java.rmi.*;
import java.rmi.registry.*;
public class Datenbank2 {

    public static void main (String args[]) {

      if (args.length != 2) {

        System.err.println("Eingabe: java server.Datenbank2 <Server> <Port>");
        System.exit(1);

      }
      String server = args[0];
      int port = Integer.parseInt(args[1]);

      //Neuer Security Manager
      System.setSecurityManager(new RMISecurityManager());

      try {

        //Ort der Registry
        LocateRegistry.createRegistry(port);
        System.out.println("Registry definiert");

        //Instanz der Datenbankanwendung
        DatabaseUpdateImpl2 dui = new DatabaseUpdateImpl2();

        //Binden der Objektinstanz an entfernte Registry
        String urlString="//"+server+":"+port+"/DatabaseUpdate2";

        System.out.println("Namensbindung erfolgt");
        Naming.rebind(urlString, dui);

      }catch (Exception e) {

        System.out.println("Fehler aufgetreten");
        e.printStackTrace();
        System.out.println(e.getMessage());

      }

    }

}

Aufgabe dieser Klasse ist die Registrierung der Datenbankanbindung. Die Funktion wurde im Rahmen von Kapitel 11.4 bereits ausführlich erläutert.

Zur Implementierung der Funktionalität wird nun das (ebenfalls unveränderte) Interface angegeben.

code 

package server2;
import java.rmi.*;
public interface DatabaseUpdate2 extends Remote {

    //Aktualisierung des Spielstands

    public int updateDatenbank(int spiel, Passwort2 pwd) throws RemoteException;

}

Die Implementierungsklasse DatabaseUpdateImpl2 wurde um den Datenbankzugriff erweitert. Diese Klasse wird vom RMI-Client aufgerufen, wenn ein Spiel verloren oder gewonnen wurde. Nachdem die Verbindung zur Datenbank geöffnet wurde, startet diese Klasse die beim Client gespeicherte Klasse PasswortImpl2. Der Benutzer wird anschließend dazu aufgefordert, sein User-ID und ein Passwort einzugeben. Danach wird das Passwort mit dem in der Datenbank abgespeicherten Passwort verglichen. Im Anschluss daran erfolgt die Anfrage an die Datenbank (in diesem Fall der Eintrag in die Datenbank). Danach werden einige Informationen angezeigt (bspw. User-ID, Anzahl der Spiele, Anzahl gewonnener und verlorener Spiele). Zum Schluss wird die Datenbankverbindung geschlossen. Die Implementierungsklasse des Interfaces sieht wie folgt aus:

code 

package server2;
import java.rmi.*;
import java.rmi.server.*;
import java.io.Serializable;
import java.sql.*;
import java.io.*;
public class DatabaseUpdateImpl2 extends UnicastRemoteObject  implements DatabaseUpdate2, Serializable {

    protected Connection dbConnection;
    protected String dbURL = "jdbc:odbc:SpielerDatabase";
    protected String userID = "";
    protected String passwd = "";
    protected String CR = "\n";
    int gewinnAnzahl;
    int verlustAnzahl;
    int spieleZahl;
    ResultSet   mein_ergebnis;
    ResultSet   mein_ergebnis2;
    public DatabaseUpdateImpl2 () throws RemoteException {    }

    public int updateDatenbank (int spiel, Passwort2 pw) throws RemoteException {

      String clientPasswort= "";
      String UserID = "";
      System.out.println("innerhalb updateDatenbank");

Zuerst werden neben der Signatur und den Methoden der Klassen die Klassenvariablen definiert. Der Parameter spiel gibt an, ob der Spieler gewonnen oder verloren hat. Der Parameter pw ermöglicht die Abfrage des Passworts beim Client. Im Anschluss daran erfolgt der Datenbankzugriff:

code 

      // Verbinden mit der Datenbank
      try {

        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

        dbConnection = DriverManager.getConnection(dbURL, userID, passwd);

        PreparedStatement mein_prstmnt  =  dbConnection.prepareStatement("SELECT * FROM SpielerDatabase WHERE UID=?" );

        PreparedStatement mein_prstmnt2  =  dbConnection.prepareStatement("SELECT * FROM SpielerDatabase WHERE UID=?" );

        PreparedStatement gewinn_prstmnt =  dbConnection.prepareStatement("UPDATE SpielerDatabase SET Gewonnen=Gewonnen+? WHERE UID=?" );

        PreparedStatement verlust_prstmnt =  dbConnection.prepareStatement("UPDATE SpielerDatabase SET Verloren=Verloren+? WHERE UID=?" );

        PreparedStatement spiele_prstmnt =  dbConnection.prepareStatement("UPDATE SpielerDatabase SET Gespielt=Gespielt+? WHERE UID=?" );

Nach der Definition der Datenbankzugriffe erfolgt die Rückfrage nach dem Passwort beim Client.

code 

        UserID = pw.uid();
        mein_prstmnt.setString(1, UserID);
        mein_ergebnis =mein_prstmnt.executeQuery();

Nach dem Datenbankzugriff werden die Informationen zunächst auf der Konsole ausgegeben.

code 

        System.out.println();
        System.out.println("--openjava----openjava----Zeige Tabelleninhalt----openjava----openjava--");
        System.out.println();
        while (mein_ergebnis.next() ){

          clientPasswort = mein_ergebnis.getString("Passwort");
          String s2 = mein_ergebnis.getString("UID");
          System.out.println("UID: " +s2);
          gewinnAnzahl =  mein_ergebnis.getInt("Gewonnen");
          verlustAnzahl = mein_ergebnis.getInt("Verloren");
          spieleZahl = mein_ergebnis.getInt("Gespielt");

        }

Das Ergebnis der Abfrage wird hier in Variablen eingelesen und anschließend weiter verarbeitet.

code 

        if (pw.passwort().equals(clientPasswort)){

          System.out.println("Passwort OK");
          if (spiel == 1)  {

            gewinn_prstmnt.setInt(1,1);
            System.out.println(" Spiel: Gewonnen ");
            gewinn_prstmnt.setString(2, UserID);
            gewinn_prstmnt.executeUpdate();
            spiele_prstmnt.setInt(1,1);
            spiele_prstmnt.setString(2, UserID);
            spiele_prstmnt.executeUpdate();

          } // Spiel Verloren spiel = 0

Nach der Prüfung des Passworts (beim Client) erfolgt der Aktualisierungsaufruf der Datenbank. Im Anschluss daran wird verarbeitet, dass der Spieler verloren hat.

code 

          else {

            System.out.println(" spiel: Verloren  ");
            verlust_prstmnt.setInt(1,1);
            verlust_prstmnt.setString(2, UserID);
            verlust_prstmnt.executeUpdate();
            spiele_prstmnt.setInt(1,1);
            spiele_prstmnt.setString(2, UserID);
            spiele_prstmnt.executeUpdate();

          }

Nachdem die Datenbankaktualisierung erfolgt ist, werden die neuen Informationen aus der Datenbank ausgelesen.

code 

          // Ausgabe der Ergebnisse aus der Datenbank
          mein_prstmnt2.setString(1, UserID);
          mein_ergebnis2 =mein_prstmnt2.executeQuery();
          System.out.println();
          System.out.println("--openjava----openjava----Zeige Tabelleninhalt----openjava----openjava--");
          System.out.println();
          while (mein_ergebnis2.next() ){
          gewinnAnzahl =  mein_ergebnis2.getInt("Gewonnen");
          verlustAnzahl = mein_ergebnis2.getInt("Verloren");
          spieleZahl = mein_ergebnis2.getInt("Gespielt");
          System.out.println("Gewonnen: " +gewinnAnzahl);
          System.out.println("Verloren: " +verlustAnzahl);
          System.out.println("Gespielt: " +spieleZahl);

        }

Im Anschluss daran wird die Verbindung zur Datenbank geschlossen, woraufhin die Methode endet. Wurde allerdings ein falsches Passwort eingegeben, so liefert die Methode den Rückgabewert -1 an den Client zurück.

code 

        // Schliesse Verbindung zur Datenbank
        cleanUp();
        return spiel;

      } // Passwort OK

    } catch (Exception e){

      cleanUp();
      e.printStackTrace();

    }

    cleanUp();
    System.out.println("Falsches Passwort und/oder UserId");
    return -1;

    }

    public void cleanUp(){

      try {

        System.out.println("Closing database connection");
        dbConnection.close();

      } catch (SQLException e) {

        e.printStackTrace();

      }

    }

}

Die Ausgabe dieses Programmteils ist in Abb. 14-2 dargestellt.

ausgabe-rmiserver-db 

Abb. 14.2: Ausgabe der Datenbankaktualisierung

Das Übersetzen der Klassen erfolgt, wie bereits beschrieben, in den folgenden Schritten:

  • Aufruf javac -d . *.java
  • Aufruf rmic -d . server2.PasswortImpl2
  • Aufruf rmic -d . server2.DatabaseUpdateImpl2

Zum Starten des RMI-Servers wurde eine Batch-Datei geschrieben, die die RMI-Registry aufruft, die CLASSPATH-Variable auf den Wert server2 setzt und die Policy-Datei festlegt (java.policy ). Hierbei muss aber der Wert des Verzeichnisses, in dem die Klassen gespeichert sind, geeignet angepasst werden.

code 

start rmiregistry

java -classpath d:\tmp;d:\tmp\server2;%CLASSPATH%  -Djava.security.policy=d:\tmp\java.policy server2.Datenbank2 localhost 1234

Ablauf des Spiels

Da der Client als Applet ablaufen muss und aufgrund der Sicherheitseinschränkungen der RMI-Aufrufe, muss das Applet als signierte JAR-Datei geladen werden. Hierbei muss man sich verdeutlichen, dass aus dem Applet heraus Netzwerkzugriffe erfolgen, was eben nur erlaubt ist, wenn der Code signiert ist. Das Laden des Applets erfolgt in diesem Beispiel mit dem Java-Plug-In von JDK in der Version 1.2. Es wurde in der Einleitung bereits beschrieben, dass ein sicherer Betrieb der aktuellen Java-Version immer garantiert werden kann, wenn nicht die VM des Browsers, sondern ein Plug-In verwendet wird. Im Einzelnen werden die folgenden Schritte ausgeführt, um das Applet zu erzeugen bzw. aufzurufen.

Duplikation der server2-Klassen

Die Implementierungsklassen des Packages server2 müssen sowohl auf dem Server als auch auf dem Client verfügbar sein, da der Callback-Mechanismus verwendet wird. Aus diesem Grund muss gewährleistet sein, dass die Dateien sowohl auf dem Server als auch auf dem Client zur Verfügung stehen.

Erzeugung der JAR-Datei

Der Client lädt das Applet in Form einer JAR-Datei. Um diese zu erzeugen, muss die folgende Anweisung ausgeführt werden:

code 

jar -cf0 client.jar *.class server2/*.class

Signatur der JAR-Datei

Nachdem die JAR-Datei erzeugt wurde, muss sie signiert werden. Hierzu wurde eine Batch-Datei entwickelt, die diese Aufgabe unterstützt. Diese Datei ist im Folgenden angegeben:

code 

keytool -genkey -alias openJ -dname "cn=Open_Java, ou=KOM,o=TUD, c=GERMANY"
keytool -export -alias openJ -file openjava.x509
jarsigner -signedjar sclient.jar client.jar openJ

Zuerst wird ein Keystore erzeugt, in dem ein Schlüssel angelegt wird, der als Passwort den Bezeichner openjava verwendet. In Abb. 14-3 ist der momentane Inhalt dieses Keystores dargestellt.

keytool-list 

Abb. 14.3: Inhalt des Keystores

Anschließend wird dieser Schlüssel exportiert und hierzu in einer Datei openjava.x509 abgelegt (Zertifikat). Zur Signatur der JAR-Datei wird dann das Tool jarsigner verwendet, das eine signierte Datei mit Namen sclient.jar erzeugt und hierzu den vorher definierten Schlüssel benutzt. Durch die Ausführung dieser Batch-Datei werden alle Klassen in der JAR-Datei signiert. Dieser Vorgang ist auch in Abb. 14-4 dargestellt.

sign-klein 

Abb. 14.4: Signatur der JAR-Datei

Resultat der Ausführung der Batch-Datei sind dann die Dateien sclient.jar und openjava.x509 (Signatur). Die Signatur muss jedem Benutzer zur Verfügung gestellt werden.

policytool 

Abb. 14.5: Ausgabe des Policy-Tools

Setzen der Berechtigungen

Im nächsten Schritt müssen die Berechtigungen beim Client gesetzt werden, die dem Code eines bestimmten Absenders eingeräumt werden sollen, der über den öffentlichen Schlüssel identifiziert wird. Hierzu wird das Policy-Tool verwendet. In diesem Beispiel werden dem Code mit dem Schlüssel openJ alle Rechte eingeräumt. Es sei darauf hingewiesen, dass dies nur der Einfachheit halber erfolgt und ein Sicherheitsrisiko darstellt. Anschließend werden die folgenden Schritte durchgeführt:

  • Eingabe des Befehls policytool. Hieraus ergibt sich die in Abb. 14-5 dargestellte Ausgabe.
  • Auswahl der Option SignedBy und Eingabe von openJ. Hierdurch wird angegeben, welcher Absender von Code vertrauenswürdig ist. Dieser Schritt ist in Abb. 14-6 dargestellt.

policy-entry 

Abb. 14.6: Eintrag im Policy-Tool

  • Die Abspeicherung der Dateien .java.policy und .keystore müssen bei der Verwendung des Java-Plug-Ins unter Windows 95 im Verzeichnis c:\windows liegen. Bei der Verwendung von Mehrbenutzerbetriebssystemen, wie bspw. Windows NT, werden dieser Dateien im Verzeichnis profile abgelegt.
  • Es darf nicht vergessen werden, das Zertifikat des Senders zu importieren. Dieser Vorgang ist in Abb. 14-7 dargestellt.

trust-cer 

Abb. 14.7: Importierung des Zertifikats

Nachdem die Sicherheitsberechtigungen vorhanden sind, kann nach dem Start der Server die Anwendung ausgeführt werden. Hierzu müssen die jeweiligen HTML-Seiten beim Client im Browser geladen werden. Hierzu wurde die Datei index.html um den Link Starte das Applet erweitert. Wird dieser Link betätigt, so wird die Datei GUISVUserSJar.html aufgerufen, die das Applet im Plug-In startet. Diese HTML-Datei ist im Folgenden angegeben.

code 

<html>

    <head></head>

    <body>

      <p>

        <h3 text="#ffff80" align=center>OpenJava-Applet</h3>

      </p>

      <OBJECT classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" WIDTH = 100 HEIGHT = 100  codebase="http://java.sun.com/products/plugin/1.2/jinsta ll-12-win32.cab#Version=1,2,0,0">

        <PARAM NAME = CODE VALUE = "GUISVUser.class" >
        <PARAM NAME="type" VALUE="application/x-java-applet; version=1.2">

        <COMMENT>
        <EMBED type="application/x-java-applet;version=1.2" java_CODE = "GUISVUser.class"  java_ARCHIVE="sclient.jar" WIDTH = 800 HEIGHT = 400   pluginspage="http://java.sun.com/products/plugin/1.2/p lugin-install.html"><NOEMBED></COMMENT>
        </NOEMBED></EMBED>

      </OBJECT>

    </body>

</html>

Die Bildschirmdarstellung dieser Datei ist in Abb. 14-8 dargestellt. Wird das Plug-In ausgeführt, so startet das in Abb. 14-9 angegebene Fenster.

policy-entry 

Abb. 14.8: Eintrag im Policy-Tool

plugin 

Abb. 14.9: Start des Plug-Ins

Nachdem der Spieler entweder gewonnen oder verloren hat, wird die Aktualisierung der Datenbank vorgenommen. Hierbei wird der Benutzer um die Eingabe einer User-ID und eines Passworts gebeten. Das dann erscheinende Fenster ist in Abb. 14-10 dargestellt.

eingabeFenster 

Abb. 14.10: Passwortabfrage

 


SPNavRight SPNavRight SPNavRight
BuiltByNOF