From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100087.html | 601 +++++++++++++++++++++ 1 file changed, 601 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100087.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100087.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100087.html b/Master/Reference Architectures and Patterns/hjp5/html/k100087.html new file mode 100644 index 0000000..700d5bd --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100087.html @@ -0,0 +1,601 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 13 - Strukturierung von Java-Programmen +
+
+ + + + +

13.4 Auslieferung von Java-Programmen

+
+ +
+ + + + +

13.4.1 Weitergabe des Bytecodes

+ + + + +

Direkte Weitergabe der Klassendateien

+ +

+Der ausführbare Programmcode einer Java-Applikation befindet +sich in den vom Compiler erzeugten .class-Dateien. +Sie sind der wichtigste Bestandteil bei der Auslieferung eines Java-Programms. +Wir werden uns in diesem Abschnitt ansehen, auf welche Weise die Klassendateien +weitergegeben werden können und was dabei zu beachten ist. Neben +den Klassendateien müssen mitunter auch zusätzliche Dateien +mit Übersetzungstexten, Fehlermeldungen, Icons oder Bilddateien +weitergegeben werden. Wie diese zusammen mit den Klassendateien ausgeliefert +und verwendet werden können, zeigt der nächste Abschnitt. +

+ + + + + + + + + + + +
+ +

+Die Ausführungen in diesem und dem nächsten Abschnitt sind +möglicherweise schwierig zu verstehen, denn sie setzen Kenntnisse +voraus, die teilweise erst in späteren Kapiteln vermittelt werden. +Wegen ihres engen thematischen Bezuges zum vorliegenden Kapitel sind +sie hier dennoch richtig aufgehoben, und Sie sollten den Ausführungen +zumindest in groben Zügen folgen können. Lesen Sie diese +Abschnitte bei Bedarf einfach zu einem späteren Zeitpunkt noch +einmal, und viele Dinge, die jetzt unklar sind, werden dann leicht +zu verstehen sein.

+ + + + +
 Warnung 
+
+ +

+Die einfachste Möglichkeit, ein Java-Programm auszuliefern, besteht +darin, die Klassendateien so zu belassen, wie sie sind, und sie entsprechend +ihrer Paketstruktur auf das Zielsystem zu kopieren. Das Programm kann +dann auf dem Zielsystem gestartet werden, indem der Java-Interpreter +aus dem Installationsverzeichnis aufgerufen und der Name der zu startenden +Klasse als Argument angegeben wird. + +

+Als Beispiel wollen wir uns eine hypothetische Applikation ansehen, +die aus folgenden Klassen besteht: + +

+com.gkrueger.app.App
+com.gkrueger.app.AppFrame
+com.gkrueger.util.Logger
+
+ + +

+Die Klassen App und AppFrame +befinden sich im Paket com.gkrueger.app +und die Klasse Logger im Paket +com.gkrueger.util. Ausgehend +vom Basisverzeichnis, liegen die Klassendateien also in den Unterverzeichnissen +com\gkrueger\app und com\gkrueger\util. +Die main-Methode +befindet sich in der Klasse App. +Soll das Programm auf dem Zielsystem beispielsweise im Verzeichnis +c:\gkapp installiert werden, müssen +die drei Dateien wie folgt kopiert werden: +

+ +

+Das Programm kann dann aus dem Installationsverzeichnis c:\gkapp +durch Angabe des qualifizierten Klassennamens gestartet werden: + +

+java com.gkrueger.app.App
+
+ + +

+Voraussetzung zum Starten der Applikation ist, dass der Klassenpfad +korrekt gesetzt ist. Soll das Programm stets aus dem Installationsverzeichnis +heraus gestartet werden, muss das aktuelle Verzeichnis ».« +im CLASSPATH +enthalten sein (alternativ kann seit dem JDK 1.2 auch gar kein Klassenpfad +gesetzt sein). Soll das Programm von beliebiger Stelle aus gestartet +werden können, muss der Pfad des Installationsverzeichnisses +c:\gkapp in den Klassenpfad aufgenommen +werden. Dazu kann entweder die Umgebungsvariable CLASSPATH +modifiziert oder der gewünschte Klassenpfad mit einer der Optionen +-cp +oder -classpath +an den Java-Interpreter übergeben werden. Dafür wäre +beispielsweise folgender Aufruf geeignet: + +

+java -cp c:\gkapp com.gkrueger.app.App
+
+ +

+ + + + + + + + + +
+ +

+Die Variante, bei der die Umgebungsvariable unverändert bleibt, +ist natürlich die bessere, denn Modifikationen an Umgebungsvariablen +beeinflussen möglicherweise auch andere Programme. Zudem hat +die explizite und präzise Übergabe des Klassenpfades an +den Interpreter den Vorteil, dass nicht versehentlich weitere Verzeichnisse +im Klassenpfad enthalten sind und unerwünschte Nebeneffekte verursachen +können.

+ + + + +
 Hinweis 
+
+ + + + +

Verwendung eines jar-Archivs

+ +

+Noch einfacher wird die Installation, wenn anstelle der verschiedenen +Klassendateien eine einzelne jar-Datei ausgeliefert wird. Auf dem +Zielsystem spielt es dann keine Rolle mehr, aus welchem Verzeichnis +heraus die Applikation aufgerufen wird und wie die CLASSPATH-Variable +gesetzt ist. Weitere Hinweise zur Anwendung des jar-Programms finden +sich in Abschnitt 51.6. Dort +wird auch erläutert, wie Applets in jar-Dateien ausgeliefert +werden können. + +

+Angenommen, das Basisverzeichnis für die Programmentwicklung +auf der Quellmaschine ist c:\prog\java +und die Klassendateien liegen in den Verzeichnissen c:\prog\java\com\gkrueger\app +und c:\prog\java\com\gkrueger\util. Um +eine jar-Datei zu unserem Beispielprojekt zu erstellen, ist aus dem +Entwicklungsverzeichnis c:\prog\java +heraus folgendes jar-Kommando +abzusetzen: + +

+jar cvf myapp.jar com
+
+ + +

+Dadurch werden alle Dateien aus dem Unterverzeichnis com +und allen darin enthaltenen Unterverzeichnissen unter Beibehaltung +der Verzeichnishierarchie in die jar-Datei myapp.jar +kopiert. Um das Programm auf der Zielmaschine aufzurufen, reicht es +aus, myapp.jar an eine beliebige Stelle +zu kopieren und den Java-Interpreter wie folgt zu starten: + +

+java -cp myapp.jar com.gkrueger.app.App
+
+ + +

+Anstelle des Unterverzeichnisses mit den Klassendateien haben wir +nun die jar-Datei im Klassenpfad angegeben. Für den Interpreter +sind beide Varianten gleichwertig und er wird das Programm in der +gleichen Weise wie zuvor starten. Vorteilhaft an dieser Vorgehensweise +ist, dass auf der Zielmaschine nur noch eine einzige Datei benötigt +wird und nicht mehr ein ganzes System von Verzeichnissen und Unterverzeichnissen +wie zuvor. Das vereinfacht nicht nur die Installation, sondern erhöht +auch ihre Haltbarkeit, denn einzelne Dateien können nicht mehr +überschrieben werden oder verlorengehen. +

+ + + + + + + + + + + +
+ +

+Das oben beschriebene jar-Kommando kopiert alle Dateien aus +dem com-Unterverzeichnis und den darin +enthaltenen Unterverzeichnissen in die jar-Datei. Dazu zählen +natürlich auch die Quelldateien, sofern sie in denselben Verzeichnissen +liegen. Da diese aber meist nicht mit ausgeliefert werden sollen, +empfiehlt es sich, entweder die Klassendateien (unter Beibehaltung +der Unterverzeichnisstruktur) zuvor in ein leeres Verzeichnis zu kopieren +und das jar-Kommando von dort aus zu starten. Oder die Klassendateien +werden schon bei der Entwicklung von den Quelltexten getrennt, indem +beim Kompilieren die Option -d +verwendet wird.

+ + + + +
 Warnung 
+
+ + + + +

Ausführbare jar-Archive

+ +

+Wir können sogar noch einen Schritt weitergehen und die jar-Datei +selbst »ausführbar« machen. Dazu müssen wir eine +Manifest-Datei erstellen, in der der +Name der Klasse angegeben wird, die die main-Methode +enthält. Anschließend läßt sich die jar-Datei +mit der Option -jar +des Interpreters ohne explizite Angabe der Hauptklasse starten. +

+ + + + + + + + + +
+ +

+Die Manifest-Datei ist eine Hilfsdatei mit +Informationen über den Inhalt der jar-Datei. Sie hat den Namen +manifest.mf und liegt im Unterverzeichnis +meta-inf des jar-Archivs. +Die Manifest-Datei kann mit einem normalen Texteditor erstellt und +mit Hilfe der Option »m« in das jar-Archiv eingebunden werden. +In unserem Fall muss sie lediglich einen Eintrag Main-Class +enthalten. Weitere Informationen zu Manifest-Dateien finden sich in +Abschnitt 44.3.3 bei der +Beschreibung der Java-Beans-Architektur.

+ + + + +
 Hinweis 
+
+ +

+Wir erstellen also zunächst eine Textdatei manifest.txt +mit folgendem Inhalt (bitte mit einer Zeilenschaltung abschließen, +sonst wird die Manifest-Datei nicht korrekt erstellt): + +

+Main-Class: com.gkrueger.app.App
+
+ + +

+Nun kann das jar-Archiv erzeugt und die Manifest-Datei einbezogen +werden: + +

+jar cvfm myapp.jar manifest.txt com
+
+ + +

+Dieses Archiv kann nun mit Hilfe der Option -jar +ohne Angabe des Klassennamens gestartet werden: + +

+java -jar myapp.jar
+
+ + +

+Auch jetzt verhält sich das Programm genauso wie in den beiden +zuvor beschriebenen Beispielen. Diese Variante ist vor allem nützlich, +wenn die komplette Applikation in einem einzigen jar-Archiv ausgeliefert +wird und nur eine einzige main-Methode +enthält. Sie bietet sich beispielsweise für kleinere Programme, +Beispiel- oder Testapplikationen an (die Demos des JDK sind beispielsweise +in derartigen jar-Dateien untergebracht). Ist die Konfiguration dagegen +komplizierter oder gibt es mehr als eine lauffähige Applikation +in der jar-Datei, sollte der zuvor beschriebenen Variante der Vorzug +gegeben werden. + + + + +

13.4.2 Einbinden von Ressourcen-Dateien

+ +

+Wie eingangs erwähnt, benötigt ein Programm neben den Klassendateien +meist weitere Dateien mit zusätzlichen Informationen. Wird auf +diese ausschließlich lesend zugegriffen, wollen wir sie als +Ressourcen-Dateien bezeichnen, also +als Dateien, die dem Programm neben den Klassendateien als zusätzliche +Informationslieferanten zur Laufzeit zur Verfügung stehen. Die +Ressourcen-Dateien könnten zwar als separate Dateien ausgeliefert +und mit den Klassen zur Dateiverarbeitung eingelesen werden (diese +werden ab Kapitel 18 +vorgestellt). Das hätte jedoch den Nachteil, dass sich nicht +mehr alle benötigten Dateien in einem jar-Archiv befinden würden +und die Auslieferung dadurch verkompliziert würde. + +

+Glücklicherweise gibt es im JDK die Möglichkeit, auch Ressourcen-Dateien +in jar-Archiven unterzubringen und zur Laufzeit daraus einzulesen. +Mit dem Klassenlader steht das dafür erforderliche Werkzeug allen +Java-Programmen standardmäßig zur Verfügung. Während +der Klassenlader normalerweise von der virtuellen Maschine dazu verwendet +wird, Klassendateien einzulesen, kann er von der Anwendung zum Einlesen +beliebiger Dateien zweckentfremdet werden. Einzige Bedingung ist, +dass die Ressourcen-Dateien in einem Verzeichnis stehen, das vom Klassenlader +erreicht werden kann, das also innerhalb des Klassenpfades liegt. + +

+Dazu kann beispielsweise im Basisverzeichnis der Anwendung ein Unterverzeichnis +resources angelegt und die Ressourcen-Dateien +dort hineinkopiert werden. In unserem Fall würden wir also ein +Unterverzeichnis com.gkrueger.resources +anlegen. Mit Hilfe der Methode getResourceAsStream +der Klasse Class +(siehe Abschnitt 43.2.2) +kann ein InputStream +beschafft werden, mit dem die Datei Byte für Byte eingelesen +werden kann: +

+ + + + + +
+ +
+public InputStream getResourceAsStream(String name)
+
+
+
+java.lang.Class
+ +

+Als Parameter wird der vollständige Name der Ressourcen-Datei +angegeben, allerdings unter Beachtung einiger Besonderheiten. Zunächst +werden die einzelnen Paketnamen nicht wie gewohnt durch Punkte, sondern +durch Schrägstriche »/« voneinander getrennt (auch +unter Windows wird dazu nicht der Backslash verwendet). Nur die Dateierweiterung +wird wie üblich hinter einem Punkt angegeben. Zudem muss als +erstes Zeichen ebenfalls ein Schrägstrich angegeben werden. Soll +also beispielsweise die Datei hello.txt +aus dem Ressourcenverzeichnis geladen werden, so lautet der an getResourceAsStream +zu übergebende Dateiname /com/gkrueger/resources/hello.txt. + +

+Das folgende Listing zeigt eine einfache Methode, in der die angesprochenen +Regeln implementiert werden. Sie beschafft zu einer beliebigen Datei, +die sich in einem vorgegebenen Ressource-Verzeichnis befindet, das +innerhalb des Klassenpfades liegt, einen InputStream, +mit dem die darin enthaltenen Daten eingelesen werden können: + + +

+ + + + +
+ +
+001 private InputStream getResourceStream(String pkgname, String fname)
+002 {
+003   String resname = "/" + pkgname.replace('.', '/') + "/" + fname;
+004   Class clazz = getClass();
+005   InputStream is = clazz.getResourceAsStream(resname);
+006   return is;
+007 }
+
+
+ +Listing 13.7: Einen InputStream zu einer Ressourcen-Datei beschaffen

+ +

+Als Argument wird der Paketname (in Punktnotation) und der Name der +Ressourcen-Datei angegeben. Den obigen Regeln entsprechend werden +beide in einen Ressourcen-Namen umgewandelt und an getResourceAsStream +übergeben. Diese liefert einen InputStream, +der zum Einlesen der Daten verwendet werden kann. Soll beispielsweise +eine Text-Ressource in einen String +geladen werden, kann dazu folgende Methode verwendet werden: + + +

+ + + + + +
+ +
+001 /* TestResource.inc */
+002 
+003 import java.io.*;
+004 import java.awt.*;
+005 
+006 //...
+007 
+008 public String loadTextResource(String pkgname, String fname)
+009 throws IOException
+010 {
+011   String ret = null;
+012   InputStream is = getResourceStream(pkgname, fname);
+013   if (is != null) {
+014     StringBuffer sb = new StringBuffer();
+015     while (true) {
+016       int c = is.read();
+017       if (c == -1) {
+018         break;
+019       }
+020       sb.append((char)c);
+021     }
+022     is.close();
+023     ret = sb.toString();
+024   }
+025   return ret;
+026 }
+027 
+028 //...
+ +
+TestResource.inc
+ +Listing 13.8: Laden einer Text-Ressource

+ +

+Auch das Laden von Image-Ressourcen +(Programm-Icons, Abbildungen etc.) kann auf ähnliche Weise realisiert +werden: + + +

+ + + + + +
+ +
+001 /* ImageResource.inc */
+002 
+003 import java.io.*;
+004 import java.awt.*;
+005 
+006 //...
+007 
+008 public Image loadImageResource(String pkgname, String fname)
+009 throws IOException
+010 {
+011   Image ret = null;
+012   InputStream is = getResourceStream(pkgname, fname);
+013   if (is != null) {
+014     byte[] buffer = new byte[0];
+015     byte[] tmpbuf = new byte[1024];
+016     while (true) {
+017       int len = is.read(tmpbuf);
+018       if (len <= 0) {
+019         break;
+020       }
+021       byte[] newbuf = new byte[buffer.length + len];
+022       System.arraycopy(buffer, 0, newbuf, 0, buffer.length);
+023       System.arraycopy(tmpbuf, 0, newbuf, buffer.length, len);
+024       buffer = newbuf;
+025     }
+026     //create image
+027     ret = Toolkit.getDefaultToolkit().createImage(buffer);
+028     is.close();
+029   }
+030   return ret;
+031 }
+032 
+033 //...
+ +
+ImageResource.inc
+ +Listing 13.9: Laden einer Image-Ressource

+ +

+Voraussetzung ist natürlich, dass das Format der in den Puffer +buffer eingelesenen Datei von +der Methode createImage +verstanden wird. Beispiele für gültige Formate sind GIF +oder JPEG. + +

+Der große Vorteil bei dieser Vorgehensweise ist, dass sie sowohl +mit separaten Klassendateien funktioniert, als auch, wenn die komplette +Applikation inklusive der Ressourcen-Dateien innerhalb einer einzigen +jar-Datei ausgeliefert wird. Für die Anwendung selbst ist es +vollkommen gleichgültig, aus welcher Quelle die Dateien stammen. +


+ + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 +
 <<  +  <   +  >   + >>  + API  +© 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de +
+ + + -- cgit v1.2.3