Erhältlich u.a. bei Amazon.de
12
Von Tobias Trelle
LiveConnect
LiveConnect bezeichnet einen Mechanismus innerhalb des Web-Browsers, der die Kommunikation zwischen Java-Applets und Plug-ins auf der einen und JavaScript auf der anderen Seite ermöglicht.
LiveConnect gehört zu der bereits in der Einleitung angesprochenen Gruppe von Begriffen, die vom Browser-Hersteller Netscape geprägt wurden. Wir werden uns bei unseren Betrachtungen jedoch nicht auf den Netscape Navigator beschränken. Im Internet Explorer sind bei Java-Applets die gleichen, bei Plug-ins entsprechende Mechanismen vorhanden.
Java
Für den Entwickler stellt sich LiveConnect im Wesentlichen als Definition von Schnittstellen dar. Neben einer ausführlichen Betrachtung aus der Sichtweise von JavaScript werden wir für ein umfassendes Verständnis auch auf Konzepte der Sprache Java zurückgreifen müssen. Vorkenntnisse in diesem Bereich sind daher von Vorteil, da wir uns aufgrund der Fülle nur sehr rudimentär mit Java-Grundlagen beschäftigen können. Eine Einführung in Java-Programmierung finden Sie zum Beispiel in [FLAN97].
Nach dem Durcharbeiten dieses Kapitels werden Sie in der Lage sein, mit JavaScript-Befehlen innerhalb Ihrer HTML-Seiten Java-Applets und Plug-ins zu steuern und Event-Handler zum Empfangen von Nachrichten dieser eingebetteten Objekte zu definieren.
Da wir den aktuellen Stand der Entwicklung berücksichtigen wollen, sollten Sie die Beispiele und Übungen in diesem Kapitel mindestens mit dem IE4 und NN4.5 durcharbeiten. Wir möchten an dieser Stelle darauf hinweisen, dass wir das DOM des NN6PR1 nicht berücksichtigen, da LiveConnect voraussichtlich komplett anders realisiert werden wird.
Java-Applet
Java-Applets sind kleine Programme, die in der Programmiersprache Java entwickelt werden. ("Applet" = Verniedlichung von "Application" in der englischen Sprache). Im Folgenden werden wir die Begriffe Applet und Java-Applet synonym verwenden. Ein wesentlicher Aspekt von Applets ist die Möglichkeit, sie in andere Anwendungen einzubetten. Die bekannteste dieser einbettenden Anwendungen ist der Web-Browser. Auf diesen wollen wir uns im Folgenden konzentrieren.
Die grafische Ausgabe von Applets erfolgt in einem rechteckigen Ausschnitt innerhalb einer HTML-Seite. Dabei werden bestimmte Methoden des Applets in einer fest definierten Reihenfolge aufgerufen. Diese bestimmt den Lebenszyklus des Applets.
JVM
Eine wesentliche Eigenschaft von Applets (und Java-Anwendungen im Allgemeinen) ist die Plattformunabhängigkeit. Ein auf Betriebssystem A erstelltes Applet ist auch auf Betriebssystem B lauffähig, sofern es für beide eine Java-Unterstützung gibt. Die Plattformunabhängigkeit wird dadurch erreicht, dass bei der Kompilierung nicht direkt in betriebssystem-spezifischen Objektcode übersetzt wird. Stattdessen wird ein so genannter Bytecode generiert, der von der Java Virtual Machine (JVM) und nicht vom Betriebssystem ausgeführt wird. Jeder Java-fähige Web-Browser beinhaltet eine solche JVM.
Bitte beachten Sie, dass die gängigen Browser, d.h. Microsoft Internet Explorer 5.x und Netscape Navigator 4.x, ohne Zusatzmaßnahmen nur Java in der Version 1.1 unterstützen. Unter Zuhilfenahme des Java1.3-Plug-ins 1 kann jedoch auch Java in der Version 1.3 unterstützt werden. Ältere Web-Browser unterstützen teilweise nur Java in der Version 1.0.2. Denken Sie daran, wenn Sie auf Abwärtskompatibilität besonderen Wert legen (müssen).
Aus Sicherheitsgründen stehen dem Entwickler innerhalb von Applets nicht alle Befehle der Programmiersprache Java zur Verfügung. Unter anderem können keine Zugriffe auf das lokale Dateisystem vorgenommen oder Netzwerkverbindungen zu beliebigen fremden Rechnern aufgebaut werden. Diese Sicherheitsbeschränkungen können durch technische Maßnahmen aufgehoben werden, sie sind für uns allerdings nicht relevant und werden daher im Folgenden nicht berücksichtigt.
Java-Konsole
In der Einleitung wurde bereits darauf hingewiesen, wie die JavaScript-Konsole im NN zur Fehleranalyse automatisch angezeigt werden kann. Auch die JVM des Web-Browsers bietet die Möglichkeit, Text in einem Konsolenfenster auszugeben. Davon werden wir im Folgenden Gebrauch machen. Innerhalb eines Applets können Sie dazu unter anderem den Befehl
System.out.println(String sMessage);
verwenden. Um die Java-Konsole zu aktivieren, wählen Sie im Internet Explorer 5 den Menüpunkt
Ansicht/Java-Befehlszeile
und im Netscape Navigator 4
Communicator/Tools/Java Console
Ein Applet konkretisiert sich in einer oder mehreren Datei(en) mit der Endung .class. Bei der Einbettung des Applets gibt es eine Hauptklasse, die von java.applet.Applet abgeleitet wird. Der Name dieser Klasse wird in der HTML-Seite angegeben:
<applet>
<applet name="myApplet" width="500" height="200" code="HelloLiveConnect.class"> <param name="message" value="Hello LiveConnect"> Alternativtext für Browser ohne Java-Unterstützung </applet>
Obiges HTML-Element bindet ein Applet ein, dessen Klasse HelloLiveConnect heißt und sich (da relativ adressiert) im gleichen Verzeichnis wie die HTML-Datei befinden muss. Der Name des Applets ist myApplet. Dies wird später von Belang sein, wenn das Applet per JavaScript gesteuert wird. Durch die Angabe der Parameter width und height wird die Dimension des Applets festgelegt. Das Applet hat die Möglichkeit, diese Werte abzufragen und gegebenenfalls darauf zu reagieren, zum Beispiel für eine zentrierte Textausgabe. Die Parameter width und height sind sowohl von JavaScript als auch von Java aus betrachtet nur lesbar, können also nicht nachträglich vom Programm verändert werden.
archive JAR, ZIP
In der Regel bestehen Applets aus mehr als einer .class-Datei. Die Browser bieten ab der Version 4 (genauer gesagt: seit sie eine JVM der Version 1.1 benutzen) die Möglichkeit, alle .class-Dateien in einem JAR- oder ZIP-Archiv zusammenzufassen. Dies beschleunigt den Download einerseits, weil nicht mehr für jede einzelne Datei ein eigener HTTP-Request durchgeführt werden muss, andererseits, weil die Dateien außerdem auch noch komprimiert werden können. JAR steht dabei für JavaARchive. Technisch ist ein JAR nichts anderes als eine ZIP-Datei. Die Browser können mit Archiven der Endung .jar und .zip gleichermaßen gut umgehen. Wenn Sie ein Archiv einsetzen wollen, erzeugen Sie zum Beispiel ein JAR namens mypack.jar mit all Ihren .class-Dateien. Dies geschieht in der Regel mit dem Java-Tool namens jar (dies ist Bestandteil des Java Development Kits, kurz auch JDK), Ihrer Lieblings-Java-IDE oder einfach mit einem ZIP-Tool, z.B. WinZIP. Den Namen des Archivs teilen Sie dem Applet über das Attribut archive mit:
<applet name="myApplet" width="500" height="200" code="HelloLiveConnect.class" archive="mypack.jar"> </applet>
Der Browser lädt nun nur noch das Archiv mypack.jar und extrahiert daraus die Klasse HelloLiveConnect und alle anderen Dateien. Sie können sogar mehrere solcher Archive angeben, indem Sie sie durch Kommata trennen:
archive="mypack0.jar,mypack1.zip,mypack2.zip"
getParameter(String)
Eigene Parameter definieren Sie über das HTML-Element param. In unserem Beispiel wird ein Parameter mit Namen message definiert, dessen Wert "Hello LiveConnect" ist. So definierte Parameter können innerhalb des Applets über die Methode getParameter(String sParamName) abgefragt werden.
Ein wie oben benanntes Applet ist im DOM als
document.myApplet document.applets["myApplet"] document.applets[0]
sichtbar, sofern wir von einem Applet pro Seite ausgehen. Bei letzterer Schreibweise besitzt jedes Applet einen anderen Index innerhalb des applets-Arrays. Der Index wird bestimmt durch die Reihenfolge, in der die Applets in der HTML-Seite eingebettet sind. Dabei startet die Nummerierung mit dem Wert 0. Aus Gründen der Übersichlichkeit sollten Sie nur mit einer der ersten beiden Varianten arbeiten. Wir werden im Folgenden stets mit der ersten Variante, also document.myApplet, arbeiten.
Das Beispiel 1 demonstriert, wie das Applet über alle drei Varianten ansprechbar ist:
alert(document.myApplet); alert(document.applets["myApplet"]); alert(document.applets[0]);
Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\01\index.html.
Beachten Sie bei der Verwendung von Layern, dass der NN pro Layer ein separates document-Objekt vorhält, d.h., ein Applet myApplet im Layer myLayer wird als
document.layers["myLayer"].document.myApplet
adressiert. Applets werden übrigens unabhängig vom z-Index des Layers immer im Vordergrund dargestellt.
navigator.javaEnabled
Um zu testen, ob Ihr Browser Applets darstellen kann, können Sie die JavaScript-Funktion navigator.javaEnabled() verwenden. Sie liefert true zurück, wenn der Browser eine Java-Unterstützung hat, ansonsten false:
alert("Java enabled: " + navigator.javaEnabled());
Dies können Sie ebenfalls in Beispiel 1 ausprobieren.
Werfen wir nun noch kurz einen Blick auf den Lebenszyklus eines Applets.
import java.awt.*; import java.awt.event.*; public class Sizelet extends java.applet.Applet { public void init() { ... } public void start() { ... } public void stop() { ... } public void destroy() { ... } ... }
Vier Methoden spielen in jedem Applet eine besondere Rolle, da diese zu bestimmten Zeitpunkten ganz ohne unser Zutun vom Web-Browser aufgerufen werden.
init(), destroy()
Dies sind init(), start(), stop() und destroy() in genau dieser Reihenfolge. Gemäß Spezifikation werden init() und destroy() jeweils nur einmal bei Anlegen und Zerstören des Applets aufgerufen. So erhält der Programmierer die Gelegenheit, in init() Variablen zu initialisieren und Objekte anzulegen und später bei Beendigung in destroy() gegebenenfalls benutzte Ressourcen wieder freizugeben.
Ein anschauliches Beispiel hierfür ist eine Netzwerkverbindung eines Chat-Applets zu einem zentralen Server, die in init() aufgebaut und in destroy() sauber wieder geschlossen wird.
start(), stop()
Der Aufruf der Methoden start() und stop() erfolgt ebenso symmetrisch, d.h., zu jedem Aufruf von start() gibt es zu einem späteren Zeitpunkt auch ein stop(). Allerdings können diese beiden Methoden mehr als nur einmal aufgerufen werden.
Ein denkbares Ereignis, das den Aufruf von start() und stop() mehrfach auslösen kann, ist das Sichtbar- beziehungsweise Unsichtbarwerden des Applets beim Scrollen einer HTML-Seite oder das Minimieren oder Maximieren des Browser-Fensters. Eine rechenintensive Animation könnte so sinnvoll angehalten und bei Bedarf wieder gestartet werden.
Leider leben wir nicht in einer idealen Welt, und so verhalten sich der IE und der NN an dieser Stelle auch inkonsistent. Testen Sie das Beispiel 2 mit eingeschalteter Java-Konsole. Ändern Sie die Größe des Browser-Fensters oder wechseln Sie zu dem verlinkten URL und wieder zurück. Beobachten Sie dabei die Ausgabe im Konsolenfenster.
Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\02\index.html.
Grundsätzlich kann die Kommunikation zwischen JavaScript und Java-Applets bidirektional verlaufen, also in beide Richtungen. Einerseits können wir mittels JavaScript ein Applet durch Methodenaufrufe steuern, andererseits hat ein Applet Zugriff auf das DOM und damit auf die JavaScript-Objekte des Browsers. Wir werden uns in dieser Reihenfolge näher mit Applets beschäftigen.
Werfen wir zunächst einen Blick auf den Quelltext dieses einfachen Applets:
import java.awt.*; public class HelloLiveConnect extends java.applet.Applet { public String sMessage; // text message public void db(String sText) { System.out.println(sText); } public String getJavaVersion() { return "Java version: " + System.getProperty("java.version"); } public void init() { // init global values sMessage = getParameter("message"); if (sMessage==null) sMessage = "<Parameter missing>"; // paint the applet repaint(); } public void paint(Graphics g) { // draw background g.setColor(Color.blue); // blue g.fillRect(0,0, getSize().width, getSize().height); // draw message g.setColor(Color.white); g.drawString(sMessage, 0, 20); } }
Listing 12.1: HelloLiveConnect.java
Dieses Applet gibt eine Textnachricht in weißer Schrift auf blauem Hintergrund aus.
Wie können wir nun mit dem Applet in Verbindung treten, uns LiveConnecten? Prinzipiell ist alles recht einfach: Sie können auf öffentliche Variablen des Applets lesend und schreibend zugreifen sowie öffentliche Methoden aufrufen.
public
Variablen und Methoden werden in Java durch das Schlüsselwort public als öffentlich zugänglich gekennzeichnet. In obigem Listing sind dies u.a. die String-Variable sMessage und die Methoden db(String sText) und getJavaVersion(). Daneben besitzt jedes Applet eine Reihe weiterer öffentlicher Standard-Methoden. Eine davon ist von besonderem Interesse: repaint(). Wenn sich die Inhalte von Variablen, die die grafische Ausgabe des Applets beeinflussen, geändert haben, sollten Sie immer ein repaint() durchführen, entweder innerhalb des Applets selbst oder per JavaScript. Erst sein Aufruf löst das Neuzeichnen aus.
Greifen wir nun einmal per JavaScript auf die Variable sMessage zu:
alert(document.myApplet.sMessage);
Ebenso simpel ist der Aufruf der Methode db(String sText). Aktivieren Sie bitte vorher die Java-Konsole:
document.myApplet.db("We are LiveConnected!");
Methoden können auch Rückgabewerte liefern. Rufen Sie die Methode getJavaVersion() auf, die einen String mit Informationen über die Version der JVM zurückliefert:
alert(document.myApplet.getJavaVersion());
Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\03\index.html.
Bitte beachten Sie, dass Sie ein Applet erst manipulieren sollten, nachdem es vollständig geladen wurde. Abhängig von der Größe und Anzahl der benötigten class-Dateien kann der Download aus dem Netz durchaus einige Zeit in Anspruch nehmen. Generell gilt die Regel: Applets erst nach dem onLoad-Event (siehe Event-Handling) des Bodys manipulieren.
Um ganz sicherzugehen, fragen Sie die Existenz des Applets ab, bevor Sie auf seine Methoden oder Properties zugreifen:
if (document.myApplet) { alert(document.myApplet.getJavaVersion()); }
Unabhängig von LiveConnect sieht die Sprache Java Methoden vor, mit denen ein Applet den Applikationskontext (in unserem Fall: den Web-Browser) zur Anzeige von (Status)-Informationen veranlassen kann. Diese Methoden können durchaus nützlich sein, daher sollen sie hier auch Erwähnung finden. Im Einzelnen sind dies:
showDocument(URL url); showDocument(URL url, String sTarget); showStatus(String sStatus);
Im Web-Browser lösen diese Methoden folgenden Aktionen aus:
showDocument(...) showStatus(...)
Die Methode showDocument(URL url) lädt eine unter dem URL url befindliche HTML-Seite in das aktuelle Browser-Fenster, showDocument(URL url, String sTarget) lädt diese in einen HTML-Frame beziehungsweise ein Window namens sTarget. Dabei sind die reservierten Namen _self, _parent, _top und _blank als Ziel zulässig.
So ruft beispielsweise
URL url = URL("http://www.addison-wesley.de/"); showDocument(url, "_blank");
die Homepage unseres Verlags in einem neuen Browser-Fenster auf.
Mit showStatus(String sStatus) können Sie den Text sStatus in die Statuszeile des Web-Browsers schreiben. Diese Methode kann also mit der JavaScript-Anweisung
window.status = sStatus;
verglichen werden. Ein exemplarischer Zugriff auf die Statuszeile des Browsers kann so aussehen:
showStatus("Applet talks to browser.");
Neben der Ausführung obiger Aktionen kann ein Applet noch Informationen über seine Herkunft und die JVM einholen. Mit den Methoden
URL getDocumentBase(); URL getCodeBase();
getDocumentBase() getCodeBase()
erhalten wir den URL der einbettenden HTML-Seite beziehungsweise des Verzeichnisses der Klassendatei des Applets. Dies kann von Bedeutung sein, wenn etwa Dateien aus dem gleichen Verzeichnis manuell nachgeladen werden müssen.
Mit der Methode
String System.getProperty(String sPropName);
getProperty(String)
können verschiedene Systemeigenschaften der JVM abgefragt werden. Für uns sind besonders die Eigenschaften "java.version", "java.vendor" und "browser" interessant. Letztere ist nicht Bestandteil des Java-Standards, sondern kommt nur in den JVMs von Web-Browsern vor.
import java.awt.*; public class Java2Browser extends java.applet.Applet { java.awt.TextArea tb_info = new java.awt.TextArea("",0,0,TextArea.SCROLLBARS_VERTICAL_ONLY); public void init() { setLayout(new BorderLayout(0,0)); setSize(426,266); add(BorderLayout.CENTER,tb_info); String sInfo = "getDocumentBase(): " + getDocumentBase().toString() + "\n" + "getCodeBase(): " + getCodeBase().toString() + "\n"; String props[] = {"java.vendor", "java.version", "browser"}; for (int i=0;i<props.length; i++) sInfo += props[i] + ": " + System.getProperty(props[i]) + "\n"; tb_info.setText(sInfo); } }
Listing 12.2: Java2Browser.java
In Beispiel 4 können Sie sehen, welche Werte diese Eigenschaften in Ihrem Browser haben.
Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\04\index.html.
Dieser Abschnitt setzt durchschnittliche Java-Kenntnisse voraus. Sie sollten insbesondere wissen, wie man den Java-Compiler konfiguriert.
Wir werden sehen, wie man von einem Java-Applet aus auf JavaScript-Objekte zugreifen und deren Methoden aufrufen und Properties lesen bzw. setzen kann.
netscape.javascript
Die Browser-JVMs beinhalten zur Ansprache von JavaScript-Objekten ein Java-Package, das nicht Bestandteil des Standardumfangs von Java ist. Dabei handelt es sich um das Package netscape.javascript. Im Netscape Navigator gibt es dies seit der Version 3.0, im Internet Explorer seit der Version 4.0.
Bevor wir uns näher mit den Objekten und Methoden dieses Packages beschäftigen, müssen wir wissen, wie Applets kompiliert werden, die LiveConnect benutzen.
Innerhalb des Applets wird zunächst das Package über die import-Anweisung bekannt gemacht:
import netscape.javascript.*; ...
Classpath
Nun müssen wir den Classpath des Java-Compilers so anpassen, dass er anschließend das Package netscape.javascript bearbeiten kann. Wie Sie den Classpath Ihres Compilers anpassen, hängt von der Entwicklungsumgebung ab, mit der Sie arbeiten. Konsultieren Sie bitte die entsprechende Dokumentation.
Wenn Sie mit dem Internet Explorer arbeiten, sollten Sie in der Regel unter
C:\Windows\JAVA\Packages
eine Reihe von ZIP-Dateien finden. Die Namen dieser Dateien sind auf jedem System unterschiedlich. Die Datei, die am größten ist (ca. 5,6 MB), etwa
C:\Windows\JAVA\Packages\BLN5V7VR.ZIP
ist das Archiv, welches das Java-Package netscape.javascript enthält. Diesen Namen fügen Sie dem bestehenden Classpath hinzu.
Wenn Sie mit dem Netscape Navigator auf Windows-Systemen arbeiten, heißt das entsprechende Archiv in der Regel
C:\Programme\Netscape\Communicator\Program\Java\classes\java40.jar
und unter UNIX-Systemen zum Beispiel
/opt/netscape/java/classes/java40.jar
Sollten Sie Ihren Web-Browser in ein anderes Verzeichnis installiert haben, muss der Dateiname entsprechend anders gewählt werden.
Bitte beachten Sie, dass sich im Navigator Version 4.6 ein Fehler in die JVM eingeschlichen hat. Das Package netscape.javascript kann nicht benutzt werden. Ein Zugriff darauf liefert immer eine Fehlermeldung. Mit der Version 4.61 wurde dieser Fehler von Netscape wieder behoben. Die Version 4.6 findet sich nicht im Download-Archiv von Netscape. Auf einigen Mirrors ist sie jedoch noch verfügbar, falls Sie damit experimentieren möchten.
Kleiner Tipp am Rande: Öffnen Sie das Archiv einmal mit WinZIP (oder einem vergleichbarem Tool) und schauen Sie sich die Einträge im Unterverzeichnis netscape/javascript an. Dort finden Sie die Java-Klassen, die wir verwenden werden.
Bevor wir uns an die Programmierung eines LiveConnect-Applets begeben, müssen wir zunächst noch die einbettende HTML-Seite entsprechend vorbereiten. Dem Applet ist explizit der Zugriff auf die JavaScript-Objekthierarchie zu erlauben. Dies geschieht durch die Angabe des Attributes mayscript:
mayscript
<applet name="myApplet" width="500" height="200" code="HelloLiveConnect.class" mayscript> </applet>
JSObject
Das Package netscape.javascript stellt uns im Wesentlichen eine Klasse JSObject zur Verfügung, durch deren Instanzen JavaScript-Objekte repräsentiert werden. Die Klasse JSObject hat unter anderem folgende Methoden:
public static JSObject getWindow(java.applet.Applet applet); public Object getMember(String sMemberName); public Object getSlot(int iSlotIndex); public void setMember(String sMemberName, Object oValue); public void setSlot(int iSlotIndex, Object oValue); public void removeMember(String sMemberName); public Object call(String sMethodName, Object[] args); public Object eval(String sCode);
Mit Hilfe dieser Methoden können JavaScript-Objekte des hierarchischen Browser-DOMs leicht auf Java-Objekte innerhalb des Applets abgebildet werden.
window
Die Wurzel der gesamten Objekthierarchie bildet das window-Objekt, dem alle anderen Objekte untergeordnet sind:
try { JSObject window = JSObject.getWindow(this); } catch (Exception e) { }
call(...)
wobei this eine Referenz auf das Applet ist. Mit der Methode call() eines solchen Objekts können Sie seine Methoden aufrufen, zum Beisiel:
try { JSObject window = JSObject.getWindow(this); Object[] args = {"Hello LiveConnect!"}; window.call("alert", args); } catch (Exception e) { }
eval(...)
Mit der JavaScript-Methode eval(String sCode) wird der JavaScript-Code in sCode ausgeführt. So gibt
try { JSObject window = JSObject.getWindow(this); window.eval("s=''; for(i=0; i<10; i++) s += i + ' '; alert(s)"); } catch (Exception e) { }
einen String mit den Zahlen von 0 bis 9 in einer alert-Box aus.
getMember(...)
Mit getMember(String sMemberName) beziehungsweise getSlot(int iSlotIndex) können nun Referenzen auf Properties von JavaScript-Objekten angefordert werden. Versuchen wir also einmal auf den Text des Input-Feldes des Formulares
<form name="myForm"> <input name="myInput" type="text" value="JavaScript Workshop"> </form>
zuzugreifen. Vom Applet aus arbeiten wir mit
try { JSObject win = JSObject.getWindow(this); JSObject doc = (JSObject)win.getMember("document"); JSObject frm = (JSObject)doc.getMember("myForm"); JSObject inp = (JSObject)frm.getMember("myInput"); String val = (String)inp.getMember("value"); // output on java console System.out.println("value=" + val); } catch (Exception e) { }
Ein Zugriff, der getSlot(...) verwendet, würde so aussehen:
try { JSObject win = JSObject.getWindow(this); JSObject doc = (JSObject)win.getMember("document"); JSObject frms = (JSObject)doc.getMember("forms"); JSObject frm = (JSObject)frms.getSlot(0); JSObject elms = (JSObject)frm.getMember("elements"); JSObject inp = (JSObject)elms.getSlot(0); String val = (String)inp.getMember("value"); // output on java console System.out.println("value=" + val); } catch (Exception e) { }
Der Internet Explorer unterstützt die Methode getSlot(...) nicht! Wir werden daher im Folgenden immer mit der Variante getMember(...) arbeiten, die in beiden Browsern funktioniert.
setMember(...)
Analog zu den get-Methoden zum Lesen können Sie mit setMember(String sMemberName) bzw. setSlot(int iSlotIndex) schreibend auf JavaScript-Objekte zugreifen. Hier gelten die gleichen Restriktionen beim Schreibzugriff wie unter JavaScript.
Versuchen wir also einmal, den Wert des Input-Feldes des obigen HTML-Formulars zu ändern. In JavaScript würden wir eine Wertzuweisung so vornehmen:
document.myForm.myInput.value = "LiveConnect";
In unserem Applet arbeiten wir uns zunächst mit einer Reihe von Aufrufen von getMember(...) zu der Referenz auf myInput vor, um dann mit einem Aufruf von setMember(...) dessen Member value zu setzen:
try { JSObject win = JSObject.getWindow(this); JSObject doc = (JSObject)win.getMember("document"); JSObject frm = (JSObject)doc.getMember("myForm"); JSObject inp = (JSObject)frm.getMember("myInput"); // set value inp.setMember("value", "LiveConnect"); } catch (Exception e) { }
Wie Sie unschwer erkennen, werden bei tief im DOM liegenden JavaScript-Objekten so viele stets ähnliche Befehle verwendet. Wir werden in den Übungen daher eine Java-Klasse entwerfen, die den Zugriff auf DOM-Objekte wesentlich vereinfacht.
Beispiel 5 demonstiert den Umgang mit der Klasse JSObject. Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\05\index.html.
Abbildung 12.1 fasst die Kommunikation zwischen Applets und JavaScript noch einmal zusammen:
Abbildung 12.1: Applet/JavaScript-Kommunikation
Plug-in
Im Allgemeinen versteht man unter einem Plug-in die funktionale Erweiterung einer Anwendung über eine allgemein gehaltene Schnittstelle, das so genannte Plug-in-API. Wir werden uns bei unseren Betrachtungen auf die Anwendungen Microsoft Internet Explorer und Netscape Navigator konzentrieren.
Jeder der großen Browser-Hersteller verfolgt dabei einen eigenen Ansatz, was sich für den Webdesigner, aber insbesondere auch für den Programmierer von Plug-ins in deutlichem Mehraufwand niederschlägt. Im Gegensatz zu Java-Applets klinken sich Plug-ins direkt in den Web-Browser ein und müssen daher in betriebsssystem-spezifischem Objektcode vorliegen.
In der Regel werden Plug-ins verwendet, um zusätzliche Medientypen wie Videos oder 3-D-Welten anzuzeigen. Die grafische Ausgabe erfolgt dabei in einem rechteckigen Bereich einer HTML-Seite. Es sind jedoch auch unsichtbare Plug-ins denkbar, die zum Beispiel Werbebanner aus HTML-Seiten filtern.
Macromedia Flash
Wir werden die Verwendung von Plug-ins anhand des Macromedia Flash Plug-ins (im Folgenden kurz Flash) besprechen. Diese Wahl wurde aufgrund des steigenden Verbreitungsgrades dieses Plug-ins getroffen. Die zu besprechenden Techniken sind prinzipiell jedoch allgemeiner Natur. Flash-spezifische Sachverhalte werden ausdrücklich als solche gekennzeichnet werden.
Um die Beispiele und Übungen dieses Abschnitts zu bearbeiten, benötigen Sie die Version 4 des Flash Plug-ins. Dieses können Sie kostenlos von der Website http://www.macromedia.com/ herunterladen. Vorkenntnisse in der Erstellung von Flash-Movies sind nicht notwendig, wenngleich dieser Abschnitt auch für Flash-Entwickler interessant ist.
ActiveX
Im Internet Explorer sind Plug-ins technisch als ActiveX-Objekte realisiert. Unter einem ActiveX-Objekt können wir uns stark vereinfacht eine Softwarekomponente vorstellen, die von anderen Anwendungen eingebettet werden kann und deren Properties, Methoden und Events nach außen hin direkt sichtbar sind.
Die Einbettung in eine Webseite wird über das HTML-Element object vorgenommen. Wir werden aus der Vielzahl seiner Attribute nur diejenigen besprechen, die für uns von Bedeutung sind. Eine vollständige Auflistung finden Sie in [GOOD98].
<object>
<object id="myPlug" classid="clsid:D27CDB6E-AE6D-11CF-96B8-444553540000" codebase="http://active.macromedia.com/flash4/cabs/ swflash.cab#version=4,0,0,0" width="500" height="400"> <param name="movie" value="test.swf"> <param name="play" value="false"> <param name="quality" value="high"> Dieser Text wird vom Internet Explorer ignoriert. </object>
Bitte beachten Sie, dass es natürlich codebase="..." heißen muss. Aus Platzgründen haben wir den URL auf zwei Zeilen verteilt.
Die Parameter haben folgende Bedeutung:
Parameter | Bedeutung |
---|---|
id | Name des Plug-ins im DOM |
classid | GUID - ein Global Unique Identifier. Dies ist eine 128 Bit große Zahl, die vom Hersteller vergeben wird und das Plug-in weltweit eindeutig identifiziert. D27CDB6E-AE6D-11CF-96B8-444553540000 identifiziert das Flash Plug-in. Die GUIDs anderer Plug-ins finden Sie in der Dokumentation des Herstellers. |
codebase | Der IE kann bei Bedarf die aktuelle Version des erforderlichen Plug-ins automatisch von der angegebenen URL aus laden. Diese URL ist abhängig vom Hersteller. Sie sollten immer die Version verwenden, die die darzustellenden Inhalte auch anzeigen kann. Wenn Sie keinen codebase-Parameter angeben und noch gar keine Version des Plug-ins auf dem System installiert ist, wird in dem durch width und height angegebenen Bereich nichts angezeigt. |
Tabelle 12.1: Attribute des Elements object
Eigene herstellerspezifische Parameter werden ähnlich wie bei Applets über das HTML-Element param angegeben. In unserem Beispiel sind dies movie, play und quality. Welche Parameter verfügbar sind, entnehmen Sie bitte der Dokumentation des jeweiligen Plug-ins.
Im DOM wird das Plug-in als
document.myPlug document.all["myPlug"]
sichtbar.
Im Netscape Navigator sind Plug-ins technisch als Shared Libraries realisiert. In der Windows-Welt finden Sie die Bezeichnung Dynamic Link Library oder auch kurz DLL. Eine solche Library besteht im Wesentlichen aus einer Reihe von Funktionen, über die Browser und Plug-in miteinander kommunizieren.
Werfen wir einmal einen Blick in das Installationsverzeichnis Ihres Navigators auf einem Windows-System:
C:\Programme\Netscape\Communicator\Program\Plugins
Dort finden Sie (zumindest nach Installation des Flash Plug-ins) unter anderem Dateien, die der Namenskonvention np*.dll folgen, auf UNIX-Systemen np*.so. Nach solchen Dateien sucht der Navigator beim Programmstart, wenn es in der Statuszeile des Splash heißt "Loading plugins .... ".
Alle so erkannten Plug-ins können durch die Eingabe des URLs about:plugins aufgelistet werden.
Die Einbettung in eine HTML-Seite erfolgt über das Element embed. Auch hier werden wir nur die für uns relevanten Attribute besprechen. Eine vollständige Auflistung finden Sie in [GOOD98].
<embed>
<embed name="myPLug" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/shockwave/download/ index.cgi?P1_Prod_Version=Flash4" src="test.swf" width="500" height="400" play=vfalse" quality="high" swLiveConnect="true" ...> </embed>
Bitte beachten Sie, dass es natürlich pluginspage="..." heißen muss. Aus Platzgründen haben wir den URL auf zwei Zeilen verteilt.
Die Parameter haben folgende Bedeutung:
Parameter | Bedeutung |
---|---|
name | Name des Plug-ins im DOM |
src | Der URL der anzuzeigenden Datei. Hier kommt der Dateiendung (in diesem Fall .swf) eine wichtige Bedeutung zu. Sie bestimmt dem MIME-Typen. Das ist eine im Internet verwendete Kategorisierung von Medientypen (siehe [RFC1521]). So gehört z.B. zu .html-Dateien der MIME-Typ text/html und eben zu .swf-Dateien der Typ application/x-shockwave-flash. Wird eine URL vom Webserver geholt, verschickt dieser den zugehörigen MIME-Typen als Bestandteils des HTTPs (Hypertext Transfer Protocol, siehe [RFC2616]). Die Zuordnung zwischen MIME-Typ und Dateiendung geschieht also auf dem Webserver. Der Navigator entscheidet nun anhand des empfangenden MIME-Typs, welches Plug-in er aufruft, um die Datei darzustellen. Wenn Sie also eher unbekannte Plug-ins verwenden, kann es vorkommen, dass der zugehörige MIME-Typ im Webserver erst noch konfiguriert werden muss. Es sei denn, Sie benutzen den nächsten Parameter ... |
type | ... und geben den MIME-Typ für Ihr Plug-in explizit an. Dies ist insbesondere dann notwendig, wenn Sie ein Plug-in verwenden, das den Parameter src nicht verwendet. Denken Sie an Plug-ins, die gar keine Dateien anzeigen, zum Beispiel eine Chat-Anwendung oder ein Datenbank-Interface. |
pluginspage | Diese URL wird nach Bestätigung durch den Benutzer angezeigt, wenn das Plug-in noch nicht installiert ist. Die URL sollte also nach Möglichkeit auf den Download-Bereich des Plug-in-Herstellers zeigen. |
Tabelle 12.2: Attribute des Elements embed
Eigene, herstellerspezifische Parameter werden zusätzlich zu den allgemeinen innerhalb des Start-Tags embed definiert. In unserem Beispiel sind dies play, quality und swLiveConnect. Welche Parameter verfügbar sind, entnehmen Sie bitte der Dokumentation des jeweiligen Plug-ins.
swLiveConnect
Wir richten besonderes Augenmerk auf den Parameter swLiveConnect="true". Um das Flash Plug-in im Rahmen von LiveConnect verwenden zu können, müssen wir ihm dies explizit mitteilen. Warum dies so ist, werden wir später beim Blick hinter die Kulissen sehen.
Im DOM wird das Plug-in als
document.myPlug document.embebds["myPlug"] document.embeds[0]
sichtbar, sofern wir von einem Plug-in pro Seite ausgehen. Bei letzterer Schreibweise erhält jedes Plug-in einen anderen Index innerhalb des embeds-Arrays. Der Index wird bestimmt durch die Reihenfolge, in der die Plug-ins in der HTML-Seite eingebettet sind. Dabei startet die Nummerierung mit dem Wert 0. Aus Gründen der Übersichlichkeit sollten Sie nur mit einer der ersten beiden Varianten arbeiten. Wir werden im Folgenden stets mit der ersten Variante, also document.myPlug arbeiten.
Beachten Sie bei der Verwendung von Layern, dass der NN pro Layer ein separates document-Objekt vorhält, d.h. ein Plug-in myPlug im Layer myLayer wird als
document.layers["myLayer"].document.myPlug
adressiert. Plug-ins werden übrigens unabhängig vom z-Index des Layers immer im Vordergrund dargestellt. Daneben gibt es die Arrays
navigator.plugins[] navigator.mimeTypes[]
die Auskunft über die installierten Plug-ins geben. Wir werden in einer der Übungen näher darauf eingehen. Im Internet Explorer sind diese Arrays immer leer.
Wie wir in den letzten beiden Abschnitten gesehen haben, verläuft die Einbettung von Plug-ins im IE und im NN mit unterschiedlichen HTML-Elementen. Mit einem einfachen Trick ist es jedoch möglich, auf zwei verschiedene HTML-Dokumente oder ein in Abhängigkeit vom Browser dynamisch erzeugtes Dokument zu verzichten.
Dies erreichen wir dadurch, dass wir die Elemente object und embed ineinander schachteln:
<object id="myPlug" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://active.macromedia.com/flash4/cabs/ swflash.cab#version=4,0,0,0" width="550" height="400"> <param name="movie" value="test.swf"> <param name="play" value="false"> <param name="quality" value="high"> <embed name="myPlug" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/shockwave/download/ index.cgi?P1_Prod_Version=Flash4" width="550" height="400" src="test.swf" play="false" quality="high" swLiveConnect="true"> </embed> </object>
In der Definition des Elements object hatten wir ja gesehen, dass der Internet Explorer jegliches HTML zwischen Start- und End-Tag <object> ... </object> ignoriert (wenn wir von Elementen des Typs param einmal absehen). Daher ist es möglich, dort ein HTML-Element embed zu platzieren.
Der Netscape Navigator wiederum ignoriert das Element object, weil dies für ihn völlig unbekannt ist. Für ihn ist praktisch nur der Abschnitt zwischen <embed> ... </embed> sichtbar.
Beispiel 6 fasst die Details der Einbettung noch einmal zusammen. Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\06\index.html.
Wir werden in den folgenden Beispielen immer mit einer Kombination der HTML-Elemente embed und object arbeiten.
Der Flash-Movie in den vorangegangenen Beispielen ist durch die Angabe des Parameters play="false" nicht automatisch gestartet worden. Gehen wir nun daran, ihm ein wenig Leben einzuhauchen.
Play(), StopPlay()
Dabei bedienen wir uns der Methoden Play() und StopPlay(). Bitte beachten Sie die Groß- und Kleinschreibung. Mit
document.myPlug.Play(); document.myPlug.StopPlay();
wird der Flash-Movie also gestartet bzw. angehalten. Eine komplette Aufstellung aller Methoden und Properties des Flash Plug-ins finden Sie unter
http://www.macromedia.com/support/flash/publishexport/scriptingwithflash4/
Der Zugriff auf Properties eines Flash-Movies geschieht über die generischen Methoden
TGetProperty(...), TSetProperty(...)
String TGetProperty(sTarget, iProperty) TSetProperty(sTarget, iProperty, sValue)
Das hat den Vorteil, dass die Schnittstelle des Plug-ins erstens sehr überschaubar bleibt und sich zweitens beim Hinzufügen neuer Properties nicht ändern muss. Wie wir im Blick hinter die Kulissen sehen werden, reduziert dies den Entwicklungsaufwand des Plug-ins.
Auf welche Property dabei zugegriffen wird, bestimmt der Parameter iProperty vom Typ Integer! Sie müssen also in der Dokumentation nachschlagen, welche Zahl für welche Property steht. Wir werden in den Übungen näher auf die einzelnen Werte eingehen.
Greifen wir nun einmal auf die Properties CurrentFrame (4) und TotalFrames (5) zu. Erstere gibt den aktuellen Frame, letztere die Gesamtzahl an Frames eines Movies an:
alert(ocument.myPlug.TGetProperty("/", 4)); alert(document.myPlug.TGetProperty("/", 5);
Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\07\index.html.
Ein Plug-in kommuniziert in der Regel durch den Aufruf einer JavaScript-Funktion mit der HTML-Seite. Die aufgerufene Funktion kann als Event-Handler des Plug-ins aufgefasst werden. Im Internet Explorer ist dies tatsächlich auch technisch der Fall.
Der Netscape Navigator hingegen verfolgt einen ganz anderen Ansatz, daher werden wir beide Fälle im Folgenden getrennt betrachten.
Im Netscape Navigator wird in Abhängigkeit von bestimmten Aktionen im Plug-in eine JavaScript-Funktion aufgerufen. Diese kann eine Parameterliste beinhalten, um die Art des auslösenden Events zu identifizieren:
<head> ... <script language="JavaScript"> function myHandler(sCmd, sParam) { alert("Plug-in event:\n\nsCmd=" + sCmd + "\nsParam=" + sParam); } </script> </head>
Definieren Sie solche JavaScript-Funktionen immer im Head-Bereich der HTML-Seite.
Der Name und die Anzahl sowie Bedeutung der übergebenen Parameter hängt ganz individuell vom Plug-in ab. Werfen Sie also immer einen Blick in die Dokumentation Ihres Plug-ins.
Das Flash Plug-in verfügt über einen Event-Handler namens {nameDesPlugins}_DoFSCommand mit zwei Parametern sCmd und sParam. Sie können die Parameter innerhalb ihrer JavaScript-Funktionen frei benennen. Allerdings sollten Sie sinnvollerweise auch namentlich die gleiche Bedeutung wie innerhalb des Plug-ins haben. Anstatt {nameDesPlugins} muss der konkrete Name des Plug-ins wie im Element embed definiert stehen:
<head> ... <script language="JavaScript"> function myPlug_DoFSCommand(sCmd, sParam) { alert("Flash event:\n\nsCmd=" + sCmd + "\nsParam=" + sParam); } </script> </head>
VBScript
Im Internet Explorer werden Event-Handler von Plug-ins nicht in JavaScript, sondern in der Skriptsprache VBScript definiert. Dies bedeutet in der Praxis jedoch nur geringen Mehraufwand, da wir von VBScript aus eine JavaScript-Funktion aufrufen können. Wir können also auf den zuvor in JavaScript implementierten Event-Handler zurückgreifen:
call
<head> ... <script language="JavaScript"> function myPlug_DoFSCommand(sCmd, sParam) { alert("Flash event:\n\nsCmd=" + sCmd + "\nsParam=" + sParam); } </script> <script language="VBScript"> sub myPlug_FSCommand(sCmd, sParam) call myPlug_DoFSCommand(sCmd, sParam) end sub </script> </head>
Beachten Sie, dass der Event-Handler in VBScript einen anderen Namen hat: {nameDesPlugins}_FSCommand. Ganz allgemein müssen Event-Handler in VBScript immer in folgender Form benannt werden:
{objectName}_{eventName}
wobei für {objectName} und {eventName} jeweils konkrete Namen einzusetzen sind. Die Benennung des Event-Handlers des Flash Plug-ins für den Navigator folgt dieser Namenskonvention wohl aus Gründen der Konsistenz.
Der entsprechende VBScript-Block sollte sich ebenfalls im Head-Bereich der HTML-Seite und vor allem nach der Definition des Skriptblocks mit dem JavaScript-Event-Handler befinden.
Obiges Beispiel sollten Sie auch ohne Kenntnisse der Sprache VBScript leicht nachvollziehen können, da im Vergleich zu JavaScript praktisch nur eine andere Syntax verwendet wird.
Noch ein Hinweis für Visual-Basic-Programmierer: in VBScript können Variablen nur untypisiert deklariert werden, sind also immer vom Typ Variant.
Beispiel 8 demonstriert, wie Sie Event-Handler definieren und miteinander kombinieren. Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\08\index.html.
Auch wenn die Unterschiede aus der Sicht des JavaScript-Entwicklers gering erscheinen: im Internet Explorer und Netscape Navigator sind Plug-ins völlig anders organisiert.
Java Runtime Interface
Im Netscape Navigator verläuft die Kommunikation mit dem Plug-in über eine Java-Klasse myWrapper.class (dies ist ein exemplarischer Name!), die das Plug-in JavaScript gegenüber repräsentiert. Genau wie bei einem Applet sind öffentliche Methoden und Properties dieser Klasse von JavaScript aus zugänglich. Von myWrapper.class aus findet die Kommunikation mit der DLL des Plug-ins über das Java Runtime Interface (JRI) statt.
Wenn Sie im Navigator ein LiveConnect-fähiges Plug-in verwenden, wird also immer die Java-Maschine (JVM) benötigt, was natürlich die Gesamt-Performance negativ beeinflusst. Werfen Sie beim Laden von HTML-Seiten, die Plug-ins mit LiveConnect benutzen, einen Blick auf die Statuszeile des Navigators. Dort sehen Sie die Meldung "Starting Java... " Beim Flash Plug-in muss man daher über den Parameter swLiveConnect explizit angeben, ob man LiveConnect benutzen möchte. Die zum Flash Plug-in gehörende Stellvertreterklasse befindet sich (nach dem ersten Start des Plug-ins mit swLiveConnect="true") im Plug-in-Verzeichnis
C:\Programme\Netscape\Communicator\Program\Plugins
Sie heißt ShockwaveFlash.class.
Bitte beachten Sie, dass ausschließlich die in myWrapper.class definierten Methoden und Properties im JavaScript-DOM auftauchen. Parameter des Elements embed wie etwa width oder height können im Navigator nicht per JavaScript abgefragt werden. Dies gilt im Übrigen auch für Java-Applets.
Wie gelangt nun ein Event vom Plug-in bis zur HTML-Seite? Über das JRI wird aus der DLL des Plug-ins eine Methode myConnection in myWrapper.class aufgerufen. In dieser Methode kann ganz so wie in einem Applet auf das DOM des Browsers zugegriffen werden. In der Regel wird aber immer eine parametrisierte JavaScript-Funktion aufgerufen:
netscape.plugin
import netscape.plugin.*; public class myWrapper extends Plugin { public void myConnection(String sCmd, String sParam) { try { JSObject window = getWindow(); Object[] args = {sCmd, sParam}; window.call("myHandler", args); } catch (Exception e) { } } }
Beachten Sie, dass in diesem Fall getWindow() eine Methode der geerbten Klasse netscape.plugin.Plugin ist.
Abbildung 12.2 fasst die Kommunikation noch einmal zusammen:
Abbildung 12.2: Plug-in/JavaScript-Kommunikation im Netscape Navigator
Der Internet Explorer kann ein Plug-in unmittelbar in eine HTML-Seite integrieren. Wie eingangs erwähnt, sind Plug-ins nicht anderes als ActiveX-Objekte. Die Methoden und Properties dieser Objekte werden eins zu eins ins DOM des Internet Explorers abgebildet, Event-Handler als VBScript-Funktionen implementiert:
Abbildung 12.3: Plug-in/JavaScript-Kommunikation im Internet Explorer
Wenn Sie sich näher mit der ActiveX-Technologie beschäftigen möchten, ist [CHAP97] ein guter Einstieg.
Beachten Sie, dass sich nicht jedes ActiveX-Objekt zur Einbettung in HTML-Seiten eignet. Dazu muss es mit speziellen Interfaces ausgestattet sein.
Unter Umständen kann es nötig werden, in einer HTML-Seite die Existenz eines Plug-ins zu erfragen. Möglicherweise möchten Sie den Benutzer Ihrer Site darauf hinweisen oder ihm die Wahlmöglichkeit zwischen einer "normalen" und einer Plug-in-Repräsentation geben. Von einer automatisierten Weiterleitung in Abhängigkeit von der Installation des Plug-ins sollten Sie jedoch absehen. Der Benutzer weiß selbst am besten, ob er eine geflashte Version Ihrer Website sehen möchte oder nicht.
Wie gehen wir nun vor, um herauszufinden, ob ein bestimmtes Plug-in installiert ist oder nicht? Auch an dieser Stelle müssen wir als JavaScript-Entwickler auf die unterschiedlichen Ansätze der Browser-Hersteller eingehen.
navigator.plugins[]
Im Netscape Navigator können wir auf das Array navigator.plugins[] zugreifen. In Übung 14 wird der Umgang damit detailliert besprochen.
Jeder Array-Eintrag enthält ein Objekt vom Typ Plugin, das unter anderem die Property name hat, die auch als Array-Index verwendet werden kann:
<script language="JavaScript"> var plugFlash = navigator.plugins["Shockwave Flash"]; if (plugFlash != null) { alert("Detected Flash plug-in."); } else { alert("Flash not installed."); } </script>
about:plugins
Dazu muss natürlich der Name des zu testenden Plug-ins bekannt sein. Durch die Eingabe des URLs about:plugins finden Sie ihn heraus.
Im Internet Explorer müssen wir anders vorgehen, denn dort ist navigator.plugins[] immer leer. Außerdem existiert in seinem DOM auch keine andere direkte Repräsentation der installierten Plug-ins. Mit einigen VBScript-Funktionen kommen wir jedoch an die gewünschten Informationen:
CreateObject, Err
<script language="VBScript"> On Error Resume Next set plugFlash = CreateObject("ShockwaveFlash.ShockwaveFlash.4") isFlashed = (Err.Number = 0) set plugFlash = Nothing if isFlashed then alert "Detected Flash plug-in." else alert "Flash not installed." end if </script>
OLE/COM Viewer
Die Funktion CreateObject(sProgID) benötigt einen Parameter vom Typ String, der das Plug-in identifiziert. Diesen sollten Sie in der Dokumentation des Herstellers finden. Sie können aber auch z.B. mit Werkzeugen wie dem OLE/COM Viewer arbeiten, einem Tool aus der Entwicklungsumgebung Visual Studio von Microsoft.
Abbildung 12.4: OLE/COM Viewer: Shockwave Flash
Wir wollen nun einmal Java-Applets und Plug-ins gegenüberstellen, und zwar in Bezug auf Handling, Plattformunabhängigkeit, Performance, Sicherheit und Restriktionen.
Fangen wir an mit dem ...
HTML
Applets werden über das HTML-Element applet, Plug-ins je nach Browser über embed oder object eingebettet. Der Aufwand hierfür hält sich in etwa die Waage.
Installation
Anspruchsvolle Applets bestehen in der Regel aus einer Vielzahl von .class-Dateien, die alle bei jedem Laden der einbettenden HTML-Seite immer wieder geladen werden müssen. Ein wenig Abhilfe schafft hier der Einsatz eines JARs, in dem mehrere .class-Dateien zusammengefasst werden können. In beiden Fällen können die benötigte(n) Datei(en) im Cache des Browsers zwischengespeichert werden. Da die Cache-Konfiguration aber willkürlich ist und der Cache jederzeit manuell geleert werden kann, kann man sich nicht darauf verlassen, dass sich das Applet im Cache befindet.
SmartUpdate
Plug-ins hingegen werden genau einmal heruntergeladen und installiert. Dies geschieht in der Regel im Internet Explorer automatisch und im Netscape Navigator halbautomatisch durch Anzeige eines Download-URLs, unter dem das Setup des Plug-ins heruntergeladen werden kann. Im Internet Explorer bürgt der Anbieter durch den Einsatz einer digitalen Signatur mit einem starken asymmetrischen Verschlüsselungsverfahren für die Echtheit seiner Software. Auch im Navigator gibt es einen solchen automatischen Download-Mechanismus namens SmartUpdate, der aber selbst bei Entwicklern wenig bekannt ist und daher eher selten verwendet wird.
Download
Was nützt das schönste Applet oder Plug-in, wenn es keine Daten zum Anzeigen hat? Hier gibt es keinen Unterschied, denn sowohl Applets als auch Plug-ins müssen ihre Daten zunächst einmal aus dem Netz laden. Beide können dabei auf den Browser-Cache hoffen, doch Plug-ins können ihre Daten grundsätzlich auch selbst auf der Festplatte cachen, um einen Mehrfach-Download zu vermeiden.
Hier haben Applets ganz klar die Nase vorn, da sie auf der Sprache Java aufbauen, die grundlegend als plattformunabhängig konzipiert wurde.
Bei Plug-ins muss man berücksichtigen, dass der Netscape Navigator für viele Plattformen, der Internet Explorer in vollem Umfang "nur" auf Windows-Plattformen verfügbar ist. Bedenken Sie weiterhin, dass ein Hersteller von Plug-ins für den Netscape Navigator auch eine eigene Version seiner Software für jedes Betriebssystem anfertigen muss. Bei Betriebssystemen mit geringem Marktanteil ist dies wirtschaftlich nicht immer sinnvoll.
In dieser Rubrik erweist sich der Vorteil der Plattformunabhängigkeit von Java-Applets ganz klar als Nachteil. Durch die Verwendung der JVM ergibt sich ein spürbarer Geschwindigkeitsverlust im Vergleich zu nativem Objektcode. Dies wird insbesondere bei grafisch anspruchsvollen Anwendungen wie der Wiedergabe von Videos oder 3-D-Welten deutlich. Auch Just-In-Time (JIT) Compiler, die teilweise in den JVMs der Browser zum Einsatz kommen, schaffen hier keine Abhilfe.
Plug-ins hingegen können bei Bedarf sogar auf hochoptimierte Assemblerroutinen und direkt auf spezielle Dienste des Betriebssystems zurückgreifen.
Stellen Sie sich einmal die Frage, warum die Hersteller von Top-Computerspielen den "Vorteil" der Plattformunabhängigkeit der Sprache Java ignorieren und sich auf spezielle Plattformen wie Windows PC oder die Playstation "beschränken"!
Um es kurz zu machen: Java-Applets unterliegen einem Sicherheitsmechanismus, Plug-ins nicht. "Nieder mit den Plug-ins!" werden Sie nun laut ausrufen. Denken Sie zuvor aber noch kurz nach.
Security Manager
Bei Applets ist ein Sicherheitsmechanismus bitter nötig, da diese beim Laden einer HTML-Seite sofort in Aktion treten und so im Zweifelsfall Schaden auf Ihrem System anrichten könnten. Damit dies nicht geschieht, dürfen Applets bestimmte Befehle der Sprache Java nicht ausführen. Der Security Manager des Browsers entscheidet, welche Befehle ausgeführt werden dürfen und welche nicht. Die Entscheidung findet also erst zur Laufzeit des Applets statt, nicht während der Entwicklungszeit.
Plug-ins hingegen müssen zunächst einmal auf Ihr System geladen und danach installiert werden. Dies geschieht nie ohne Ihre ausdrückliche Zustimmung, unabhängig davon, ob der Installationsprozess nun automatisch oder manuell erfolgt.
Allerdings ist es richtig, dass Plug-ins nach der Installation im Gegensatz zu Applets grundsätzlich vollen Systemzugriff haben. Hier gilt für Plug-ins das Gleiche wie für jede andere Software, die Sie aus dem Netz laden oder von CD-ROM installieren: Sie vertrauen dem Hersteller und stimmen vor der Nutzung dem End User License Agreement (EULA) zu.
Eine direkte Folge von Sicherheitsmechanismen sind funktionelle Beschränkungen der Software. Applets unterliegen einer ganzen Reihe solcher Restriktionen. Der Zugriff auf das lokale Dateisystem ist ebenso verboten wie die Errichtung von Netzwerkverbindungen zu fremden Servern oder der Zugriff auf Systemeigenschaften wie etwa den Usernamen.
Plug-ins unterliegen keinen solchen Beschränkungen.
Tabelle 12.3 fasst unsere Vergleiche noch einmal zusammen.
Applets | Plug-in IE | Plug-in NN | |
---|---|---|---|
Handling | - | + | / |
Plattformunabhängigkeit | + | - | / |
Performance | - | + | + |
Sicherheitsmechanismus | vorhanden | nicht vorhanden | nicht vorhanden |
Restriktionen | ja | nein | nein |
Tabelle 12.3: Vergleich zwischen Applets und Plug-ins: + = gut, / = mittel, - = schlecht
Dieser Abschnitt setzt durchschnittliche Kenntnisse in Java voraus.
JavaPackage JavaClass JavaObject JavaArray
Der Vollständigkeit halber wollen wir noch einen Aspekt von LiveConnect erwähnen, den es nur im Netscape Navigator gibt. Dort haben Sie die Möglichkeit, von JavaScript aus auf die Objekt-Hierarchie der Java Virtual Machine zuzugreifen. Intern geschieht dies durch die Nutzung von JavaScript-Objekten vom Typ JavaPackage, JavaClass, JavaObject und JavaArray.
Im Wesentlichen haben wir Zugriff auf öffentliche statische Methoden und Properties von Java-Klassen. Ebenso können wir neue Instanzen von Java-Objekten erzeugen. Ganz wie bei Java-Applets können Sie dann auf öffentliche Methoden und Properties solcher Instanzen zugreifen.
Beachten Sie, dass Java eine typisierte Sprache ist. Wenn Sie Properties Werte zuweisen, muss der Typ des Wertes dem der Property entsprechen. Andernfalls erhalten Sie einen JavaScript-Fehler. Gleiches gilt auch für den Aufruf von parametrisierten Methoden und Konstruktoren.
Schauen wir uns nun einmal einfach einige Beispiele an. Sie werden sehen, dass sich bisher alles viel komplizierter angehört hat, als es ist. Mit
java.lang.System.out.println("Hello Java");
können wir z.B. Text auf die Java-Konsole ausgeben, wie wir es auch schon bei der Behandlung von Applets gesehen haben. Dabei greifen wir auf das Objekt java.lang.System zu. Dieses besitzt eine statische Property out (vom Typ java.io.PrintStream, wie wir gleich sehen werden). Dessen statische Methode println(String) erledigt die Ausgabe auf die Java-Konsole. Java-Objekte können ganz wie JavaScript-Objekte gehandhabt werden:
var java_console = java.lang.System.out; java_console.println("Hello Java");
Gerade wenn nun mehrmals ein println(...) aufgerufen werden sollte, sparen wir uns nicht nur Tipparbeit, sondern ersparen LiveConnect auch, jedes Mal durch die Objekthierarchie zu wandern, bis es bei out angelangt ist.
Testen Sie, was
alert(java_console);
getClass(...)
ausgibt. Mit der JavaScript-Funktion getClass(...) können Sie den Klassennamen des angegebenen Objekts erfragen. Sie liefert einen Rückgabewert vom Typ JavaClass.
alert(getClass(java_console));
liefert [JavaClass java/io/PrintStream].
Verwechseln Sie die JavaScript-Funktion getClass(...) nicht mit der Java-Methode getClass(), die jedem Java-Objekt zu eigen ist.
In Beispiel 9 können Sie Obiges direkt ausprobieren. Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\java\console.html.
new
Erzeugen wir nun eine Instanz eines Java-Objekts:
var url = new java.net.URL("http://www.thx.com:1138/darth/vader.html");
Ganz wie JavaScript-Objekte werden auch Java-Objekte mit dem Operator new erzeugt. Greifen wir nun auf einige Methoden von url zu:
url... | liefert ... |
---|---|
getProtocol(); | http |
getHost(); | www.thx.com |
getPort(); | 1138 |
getPath(); | /darth/vader.html |
Den Umgang mit dem URL-Objekt können Sie in Beispiel 10 nachvollziehen. Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\java\url.html.
Als Java-Programmierer werden sie aufhorchen und merken, dass wir beim vorangegangenen Konstruktor-Aufruf keine Exception abgefangen haben.
Dies ist eine der Schwächen von LiveConnect. Es gibt kein Exception-Handling. Beim Aufruf von
var url = new java.net.URL(document);
bekommen wir einen JavaScript-Error. Ebenso wenig können wir eigene Java-Klassen definieren oder neue von den Basisklassen ableiten. Auch gibt es keine direkte Unterstützung für das Event-Handling von AWT-Komponenten.
Daher tendiert der praktische Nutzen dieses Aspekts von LiveConnect auch gegen Null, zumal er nur im Netscape Navigator vorhanden ist.
Doch lassen wir uns davon nicht abschrecken und erzeugen einmal eine grafische Oberfläche:
var fMain = new java.awt.Frame("Hello Java"); with (fMain) { tbInput = new java.awt.TextArea( "The quick\nbrown fox\njumps over\nthe lazy dog.", 15,60); setLayout(new java.awt.BorderLayout()); add(tbInput, "Center"); pack(); setLocation(0,0); setVisible(true); }
Abbildung 12.5: Java-Dialog mit JavaScript erzeugt
Im Großen und Ganzen (bis auf das with-Statement) ist dies auch der Code, der in einer entsprechenden Java-Anwendung verwendet werden würde.
Sie werden merken, dass Sie das neu erzeugte Window nicht mit dem Kreuz rechts in der Titelleiste schließen können. Dies liegt an dem bereits erwähnten fehlenden Event-Handling. Es schließt sich erst, wenn Sie das Browser-Fenster schließen.
Der folgende Code demonstriert den Einsatz eines FileDialogs:
fFile = new java.awt.FileDialog( fMain, "Datei öffnen", java.awt.FileDialog.LOAD); with (fFile) { show(); var fn = getFile(); if (fn) { fn = getDirectory() + fn; var fp = new java.io.File(fn); alert(fn + " size: " + fp.length()); } }
Beispiel 11 fasst den Umgang mit AWT-Komponenten noch einmal zusammen. Das vollständige Beispiel finden Sie auf dem beiliegenden Datenträger unter \kapitel12\beispiele\java\awt.html.
Sicherheit
Wie Sie sehen, erhalten wir so scheinbar einen Zugriff auf das lokale Dateisystem. Die Betonung liegt auf scheinbar, denn wir können die Datei nun nicht zum Lesen oder gar Schreiben öffnen. Wir unterliegen den gleichen Sicherheitsrestriktionen der JVM wie Applets. Seien Sie also beruhigt.
Fassen wir noch einmal zusammen. Das direkte Ansprechen von Java-Objekten ist für den JavaScript-Entwickler von geringem Nutzen, weil es ...
Dennoch werden die Tüftler unter Ihnen in den Übungen noch einige nützliche Dinge entdecken.
Die folgenden Betrachtungen spiegeln den Wissensstand im Juni 2000 wider. Bis zum Erscheinen dieses Werkes kann und wird sich wahrscheinlich einiges geändert haben.
Netscape 6
Was wird uns Netscape 6 hinsichtlich LiveConnect bringen? Insbesondere im Zusammenhang mit dieser Frage sollten Sie beachten, dass der Netscape 6 Preview Release 1 technisch gesehen noch nicht einmal im Beta-Stadium ist. Der Release einer Version 6 hat in der Hauptsache marketingtechnische Gründe, unter anderem nämlich den, den Internet Explorer, der zur Zeit bei 5.5 Beta ist, in der Versionsnummer (und auch wirklich nur hier) zu überrunden. Ein weiteres Argument aus der Marketingabteilung ist das des geringen Downloads gegenüber dem Internet Explorer, was unter anderem auch dadurch erreicht wird, dass die Java Virtual Machine nicht mehr in der Standard-Distribution von Netscape 6 enthalten sein wird. Allerdings ist dies auch eine Folge der strikten Beachtung der W3C-Standards.
Wie wir jedoch gesehen haben, basiert LiveConnect komplett auf einer JVM. Daher werden Java-Applets und Plug-ins, die LiveConnect verwenden, mit einer Standardinstallation nicht funktionieren. Doch auch mit einer optional installierten JVM (wie es z.B. beim Preview Release mit seinem "mageren" 15 MB Download möglich ist) funktionieren Plug-ins mit LiveConnect nicht, weil sich die Plug-in-API geändert hat und so zunächst der Plug-in-Hersteller eine neue Version seiner Software bereitstellen muss.
XPConnect
Doch seien Sie unbesorgt. Es wird LiveConnect auch in Zukunft geben, dann jedoch unter anderem Namen: XPConnect. XP steht dabei für Cross Platform (engl. cross = Kreuz = X), ein Präfix für eine ganze Reihe von neuen Begriffen wie XPCOM und XPInstall. Näheres hierzu finden Sie unter http://www.mozilla.org/projects/xpcom/.
XPConnect wird ohne eine JVM die Kommunikation zwischen Plug-ins und JavaScript ermöglichen. Dies ist zu begrüßen, da Plug-ins dann ohne den Umweg über Java gescriptet werden können.
XPInstall
XPInstall wird aller Voraussicht nach der Nachfolger von SmartUpdate sein. SmartUpdate basiert momentan auch noch komplett auf der JVM des Browsers, so dass auch hier ein passender Ersatz gefunden werden muss.
Wann aber mit einer Beta-Version von Netscape 6, die all die Features anbietet, oder gar einer Vollversion gerechnet werden kann, ist zur Zeit noch nicht absehbar. Die aktuellsten Testversionen finden Sie stets unter http://www.mozilla.org/. Unterschiede zwischen diesem Open-Source- Softwareprojekt und dem späteren davon abgeleiteten Netscape 6 sind allerdings nicht auszuschließen.
leicht
Wie referenzieren Sie im Netscape Navigator ein Applet namens myApplet in einem Layer namens myLayer? Nur eine der möglichen Antworten ist richtig.
leicht
Wie betten Sie ein Applet namens myApplet der Größe 500 mal 200 Pixel, dessen Java-Klasse myJava.class ist, in eine HTML-Seite ein?
leicht
Ändern Sie den Text, den das Applet HelloLiveConnect anzeigt. Manipulieren Sie dazu die öffentliche Variable sMessage. Benutzen Sie zur Texteingabe den JavaScript-Befehl prompt.
mittel
Erweitern Sie das Applet aus Übung 3 um eine öffentliche Variable nRepeats. Dabei soll nRepeats vom Typ int sein und angeben, wie oft der Text in sMessage untereinander gezeichnet werden soll. Der Zeilenabstand betrage 20 Pixel. Fügen Sie dem HTML-Dokument ein Formular hinzu mit Eingabe-Feldern für sMessage und nRepeats.
leicht
Erweitern Sie das Applet aus Übung 4 um eine öffentliche Variable isDropShadow. Dabei soll isDropShadow vom Typ boolean sein, und angeben, ob die Textausgabe mit Schattenwurf erfolgen soll. Fügen Sie dem HTML-Dokument eine Checkbox hinzu, deren Zustand den Wert von isDropShadow bestimmt.
schwer
Schreiben Sie eine JavaScript-Funktion inspectObject(sItem), die alle Properties eines Applets in einem neuen Browser-Fenster auflistet. Dabei soll sItem ein String sein, der die Referenz auf ein Applet enthält, so z.B. "document.myApplet".
schwer
Schreiben Sie ein Applet, das beim Bewegen der Maus deren Koordinaten in der Statuszeile des Browsers ausgibt. Außerdem soll das Applet beim Mausklick den URL in einem neuen Browserfenster anzeigen, der im Applet-Parameter url definiert wird.
leicht
Wie wird einem Applet der Zugriff auf die JavaScript-Objekthierarchie erlaubt? Nur eine der möglichen Lösungen ist richtig:
schwer
Entwerfen Sie eine Java-Klasse JSWrapper(java.applet.Applet), die den Zugriff von Java aus auf die JavaScript-Objekthierarchie vereinfacht. Sie soll zwei Methoden
String get(sDOMItem, sProperty); void set(sDOMItem, sProperty, sValue);
haben, mit denen Sie z.B. auf document.myForm.myInput.value wie folgt zugreifen:
JSWrapper jsw = new JSWrapper(this); String val = jsw.get("document.myForm.myInput", "value"); jsw.set("document.myForm.myInput", "value", "LiveConnect");
Gehen Sie dabei von Beispiel 5 aus und ändern Sie dort die Methoden do_read() und do_write() so ab, dass Sie mit der neuen Klasse JSWrapper arbeiten.
mittel
Geben Sie in einem Input-Feld eines HTML-Formulars den aktuellen Frame eines Flash-Movies aus, während dieser abgespielt wird. Gehen Sie dabei von Beispiel 7 aus.
leicht
Wie rufen Sie aus einer VBScript-Funktion eine JavaScript-Funktion namens myJavaScript(sParam) auf? Nur eine der möglichen Lösungen ist richtig:
mittel
In dieser Übung machen wir uns mit den verschiendenen Properties eines Flash-Movies vertraut. Tabelle 12.4 stellt nur einen Teil aller Properties dar.
Property-ID | Bedeutung |
---|---|
0 | X-Position (0, ...) |
1 | Y-Position (0, ...) |
2 | Skalierung in X-Richtung (prozentual, ganzzahlig) |
3 | Skalierung in Y-Richtung (prozentual, ganzzahlig) |
6 | Alpha-Blending (0=transparent, ..., 100=voll deckend) |
7 | Rotation in Grad (0, ..., 360) |
Tabelle 12.4: Ausgesuchte Properties eines Flash-Movies
Fügen Sie Ihrem HTML-Dokument ein select-Feld hinzu, dessen option-Elemente jeweils die Property-ID als Wert haben. In einem input-Feld kann ein Wert eingegeben werden, der per Druck auf einen Button dann der entsprechenden Property zugewiesen wird.
schwer
Benutzen Sie ein fertiges Applet HSlider.class. Dieses stellt einen horizontalen Scrollbalken zur Verfügung, der bei Änderung seines Wertes eine JavaScript-Funktion aufruft, die den Wert als Parameter übergeben bekommt. Den Namen der aufzurufenden JS-Funktion und den Wertebereich legen Sie mit der Applet-Methode
setHook(String sJSFunc, int iMin, int iMax);
fest. Erweitern Sie die vorangegangene Übung um zwei Input-Felder, über die Sie die Werte für iMin und iMax eingeben können. Diese und der Name einer JavaScript-Funktion
function changeProperty(sValue)
übergeben Sie per Knopfdruck an das Applet. In changeProperty(...) werten Sie den Wert der select-Box aus, um die zu ändernde Property des Flash-Movies zu erhalten.
mittel
Schreiben Sie für den Netscape Navigator einen Ersatz für den URL about:plugins. Dieser existiert im Preview Release des Netscape 6 noch nicht und wir möchten trotzdem gerne wissen, welche Plug-ins wir dort installiert haben.
schwer
Zeigen Sie in einem neuen Browser-Fenster den Inhalt der aktuellen HTML-Seite als ASCII-Text an. Es geht also darum, eine eigene Behandlung des Pseudo-Protokolls view-source: zu implementieren.
schwer
Schreiben Sie eine JavaScript-Funktion HTTPHeader(sURL), die einen HTTP HEAD-Request auf den angegebenen URL ausführt und das Ergebnis der Anfrage in einem neuen Browser-Fenster ausgibt.
Initialisieren Sie den Wert von nRepeats in der init()-Methode des Applets auf 1. Eine for-Schleife hat in Java fast genau die gleiche Syntax wie in JavaScript:
for (int i=1; i <= nRepeats; i++);
Initialisieren Sie den Wert von isDropShadow in der init()-Methode des Applets auf false. Um einen Schattenwurf zu erreichen, zeichnen Sie den Text ein zweites Mal um jeweils zwei Pixel nach rechts und nach unten versetzt in schwarzer Farbe. Der Wert für Schwarz ist in Color.black gespeichert. Ein Input-Element vom Typ checkbox besitzt eine Property checked, die angibt, ob das Häkchen gesetzt ist oder nicht.
Benutzen Sie die Funktion eval(...), um aus dem String sItem eine Objekt-Referenz zu erstellen. Im Kapitel über den Umgang mit Objekten haben wir gelernt, wie man alle Properties eines Objekts auflisten kann. Bei Applets kann genauso vorgegangen werden. Wie wir allerdings sehen werden, müssen wir an dieser Stelle für den Internet Explorer eine spezielle Fehlerbehandlung einbauen.
Um ein neues Browser-Fenster zu öffnen, verwenden Sie die Methode window.open(...). In das abgeleitete document-Objekt schreiben Sie Ihr Dokument vom Type text/plain.
Eigene Parameter werden in der HTML-Seite mit
<param name="url" value="http://www.addison-wesley.de/">
gesetzt und werden im Applet während der Initialisierung (also in der Methode init()) mit der Methode getParameter(String sParamName) ausgelesen. Die Methoden showStatus(String sText) und showDocument(URL url, String sTarget) gehören zu einem Objekt vom Typ AppletContext, das Sie mit getAppletContext() erfragen können.
Um die Events MouseMove und MouseClick abzufangen, sollten Sie mit dem Event-Modell von Java vertraut sein. Andernfalls werfen Sie heimlich einen Blick in die Lösung.
Zerlegen Sie das Problem in kleinere Teilprobleme. Bevor wir an die Methoden get(...) und set(...) gehen, entwerfen wir zunächst eine Methode
JSObject getObject(String sDOMItem);
die uns eine Referenz auf ein beliebiges JavaScript-Objekt unterhalb von window liefert.
Dann verwenden Sie die Klasse java.util.StringTokenizer, mit der wir einen String durch Angabe eines Trennzeichen in mehrere Teilstrings zerlegen können. Dies ist in etwa mit der JavaScript-Funktion String.split(...) zu vergleichen. Innerhalb einer for-Schleife wandern Sie dann durch die Objekt-Hierarchie.
Wenn getObject(...) implementiert ist, sollen get(...) und set(...) darauf zurückgreifen.
Verwenden Sie eine JavaScript-Funktion readFrame(), die Timer-gesteuert alle 100 Millisekunden den aktuellen Frame mit der Methode TGetProperty("/",4) vom Plug-in erfragt. Bei Starten des Movies soll der Timer gestartet, beim Stoppen beendet werden.
Greifen Sie auf das Array navigator.plugins[] zurück. Dieses enthält JavaScript-Objekte vom Typ Plugin mit den Properties description, filename, name und length. Dabei gibt length die Anzahl der vom Plug-in unterstützten MIME-Typen an. Ein Plugin-Objekt ist nämlich auch wiederum ein Array, das Objekte vom Typ MimeType enthält, d.h. mit
navigator.plugins[i][j]
greifen Sie auf das j-te MimeType-Objekt des i-ten Plug-ins zu. Ein Objekt vom Typ MimeType besitzt unter anderem die Properties type und suffixes.
Erzeugen Sie zunächst wie in Beispiel 10 ein URL-Objekt. Dessen Methode openStream() liefert ein Objekt vom Typ java.io.InputStream, mit dem wir per Konstruktor-Aufruf ein Objekt din vom Typ java.io.DataInputStream erzeugen. Ein solches Stream-Objekt hat eine Methode readLine(), die so lange einen String zurückgibt, bis das Ende des Streams erreicht ist.
Um ein neues Browser-Fenster zu öffnen, verwenden Sie die Methode window.open(...). In das abgeleitete document-Objekt schreiben Sie Ihr Dokument vom Type text/plain.
Bauen Sie auf der Lösung zu der vorangegangenen Übung auf. Verwenden Sie ein Objekt sock vom Typ java.net.Socket, mit dem man TCP/IP-Netzwerkverbindungen aufbauen kann. Ein HTTP HEAD-Request zu einem URL http://www.host.de/path/file.gif sieht minimal so aus:
HEAD /path/file.gif HTTP/1.1 Host: www.host.de
Nähere Informationen finden Sie in [RFC2616]. Leiten Sie von sock zwei Objekte dout vom Typ java.net.DataOutputStream und din vom Typ java.net.DataInputStream ab. Schreiben Sie den Request nach dout und lesen Sie die Response mit din.readLine() so lange, bis eine Leerzeile zurückgegeben wird.
Beachten Sie, dass die entsprechende HTML-Seite von einem Webserver geladen werden muss, weil wir eine echte TCP/IP-Netzwerkverbindung aufbauen. Einen kostenlosen Webserver können Sie beispielsweise unter http://www.apache.org/ herunterladen.
Richtig ist
document.layers["myLayer"].document.myApplet
Der Navigator verwaltet für jeden Layer ein eigenes document-Objekt. Zunächst referenzieren wir über das layers-Array den Layer selbst:
document.layers["myLayer"]
Nun greifen wir auf dessen document zu, das schließlich das Applet myApplet enthält, und erhalten so:
document.layers["myLayer"].document.myApplet
Richtig ist
<applet name="myApplet" code="myJava.class" width="500" height="200"></applet>
Der Name des Applets wird über das Attribut name angegeben, mit code benennen wir die Java-Klasse, width und height bestimmen die Abmessungen des Applets.
var sText = prompt("Neuer Text", "Hello Addison-Wesley"); if (sText) { document.myApplet.sMessage = sText; document.myApplet.repaint(); }
Die Anweisung document.myApplet.repaint(); mag Sie verwundern. Warum reicht es nicht aus, die Eigenschaft sMessage zu setzen, um auch visuell eine Veränderung zu erzielen? Nun, das Setzen der Eigenschaft sMessage löst kein Neuzeichnen des Applets aus, sondern weist lediglich einer Variable einen neuen Wert zu. Die Methode repaint() haben wir nicht selbst implementiert, sie ist allen Applets eigen. Erst ihr Aufruf löst das Neuzeichnen aus.
Um Tipparbeit (und damit verbundene mögliche Fehlerquellen) zu vermeiden, können wir auch bei Referenzen auf Java-Applets mit dem with-Statement arbeiten:
var sText = prompt("Neuer Text", "Hello Addison-Wesley"); if (sText) with (document.myApplet) { sMessage = sText; repaint(); }
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\03\index.html.
Schauen wir uns zunächst die Erweiterungen des Java-Applets gegenüber Listing 12.1: HelloLiveConnect.java an:
import java.awt.*; public class HelloLiveConnect2 extends java.applet.Applet { public String sMessage; // text message public int nRepeats; // #lines public void init() { // init global values sMessage = getParameter("message"); if (sMessage==null) sMessage ="<Parameter missing>"; nRepeats = 1; // paint the applet repaint(); } public void paint(Graphics g) { // draw background g.setColor(Color.blue); g.fillRect(0,0, getSize().width, getSize().height); // draw message g.setColor(Color.white); for (int i=1; i<=nRepeats; i++) g.drawString(sMessage, 20, 20*i); } }
In die HTML-Seite fügen wir ein Element form ein:
<form name="myForm"> sMessage: <input name="myMessage" type="text" value="Hello Addison-Wesley"><br> nRepeats: <input name="myRepeats" type="text" value="5"><br><br> <input type="button" value="Set values" onClick="doChanges(this.form)"> </form>
Beachten Sie den Parameter, den wir der Funktion doChanges(...) übergeben. this verweist im Kontext des Elementes input auf den Button selbst. Die Property form ist ein Verweis auf die Form des Buttons. So ersparen wir uns eine absolute Adressierung der HTML-Form in der Funktion doChanges:
function doChanges(src) { with (document.myApplet) { sMessage = src.myMessage.value; nRepeats = parseInt(src.myRepeats.value); repaint(); } }
Beachten Sie die Verwendung der Funktion parseInt(...). Unser Applet erwartet für nRepeats einen Wert vom Typ int (Integer).
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\04\index.html.
Wir werden ausgehend von der Lösung 4 nur noch die Änderungen am Sourcecode des Applets herausstellen:
import java.awt.*; public class HelloLiveConnect3 extends java.applet.Applet { ... public boolean isDropShadow; // drop shadow? public void init() { ... isDropShadow = false; } public void paint(Graphics g) { ... // draw message for (int i=1; i<=nRepeats; i++) { if (isDropShadow) { g.setColor(Color.black); g.drawString(sMessage, 22, 20*i + 2); } // text g.setColor(Color.white); g.drawString(sMessage, 20, 20*i); } }
Unser HTML-Formular erweitern wir um eine Checkbox ...
<input name="myDropShadow" type="checkbox" value="true" checked> isDropShadow
... und die JavaScript-Funktion doChanges(...) um folgende Zeile:
isDropShadow = src.myDropShadow.checked;
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\05\index.html.
Zuerst wandeln wir den String sItem in eine Objekt-Referenz um:
var oItem = eval(sItem);
Außerdem legen wir einen String p an, der alle Properties von oItem aufnimmt und außerdem die textuelle Repräsentation des Objekts enthält:
var p = new String("ObjectInspector: " + sItem + "\n\n");
Nun schauen wir uns an, wie die Auflistung der Properties geschieht. Wie bei jedem Objekt kann auf die Properties über ein assoziatives Array zugegriffen werden. Der Name der Property dient dabei als Index:
for(prop in oItem) p += prop + ": " + oItem[prop] + "\n";
Mittels der for/in-Anweisung können wir alle Properties von oItem durchlaufen. Wir schreiben jeweils die Property und ihren Wert getrennt durch einen Doppelpunkt in eine eigene Zeile.
Im Internet Explorer produziert diese syntaktisch korrekte Lösung allerdings einen JavaScript-Fehler. Vermutlich werden nicht alle Objekte korrekt im DOM des IEs repräsentiert. Durch den Einsatz einer try/catch-Anweisung und einer Ersetzung des []-Operators durch eine eval(...)-Anweisung kommen wir weiter:
try { for(prop in oItem) p += prop + ": " + eval(sItem + "." + prop) + "\n"; } catch (e) { }
Wir nutzen dabei die Tatsache aus, dass die beiden Ausdrücke oItem[prop] und eval(sItem + "." + prop) äquivalent sind. Um mit einem einzigen Script für beide Browser auszukommen, müssen wir noch zu einem Trick greifen, da der Navigator die try/catch-Anweisung (noch) nicht versteht. Wir lassen den Block mit try/catch innerhalb einer eigenen eval()-Anweisung ausführen. Eine Unterscheidung zwischen NN und IE treffen wir der Einfachheit halber über die Existenz des Objektes document.all:
function inspectObject(sItem) { var oItem = eval(sItem); var p = new String("ObjectInspector: " + sItem + "\n\n"); if(document.all) eval('try{for(prop in oItem) p += prop + ":" + eval' + '(sItem + "." + prop) + "\\n"} catch(e){}' ); else for(prop in oItem)p += prop + ": " + oItem[prop] + "\n"; // show page in new window var w = window.open("", "CC_ObjInsp", "width=300,height=600,screenX=1,screenY=1,top=1,left=1," +"resizable=yes,scrollbars=yes"); var d = w.document; d.open("text/plain"); d.write(page); w.focus(); }
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\06\index.html.
Wir wollen an dieser Stelle nicht zu detailliert auf die eigentliche Java-Programmierung eingehen. Insbesondere die Details zum Event-Handling entnehmen Sie bitte dem Quellcode. Wir werden uns darauf konzentrieren, was beim Bewegen und Klicken der Maus passiert:
public void mouseMoved(MouseEvent event) { Point p = event.getPoint(); x = p.x; y = p.y; repaint(); // show status getAppletContext().showStatus("x=" + x + " y=" + y); }
Das Objekt event vom Typ MouseEvent liefert seine Koordinaten mit der Methode getPoint(). Diese werden für das Neuzeichnen des Applets verwendet und eben auch für den Aufruf von showStatus(...).
public void mouseClicked(MouseEvent event) { try { URL url = new URL(sURL); getAppletContext().showDocument(url, "_blank"); } catch (MalformedURLException e) { } }
Die Variable sURL ist eine globale Variable vom Typ String, die in der init()-Methode vorbelegt wurde. Der try/catch-Block ist notwendig, weil in sURL ein syntaktisch falscher URL stehen könnte, was beim Erzeugen des URL-Objekts eine Exception vom Typ MalformedURLException werfen könnte. Wenn es keinen Fehler gibt, wird schließlich showDocument(...) aufgerufen. Als Target geben wir "_blank" an, um ein neues Browser-Fenster zu öffnen.
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\07\index.html.
Die richtige Lösung lautet:
<applet name="myApplet" code="myClass.class" width="100" height="50" mayscript> </applet>
Das Attribut mayscript des HTML-Elements applet muss angegeben werden.
Zunächst kümmern wir uns um die Klassendefinition und den Konstruktor:
import java.util.StringTokenizer; import netscape.javascript.*; public class JSWrapper { private JSObject oWindow; public JSWrapper(java.applet.Applet applet) { try { oWindow = JSObject.getWindow(applet); } catch (Exception e) { } } ... }
Die Wurzel der Objekt-Hierarchie oWindow wird im Konstruktor angelegt und steht allen Methoden der Klasse im weiteren Verlauf zur Verfügug.
public JSObject getObject(String sDOMItem) { try { JSObject oParent = oWindow; StringTokenizer st = new StringTokenizer(sDOMItem, "."); int nTokens = st.countTokens(); for (int i=0; i < nTokens; i++) oParent = (JSObject)oParent.getMember(new String(st.nextToken())); return oParent; } catch (Exception e) { return null; } }
Der fettgedruckte Bereich ist von Interesse. Dort wird für jedes Unter-Objekt einmal getMember(...) aufgerufen, und der Objekt-Verweis oParent rückt weiter nach unten in der Hierarchie. Diese Schleife würde z.B. für "document.myForm.myInput" dreimal durchlaufen, oParent beginnt als Verweis auf das JavaScript-Objekt window und steht am Ende schließlich auf document.myForm.myInput.
Die Methoden get(...) und set(...) sind nun äußerst einfach zu gestalten:
public String get(String sDOMItem, String sMember) { JSObject oItem = getObject(sDOMItem); return (String)(oItem.getMember(sMember)); } public void set(String sDOMItem, String sMember, String sValue) { JSObject oItem = getObject(sDOMItem); oItem.setMember(sMember, sValue); }
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\09\index.html.
Der Name des Plug-ins sei myPlug:
var timerID; function flStart() { document.myPlug.Play(); timerID = setInterval(readFrame,100); } function flStop() { document.myPlug.StopPlay(); clearInterval(timerID); } function readFrame() { document.myForm.myFrame.value = document.myPlug.TGetProperty("/",4); }
Die JavaScript-Methode setInterval(...) liefert in flStart() die ID des Timers zurück, die dann später in flStop() verwendet wird, um den Timer zu stoppen.
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\10\index.html.
Die richtige Lösung lautet:
call myJavaScript(sParam)
VBScript ist eine zeilenorientierte Sprache, die das Semikolon ; nicht als Trenner zwischen zwei Befehlen kennt. Der Aufruf wird mit call ... erledigt.
Das select-Feld können Sie etwa so gestalten:
<select name="mySelect"> <option value="0" selected> 0 - X Position: >=0 <option value="1"> 1 - Y Position: >=0 <option value="2"> 2 - X Scale: Percentage <option value="3"> 3 - Y Scale: Percentage <option value="6"> 6 - Alpha: 0-100 <option value="10">10 - Rotation: 0-360 </select>
Im Event-Handler für den Button-Click gehen Sie so vor:
var s = document.myForm.mySelect; var iPropID = parseInt(s.options[s.selectedIndex].value); var dValue = parseFloat(document.myForm.myValue.value); document.myPlug.TSetProperty("/", iPropID, dValue);
Beachten Sie, dass die Property-ID als Ganzzahl und der entsprechende Wert als Fließkommazahl übergeben werden.
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\12\index.html.
Zunächst betten Sie das Applet ein:
<applet name="myApplet" width="550" height="15" code="HSlider.class" mayscript> </applet>
Dann müssen Sie dem Applet mitteilen, welchen Wertebereich der Scrollbalken haben soll:
var min = parseInt(document.myForm.myMin.value); var max = parseInt(document.myForm.myMax.value); document.myApplet.setHook("changeProperty", min, max); // save prop ID in global var var s = document.myForm.mySelect; iPropID = parseInt(s.options[s.selectedIndex].value);
Wir speichern die Property-ID in einer globalen Variable iPropID, um diese später sofort zur Verfügung zu haben:
function changeProperty(sValue) { document.myForm.myCurrent.value = sValue; document.myPlug.TSetProperty("/", iPropID, parseFloat(sValue)); }
Der Übersicht halber geben wir den aktuellen Wert des Scrollbalkens in einem input-Feld namens myCurrent aus.
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\13\index.html.
var l = navigator.plugins.length; var s ="<h1>Plug-ins: #" + l + "</h1>"; for (var i=0; i<l; i++) { plugin = navigator.plugins[i]; s += "<hr><h2>" + plugin.name + "</h2><b>Description:</b> " + plugin.description + "<br><b>File:</b> " + plugin.filename + "<br>"; for (var j=0; j < plugin.length; j++) { mime = plugin[j]; s += "MIME-Typ " + j + ": <b>" + mime.type + "</b> Extension(s): <b>" + mime.suffixes + "</b><br>"; } } document.write(s);
Einfacher als gedacht, nicht wahr? In der äußeren Schleife durchlaufen wir alle Plug-in-Objekte plugin und geben deren Eigenschaften aus. In einer inneren Schleife geben wir alle zugehörigen MIME-Typen mime und deren Eigenschaften type und suffixes aus.
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\nnplugins.html.
Sie werden überrascht sein, wie simpel dies ist:
function getURLContent(sURL) { var url = new java.net.URL(sURL); var din = new java.io.DataInputStream(url.openStream()); var con = "", line = ""; while ( ( line = din.readLine() ) != null) con += line + "\n"; return con; } function ViewSource(sURL) { var page = getURLContent(sURL); // show page in new window var w = window.open("", "CC_ViewSource", "width=400,height=600,screenX=1,screenY=1,top=1,left=1," +"resizable=yes,scrollbars=yes"); var d = w.document; d.open("text/plain"); d.write(page); w.focus(); }
Wir kommen mit ganzen fünf Zeilen Code aus, um ein Dokument zeilenweise von einem URL zu lesen. Dies ist unabhängig davon, ob es sich um einen http:-URL handelt oder um eine lokale Datei.
Zunächst wird aus dem String sURL ein neues Objekt url vom Typ java.net.URL konstruiert. Mit Hilfe von url.openStream() konstruieren wir dann ein Objekt din vom Typ java.io.DataInputStream. Nun können wir mit einer while-Schleife so lange zeilenweise einen String aus dem Stream din einlesen, bis readLine() nur noch null zurückgibt. Der Inhalt wird zeilenweise zu con aufaddiert und findet schließlich als Rückgabewert der Funktion getURLContent(...) Verwendung. Die Funktion ViewSource(...) erledigt die Ausgabe in ein neues Browser-Fenster, was wir an dieser Stelle nicht weiter kommentieren.
Mit getURLContent(...) haben Sie nun also eine Möglichkeit, auch im Netscape Navigator an den Quelltext der aktuellen HTML-Datei zu gelangen. Es ist auf diese Weise sogar möglich, auf Dateien im gleichen Verzeichnis oder in Unterverzeichnissen zuzugreifen. Dabei müssen Sie lediglich beachten, dass getURLContent(...) bzw. ViewSource(...) immer eine absolute URL verlangt. Mit einem kleinen Trick ist aber auch dies ohne das manuelle Zusammenbasteln von Strings leicht getan:
function ViewSourceRel(sURL) { var url_self = new java.net.URL(self.location.href); var url_abs = new java.net.URL(url_self, sURL); ViewSource(url_abs); }
Die Klasse java.net.URL verfügt nämlich auch über einen Konstruktor mit zwei Parametern:
public URL(URL BaseURL, String sRelativeURL);
So können wir also relativ zu dem URL BaseURL einen String angeben. Daraus entsteht dann die absolute Adresse zu sRelativeURL. In unserem Fall ist der Basis-URL der des aktuellen HTML-Dokuments, also self.location.href. Nach Erzeugen des absoluten URLs übergeben wir diesen wie gehabt an ViewSource(...).
Was so allerdings (noch) nicht funktioniert, ist der Zugriff auf einen beliebigen URL. Dies ist aufgrund der Sicherheitsbeschränkungen der JVM zunächst noch nicht möglich, weil Netzwerkverbindungen zu fremden Servern verboten sind. Wir können dieses Recht jedoch anfordern. Dies geschieht mit dem Befehl
netscape.security.PrivilegeManager.enablePrivilege("UniversalConnect");
Es erscheint ein Dialog, auf dem Sie den Button Grant drücken, um das angeforderte Privileg zu gewähren. Danach können wir auch auf beliebige URLs zugreifen. Dies ist allerdings nur möglich, wenn Sie die HTML-Seite von Ihrer Festplatte aus öffnen!
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\java\viewsource.html.
Wir betrachten nur den Download der Information, die Ausgabe in ein neues Browser-Fenster geschieht ganz genau so wie in der vorangegangenen Lösung:
function getHTTPHeader(sURL) { var url = new java.net.URL(sURL); var hostname = url.getHost(); var port = url.getPort(); if (port == -1) port = 80; // default HTTP port var sock = new java.net.Socket(hostname, port); sock.setSoTimeout(30 * 1000); // 30 secs var din = new java.io.DataInputStream(sock.getInputStream()); var dout = new java.io.DataOutputStream(sock.getOutputStream()); var con = "", line = ""; var CRLF = "\r\n"; var request = "HEAD " + url.getFile() + " HTTP/1.1" + CRLF + "Host: " + hostname + ":" + port + CRLF + "User-agent: " + navigator.userAgent + CRLF + CRLF; dout.writeBytes(request); dout.flush(); while ((line=din.readLine()) != null && (line!="")) con += line + "\n"; return con; }
Beachten Sie, dass ein URL-Objekt ohne explizite Angabe eines Ports in sURL immer -1 zurückgibt. In dieser Situation benutzen wir den Standard-Port 80 von Webservern. Nachdem wir unseren Request geschrieben haben, lesen wir so lange, bis das Ende der Response, markiert durch eine Leerzeile, erreicht ist.
Die vollständige Lösung finden Sie auf dem beiliegenden Datenträger unter \kapitel12\uebungen\java\httpheader.html.
1 Die aktuelle Version des Java-Plug-ins finden Sie unter http://java.sun.com/products/plugin/ [zurück]