Advanced
Java
Services
|
Applet Grundlagen |
Im Gegensatz zu einer Applikation ist ein Applet ein Javaprogramm, das in eine HTML-Seite eingebettet
ist und vom Browser aufgerufen wird, wenn dieser die HTML-Datei abarbeitet. Genauer gesagt, enthält
die HTML-Datei einen Verweis auf eine class-Datei. Neben diesem Verweis befindet sich noch eine
Größenangabe für eine rechteckige Fläche, die vom Browser für das Applet reserviert wird. Mit Hilfe
einer Javaumgebung versucht dann der Browser, dieses Programm zu laden und innerhalb dieser Fläche
darzustellen. Dazu braucht der Browser notwendigerweise eine Javaumgebung. MS-InternetExplorer hat die
Unterstützung für Applets seit der Version 1.1 eingestellt, es ist aber keine Problem, über ein plug-in
dem Internet Explorer eine zur Verfügung zu stellen, siehe JavaUmgebung .
Netscape oder Mozilla etwa haben von Haus aus eine neuere Javaumgebung.
Ein Applet ist ein Panel
Betrachtet man die AWT-Klassenhierarchie, sieht man,
daß ein Applet eine Unterklasse von Panel ist. Das für ein Applet voreingestellte Layout ist also
wie bei einem Panel FlowLayout. Das vergißt man immer wieder gerne, wenn man eine Application in ein
Applet verandeln will, da Frame Borderlayout als DefaultLayout hat. Verwendet man ein JApplet, so
entfällt diese Überlegung. Ein JApplet benützt ebenso wie ein JFrame ein JRootpane als Container.
JFrame und JApplet haben damit das gleiche objektorientierte Design.
Das Applet-Tag
Als Minimalinformation muß der Browser den Namen der class-Datei des Applets und die zu reservierende
Größe erhalten
<applet code="MyApplet.class" width=400 height=300>
Text an dieser Stelle wird vom Browser nicht angezeigt
</applet>
Text zwischen dem Anfangs- und Endetag wird vom Browser nicht angezeigt. Falls ein Applet nicht gefunden
wird erscheint in der Statuszeile ein Text wie "Applet MyApplet not inited", die in HTML reservierte
Fläche bleibt dunkelgrau und ein rollover mit der Maus bringt, falls man einen deutschsprachigen
Browser benutzt die Meldung "Fehler beim Laden des Applets".
Aufbau und Lebenszyklus
Da der Browser für die Darstellung eines Applets eine Fläche reserviert, die er aus dem Applet-Tag
erfährt, hat ein Applet immer eine graphische Oberfläche, es gibt also keine Konsolanwendungen.
Meldungen mit System.out.println() werden vom Browser einfach verschluckt. Zum Austesten sind
aber solche Meldungen trotzdem sinnvoll, denn der Appletviewer zeigt Konsolausgaben an.
Des weiteren muß eine AppletAnwendung immer von der Klasse Applet abgeleitet werden. Ein Applet
braucht auch keine main()-Methode, da der Browser mit Hilfe der JavaUmgebung direkt ein Objekt
instanziert und dann eine Reihe von StandardMethoden aufruft, über die jedes selbstgeschrieben
Applet über Vererbung verfügt. Durch die Vererbung ist sichergestellt, daß jede AppletAnwendung
diese Methoden besitzt. Die Methoden init(), start(), stop() und destroy() werden vom Browser
in einer bestimmten Reihenfolge aufgerufen und bestimmen den Lebenszyklus eines Applets.
Aufbau eines Applets
import java.awt.* ;
import java.applet.* ;
public class MyApplet extends Applet
{
public MyApplet()
{
// Der Konstruktor wird meist weggelassen
// Initialisierungen werden für gewöhnlich in der init()-Methode erledigt
}
public void init()
{
// Der Browser ruft diese Methode unmittelbar nach dem Konstruktor
// und nur ein einziges Mal
}
public void start()
{
// Diese Methode wird zum erstenmal nach init() aufgerufen,
// dann wieder nach der stop()-Methode, falls der Betrachter
// im Browser wieder zu der Appletfläche zurückkehrt
}
public void stop()
{
// Diese Methode kann der Browser rufen, wenn der Benutzer den
// DarstellungsBereich des Applets verläßt, aber nicht die HTML-Datei
// Kehrt der Betrachter zum Appletbereich zurück, ruft der Browser wierder start()
}
public void destroy()
{
// verläßt der Benutzer die HTML-Datei, ruft der Browser diese Methode
}
public void paint(Graphics g)
{
//Diese Methode wird für graphische Spielereien gebraucht
}
} // end MyApplet
Bis auf paint() werden diese Methoden als Leerhüllen von Applet geerbt. Ein einfaches Applet etwa
braucht nur die init()-Methode und nicht mehr. Verwendet ein Applets Threads, so werden diese
gerne in start() erzeugt und in stop() aufgeräumt. Baut ein Applet eine (z. Bsp. Datenbank-)
Verbindung zum Server auf, so wird diese gerne in init() aufgebaut und sollte dann spätestens in
destroy() wieder geschlossen werden. Wann allerdings ein Browser die stop()-Methode aufruft ist
Sache des Browsers, er könnte beim Verlassen der Applet-Stelle im HTML-File auch die stop()-Methode
nicht aufrufen und das Applet einfach weiterlaufen lassen.
Lebenszyklus eines Applets
Ein Applet soll zwar in einem Browser laufen, aber zum Debuggen ist ein Browser ungeeignet, da
die Fehlermeldungen der Statuszeile in der Regel völlig unzureichend sind. Oft erscheinen Meldungen
wie "Applet not inited" oder "Class not found", die zum debuggen keine Hilfe sind. So werden etwa auch
keine Exceptions, die geworfen werden, gemeldet. Hier hilft uns der Appletviewer, eine kleine Application,
mit der man Applets starten kann. Der Appletviewer verfügt über ein Konsolfenster, auf das er im
Fehlerfall Ausgaben macht.
Der Appletviewer wird von der Konsole aus gestartet und erwartet als Parameter eine HTML-Datei.
Diese Datei muß zumindest das Applet-Tag enthalten. Der Appletviewer extrahiert daraus den Namen
der Klasse und die Größe und stellt das Applet in einem Fenster dieser Größe dar. Das folgende kleine
Applet soll in einer TextArea "Hello World" ausgeben und hat einen kleinen Fehler.
Vorsicht:
Beim Appletviewer läßt sich die Größe des Applets beliebig verändern, dies entspricht nicht der
Situation in einem Browser, wo die Größe des Rechtecks im Applettag festgelegt wird.
import java.awt.* ;
import java.applet.* ;
public class HelloWorldApplet extends Applet
{
private TextArea tear ;
public void init()
{
setLayout( new BorderLayout() );
add(tear);
tear.append("Hello World");
}
}
Das Kompilieren macht keine Probleme. Eine simple HTML-Datei dazu kann folgendermaßen aussehen.
<html>
<head>
<title>A Simple Applet<title>
</head>
<body>
<h2>A Simple Applet</h2>
<br>
<applet code="HelloWorldApplet.class" width=400 height=200>
</applet>
<body>
</html>
Wir nennen die Datei HelloWorld.html. Leider läuft das Applet nicht. In der Statuszeile erscheint
die Meldung "Applet HelloWorldApplet not inited", das war's. Wir rufen den Appletviewer auf
appletviewer HelloWorld.html
Das Applet läuft (natürlich) auch nicht, aber auf der Konsole erscheint die Meldung NullPointerException
mit genauer Angabe des Klassennamens, der Methode und der Zeilennummer des Quelltextes. Jetzt können
wir den Fehler sofort beheben. Natürlich haben sie schon beim Durchlesen des Codes erkannt, daß die
TextArea nicht initialisiert wurde...
ASCII-Editoren wie etwa TextPad oder UltraEdit integrieren den Appletviewer und ermöglichen so
einen Aufruf aus dem Editor heraus.
Obwohl ein Applet formal einfacher aufgebaut ist wie eine Applikation, kann die Entwicklung einigen
Aufwand kosten. Wenn das Applet kompilierbar ist, sollte man es zuerst im Appletviewer testen.
Treten hier keine Laufzeitfehler mehr auf, so testet man das Applet mit mindestens zwei Browsern,
meist sind dies InternetExplorer und Netscape. Will man Applets haben, die auch in älteren Browsern
laufen, die kein plug-in benutzen, so muß man zurückgehen auf die erste JavaVersion 1.0 . Aber in
dieser Version gab es weder eine schöne Ereignisbehandlung noch die Möglichkeit, ein eigenes look and
feel zu verwenden. Die andere Möglichkeit, ist, dem Benutzer nahezulegen, ein
plug-in für den Browser zu verwenden.
Was darf ein Applet
Die Antwort lautet: wenig, und das ist gut so...
Aus Sicherheitsgründen sind die Möglichkeiten eines Applets stark eingeschränkt. So hat ein Applet
auf lokale Daten werder lesenden noch schreibenden Zugriff. Auch kann ein Applet keine Programme
auf dem Client starten. Ein Applet kann jedoch mit dem Server, von dem es der Browser ausliest und
startet Verbindung aufnehmen und hat dort lesenden Zugriff auf Dateien in seinem eigenen Verzeichnis.
Über den Umweg einer serverseitigen Anwendung, also einem Servlet oder einem CGI-Perlscript etwa kann ein
Applet auch schreibend auf serverseitige Daten zugreifen, indem es dem Servlet oder dem CGI-Script
seinen Schreibwunsch mitteilt, ein direkter serverseitiger Schreibzugriff ist dagegen nicht möglich.
Auslesen der SystemProperties
Auch vom lokalen Rechner erfährt ein Applet sehr wenig. Eine Applikation etwa kann mit Hilfe der
statischen Methode getProperties() der Klasse System einiges über den lokalen Rechner erfahren.
Von den rund 50 Einträgen in der Propertiestabelle darf ein Applet nur 9 (wenig informative) Einträge
Auslesen. Schon der Aufruf der obigen Methode führt zu einer java.security.AccessControlException.
Ein Applet kann lediglich die Methode getProperty(String propertyName) aufrufen. Das folgende
Applet liest die neun möglichen Properties aus und versucht den Pfad der JavaUmgebung zu ermitteln,
was nicht gelingt.
import java.awt.* ;
import java.applet.* ;
public class SystemPropertiesApplet extends Applet
{
private TextArea tear = new TextArea() ;
public void init()
{
setLayout( new BorderLayout() );
tear.setEditable(false);
add(tear, BorderLayout.CENTER);
String prop;
prop = System.getProperty("java.vendor");
tear.append("java.vendor : " +prop + "\n");
prop = System.getProperty("java.vendor.url");
tear.append("java.vendor.url : " + prop + "\n");
prop = System.getProperty("java.version");
tear.append("java.version : " + prop + "\n");
prop = System.getProperty("java.class.version");
tear.append("java.class.version : " + prop + "\n");
prop = System.getProperty("os.arch");
tear.append("os.arch : " + prop + "\n");
prop = System.getProperty("os.name");
tear.append("os.name : " + prop + "\n");
prop = System.getProperty("file.separator");
tear.append("file.separator : " + prop + "\n");
prop = System.getProperty("path.separator");
tear.append("path.separator : " + prop + "\n");
prop = System.getProperty("line.separator");
if (prop.equals("\n") )
tear.append("line.separator : \\n \n"); // unix
else if (prop.equals("\r\n") )
tear.append("line.separator : \\r\\n \n"); // windows
else if (prop.equals("\r") )
tear.append("line.separator : \\r \n"); // mac
else
tear.append("line.separator : kein standard line separator \n");
try
{
prop = System.getProperty("java.home");
tear.append(prop + "\n");
}
catch(java.security.AccessControlException ex)
{
tear.append(ex + "\n");
}
}
} // end class
Das Applet kann z.Bsp. die folgende Ausgabe machen:
java.vendor : Sun Microsystems Inc.
java.vendor.url : http://java.sun.com/
java.version : 1.4.2
java.class.version : 48.0
os.arch : x86
os.name : Windows 2000
file.separator : \
path.separator : ;
line.separator : \r\n
java.security.AccessControlException:
access denied (java.util.PropertyPermission java.home read)
Eine bessere Möglichkeit für ein Applet, etwas über die nähere Umgebung zu erfahren, ist das
Interface AppletContext. Hier ein simples Applet, das nur den Namen der Klasse ausgibt, die
dieses Interface implementiert.
import java.awt.*;
import java.applet.*;
public class AppletContextDemo extends Applet
{
private Label label = new Label("", Label.CENTER);
public void init()
{
setLayout( new BorderLayout());
label.setBackground( new Color(220,230,240) );
add(label);
AppletContext ac = getAppletContext() ;
label.setText(ac.getClass().getName() );
}
} // end class
Wo ist diese Klasse versteckt ?
Falls der Appletviewer benützt wird, meldet sich als Kontext die Klasse "sun.applet.AppletViewer". Sie
befindet sich im JavaArchiv "rt.jar" . rt.jar findet man auf WindowsSystemen unter einem Pfad analog zu
"C:\Programme\Java\j2re1.4.2\lib" .
Falls der Browser ein plug-in benützt findet sich eine Implementierung dieser Klasse im JavaArchiv
"plugin.jar", das z.B. bei Windowssystemen ebenfalls unter "C:\Programme\Java\j2re1.4.2\lib" zu finden ist.
Der AppletContext wird oft indirekt benützt. So stützen sich die Methoden getAudioClip(), getImage()
und showStatus() aus der Klasse Applet auf den AppletContext, wie ein Blick in den Quelltext der Klasse
Applet zeigt:
public void showStatus(String msg)
{
getAppletContext().showStatus(msg);
}
public Image getImage(URL url)
{
return getAppletContext().getImage(url);
}
public AudioClip getAudioClip(URL url)
{
return getAppletContext().getAudioClip(url);
}
Eine weitere Situation, in der man den AppletContext braucht ist, wenn man aus dem Applet heraus
zu einer anderen Webadresse weiterleiten will. dazu benötigt man die Methode showDocument(URL url)
aus AppletContext, siehe hierzu das spätere Kapitel Eine andere website laden.
Der AppletContext wird auch gebraucht, wenn ein Applet herausfinden will, ob sich noch weitere
Applets im gleichen HTML-File tummeln. Mit den Methoden getApplet() und getApplets() erfährt man
von der Existenz evtl. weiterer Applets, siehe
Kommunikation zwischen Applets.
Eine Application in eine Applet verwandeln
Natürlich gibt es kein starres Schema, aber einige allgemeine Regeln kann man doch aufstellen.
(setSize(), setTitle() und setVisible() entfallen)
(Sie wird vom Browser bzw. Appletviewer ignoriert)
ein Applet aber ein FlowLayout, d.h. in vielen Fällen wird man
am Anfang von init() ein setLayout( new BorderLayout() ); einfügen.
Die Unterklasse JApplet hat keine neuen spezifischen Appletmethoden, und damit in dieser Beziehung
keine zusätzlichen Fähigkeiten gegenüber der Klasse Applet. Der wesentliche Unterschied liegt im
Design der Klasse. Wie die Swingklassen JDialog, JFrame, JInternalFrame und JWindow enthält ein
JApplet einen RootPaneContainer, also ein Objekt vom Typ JRootPane. deswegen werden Komponenten
nicht zum JApplet hinzugefügt, sondern zum ContentPane des RootPaneContainers, also
nicht : jApplet.add(child)
sondern : jApplet.getContentPane().add(child);
Außerdem ist zu beachten, daß das ContentPane per default ein BorderLayout besitzt. Da auch
JFrame einen RootPaneContainer benützt, ist die Umwandlung einer JApplication in ein JApplet
einfacher als die Umwandlung einer Application in ein Applet. Durch die Klassen ImageIcon und
JLabel ergeben sich auch Vorteile beim Laden und Darstellen von Bildern, siehe
Darstellen von Bildern.
Hybriden (Applet + Application in einem)
Es ist nicht schwer eine Applet zu konstruieren, das auch als Application läuft. Dabei werden
zwei Eigenschaften konsequent genutzt. Erstens ist ein Applet ist ein (spezielles) Panel und
zweitens wird die main()-Methode vom Browser ignoriert, es gibt also keine Fehlermeldung, falls
sie doch vorhanden ist. Damit ergibt sich der folgende Aufbau:
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class Hybrid extends Applet
{
Label label = new Label("Ich bin ein Hybrid");
public void init()
{
setBackground( new Color(220,230,240) );
add(label);
}
public static void main(String args[])
{
Frame f = new Frame();
Applet applet = new Hybrid1();
applet.init();
f.add(applet);
f.setBounds(100,100,300,200);
f.setTitle("The Applicationside of the Hybrid");
f.addWindowListener( new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
f.setVisible(true);
} // end main
} // end Hybrid class
Natürlich kann man die Initialisierungen in main() in eine eigene Methode legen, die muß dann aber statisch sein, wenn man sie aus main() heraus rufen will.
Für ein JApplet ist es noch einfacher, auch als JApplication zu erscheinen. Da sowohl JApplet als auch
JFrame ein JRootpane verwenden ersetzt man einfach ein Contentpane durch ein anderes.
import java.awt.*;
import javax.swing.*;
public class JHybrid extends JApplet
{
Container conPane = getContentPane();
JLabel label = new JLabel("Ich bin ein JHybrid", SwingConstants.CENTER);
public void init()
{
label.setForeground( new Color(210,0,0) );
conPane.add(label);
}
public static void main(String args[])
{
JFrame f = new JFrame();
JApplet applet = new JHybrid();
applet.init();
f.setContentPane(applet.getContentPane());
f.setBounds(100,100,300,200);
f.setTitle("The JApplicationside of the JHybrid");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setVisible(true);
} // end main
} // end JHybrid class
Die Klasse Applet (Auszug aus der API)
Konstruktor | |
Applet() | Wird vom Browser gerufen |
Wichtige Methoden | |
Returntyp | Name der Methode |
Lebenszyklus | |
---|---|
void | init() Called by the browser or applet viewer to inform this applet that it has been loaded into the system. |
void | start() Called by the browser or applet viewer to inform this applet that it should start its execution. |
void | stop() Called by the browser or applet viewer to inform this applet that it should stop its execution. |
void | destroy() Called by the browser or applet viewer to inform this applet that it is being reclaimed and that it should destroy any resources that it has allocated. |
boolean | isActive() Determines if this applet is active. |
Information über Server und Client | |
AppletContext | getAppletContext() Determines this applet's context, which allows the applet to query and affect the environment in which it runs. |
URL | getCodeBase() Gets the base URL. |
URL | getDocumentBase() Gets the URL of the document in which this applet is embedded. |
Laden von Bildern und Sounds | |
AudioClip | getAudioClip(URL url) Returns the AudioClip object specified by the URL argument. |
AudioClip | getAudioClip(URL url, String name) Returns the AudioClip object specified by the URL and name arguments. |
static AudioClip | newAudioClip(URL url) Get an audio clip from the given URL. |
void | play(URL url) Plays the audio clip at the specified absolute URL. |
void | play(URL url, String name) Plays the audio clip given the URL and a specifier that is relative to it. |
Image | getImage(URL url) Returns an Image object that can then be painted on the screen. |
Image | getImage(URL url, String name) Returns an Image object that can then be painted on the screen. |
Information aus der HTML-Datei abholen, Zugriff auf die Statuszeile | |
String | getParameter(String name) Returns the value of the named parameter in the HTML tag. |
void | showStatus(String msg) Requests that the argument string be displayed in the "status window". |
Das Interface AppletContext (Auszug aus der API)
Wichtige Methoden | |
Returntyp | Name der Methode |
Applet | getApplet(String name) Finds and returns the applet in the document represented by this applet context with the given name. |
Enumeration | getApplets() Finds all the applets in the document represented by this applet context. |
AudioClip | getAudioClip(URL url) Creates an audio clip. |
Image | getImage(URL url) Returns an Image object that can then be painted on the screen. |
void | showDocument(URL url) Replaces the Web page currently being viewed with the given URL. |
void | showDocument(URL url, String target) Requests that the browser or applet viewer show the Web page indicated by the url argument. |
void | showStatus(String status) Requests that the argument string be displayed in the "status window". |