Wie schon im Überblick angedeutet, reguliert der Roblet®-Server den Zugriff auf Ressourcen und stellt darüber hinaus eine Laufzeitumgebung für Roblets® bereit. Die Ressourcen werden vom Server jedoch nicht direkt betreut. Diese Aufgabe haben Module.
Im Prinzip ist ein Modul eine Java™-Klasse, die die Schnittstelle genRob.genControl.modules.Module implementiert. Damit der Server eine Modul-Klasse verwendet, muß ihr Name bei seiner Ausführung als Argument übergeben werden und die Klasse über den Klassenpfad auffindbar sein.
Modul initialisieren
Der Konstruktor einer Modul-Klasse sollte wenig oder nichts machen, um eine störungsfreie Initialisierungsphase zu ermöglichen und in vielen Fällen dadurch auch die Fehlersuche zu vereinfachen. Die eigentliche Initialisierung eines Moduls erfolgt hingegen in der zu implementierenden Methode genRob.genControl.modules.Module.moduleInit(...).
Der Server ruft moduleInit nur einmal nach dem Laden der Module-Klasse auf. An dieser Stelle kann ein Modul z.B. persistente Daten vom letzten Lauf laden oder einmalig notwendige Untersuchungen des unterliegenden Systems vornehmen. Dabei sind zeitlich grundsätzlich keine Grenzen gesetzt. Log-Ausgaben sind möglicherweise bei längeren Arbeiten sinnvoll.
Die Methode moduleInit wird durch einen Thread aufgerufen, der im offenen Sicherheitsbereich des Servers läuft. Das bedeutet, daß standardmäßig sämtlicher Code eines Moduls inkl. all seiner Klassen unbeschränkten Zugriff auf die Klassen- und Laufzeitbibliothek des Java™-Systems (Java™-Bibliothek) hat. Administratoren, die mit eigenen Sicherheitsrichtlinien arbeiten, müssen gegebenenfalls für die jeweiligen Module "Löcher" ermöglichen.
moduleInit bekommt als Parameter einen Verwalter und einen Nutzungszähler. Der Verwalter ist eine Instanz von genRob.genControl.modules.Supervisor und dient als Server-Kontext zum Zugriff auf gewisse Teile des Servers. Der in moduleInit übergebene Nutzungszähler wird nicht mehr benutzt.
Läuft ein Modul während der Initialisierung auf einen Fehler, der seine Funktion verhindert, so ist es üblich, eine Ausnahme zu werfen. In diesem Falle wird der Server alle zuvor initialisierten Module wieder Aufräumen und seine Arbeit beenden.
Modul aufräumen
Will der Server enden, so beendet er zunächst alle Roblets® und ruft danach für jedes Modul einmal die Methode genRob.genControl.modules.Module.moduleDone(...) auf. Dadurch hat jedes Modul die Möglichkeit, abschließende Aufgaben zu erledigen. Dabei können z.B. Daten für den nächsten Lauf persistent gemacht werden, Datei- und Netzwerk-Verbindungen geschlossen werden u.v.a.m.
Im wesentlichen gelten für diese Methode die gleichen Bemerkungen, wie für moduleInit. Der zentrale Unterschied ist jedoch, daß keine Ausnahmen geworfen werden dürfen, um sicherzustellen, daß der Server alle Module aufräumen kann. Hier sind dann nur Log-Ausgaben erlaubt.
Einheiten bereitstellen
Ein Modul kann eine beliebige Anzahl von Einheiten umfassen. Meist bestimmen entwicklungsrelevante Bedingungen, wie z.B. die Funktionalität einer Hardware, die Grenzen einer Einheit.
Die Methode genRob.genControl.modules.Module.getUnit4Slot(...) (gelesen "get unit for slot") ermöglicht dem Server, die Anfrage eines Roblets® auf Herausgabe einer Instanz einer Einheiten-Implementierung einer bestimmten Einheiten-Definition zu beantworten. Dabei ist zu beachten, daß die Laufzeitumgebung des Servers die parallele, gleichzeitige Ausführung von mehreren Roblets® erlaubt, weshalb pro Roblet® eine eigene Einheiten-Instanzen herausgegeben werden muß. Konflikte, die bei der gemeinsamen Nutzung einer Ressource auftreten, müssen durch das Modul aufgelöst werden. Nachfolgend sind weitere Bedingungen für die Implementierung von Einheiten genannt.
Die Einheiten-Definition wird der Methode getUnit4Slot als Instanz vom Typ java.lang.Class übergeben. Es handelt sich um genau die Klasse, die ein Roblet® übergibt. Von einer beliebigen Einheiten-Definition ist die Klasse immer über "Einheiten-Definitions-Name.class" erhältlich. Über einen entsprechenden Vergleich (==) mit den vom jeweiligen Modul betreuten Einheiten-Definitionen kann der Wunsch eines Roblets® aufgelöst werden.
Der zweite übergebene Parameter ist ein Nutzungszähler vom Typ genRob.genControl.modules.Use. Durch den Nutzungszähler kann der Server sicherstellen, daß ein Roblets® separat beendet werden kann. Pro Roblet® erzeugt der Server genau einen Nutzungszähler. Anhand des Nutzungszählers können Roblets® demnach bei Bedarf voneinander unterschieden werden. Die Hauptbedeutung des Zählers ist aber der nachfolgend beschriebene try-finally-Mechanismus.
Als dritter Parameter zur Methode getUnit4Slot wird eine Instanz von genRob.genControl.modules.Slot übergeben. Diese Instanz repräsentiert das Fach eines Roblets® für das Module und ist pro Roblet® eindeutig. Das Fach ermöglicht den Zugriff auf die Laufzeitumgebung des jeweiligen Roblets®.
public Unit getUnit4Slot (Class clazz, Use use, Slot slot) { if (clazz == HelloUnit.class) // Es wird in diesem Fall bei jedem Aufruf eine neue Instanz erzeugt. return new HelloUnitImpl (use); // Ansonsten: Einheit nicht hier implementiert. return null; }
Nutzungszähler verwenden / try-finally-Mechanismus
Alle vom Modul an das Roblet® übergebenen Instanzen inklusive der Einheiten-Instanzen, müssen den in getUnit4Slot übergebenen Nutzungszähler des Roblets® in ihren durch das Roblet® aufrufbaren Methoden einsetzen. Dabei muß immer als erste Aussage einer Methode die Methode genRob.genControl.modules.Use.raise() aufgerufen werden. Die nächste (und letzte Aussage) ist dann stets ein try-catch-Block. Im try-Teil des Blocks findet die gewünschte Aktivität statt. Im catch-Teil darf nur die Methode genRob.genControl.modules.Use.lower() aufgerufen werden.
use. raise (); try { // ... hier die Arbeit mit der Ressource } finally { use. lower (); }
Diese Konstruktion stellt sicher, daß beim Eintritt eines Threads des Roblets® der Nutzungszähler erhöht und beim Austritt - auch im Falle einer Ausnahme - wieder erniedrigt wird. Der Server kann dadurch prüfen, ob noch Threads des Roblets® im Code des Moduls arbeiten und darüberhinaus bei Bedarf weiteren solchen Threads den Zutritt über raise hinweg verhindern.
Ressourcen privilegiert zugreifen
Da für einen Server möglicherweise die Sicherheitsmechanismen aktiviert sind, ergeben sich beim direkten Zugriff von Threads eines Roblets® auf privilegierte Ressourcen besondere Erfordernisse. Privilegierte Ressourcen sind z.B. Dateien, Netzwerkverbindungen aber auch Java™-Eigenschaften oder JNI-Anbindungen - erkennbar immer dann, wenn Methoden u.a. die Ausnahme java.lang.SecurityException werfen. Diese Einschränkungen sind natürlich genau so gewollt. Allerdings ist deshalb in ebendiesen Fällen innerhalb eines Moduls dafür Sorge zu tragen, daß die Threads die nötigen Privilegien erhalten. Diesmal kontrolliert das Modul, daß z.B. nur genau eine bestimmte Datei gelesen oder geschrieben wird etc.
Die dafür notwenigen Mechanismen der "Erhöhung" von Privilegien geschieht mit Hilfe der Klasse java.security.AccessController bzw. einiger ihrer Methoden. Angenommen man hat die Methode someMethod zuzugreifen und das Ergebnis zurückzugeben:
Clazz clazz = ...; Parameter parameter = ...; try { return clazz. someMethod (parameter); } catch (AnyException e) { // Fehler behandeln }
Dann kann man in diesem Fall wie folgt umschreiben:
final Clazz clazz = ...; final Parameter parameter = ...; try { return (Result) AccessController. doPrivileged ( new PrivilegedExceptionAction () { public Object run () throws AnyException { return clazz. someMethod (parameter); } } ); } catch (PrivilegedActionException pe) { AnyException e = pe. getException (); // Fehler behandeln }
Die angegebene Variante läßt sich im Grunde stets einsetzen. Gibt die Methode einen primitiven Datentyp wie z.B. int zurück, so ist noch ein "Umpacken" in und von dem passenden Verpackungstyp (dann z.B. java.lang.Integer) vorzunehmen. Werden mehr Ausnahmen geworfen, so ist entsprechend zu erweitern. Darüber hinaus gibt es jedoch noch einfachere Varianten. Mehr dazu kann in der Dokumentation Java™ 2 Platform Security Architecture im Kapitel 4.2.2 Handling Privileges nachgelesen werden.
Einheiten zurücksetzen
Die Methode genRob.genControl.modules.Module.resetUnit4Slot(...) (gelesen "reset unit for slot") wird vom Server immer dann aufgerufen, wenn ein Roblet® beendet wurde. Es wird eine Einheiten-Instanz als Parameter übergeben. Es handelt sich um die Einheiten-Instanz, die zurückzusetzen ist. Jedes Modul muß prüfen, ob die übergebene Instanz von ihm kommt. Die Instanz wird vom Typ genRob.roblet.Unit übergeben, kann aber bei Bedarf nach Prüfung des Typs passend gewandelt werden.
Ein Modul muß sicherstellen, daß es keine Instanzen, die in einem Roblet® erzeugt wurden, nach dessen Ende weiter aufbewahrt. Anderenfalls kann ein Roblet® nicht vollständig aufgeräumt werden, da der zugehörige Klassenlader des Roblets® noch in Verwendung ist. Gegebenenfalls müssen also nach Ende eines Roblets® Kopien von benötigten Daten im Modul erzeugt werden.
public boolean resetUnit4Slot (Unit unit) { // Eine Instanz von diesem Modul? if (unit instanceof HelloUnitImpl) { // Rufe die zugehörige Aufräum-Methode auf. ((HelloUnitImpl) unit). reset (); // Der Server braucht keine weiteren Einheiten befragen. return true; } // Ansonsten: Wir kennen die Einheit nicht. return false; }
Threads
Um verschiedene Threads eines Roblets® muß sich ein Modul hinsichlich der Nutzung von Ressourcen keine außergewöhnlichen Gedanken machen. Gegebenenfalls sind die bekannten Synchronisationsmechanismen von Java™ zu verwenden.
synchronized (...) { // ... }
Komplizierter wird es, wenn zwei Threads aus verschiedenen Roblets® die gleiche Ressource benutzen. Hier kann eine einfache Synchronisation, welche ja nur den tatsächlich quasi-parallelen Zugriff verhindert, an ihre Grenzen stoßen. Eine solche Situation entsteht, wenn eine Form der Reservierung, d.h. eine Mischung aus Berechtigung und Synchronisation, gewünscht wird.
Hier hilft dem Modul das Roblet®-spezifische Fach. Beim Erzeugen einer Einheiten-Instanz für ein Roblet® kann das Fach mit eingeschlossen werden. Es steht dadurch bei jedem Eintritt eines Threads eines Roblets® zur Verfügung. Durch Vergleich der Fach-Instanzen kann der Aufruf einem Roblet® zugeordnet werden. Diese Information kann dann für Reservierungen genutzt werden.
Unbenutzte Modul-Methoden
Die Methoden getUnit und getRegistry werden nicht mehr benutzt. Sie müssen implementiert werden, sollten aber null zurückgeben.