Stardivision erweiterte Java Grundkonzepte

Exceptions:

Die Grundidee von Exceptions ist es einen Fehlerkontext aufzuspannen, der erst näher betrachtet werden muß, wenn man Fehler korrigieren will. Der Programmcode sollte durch die Behandlung von Fehlern nicht undurchsichtig und unleserlich werden. Meiner Meinung nach sollten Exceptions deswegen auch nicht als zweiter Returnwert vergewaltigt werden.
Ziel: Nach dem Auftreten einer Exception sollte es möglichst einfach sein das System in einen definierten arbeitsfähigen Zustand zu versetzen.
Es gibt grundsätzlich drei verschiedenen Arten von Exceptions. Diese unterscheiden sich durch den Zustand in dem sie das Objekt hinterlassen.

  1. Die von der Methode benutzten Objekte sind in einem undefinierten Zustand. Jede auf dem Objekt aufgerufene Methode muß nach einer solchen Exception nicht mehr ihre Spezifikation einhalten. Diese Exception wird im folgenden mit „Undefined Exception“ benannt. Dabei ist zu beachten, daß keine weiteren Resourcen, außer die angegebenen, benutzt werden. Außerdem werden „ReadOnly“ Resourcen nicht modifiziert.

  2. Die von der Methode benutzten Objekte sind in einem genau definierten Zustand, der aber von der Zusicherung der Methode abweicht. Diese Exception wird im folgenden mit „Defined Exception“ benannt. Dabei ist zu beachten, daß keine weiteren Resourcen, außer die angegebenen, benutzt werden. Außerdem werden „ReadOnly“ Resourcen nicht modifiziert.

  3. Die von der Methode benutzten Objekte sind in ihrem vorherigen Zustand, der aber von der Zusicherung der Methode abweicht. Diese Exception wird im folgenden mit „Transacted Exception“ benannt. Dabei ist zu beachten, daß keine weiteren Resourcen, außer die angegebenen, benutzt werden. Außerdem werden „ReadOnly“ Resourcen nicht modifiziert. Diese Spezifikation trifft auch auf „Defined Exception“ zu, wegen ihrer Bedeutung führe ich sie extra auf.

Die Benutzbarkeit eines Objektes, nachdem eine Exception aufgetreten ist, ist vom obigen Typ der Exception abhängig.

Satz 1.1: Nachdem eine „Undefined Exception“ aufgetreten ist, kann mit dem Objekt sowie allen „ReadWrite“ Resourcen nicht mehr weiter gearbeitet werden.

Satz 1.2: Nachdem eine „Defined Exception“ aufgetreten ist, kann aufgrund des genau definierten Zustandes weiter gearbeitet werden.

Satz 1.3: Nach einer „Transacted Exception“ ist der gleiche Zustand wie vor dem Aufruf wiederhergestellt.

Es sollten möglichst nur „Transacted Exception“ ausgelöst werden. Bei komplizierten Methoden läßt sich aber eine „Defined Exception“ nicht immer vermeiden. Eine „Undefined Exception“ deutet immer auf eine Programmierfehler hin. Der Typ der Exeption kann nur in Zusammenhang mit der Methode in der sie Auftritt ermittelt werden.

Satz 1.4: Durch die Klasse der Exception kann niemals alleine der Typ (undefined, defined oder transacted) entschieden werden.

Resourcen (under construction)

Die Grundidee von Resourcen ist die Aufteilung eines Gebildes in weitere Einheiten. Auf diesen können dann verschiedene Aufträge gleichzeitig arbeiten, wenn sie nicht dieselben Resourcen benutzen. Z.B. kann man in einer Textverarbeitung die einzelnen Dokumente als Einheiten betrachten. Aufträge, die sich nur auf ein Dokument beziehen, können parallel zu anderen Dokumenten bearbeitet werden.
Mit Resourcen sind im System bzw. der Applikation vorhandene Objekte, Services, Kanäle ... gemeint, die zur Zeit nur von einem Thread benutzt werden können. Als Konsequenz müssen Resourcen einem Thread zugeordnet werden, bevor dieser sie benutzt.
Ziel: Es muß möglich sein, 1. Aufträge parallel abzuarbeiten, 2. die Frage „Warum können zwei Aufträge nicht parallel arbeiten?“ beantwortet zu können.
Es gibt verschiedene Möglichkeiten diese Zuordnung vorzunehmen. Zwei stelle ich kurz vor.

  1. Eine Art der Zuordnung ist das vorherige Anfordern aller für den Auftrag benötigten Resourcen. Ist dies möglich, kann der Auftrag ohne weitere Störungen ablaufen. Die Resourcen dürfen freigegeben werden, bevor der Auftrag beendet ist. Dies gilt natürlich nur für nicht mehr verwendete Resourcen. Es darf ebenfalls das Zuordnungsrecht von lesend und schreibend auf lesend zurückgenommen werden. Diese Zuornungsart wird im weiteren mit „Prealloc Resource Konzept“ bezeichnet.

  2. Eine andere Art der Zuordnung ist das Anfordern der Resourcen, wenn sie benötigt werden. Dabei kann es zu Störungen kommen, wenn sich verschiedene Aufträge um die gleiche Resource bewerben. Die Resourcen dürfen freigegeben werden, bevor der Auftrag beendet ist. Dies gilt natürlich nur für nicht mehr verwendete Resourcen. Es darf ebenfalls das Zuordnungsrecht von lesend und schreibend auf lesend zurückgenommen werden. Diese Zuornungsart wird im weiteren mit „Ondemand Resource Konzept“ bezeichnet.

Es gibt noch weitere Möglichkeiten Aufträge zu bearbeiten, die die gleichen Resourcen benutzen. Häufig findet man solche Lösungen bei Datenbankanwendungen.
In der folgenden Tabelle stehen sich die beiden Konzepte mit ihren Vor- und Nachteilen und ihren Anforderungen gegenüber.


Prealloc Resource Konzept
Ondemand Resource Konzept

Alle Resourcen müssen vor der Auftragsausführung bekannt sein.

Ja

Nein

Nicht mehr benötigte Resourcen dürfen freigegeben werden.

Ja

Ja

Es kann zu Verklemmungen oder „Races“ kommen.

Nein

Ja

In Bearbeitung befindliche Aufträge müssen, aufgrund fehlender Resourcen, abgebrochen werden.

Nein

Ja

Der Zustand der Resourcen ist zu jedem Zeitpunkt der Auftragsabarbeitung bekannt.

Ja

Nein

Algorithmus zur Resourcevergabe.

Einfach, da nur überprüft werden muß, ob alle benötigten Resourcen verfügbar sind.

Komplex, da neben dem Anfordern von Resourcen auch noch überprüft werden muß, ob das System lebendig ist.

Parallelität

Hoch, da unabhängige Aufträge meistens nur lesend auf gemeinsame Resourcen zugreifen.

Sehr hoch, da die benötigten Resourcen erst angefordert werden, wenn man sie braucht.

Meiner Meinung nach ist nur das „Prealloc Resource Konzept“ ohne richtige Programmierwerkzeuge zur Entwicklung paralleler Algorithmen (z.B. Netzprogrammierung) wenigstens ein bißchen beherschbar.

Es stellt sich die Frage wie das „Prealloc Resource Konzept“ in einem Komponenten-Modell und in einem Objekt-Environment integriert werden kann. Ein Objekt-Environment ist ein mehr oder weniger dynamische Menge von Objekten die miteinander in Verbindung stehen. Aus dem obigen Beispiel könnte man die Verbindung der Textverarbeitung zu ihren Dokumenten als Objekt-Environment bezeichnen. Ein Objekt in diesem Environment kann nun über seine Verbindungen mit anderen Objekten kommunizieren. Die Schnittstellen mit denen über die Verbindung kommuniziert wird nennt man Komponenten-Modell. Die Idee des Objekt-Environments ist es weitere Objekte möglichst einfach zu integrieren. So könnten in unserem Beispiel weitere Dokumenttypen wie ein HTML-Dokument eingebunden werden. Die Schittstellen müßten nur dem, von der Textverarbeitung geforderten, Komponenten-Modell genügen. Liefert aber das Modell, wie heute üblich, keine Information über die benötigten Resourcen bei Benutzung der Schnittstellen, dann können Verklemmungen bzw. Inkonsistenzen nicht vermieden werden. Aus diesem Grunde ist es notwendig, das Resource-Konzept in das Komponenten-Modell zu integrieren.
Ziel: Es muß ein Kompromiß zwischen hoher Nebenläufigkeit und der damit verbundenen Komplexität, sowie einfacher Programmierung und geringer Nebenläufigkeit gefunden werden.
Folgen: In einem Objekt-Environment müssen die einzelnen Objekte also dynamisch auf Verbindungen zu Objekten mit hoher oder geringer Nebenläufigkeit reagieren. Die Komplexität dieser Aufgabe darf aber nicht in die Objekte verlagert werden, da von einem seriellen Objekt (bzw. dessen Programmierer) nicht die Steuerung nebenläufiger Aufträge verlangt werden kann.
Lösungsansatz: Die Behandlung der Nebenläufigkeit wird nicht in einer einfachen Komponente implementiert. Das bedeutet sie muß mit einer Default-Behandlung zufrieden sein, die minimale Nebeläufigkeit nach sich zieht. Eine Komponente kann sich aber in die Vergabe der Resourcen einmischen. So kann sie ihren Grad der Nebenläufigkeit erhöhen. Dies ist dann aber auch mit erhöhtem Implementationsaufwand zu bezahlen. Auf der anderen Seite macht es aber keinen Sinn serielle oder Komponenten mit zu großem Resourcebedarf einzubinden, wenn das Objekt-Environment danach praktisch nicht mehr lauffähig ist. Das bedeutet, daß das Objekt-Environment auch Forderungen bezüglich des Resourcebedarf an die Komponenten stellen darf.

Anforderungen

  1. Es muß ein Modell geben, in dem alle vorhandenen Resourcen und deren Beziehung zueinander eingetragen werden. Dadurch kann abgeschätzt werden, welchen Resourcebedarf eine Komponente hat. Das „Schätzen“ ist wörtlich zu nehmen. (Im Zusammenhang mit Security wird man aber auch noch sehen, daß der Zugriff auf bestimmte Resourcen nicht möglich ist.) Für das „Prealloc Resource Konzept“ gilt, es müssen mindestens die benötigten Resourcen verfügbar sein. Zur Not sind diese alle.

  2. Eine nebenläufige Komponente muß in jeder ihrer von außen erreichbaren Methoden kontrollieren, ob die entsprechenden Resourcen für sie angefordert wurden. Damit serielle Komponenten diese Methoden nutzen können, können die benötigten Resourcen angefordert werden, wenn vorher noch keine einzige durch den ausführenden Auftrag belegt war. Zur Erläuterung: Serielle Komponenten belegen keine Resourcen. Damit würde jeder Aufruf einer nebenläufigen Komponente scheitern. Um dies zu vermeiden, werden die Resourcen in der nebenläufigen Komponente angefordert.

  3. Serielle Komponenten müssen also damit rechnen eine Fehlermeldung über nicht verfügbare Resourcen zu bekommen.

Szenarien

Von unserem bisherigen Beispiel ausgehend, gibt es eine Applikation in der sich drei Dokumente befinden. Ein serielles Textdokument, ein nebenläufiges Tabellendokument und ein nebenläufiges Präsentationsdokument. Die Applikation selbst ist nebenläufig. Die Relationen gehen von der Applikation zu den Dokumenten und umgekehrt. Die Dokumente kennen sich nicht.

Fall 1:
In das serielle Textdokument soll eine Zeichenfolge eingefügt werden. Da es sich um eine serielle Komponente handelt, kann dieses Einfügen nicht von selbst ausgelöst werden, es muß von einer nebenläufigen Komponente, hier die Applikation, angestoßen werden. Die Applikation ist aber verpflichtet die Resourcen vorher zu reservieren. Für diese Abschätzung gibt es drei realistische Möglichkeiten. 1. Sie reserviert nur das Textdokument selbst. Das bedeutet, das Textdokument kann mit keinem anderen Objekt, auch nicht mit der Applikation, kommunizieren. 2. Die Applikation und das Textdokument wird reserviert. Es ist also nur der Zugriff auf die anderen Dokumente verwehrt. 3. Alle Objekte werden reserviert. Geht es nach dem „Prealloc Resource Konzept“ muß 3. gewählt werden. Aufgrund von Sicherheitsbeschränkungen werden wir aber noch sehen, das serielle Komponenten in ihrer Auftragsbearbeitung gestoppt werden können. Wenn der Abbruch eines Auftrags möglich ist, spielt es aber keine Rolle durch wen (Resourcen oder Security) dies geschehen ist.

Fall 2:
In das nebenläufige Tabellendokument soll eine Zeichenfolge eingefügt werden. Dieser Auftrag kann von der Applikation oder der Komponente selbst ausgelöst werden. In jedem Fall müssen die Resourcen vor der Auftragsbearbeitung reserviert werden. Man kann dies auch der Komponente überlassen (siehe Anforderung 2.), aber man scheitert, wenn zwei Aufträge zu einem Auftrag zusammengefaßt werden sollen. Dies passiert z.B., wenn der Auftrag „Text ersetzen“ aus den Aufträgen „Löschen“ und „Einfügen“ besteht. Auf jeden Fall wird nur das Tabellendokument selbst reserviert, da das Einfügen keine Auswirkung auf andere Komponenten hat.

Fall 3:
In das nebenläufige Tabellendokument wird der Applikationsname aus der Applikation eingefügt. Dazu fragt das Tabellendokument nach den benötigten Resourcen, um den Namen zu holen und ihn einzufügen. Zum Holen wird die Applikation benötigt und zum Einfügen das Tabellendokument. Beide müssen vor der Auftragsausführung reserviert werden.

Fall 4:
Das nebenläufige Tabellendokument fügt selektierten Text aus dem seriellen Textdokument ein. Da das Textdokument seinen Resourcebedarf nicht mitteilt, wird einer aus Fall eins abgeschätzte Bedarf genommen. Man kann sehen, daß der Auftrag für alle drei Möglichkeiten erteilt werden kann. Seine Nebenläufigkeit wird dann durch die Abschätzung eingeschränkt. Zusätzlich müssen natürlich die benötigten Resourcen für das Einfügen geholt werden. Alle müssen vor der Auftragsausführung reserviert werden.

Programmierkonzepte

Welche Konzepte können in einer objektorientierten Sprache wie c++ oder Java oder einer prozeduralen Sprache wie Fortran oder „c“ eingesetzt werden, um Nebenläufigkeit zu erreichen.

  1. Es gibt zwei Möglichkeiten eine Resource zu belegen. Das ist Exclusive (lesen, schreiben) und „ReadOnly“. Eine Resource kann von mehreren Aufträgen benutzt werden, wenn diese nur „ReadOnly“ benötigen.

  2. Es gibt Resourcen für die man die Resourceverteilung optimieren kann. Ein Objekt welches nicht geändert werden kann und das während der Auftragsausführung immer konsistent ist kann die Anforderung „Exclusiv“ automatisch auf „ReadOnly“ abschwächen. Dies lohnt sich, wenn man serielle Komponenten hat, die nichts über die Resourceanforderungen mitteilen. Als Beispiel möchte ich eine Instanz der Klasse String in Java nennen. Ein weitere Art von Resourcen fordern bei Aufträgen an sie 1. keine weiteren Aufträge an, 2. beenden sie die Aufträge schnell und 3. die Reihenfolge der Änderung an ihnen ist für andere nicht wichtig. Dies ist zum Beispiel bei der Speicherverwaltung in c der Fall. Diese Art der Resource darf zu einem späteren Zeitpunkt angefordert werden. Sie muß sofort benutzt und wieder freigegeben werden. Aus diesem Grund erledigen solche Resourcen das Anfordern und Freigeben selbst.

  3. Bevor ein Auftrag ausgeführt werden kann, müssen alle von ihm benötigten Resourcen reserviert werden. Dies ist für einen Auftrag, der aus mehreren Teilaufträgen besteht, aufwendig. Eine Optimierung kann darin bestehen die Teilaufträge asynchron auszuführen. Allerdings dringt diese Verhaltensweise nach außen. Z.B. müssen Aufträge, die diesen dann asynchronen Auftrag nutzen, dann auch asynchron sein. Eine weitere Optimierung in der Autragsvergabe gibt es, wenn ein Autrag die Resourcervergabe nicht ändert. Es ist dann möglich mehr Aufträge vorzuziehen.

  4. Es muß eine Queue geben, in die Aufträge eingefügt werden können. Konfliktfreie Aufträge können parallel ausgeführt werden. Achtung: Der Resourcebedarf eines Auftrages kann nur bestimmt werden, wenn alle benötigten Resourcen „ReadOnly“ reserviert werden können, es sei denn kein vor ihm laufender Auftrag ändert die Resourcevergabe. Warum ist das so? Ein Auftrag kann eine Resource dahingehend ändern, daß danach andere Resourcen benötigt werden als vorher. Der vorher bestimmte Bedarf ist dann falsch.

  5. Das Modell der Resourcen kann vergröbert oder verfeinert werden. In einem Tabellendokument könnte man jede einzelne Zelle zu einer Resource machen. Um die Komplexität der Resourcemodells zu vereinfachen kann man aber weiter alle Zellen der Dokument-Resource zuordnen. Wird also aus einer anderen Komponente die Zelle angefordert, wird automatisch das ganze Dokument reserviert. Daraus ergeben sich zwei Vorteile: 1. Für den Auftraggeber ist die Vergröberung transparent und 2. Kann die Resource an dem Objekt reserviert werden, das man ohnehin kennt.

  6. Das Resource-Modell ist hierarchisch. Eine Resource kann nur einer Vergröberung zugeordnet werden. Die Tabellenzellen dürfen also nur dem Tabellendokument zugeordnet werden. Daraus ergibt sich, daß innerhalb einer solchen Hierarchie nebenläufig gearbeitet werden kann. Es dürfen dann aber keine Resourcen außerhalb der Hierarchie benutzt werden, selbst wenn diese reserviert sind.

Probleme und Lösungen

Über den Benutzer müssen Daten abgefragt werden, die über die Benutzung von Resourcen entscheidet (z.B. Dateiname):
Ein solcher Auftrag muß in zwei Teilaufträge unterteilt werden. Der erste erledigt die Abfrage. Danach werden alle Resourcen freigegeben und dann fordert der zweite seine Resourcen und wird bearbeitet. Eventuell kann ein solcher Auftrag den vorherigen ersetzten, um zu verhindern das andere abhängige Aufträge vor dem Aufgeteilten bearbeitet werden.

Ich habe mich bei einem Objekt als Listener angemeldet:
Es gibt zwei Arten von Benachrichtigungen die ich erhalte. 1. Aufgrund der Ausführung eines Auftrages und 2. einen Event von einer nebenläufigen Komponente. Im ersten Fall überprüfe ich den Resourcebedarf und führe dann den Auftrag aus. Im zweiten Fall reserviere ich die benötigten Resourcen und führen den Auftrag aus. Sind Resourcen reserviert, ist dies Fall eins, sonst Fall zwei.

Ich bin Broadcaster:
Broadcaste ich aufgrund eines Auftrags tue ich nichts weiter. Löse ich den Broadcast ohne Auftrag aus, muß ich die Resourcen für die Listener bestimmen und sie vor dem Aufruf reservieren. Die einzelnen Listener werden als unabhängig betrachtet. Im Detail findet folgender Ablauf statt. 1. Die Liste der Listener wird kopiert. 2. Für den ersten Listener wird der Resourcebedarf ermittelt.

Implementation

Die Basis für die Implementation des Resourcekonzeptes legen die Klassen Resource, ResourceList, ResourceLockException, Task, TaskManager, TaskThread, ThreadData und das Interface Resourceable fest. Um ein Objekt in das Resourcekonzept einbinden zu können sind folgende Schritte notwendig:
1. Das Resourceable Interface muß implementiert werden. 2. Ein Konstruktor mit der dem Objekte zugewiesenen Resource. 3. Jede public Methode bekommt eine *_Resource(...) Methode zur Seite, mit der der Resourcebedarf ermittelt werden kann. 4. Innerhalb der public Methode wird der Resourcebedarf ermittelt. 5. Mit dieser Information die als ResourceListe vorliegt, wird eine Auftrag (Task) erzeugt. 6. Dieser Auftrag wird beim TaskManager angemeldet. 7. Nach der Zuteilung durch den TaskManager wird der Auftrag ausgeführt. 8. Alle Resourcen und der Auftrag werden wieder freigegeben.
Diese Liste ist detailliert aber nicht vollständig. In der Klasse Resource steht imm eine Beispiel, welches aktuell sein sollte.

Folgende Programmierrichtlinien gibt es, um das „Prealloc Resource Konzept“ in Java zu integrieren:

  1. Es muß das Interface Resourceable implementiert werden. Mit Hilfe dieses Interfaces kann der Resourcebedarf eines Objektes erfragt werden. Diese Richtlinien gelten dann auch für die Superklasse.

  2. ???Es muß das Interface ModifyTestable implementiert werden. Damit kann überprüft werden, ob an den Resourcen Veränderungen vorgenommen wurden.

  3. Nur Methoden die über die Lebensdauer des Objektes keine veränderten Werte liefern dürfen immer gerufen werden. Das sind zum Beispiel alle Methoden der Klasse java.lang.Object.

  4. Um den Resourcebedarf einzelner Methoden genauer zu ermitteln kann eine Methode mit dem, um _Resource( ResourceList aRL, boolean bCheck, ... ) erweiterten Namen, gerufen werden. Ein Beispiel befindet sich in der Klasse Resource.

Security

Data Requests

Diese Schnittstelle soll das Anfordern von Daten vereinheitlichen. Das Problem, welches zu diesem Ansatz führte, ist das Lesen von Daten über einen „langsamen“ Internet-Stream. Das bedeutet es werden Daten benötigt, die erst noch übertragen werden müssen. Da das Pull-Modell immer einen eigenen Thread vorraussetzt, um die restliche Anwendung nicht zu blockieren, habe ich das Push-Modell gewählt.
Ziel: Für die Implementation sollte es möglichst transparent sein, wie die Daten herangeschafft werden. Als zweites soll die Schnittstelle für denjenigen einfach sein, der alle Daten sofort bereithält.
Lösung: Der Datenverarbeiter ist passiv. Das bedeutet, beim Entgegennehmen der Daten beginnt er nicht sie zu verarbeiten. Dies muß extra angestoßen werden. Zweitens, der Datenverarbeiter hält den Status des Datenlieferanten. Dies können EOF, für alle Daten gelesen, READY, für sind Daten da, PENDING, es kommen noch weitere Daten und NO_SOURCE, es wurden nicht alle Daten verarbeitet und es kommen keine weiteren Daten mehr. Achtung der Status ist nur zu bestimmten Zeitpunkten gültig. Der Datenverarbeiter darf nur im Zustand PENDING Daten bekommen. Diese Annahme schützt ihn vor der Implementation eines Puffers. Das QueryData - Interface ist die Spezifikation für dieses Verhalten.

Modify

Das Ziel ist nur eine Schnittstelle zu erstellen, mit der ein Objekt auf Änderungen überprüft werden kann. Da es für ein Objekt verschiedene Möglichkeiten gibt Änderungen an sich zu prüfen (z.B. Änderungszähler, Kopie), muß die Schnittstelle möglichst flexibel sein, um vielen Implementationen gerecht zu werden. Die Lösung sind zwei Methoden. Mit der einen (getModifyHandle()) kann der Zeitpunkt festgemacht werden, zu dem mögliche Änderungen überprüft werden sollen. Der Rückgabewert ist eine beliebiges Objekt, so daß in ihm die benötigte Überprüfungsinformation (z.B. der Änderungszähler) untergebracht werden kann. Danach kann mit der zweiten Methode (isModified(Object)) überprüft werden, ob eine Änderung stattgefunden hat. Das Interface für dieses Konzept heißt ModifyTestable .

LifeConnect

LifeConnect ist die Kommunikation zwischen PlugIns, Applets und JavaScript. Die Kommunikation kann in beide Richtungen erfolgen.Unter JavaScript kann auf alle Systemklassen zugegriffen werden. Die Abbildung der JavaScript-Aufrufe nach Java ist die Aufgabe der Klasse CallJava. Dazu wird das in Java 1.1 implementierte Package java.lang.reflect benutzt. Da JavaScript eine nicht typisierte Sprache ist, werden die Werte nach JavaScript-Regeln in die entsprechenden Javatypen umgesetzt. Bezüglich der Sicherheit wird ein JavaScript-Programm auf die gleiche Stufe wie ein Applet gestellt. Um den Zugriff der Applets auf JavaScript zu gestatten, muß das HTML-Tag MYSCRIPT angegeben werden. Auf die Java-Klassen kann in JavaScript mit dem Prefix „Package“ zugegriffen werden (sun, java und netscape benötigen keinen Prefix). Die Klassen netscape.plugin.Plugin, netscape.javascript.JSObject und netscape.javascript.JSException dienen zur Kommunikation von Java mit JavaScript.

Konvertierungstabelle anhand der Spezifikation „JavaScript Language Specifications“ 3.1.2 TypeConversion


byte

short

char

int

long

Undef.

Fehler

Fehler

Fehler

Fehler

Fehler

Function

(10) valueOf/error

(11) valueOf/error

(11) valueOf/error

(12) valueOf/error

(13) valueOf/error

Object

(10) valueOf/error

(11) valueOf/error

(11) valueOf/error

(12) valueOf/error

(13) valueOf/error

Object (null)

(10) 0

(11) 0

(11) 0

(12) 0

(13) 0

double

(10) Zahl oder Fehler, wenn Bereichs-überschreitung

(11) Zahl oder Fehler, wenn Bereichs-überschreitung

(11) Zahl oder Fehler, wenn Bereichs-überschreitung

(12) Zahl oder Fehler, wenn Bereichs-überschreitung

(13) Zahl oder Fehler, wenn Bereichs-überschreitung

boolean

(15) 0/1

(15) 0/1

(15) 0/1

(15) 0/1

(15) 0/1

Leer String

error

error

error

error

error

String

(10) error/ Zahl

(11) error/ Zahl

(11) error/ Zahl

(12) error/ Zahl

(13) error/ Zahl



float

double

boolean

String

Object

Undef.

Fehler

Fehler

false

„undefined“

null

Function

(14) valueOf/error

(15) valueOf/error

(15) valueOf/ true

(15) JS-Code der Funktion

(30) netscape .javascript. JSObject

Object

(14) valueOf/error

(15) valueOf/error

(15) valueOf/ true

(15) valueOf / toString

(30) Java-Cast/ error

Object (null)

(14) 0

(15) 0

(15) false

(15) „null“

(30) null

double

(14) Zahl oder Fehler, wenn Bereichs-überschreitung

(30) Zahl

(7) 0, NaN -> false !0, -+Infinite -> true

(8) Zahl, NaN, Infinity oder -Infinity

(9) Number/ error

boolean

(15) 0/1

(15) 0/1

(30) boolean

(15) „false“/ “true“

(15) Boolean/ error

Leer String

error

error

(15) false

(30) String

(15) String/ error

String

(14) error/ Zahl

(15) error/ Zahl

(15) true

(30) String

(15) String/ error


Der Algorithmus zum mappen der polymorphen Methoden in Java:
1. Die Anzahl der Parameter muß übereinstimmen.
2. Die Parameter müssen, nach der obigen Tabelle, konvertiert werden können.
3. Es gibt ein Punktesystem, nach dem die Methode ausgewählt wird. Die Punkte stehen in Klammern in den Tabelleneinträgen. Die Konvertierungspunkte für Zahlen sind typabhängig und nicht wertabhängig. Dadurch ist sichergestellt, das nicht unterschiedliche Methoden bei sich ändernden Werten gerufen werden. Kommt es allerdings zu einem Konvertierungsfehler (Überlauf), dann wird versucht eine andere Methode zu finden.
4. Es wird vorausgesetzt, daß die Methoden „valueOf“ und „toString“ keine Seiteneffekte haben. Sie dürfen also beliebig oft aufgerufen werden.
5. Es wird nur null auf eine Java-Array abgebildet.

Testen

Das Ziel dieses Abschnitts ist es Vorgehensweisen zu entwickeln, mit denen sich die Java Grundkonzepte testen lassen. Folgende Checkliste ist für jede Methode abzuarbeiten.

  1. Zu jeder Klasse gibt es eine entsprechende Testklasse. Diese steht im Package „test“.... Der Name der Klasse wird mit „Test“ erweitert. Beispiel: stardiv.memory.BitArray wird zu test.memory.BitArrayTest. Jede dieser Klassen hat eine Methode „public static void main( String [] )“. Diese Methode wird aufgerufen, um den Test aller Methoden anzustoßen. Der Test ist nicht interaktiv. Wird ein Fehler festgestellt, wird das Programm mit exit( -1 ) verlassen.

  2. Jede Methode muß unabhängig von ihren Environment getestet werden. Alle Resourcen für die Methode werden als Dummy für den Test implementiert. Diese Forderung führt zu sauberen Schnittstellen, da ansonsten für den Test ja ganze Objekte implementiert werden müssen.

  3. Das Testprotokoll protokolliert mit „System.out.println“. Vor dem Test der einzelnen Methoden wird in einer Zeile kurz über den Test informiert. Scheitert der Test, dann wird eine Fehlermeldung ausgegeben in der „failed“ enthalten sein muß.

  4. Um Defined Exception und Transacted Exception testen zu können, sollten die Resource und ModifyTestable Interfaces implementiert werden. Es kann damit automatisch geprüft werden, ob sich eine Resource unerlaubter Weise geändert hat.

Begriffe

Lebendig: Ein System wird als lebendig bezeichnet, wenn alle in ihm befindlichen Aufträge fertiggestellt werden können. Sie sich also nicht in einer Verklemmung oder einem „Race“ befinden.