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: // 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. 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: // 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. } 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: // 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. //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. // 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: 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. 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. 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. 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. 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. 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. 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. 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: 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: // 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. 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. 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. 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. 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. // 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. // 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. 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. 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: 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: 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. 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. 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. 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. 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. 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. <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. Abb. 14.8: Eintrag im Policy-Tool 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. Abb. 14.10: Passwortabfrage