Die Verwaltung

NextDer technische Kern des Modulsystems ist die Modulverwaltung. Sie ist dafür verantwortlich Module bei Bedarf zu finden und in der aktuellen Java VM (nicht in einer GUI, siehe explizites Laden) zu installieren, auszutauschen oder zu deinstallieren.

Dazu kennt die Verwaltung zum einen ein Modul Repository und zum anderen ist sie mit dem Java Class Loading Mechanismus verbunden und erweitert diesen um die Fähigkeit mit dynamischen Modulen umgehen zu können.

Dies erfolgt wie in anderen Systemen (z.B. OSGi) auch üblich durch einen Wechsel vom klassischen, hierachischen ClassLoader Modell zu einem Delegationsmodell, bei dem jedes Modul seinen eigenen ClassLoader besitzt und Anforderungen nach Klassen und Ressourcen, die es nicht zur Verfügung stellt, an die Modulverwaltung delegiert.

Die Modulverwaltung versucht dann anhand der Importinformationen des Moduls, das entsprechende, exportierende Modul zu finden bzw. zu laden um dann die Anfrage an dieses Modul weiter zu leiten.

Der Mechanismus arbeitet also dynamisch und auf Anfrage (on demand). Im Prinzip wie der ClassLoading Mechanismus von Java selbst – nur bezogen auf Module.

Das Verfahren hat den Vorteil, dass die Modulverwaltung keinen Abhängigkeitsgraphen aufbauen muss und auch sonst keinen aktiven “Prozess” darstellt oder benötigt. Aus diesem Grund benötigt das ISA Modulsystem auch keine eigene Java VM und keine expliziten, von außen durchzuführenden Modulinstallationen bzw. Deployments – so wie z.B. OSGi oder JEE Komponenten.

Der ganze Mechanismus beginnt mit einer Anforderung einer Modulverwaltung (siehe auch Moduleinbindung) über die Bootstrap Klasse des Systems. Z.B. in einer Smart Client Anwendung beim Start.

/**
*/
public class MyApplication{

	//in args z.B. ein String wie
	//"-StartupPlugIn isa.Workbench:start[version=1.0.0]"
	public static main(String[] args){
		PlugInHandler handler = null;

		//Von Bootstrap eine Verwaltung für eine Client Anwendung anfordern
		handler = isa.Boostrap.getInstance(args).getStandaloneCallHandler();

		//Verwaltung starten - das war's
		//die Verwaltung läd das Modul isa.Workbench
		//und ruft anschließend dessen "start" Methode auf
		handler.start(args);
	}
}

Mit der Erstellung einer Modulverwaltung entsteht ein Kontext, der alle Module, die durch diese Verwaltung geladen werden in einen Zusammenhang setzt. Es muss nun nur noch das erste bzw. ein Modul (z.B. ein hier in args mitgegebenes Startupmodul) explizit geladen werden und die in den Modulen definierten Abhängigkeiten (Imports) ermöglichen, dass sich ein System bzw. seine gerade benötigten Teile nicht nur auf Klassen- sondern auch auf Modulebene dynamisch und automatisch von selbst aufbaut.

/**
*/
public class MyModuleFoo{

	//Module können die Verwaltung
	@Resource()
	protected PlugInHandler handler = null;
	//und andere Ressourcen injiziert bekommen
	@Resource(Type.CONFIG)
	protected Map config = null;

	//oder über eine optionale Callback Methode erhalten
	protected void onInstanceActivation(Object config){
		this.config = (Map)config;

		handler = (PlugInHandler)config.get(PlugInHandler);
	}
}

Dieses implizite Ladeverfahren, ist das Standardverfahren auf der serviceorientierten Serverseite. Auslöser für das Laden eines Moduls (Service) ist dabei ein Aufruf durch einen Client oder einen anderen Service.

Explizites Laden von Modulen
Neben diesem automatischen, auf den impliziten Modulabhängigkeiten basierenden Lademechanismus können Module natürlich auch explizit installiert bzw. deinstalliert werden. Ein Beispiel dafür ist die Smart Client Workbench. Sie enthält neben ihren Basismodulen in ihrer Konfiguration eine Liste der Module, die zwar einen konkreten Client bilden – ABER dadurch nicht zwangsläufig Modulabhängigkeiten aufweisen müssen. Nach einer erfolgreichen Systemanmeldung werden diese Module dann installiert und dem Benutzer steht seine persönliche, individuelle Arbeitsumgebung zur Verfügung.

Das Installieren/Deinstallieren eines Moduls und seine visuelle Präsens in z.B. der Workbenchoberfläche sind dabei zwei unterschiedliche und völlig getrennte Dinge. Zuerst erfolgt eine Installation als Modul im Modulsystem. Dieser Vorgang ist auf dem Client wie auf dem Server gleich.

Erst danach wird ein Client Modul u.U. von der Umgebung (z.B. Workbench) in der es sich befindet “gefragt”, wie es in diese Umgebung funktional und visuell eingebunden werden will. Das Modul liefert dazu beschreibende Informationen (HandlingDefs) anhand derer dann die konkrete Einbindung durch und in der Umgebung erfolgt.

Client Module sind auf diese Weise nicht an eine bestimmte Umgebung bzw. Bedienungskonzept wie z.B. die Workbench gebunden – sondern können in beliebigen Client Umgebungen eingesetzt werden. So kann eine Client Umgebung z.B. auch ein Einzelfenstersystem, eine Konsole, ein Widget auf einem Desktop, eine Web-Oberfläche oder ein nicht direkt sichtbarer Agent oder Service sein.

Die deklarative Einbindung in eine Umgebung wie z.B. eine GUI Oberfläche hat zudem den Vorteil, dass sich der Entwickler nicht mehr um die entsprechenden Techniken kümmern muss. Wodurch gleichzeitig der häufig anzutreffende “Wildwuchs” von Bauteilanbindungen (Menüs, Schaltflächen, Listener etc.) verhindert wird.

Das Prinzip ermöglicht auch die ganz oder teilweise Verwendung von “Client” Modulen auf dem Server solange der GUI spezifische Teil eines solchen Moduls dabei nicht aktiviert bzw. benötigt wird. Das ist zwar konzeptionell unsauber – kann aber u.U. bei Migrationen nützlich sein, wenn Bibliotheksstrukturen in Module migriert werden müssen.