Konzeptfrage zu modularen Systemen

J

JavaZivi

Gast
Guten Morgen Java-Community,

da ich nun seit einiger Zeit mit Java arbeite und programmiere wollte ich mal ein paar Informationen zu modularen Systemen einholen. Man könnte in meinem Fall auch von "Pluginsystem" sprechen. Es läuft eigentlich auf das selbe hinaus.
Ich wollte mich grob an die Struktur von Applets halten, also init(), start(), stop() und destroy(). Dafür hatte ich gedacht ein Interface zu verwenden was halt solche 4 Methoden vorgibt. Über die genaue Bezeichnung kann man ja noch reden, würde aber als return-Type lieber boolean verwenden anstatt mit void und Exceptions um mich zu werfen. Das Exception-Handling würde ich also dann der Modul-Implementierung überlassen und im Fehlerfall einfach mit false als return signalisieren das was schief gelaufen ist. Für den Anwender wäre es ja eigentlich egal ob er nun "Fehler" bekommt oder einen langen StackTrace. Für die Fehlerbehebung müsste dann natürlich geloggt werden. Zusätzlich will ich nicht nur die Module gegen ein Interface legen sondern auch deren Verwaltung. Ich habe dazu schon verschiedene Ansätze gesehen : "Loader" und "Handler", abstract class und Interfaces, einfache Dokumentation die eine Beispiel-Implementierung vorgibt. Also eigentlich alles Mögliche aber doch nichts konkretes. Hier würde mich mal "best practice" interessieren. Vielleicht hat ja schon mal jemand sowas selbst umgesetzt und damit Erfahrung.
Eine zusätzliche Frage die sich mir stellt ist die Kommunikation der Module untereinander. Natürlich könnte man einen Workaround mit Reflections über den "Handler" (oder was auch immer) realisieren, und erlich gesagt fällt mir auch eigentlich nicht wirklich etwas anderes ein, aber irgendwie finde ich das ziemlich "dirty". Zusätzlich muss man natürlich Dead-Locks vermeiden, denn wie oben angedeutet sollen die Module zur Runtime auch wieder komplett entladen werden können. Darum wäre eine Listener-Struktur nur sinnvoll wenn die Kontrolle darüber bei der selben "Verwaltungs-Instanz" liegt die auch für das Laden/Entladen der Module verantwortlich ist. Natürlich könnte man auch das Modul-Interface um ein paar Methoden a la void doIt() oder boolean setData(Object...) erweitern, allerdings bliebt dann immer noch die Frage wie ich diese Methoden des einen Moduls aus einem anderen heraus aufrufe.

Vielleicht sollte ich erstmal erklären was die "Basis" des ganze Systems sein soll :
Das "Grundprogramm" soll später ein einfaches JFrame mit zwei JPanel sein. Eine Seite enthält ein "Menü", die andere ist der Anzeigebereich. Die Funktionsweise soll nachher entsprechend sein das ein Modul sein JPanel enthält (allerdings nicht von diesem erbt), darauf seine Informationen darstellt und sich selbst mit einem JButton im "Menü" "registriert". Warum die Module nicht von JPanel erben sollen wird spätestens klar wenn man auch "Hintergrund-Module" verwenden will, wie z.B. einen zentralen Netzwerk-Stub oder der gleichen. Also auch alles stark MVC orientiert. Und daher ist auch die Kommunikation unter den Module so wichtig.

Der letzte Punkt wäre dann vielleicht noch die Sicherheit :
- welches Modul darf was machen und was nicht
- auf welche Daten hat wer Zugriff
- wer darf wen kontrollieren
- in wie weit hat die "Basis-Struktur" einfluss auf die "Rechtesteuerung"

Ich habe natürlich auch im Rahmen meiner Suche auch von OSGI und "Plugin-Frameworks" gehört, würde das ganze jedoch lieber "selber" machen wollen. Auch wenn es dann nicht so allgemein wird sondern halt nur speziell auf meine Anwendung zugeschnitten ist dürfte es eine gute Grundlage sein.

Hoffentlich habe ich halbwegs zum Ausdruck bringen können was ich vorhabe und wo dabei meine Probleme liegen. Was das posten von Source angeht : eigentlich würden Denkanstöße und pseudo-Codes völlig reichen. Die Umsetzung dessen sollte mit meiner Erfahrung kein Problem sein. Und wenn nicht kann ich ja immer noch nach einem kleinen Beispiel fragen.

-JavaZivi
 
S

Spacerat

Gast
Also was ganz abstraktes in dieser Hinsicht, wäre SPI (ServiveProviderInterface) und die Klasse ServiceLoader. Die Möglichkeiten, die sich dadurch bieten sind aber ebenso abstrakt wie zahlreich und können im einzelnen gar nicht erklärt werden. Einfach mal nach googeln. ;)
Wenn du dich durch undokumentierten Code frickeln willst, kannst du dir ja mal meine DT_Lib ansehen, die muss zwangsläufig unheimlich modular sein, faktisch sogar noch um einiges modularer als der dort veröffentlichte Code.

Im Fehlerfall sind Exceptions im übrigen viel aufschlussreicher als ein simples boolean, denn sie sagen einem nicht nur dass etwas schief gelaufen ist, sondern sie sagen auch noch was schiefgelaufen ist. Sicher lässt sich die Vermittlungsstelle PlugIn<->User so implementieren, dass sie dem User das "was" vorenthält, sie selbst sollte aber dennoch davon in Kenntnis gesetzt werden können. Ausserdem kannst du es nicht verhindern, wenn ein PlugIn eine "unchecked" Exception innerhalb der init() wirft. Afaik ist es eine C-Unsitte, Fehler per Rückgabewert anzuzeigen.
 
Zuletzt bearbeitet von einem Moderator:

TheDarkRose

Gesperrter Benutzer
Sieh dir doch mal OSGi an. Oder wenn dir die öberfläche gefällt gleich eclipse rcp

Gesendet von meinem GT-I9000 mit Tapatalk 2
 
J

JavaZivi

Gast
Erstmal DANKE für die Antworten. Ich werde mal versuchen mich mit dem genannten auseinander zu setzen, obwohl ich ja eigentlich sagte das ich auf OSGI verzichten möchte. Von Eclipse RCP hab ich schon einiges gehört, und meine Erfahrung sollte auch ausreichen um es umzusetzen, aber es wäre vielleicht für den Anfang etwas zu viel des Guten. Natürlich sollte man bei einem größeren Projekt auf sowas zurück greifen, aber ich möchte erstmal etwas kleiner anfangen und selbst etwas zusammenbasteln.
Auch wenn ich es in meinem ersten Post nicht erwähnt habe scheidet SPI auf Grund einiger Mängel am Design für mich bereits jetzt schon aus. Das Hauptproblem für mich stellt dabei die sehr eingeschränkte Dynamik dar. Man kann zwar ServiceLoader.load(Class, ClassLoader) verwenden, jedoch führte beim Test ein URLClassLoader.close() zu einem FATAL ERROR der VM (warum auch immer) und es sind mir persönlich einfach ein paar zu viele Referenzen um die man sich kümmern muss. Außerdem ist es ja eigentlich egal ob ich nun gegen eine abstrakte Klasse arbeite oder ein Interface, und für die Information welche Klasse zu laden ist kann ich bei den Modulen auch "Main-Class" im Manifest missbrauchen und über [c]JarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS)[/c] auslesen.
Da ist es nun doch etwas einfacher (zumindest für den Anfang) mit URLClassLoader und einer List zu arbeiten, wobei ich auch eigentlich gerne bleiben würde.

Versteht mich bitte nicht falsch, ich bin euch dankbar für die Antworten und werde noch mal das Forum und Google durchsuchen um vielleicht eine Lösung für meine Probleme mit SPI zu finden. Allerdings würde ich wie oben bereits erwähnt erstmal davon abstand halten und selbst etwas entwickeln wollen.


Danke auch für die Anmerkung über die "schlechte C-Unsitte". Eigentlich wollte ich es so lösen das jedes Modul für sich selbst das Exception-Handling übernimmt und halt nur meldet ob ein Fehler aufgetreten ist oder alles in Ordnung war. Ob es Sinn macht den "Loader" darüber zu informieren was genau nun schiefgelaufen ist bin ich geteilter Meinung. Einerseits kann man so natürlich versuchen das Problem zu beheben um dann noch mal zu versuchen das Modul zu laden, andererseits würde es natürlich den Loader/Hanler aufblähen. Und wirklich jede RuntimeException kann man nun mal nicht behandeln.
Außerdem empfinde ich es als "Unsitte" überhaupt RuntimeExceptions zu behandeln, denn das Auftreten kann man (fast) immer vorher abfangen und sinnvoll behandeln.
NullPointer z.B. kann man durch [c]if(Object==null)[/c] abfangen. FileNotFound durch [c]File.exists()[/c]. Und genau desshalb wunder ich mich ja warum ich dann im Loader/Handler eine RuntimeException-Behandlung machen soll wenn man diese eigentlich durch saubere Programmierung (fast) komplett vermeiden kann. Auf RuntimeExceptions catchen ist Missbrauch des Exception-Handlings.

Dazu sollte ich vielleicht noch erwähnen das ich erstmal alle Module selbst entwickel und daher natürlich genau prüfen kann ob der Code meinen Anforderungen und dem Modul-System genügt. Eine Ausweitung auf andere Programmierer würde ich dann doch erst machen wenn ich mich dann in OSGI, SPI, Eclipse RCP oder andere Frameworks / Techniken eingearbeitet habe.


Vielleicht finde ich ja im verlinkten Source etwas was mir bei der Kommunikation der Module untereinander hilft.

-JavaZivi
 
S

Spacerat

Gast
Bei dieser "C-Unsitte" hast du evtl. etwas falsch verstanden. Die Problematik dabei ist ganz einfach die, dass Drittanbieter von PlugIns sich keinesfalls an die Spezifikation "Fehler per Returncode" halten müssen und statt dessen eine RT werfen, die nicht mal abgefangen werden muss und deswegen auch faktisch niemals abgefangen wird, weil es per Design gar nicht Vorgesehen ist. Du würdest deinen Loader von vorne herein etwa so implementieren:
Java:
public PlugIn load(Class<PlugIn> piClass) {
  PlugIn rc = PlugIn.load(piClass);
  if(rc == null || !rc.init()) {
    Log.printErr("fehler");
  }
  return rc;
}
Was aber, wenn ein Drittanbieter nun
Java:
class DAPlugIn extends PlugIn {
  public boolean init() {
    if(!PlugIn.isLoaded(DependendPlugInClass.class)) {
      throw new IllegalStateException("required plugin missing");
    }
    return initInternal();
  }
}
implementiert? (Was durchaus geht und du nicht verhindern kannst) Dein ganzes PlugIn-System würde abstürzen, weil die (bzw. nicht eine einzige) Exception nicht in der Lademethode abgefangen wird. Da fängt man doch lieber rigoros alle Exceptions im Vofeld ab und man hat obendrein noch einen passenden Messagetext für den Logeintrag.
 
J

JavaZivi

Gast
Ja, gut, ist verständlich was du meinst. Sehe ich ein das ich im Loader/Handler auf jeden Fall Exception-Handling machen sollte. Aber auf was catch ich ? RuntimeException wäre jetzt das was mir am sinnvollsten erscheint. Zumindest würde es mir das "throws X-Exception" im Interface ersparen.
Sicher ist es sinnvoll dem Handler mitzuteilen warum das laden/init/starten/stoppen/destroy/entladen fehlgeschlagen ist anstatt lediglich zu sagen das es so ist, aber dennoch müsste man das ganze doch vereinheitlichen, oder ? Was mir dazu vielleicht noch einfallen würde wären spezielle Exceptions wie z.B. DependencyException oder so. Damit würde man aber Dritten wieder vorschreiben wie sie solche Probleme zu lösen haben woran sich dann wieder einige nicht halten würden. Irgendwie dreh ich mich hier im Kreis.

Ich hab schon mal so langsam angefangen etwas zu implementieren, bin dann aber beim lesen deines Posts drauf gestoßen : Abhängigkeiten. Ich hab mich damit erlich gesagt noch gar nicht beschäftigt. Wie implementiert man eine Prüfung halbwegs "usefull" ? Meine Idee wäre jetzt das man erst alle Modul-Jar-Files läd, der List alle verfügbaren Plugins hinzufügt und dann in init() den Modul-"Manager" fragt ob diese Klasse vorhanden ist. So ein modulares System ist dann doch noch ein Stück komplexer als ich oben dachte.

Ich hab mir auch mal den Wikipedia-Artikel zu OSGI durchgelesen (auch den englischen den ich persönlich deutlich besser finde als den deutschen) und irgendwie versteh ich das nicht wirklich. OSGI an sich ist nur eine Spezifikation mit einer Referenzimplementierung (die nicht für produktiven Einsatz verwendet werden sollte). Dazu gibt es dann von verschiedenen Anbietern jeweils eigene Implementierungen die man verwenden kann. Soll das jetzt heißen das ich mir quasi mit Hilfe der Referenzimplementierung meine eigene OSGI-Plattform erstellen kann bzw. muss oder wie ist das gemeint ? Irgendwie steig ich was OSGI angeht überhaupt nicht durch und steh da ziemlich auf dem Schlauch.
Was mich aber besonders interessiert ist der "Security-Layer" in OSGI der regelt was ein Modul darf und was nicht. Wie genau wird das eigentlich realisiert ? Werden alle calls nur gegen die Plattform gerichtet die dann entscheidet ob diese zum "Ziel" weitergeleitet oder mit einem Fehler abgewiesen werden ? Oder wird da was mit SecurityManager getrickst wovon ich nur nichts weis ?

Ich merk schon, irgendwie ist es doch "etwas" schwerer ein modulares System zu bauen als nur mit ein paar Interfaces und "Standard-Klassen" der SE-API die man irgendwie in jedem Projekt irgendwie braucht.
Darum will ich ja "klein" anfangen weil ich bei den etablierten "quasi-Standards" in diesem Bereich noch ziemlich aufm dem Schlauch stehe.

-JavaZivi
 
S

Spacerat

Gast
Beim fangen von Exceptions kommt es prinzipiell nur darauf an, wieviel "Verantwortung" du selbst übernehmen bzw. wieviel du weiterreichen willst. Am sichersten ist es in dieser Beziehung alles Throwable abzufangen und an eigene Handler (Logger usw.) weiterzuleiten, die das Programm dann evtl auch sauber beenden. Was aber gar nicht geht, ist wenn man einen solchen Catch-Block schlicht leer lässt. Im übrigen musst du beim fangen gar nicht zwischen checked und unchecked Exceptions unterscheiden, weil RuntimeException (die Mutter aller unchecked Exceptions) Exception (die Mutter aller checked Exceptions) erweitert. Ferner können von einer Methode, die keine throws-Klausel hat, ja auch nur unchecked Exceptions und Errors kommen.
Java:
try {
  myPlugin.init();
} catch(Throwable e) {
  // hier muss unbedingt etwas passieren und wenn's blos
  e.printStacktrace();
  // ist.
}

Ich will ja nix sagen, aber wer hat behauptet, PlugIn-Systeme seien einfach? OSGI macht evtl. einiges vieles einfacher, hat aber den gravierenden Nachteil, dass man es mit seiner Software zusammen publizieren müsste (das gilt für jede verwendete Fremdbibliothek), weil es ja nicht zum SE-Standard gehört. Und wo wir schon beim SE-Standard sind; Hoffentlich möchtest du dein PI-System nicht auch für Android kompatibel halten, denn dort müsstest du dann erst mal Dinge wie AWT, Swing, Java2D und viele weitere fehlende SE-Standards implementieren oder darauf verzichten. Bei der DT_Lib, welche im übrigen eine Dateityp-Erkennung werden soll, hadere ich da grad' auch. Mach ich's weiträumig kompatibel oder beschränke ich mich nur auf die SE-JVM. Schon nicht leicht solche Überlegungen und keiner nimmt einem die Entscheidungen ab, sofern man kein Team hinter sich hat.
 
Zuletzt bearbeitet von einem Moderator:
J

JavaZivi

Gast
Wenn man der DOC folgt sollte man Throwable nicht direkt catchen. Als Grund dafür dient die Aussage der Sub-Klasse Error das diese normalerweise nie gecatched werden sollten. Auch allgemein Exception würde ich nicht direkt catchen da man so nicht mehr zwischen checked und unchecked Exceptions unterscheiden kann. Also entweder catched man auf RuntimeException oder definiert eigene checked Exceptions.

So wirklich "jedem recht machen" kann man es ja eh nicht. So ein bisschen "Kontrolle und Vorgabe" muss man bei so einem Framework schon machen. Darum fragte ich ja auch im Eröffnungspost nach der "best practice" für eben dieses Problem gefragt. Das einfach "true / false" nicht das beste ist wurde ja nun klar, aber einfach "wahllos" Throwable catchen ist auch nicht das beste Mittel.

-JavaZivi
 
S

Spacerat

Gast
Wenn man der DOC folgt sollte man Throwable nicht direkt catchen. Als Grund dafür dient die Aussage der Sub-Klasse Error das diese normalerweise nie gecatched werden sollten. Auch allgemein Exception würde ich nicht direkt catchen da man so nicht mehr zwischen checked und unchecked Exceptions unterscheiden kann. Also entweder catched man auf RuntimeException oder definiert eigene checked Exceptions.

So wirklich "jedem recht machen" kann man es ja eh nicht. So ein bisschen "Kontrolle und Vorgabe" muss man bei so einem Framework schon machen. Darum fragte ich ja auch im Eröffnungspost nach der "best practice" für eben dieses Problem gefragt. Das einfach "true / false" nicht das beste ist wurde ja nun klar, aber einfach "wahllos" Throwable catchen ist auch nicht das beste Mittel.

-JavaZivi
Was Throwable fangen angeht hast du im Prinzip recht, in meinem Post stand jedoch etwas von Verantwortung und das nicht umsonst. Was man nicht machen sollte aber trotzdem tut, entscheidet in einem Pluginsystem der Entwickler eines PlugIns und das ist nicht zwangsläufig der Entwickler des Pluginsystems. Die Schnittstelle PlugIn<->Entwickler ist leider um vieles sensibler, als die Schnittstelle PlugIn<->User. Wenn man sich als Entwickler des Systems nun aus der Verantwortung zieht indem man strikt nach Lehrbuch entwickelt, liefert man sich denen aus, die das aus irgendwelchen Gründen nicht tun. Solche Leute gibt es und zwar recht viele. Einige tun es aus Unwissenheit, andere vorsätzlich und letztere sind die schlimmsten. Von denen bekommst du evtl. völlig grundlos auch mal einen OutOfMemoryError oder StackOverflowError geworfen, wodurch evtl. die Software auf einem Server mit Logindaten neu gestartet wird. Natürlich wartet bereits jemand darauf und fängt bei diesem Neustart mal eben die Logindaten ab. Von daher; wahllos fängt man Throwable sicher nicht ab, aber wenn, dann hat man einen triftigen Grund.
[EDIT]Glücklicherweise ist dieses Verfahren zum Aneignen fremder Zugangsdaten längst veraltet. Heutige Verfahren funktionieren aber mitunter immer noch durch gezieltes Verursachen von Fehlern. Ist halt ein Angriffspunkt, der jede Software aus dem Takt bringt.[/EDIT]
 
Zuletzt bearbeitet von einem Moderator:
J

JavaZivi

Gast
Gut, in anbetracht von z.B. StackOverflow oder OutOfMemory macht es schon Sinn Throwable abzufangen, vor allem wenn es halt von Dritten kommt. Sowas zählt halt schon zu den nicht vorhersehbaren Dingen, auch wenn laut DOC und Lehrbuch Errors als "abnormale Zustände die eigentlich niemals auftreten sollten" definiert werden. Ich schätze in Punkto Exception-Handling muss ich wohl in diesem Zusammenhang doch noch so einiges lernen. DANKE dafür. Mir fehlt halt die Erfahrung Software in Zusammenarbeit mit bzw. für andere/n Programmierer/n zu entwickeln.

Scheint als wird das hier doch noch ein ziemlich langer und hoffentlich ausführlicher Thread.

-JavaZivi
 
S

Spacerat

Gast
Gut, in anbetracht von z.B. StackOverflow oder OutOfMemory macht es schon Sinn Throwable abzufangen...
Tja... was soll ich sagen... Diese beiden Dinger sind nicht mehr die beliebtesten bei Hackern und Virenentwicklern. Ihre "Widersacher", die Entwickler sensibler Schnittstellen (also eigentlich die Guten XD) kamen viel zu schnell drauf und und prüften schlicht die Plausibilität dieser Fehler (also ob wirklich kein Speicher mehr vorhanden war). Ein schlichtes [c]throw new Error();[/c] genügt aber den Hackern ja schon. Ist nur die Frage, wie reagiert man drauf:
1. Fehler loggen, Plausibilität des Fehlers eindeutig feststellen.
2. Fehlerhaftes PI auf Ausschlussliste setzen.
3. Ein normaler Neustart der Anwendung darf nur bei festgestellter Plausibilität erfolgen. (Evtl. Neustart mit zuvor verschlüsselt gespeicherten Zugangsdaten)
4. Solange eine Ausschlusliste vorhanden ist, in einer Art "SafeMode" starten, dass erfordert administrativen Eingriff um die Normalität widerherzustellen.

Alles in allem ist ein Pluginsystem auf die Art jene Ausnahme, die die Regel bestätigt, dass man Throwable nicht abfängt. Bei "Skylab" nannte man es damals "kontrolliert abstürzen" lassen.
 
Zuletzt bearbeitet von einem Moderator:
J

JavaZivi

Gast
Ich habe bis jetzt mal folgendes implementiert. Sieht noch ein wenig chaotisch ist und ist bestimmt auch verbesserungswürdig, aber ich finde jetzt so auf anhieb erstmal nichts.
Das public ist gewollt (bevor jetzt jemand mit "small scope" kommt) da ich das ganze als lib entwickeln will und nicht nur für dieses eine Projekt.
Java:
package modularity;
public interface Module
{
	void load(ModuleHandler moduleHandler);
	void init();
	void start();
	void stop();
	void destroy();
	void unload();
}
Java:
package modularity;
import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.*;
public class ModuleHandler
{
	private HashMap<String, Module> modules;
	public ModuleHandler()
	{
		modules=new HashMap<String, Module>();
	}
	public void initModules() throws Throwable
	{
		for(String moduleName : modules.keySet())
		{
			initModule(moduleName);
		}
	}
	public void initModule(String moduleName) throws Throwable
	{
		modules.get(moduleName).init();
	}
	public void startModules() throws Throwable
	{
		for(String moduleName : modules.keySet())
		{
			startModule(moduleName);
		}
	}
	public void startModule(String moduleName) throws Throwable
	{
		modules.get(moduleName).start();
	}
	public void stopModules() throws Throwable
	{
		for(String moduleName : modules.keySet())
		{
			stopModule(moduleName);
		}
	}
	public void stopModule(String moduleName) throws Throwable
	{
		modules.get(moduleName).stop();
	}
	public void destroyModules() throws Throwable
	{
		for(String moduleName : modules.keySet())
		{
			destroyModule(moduleName);
		}
	}
	public void destroyModule(String moduleName) throws Throwable
	{
		modules.get(moduleName).destroy();
	}
	public void unloadModules() throws Throwable
	{
		for(String moduleName : modules.keySet())
		{
			unloadModule(moduleName);
		}
	}
	public void unloadModule(String moduleName) throws Throwable
	{
		Module module=modules.get(moduleName);
		module.unload();
		modules.remove(moduleName);
		((URLClassLoader)module.getClass().getClassLoader()).close();
	}
	public boolean isModuleLoaded(String moduleName)
	{
		return modules.containsKey(moduleName);
	}
	public void addModule(String moduleName, Module module)
	{
		modules.put(moduleName, module);
	}
	public Object invokeMethod(Module sourceModule, String targetModuleName, String targetMethodName, Object... args) throws Throwable
	{
		if(checkPermission(sourceModule.getClass().getName(), targetModuleName, targetMethodName))
		{
			Module targetModule=modules.get(targetModuleName);
			Method[] moduleMethods=targetModule.getClass().getDeclaredMethods();
			for(Method targetMethod : moduleMethods)
			{
				if(targetMethod.getName().equals(targetMethodName))
				{
					return targetMethod.invoke(targetModule, args);
				}
			}
			throw new NoSuchMethodException();
		}
		throw new IllegalAccessException();
	}
	public boolean checkPermission(String sourceModuleName, String targetModuleName, String targetMethodName)
	{
		return true;
	}
}
Java:
package modularity;
import java.io.*;
import java.net.*;
import java.util.jar.*;
public class ModuleLoader
{
	private ModuleHandler moduleHandler;
	public ModuleLoader(ModuleHandler moduleHandler)
	{
		this.moduleHandler=moduleHandler;
	}
	public void loadModules() throws Throwable
	{
		File moduleDir=new File(new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile(), "modules");
		File[] moduleFiles=moduleDir.listFiles(new FilenameFilter()
							{
								public boolean accept(File file, String name)
								{
									return name.endsWith(".module");
								}
							});
		for(File moduleFile : moduleFiles)
		{
			loadModule(moduleFile);
		}
	}
	public void loadModule(File moduleFile) throws Throwable
	{
		JarFile moduleJar=new JarFile(moduleFile);
		String moduleClassName=moduleJar.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
		moduleJar.close();
		URLClassLoader moduleLoader=new URLClassLoader(new URL[] { moduleFile.toURI().toURL() });
		Class<?> moduleClass=moduleLoader.loadClass(moduleClassName);
		Module module=(Module)moduleClass.newInstance();
		module.load(moduleHandler);
		moduleHandler.addModule(moduleClassName, module);
	}
}
Bei checkPermissions weis ich noch nicht genau wie ich es implementieren soll da dies ja wiederum auch wieder nur funktioniert wenn sich jemand an mein Framework hält. Das ganze selbst mit Reflections zu umgehen ist ja kein Problem. Und dem URLClassLoader einen SecurityManager mit zu geben um damit "alles andere" zu kontrollieren ist ja nicht möglich. Wenn also hierfür jemand noch eine gute Idee hat bin ich gerne dafür offen.

-JavaZivi
 
J

JavaZivi

Gast
Das Problem am SecurityManager wäre ja, wenn ich es richtig verstanden habe, das ich Beschränkungen für das gesamte Modul-System festlegen würde. Wenn ich also File-I/O "sperre" dann kann das Modul-System dies ja ebenfalls nicht verwenden und somit die Module gar nicht erst laden. Genau so würde ich mir mit dem blockieren von Reflections ins eigene Fleisch schneiden da ich diese ja selbst für die "Intermodulkommunikation" verwende.

Wie aber könnte ich jetzt mein "checkPermissions" implementieren ? Sollte ich dem Module-Interface noch eine weitere Methode hinzufügen und die Prüfung an das targetModule delegieren oder lieber im ModuleHandler mit Properties arbeiten ? Und wenn mit einem Properties-File wie sollte es strukturiert sein ? Ich glaube das wäre auch abhängig davon wofür man das System verwendet.

-JavaZivi
 
S

Spacerat

Gast
Zunächst: Reflections ist die Brechstange, die man verwenden kann, wenn einem alle anderen Ideen ausgegangen sind. In so ziemlich allen Fällen, wo ein Entwickler Reflections verwendet hat, hat er nicht genau genug überlegt. Reflections sind daher meist der Anfang eines schwerwiegenden Designfehlers. Wäre also um so besser, wenn du dir in Sachen Reflections ins eigene Fleisch schneidest, damit du es merkst. ;)

Ansonsten können sich Module immer noch per Zertifikat authentisieren, um gewisse Rechte zu erlangen.
 
J

JavaZivi

Gast
Ist mir klar das Reflection nicht das Beste sind und man bei einem modularen System natürlich gegen das Modul-Interface programmieren soll, aber ich kann das Interface auch schlecht mit hunderten Methoden für verschiedene Parameter und return-Types zupflastern. Außerdem wüsste ich dann immer noch nicht wie ich aus einem Modul ein anderes aufrufen soll ohne sowohl das Ziel-Modul als auch die Ziel-Methode direkt anzugeben. Dazu müsste ja dann der Handler ebenfalls alle Methoden mit dem zusätzlichen Parameter des Ziel-Moduls zur verfügung stellen was diesen dann ebenfalls aufblähen würde.

Über Google findet man ein sog. Java Plugin Framework (JPF). Ich hab nur mal schnell durchs Tutorial und die Doc geklickt, aber so wirklich eine Interkommunikation zwischen verschiedenen Plugins hab ich nicht gefunden. Es sieht so aus als gäbe es dort nur Plugin <-> PluginManager, der selbst aber wiederum scheinbar keine Möglichkeit bereitstellt das ein Plugin mit einem anderen kommunizieren kann. Oder ich hab es beim drüberfliegen einfach nur nicht gefunden.

Mal so ne allgemeine Frage an alle die den Thread hier verfolgen : wie würdet ihr das umsetzen ?
Für mich persönlich ist es halt die einfachste Variante einfach durch die geladenen Module zu iterieren, bei Treffer durch die Methoden zu gehen und dann mit den Parametern zu callen.
Was mir noch so beim schreiben einfällt wäre ein Listener den das Modul beim Handler registriert. Dann kann ein Modul diesen im Handler auslösen und der Handler muss dann nur durch die geladenen Listener gehen. Wäre also besser als Reflections und auch für mich schnell implementierbar. Allerdings müsste dann jedes Modul selbst entscheiden welches Event es annimmt.

-JavaZivi
 
S

Spacerat

Gast
Das klingt schon mal nach einer viel besseren Idee als Reflections. Allerdings sollte das Schlagwort "MessagePort" statt "EventHandler" heissen.
Mal am Rande: Weisst du was ARexx ist?
Kurzum, der PI-Handler ist nicht nur Vermittlungsstelle zwischen PI und User, sondern auch zwischen PI und PI. Während der Ladephase kann man ja bereits bei diesem Vermittler nachfragen, ob ein gerfordertes Modul vorhanden ist. Mit eben genau dieser "ModulID" (wie auch immer die realisiert wird) kann man auch gezielt Objekte (Messages) zwischen Modulen austauschen.
 
J

JavaZivi

Gast
Wenn ich dich also richtig verstehe soll nicht der Handler für das callen direkt zuständig sein sondern lediglich Modul A die Referenz von Modul B liefern und Modul A kann dann mit dieser direkt die Methode von Modul B callen. Das klingt natürlich noch eleganter. Ich werds mal entsprechend umbauen.

-JavaZivi
 
S

Spacerat

Gast
Lösen kann man es auf vielen Wegen. Der einfachste wäre, dass Modul A sich vom Vermittler eine Instanz von Modul B besorgt und dessen Methoden direkt aufruft. Dummerweise könnte Hinz und Kunz sich eine solche Instanz besorgen und Methoden davon aufrufen, wie also wollte man so verschiedene Zugriffsberechtigungen verwalten. Ein anderer Weg wäre es also, wenn man für Module eindeutige IDs definiert, womit sie identifiziert werden können. Dann sendet man die Message etwa an eine "send"-Methode zum Vermittler, der sie nach einem evtl. SecurityCheck an das jeweilige Modul weiterleitet.
Java:
public PlugInResponse sendMessage(PlugInId id, PlugInRequest message) {
  // checkSecurity();
  return modules.get(id).receive(message);
}
Die PlugInId kann natürlich auch ein Member des PlugInRequests sein, das reduziert das Ganze dann halt auf einen einzigen Parameter.
 
Zuletzt bearbeitet von einem Moderator:
J

JavaZivi

Gast
Anfrage und Ergebnis in extra Klassen zu kapseln klingt auch gut. Zum Aufbau hätte ich dann aber noch eine Frage :
Wenn ich die Daten in einem Object-Array speichere muss ich ja beim wieder auslesen casten. Wäre es hier dann besser von "ModuleRequest" / "ModuleResponse" entsprechende Klassen abzuleiten ?
Meine Idee würde hier sogar noch so weit gehen das man sich über eine weitere Methode eine Instanz zu einer Request-/Response-Factory holt (die ebenfalls wieder in Super- und Sub-Klassen geteilt sind) und dann durch einen call der Factory-Methode sich ein Objekt der richtigen Request-/Reponse-Klasse erstellt. Oder wäre dann hier auch wieder irgendwo ein Cast nötig ?
Umgesetzt habe ich die Idee jetzt noch nicht da mir diese erst vor ungefähr einer halben Stunde gekommen ist, aber ich hoffe jemand versteht was ich damit mein. Ich werde vielleicht nachher noch Beispiel-Code nachliefern.

Oder vielleicht kann ich es so erklären :
Ich implementiere im Handler eine Methode [c]public ModuleRequestFactory getModuleRequestFactory(String targetModule)[/c] die dann ähnlich dem geposteten Code an das entsprechende Module delegiert. Das Module würde dann einer Instanz seiner von ModuleRequestFactory-abgeleiteten Klasse antworten. Die Factory bietet dann eine Methode [c]public ModuleRequest createModuleRequest(String targetMethod, Object[] args)[/c] mit der dann eine Instanz von z.B. LoggerModuleRequest extends ModuleRequest. Diese wird dann über den Handler mit [c]public ModuleResponse moduleRequest(String targetModule, ModuleRequest request)[/c] an das Module weitergeleitet. Da "Logger" nun weis dass das übergebene ModuleRequest-Objekt vom Typ LoggerModuleRequest ist kann das Module dann auch die entsprechend darin deklarierten Methoden callen und die Daten so auslesen wie es benötigt wird. Um Type-save zu sein castet LoggerModuleRequestFactory.createModuleRequest(String, Object) die Daten schon entsprechend. Das Logger-Module erstellt nun einfach seinen ModuleResponse über den Konstruktor seiner LoggerModuleResponse-Klasse und gibt dieses Objekt zurück. Jetzt ist natürlich die Frage wie das ursprüngliche Module weis welche Methoden dieser Response zur verfügung stellt und hätte hier eigentlich wieder nur die Möglichkeit ein Object-Array auszulesen und zu casten. Dafür müsste man aber natürlich wissen was für Typen in dem Object-Array liegen.
Hier hakt mein Plan also noch ein wenig. Oder ist die Idee an sich schon irgendwie Mist und ich sollte mir doch lieber was anderes / simpleres einfallen lassen ?

-JavaZivi
 
S

Spacerat

Gast
Warum muss alles eigentlich immer so schwierig? Nur damit's gut bzw. nach "Mühe gegeben" aussieht? :D
Java:
import java.util.IdentityHashMap;
import java.util.Map;

public class MessagePort {
	private static final Map<String, Modul> modules = new IdentityHashMap<>();

	public static void load(String id) {
		
//		if(!loaded) {
			modules.put(id, new Modul() {
				@Override
				public void stop() {
				}
				@Override
				public void start() {
				}
				@Override
				public void init() {
				}
				@Override
				public ModulResponse receive(ModulRequest request) {
					// simple echo
					return new ModulResponse(request.getId(), request.getArgs());
				}
			});
//		}
			modules.get(id).init();
			modules.get(id).start();
	}

	public static ModulResponse sendMessage(ModulRequest request) {
		return modules.get(request.getId()).receive(request);
	}
}

interface Modul {
	void start();
	void stop();
	void init();
	ModulResponse receive(ModulRequest request);
}

class ModulRequest {
	private final Object[] args;
	private final String id;

	ModulRequest(String id, Object ... args) {
		this.args = args;
		this.id = id;
	}

	String getId() {
		return id;
	}

	Object[] getArgs() {
		return args.clone();
	}
}

class ModulResponse {
	private final Object[] args;
	private final String id;

	ModulResponse(String id, Object ... args) {
		this.args = args;
		this.id = id;
	}

	String getId() {
		return id;
	}

	Object[] getArgs() {
		return args.clone();
	}
}
Am "Carsten" XD führt natürlich kein Weg vorbei. Der Request lässt sich dabei natürlich noch um Kommandospezifikationen erweitern und nicht nur das. Evtl. sollte auch noch der Sender dem Request als Parameter übergeben werden, damit der Vermittler die Zugriffsberechtigung prüfen kann. Ansonsten braucht's nichts weiter als eine Map.
 
Zuletzt bearbeitet von einem Moderator:
J

JavaZivi

Gast
Es soll ja eigentlich nich nach "Mühe gegeben" aussehen, aber das war so meine Idee aus dem was du mir gesagt hast. Den Code den du da gepostet hast entspricht in etwa meiner ursprünglichen Idee bevor mir das mit den Super- und Sub-Klassen eingenfallen ist.
Aber das man es doch so einfach machen kann erleichtert mir etwas die Arbeit. Obwohl ich jetzt den Code so nicht 1zu1 nutzen könnte hilft er mir doch schon weiter.
Ich werd mich mal ransetzen und gucken was ich so zusammen bekomme.

Danke noch mal dafür.

-JavaZivi
 
J

JavaZivi

Gast
Ok, ich habe jetzt folgendes :

Interface Module
Java:
ModuleResponse requestModuleResponse(ModuleRequest moduleRequest);

Class ModuleHandler
Java:
public ModuleResponse requestModuleResponse(ModuleRequest moduleRequest) throws Throwable
{
	return modules.get(moduleRequest.getTargetModuleName()).requestModuleResponse(moduleRequest);
}

Java:
package modularity;
public class ModuleRequest
{
	private Module sourceModule;
	private String targetModuleName;
	private Object[] requestData;
	public ModuleRequest(Module sourceModule, String targetModuleName, Object[] requestData)
	{
		this.sourceModule=sourceModule;
		this.targetModuleName=targetModuleName;
		this.requestData=requestData;
	}
	public String getTargetModuleName() { return targetModuleName; }
	public Object[] getRequestData() { return requestData; }
}

Java:
package modularity;
public class ModuleResponse
{
	private Object[] responseData;
	public ModuleResponse(Object[] responseData) { this.responseData=responseData; }
	public Object[] getResponseData() { return responseData; }
}

In den Modulen müsste dann natürlich das Object-Array gecastet werden. Gibt es hierfür irgendeine Möglichkeit das zu vereinfachen ? Ich könnte mir hier vielleicht noch eine Map vorstellen die zu den jeweiligen Objekten die entsprechende Class enthält. Da fällt mir ein : würde bei einem Object-Array eigentlich Autoboxing für primitive Datentypen greifen oder muss ich da selbst Hand anlegen ?

Zur "Sicherheitsprüfung" hab ich mir jetzt eher weniger Gedanken gemacht, würde aber vermutlich lediglich prüfen ob der call Source>Target.Method erlaubt ist. Ich bin nur noch nicht ganz sicher wie. Die Prüfung dem Modul zu überlassen will ich eigentlich eher weniger, andererseits müsste dann jedes neue Modul seine "Zugriffsliste" beim Handler anmelden, womit die Entscheidung dann doch wieder auf das Module zurückfällt.

-JavaZivi
 
S

Spacerat

Gast
1. Evtl. planst du oder Drittanbieter von Modulen ja, sich wiederholende Requests zu cachen. Schon allein deswegen sollte man diese Arrays nur geklont zurückgeben, sonst kann ihr Inhalt von aussen verändert werden und dass würde ihn für andere Module verfälschen.

2. Autoboxing, da sagst du was. Genau weiss ich das grad nicht, aber ich denke mal, dass es auch bei Arrays greift.

3. In welche Klasse gecastet werden muss, ebenso welche Requests gültig sind, sollte in der Beschreibung der jeweiligen Module stehen. Wenn man da noch viel mit instanceof oder mit sonstigen programmtechnischen Mitteln Typabfragen tätigen muss, stimmt etwas am Design oder an den Konventionen nicht. Wenn einem am Ende von einigen Modulen CCEs um die Ohren fliegen, hat das jeweilige Modul in der Typisierung was falsch gemacht.
 
J

JavaZivi

Gast
Also Object.clone() besagt das bei Objekten die nicht Cloneable implementieren eine CloneNotSupportedException geworfen wird. Bei Arrays jedoch wird (soweit ich es verstanden habe) eine neue Instanz des Objektes erzeugt und eine deep-clone-ähnliche Kopie mit jeweils korrekt initialisierten Objekten erzeugt. Warum man Array.clone() im vorliegende Fall nutzen sollte ist mir bewusst (darum hatte ich nicht explizit gefragt), allerdings kannte ich die eigenschaft von Object.clone() bezüglich Arrays noch nicht. Werde das nachbessern.

Bezüglich Antwort 3 hätte ich dann eine (in deinen Augen vermutlich wieder "zu umständliche") Idee das sich ein Modul beim init() mit einer Map die den jeweiligen Methoden eine Liste sowie die Reihenfolge beim Handler registriert und sich andere Module diese holen und verarbeiten können. So wäre zumindest vom Framework her eine Prevention gegen CCEs gegeben. Ob man als Modul-Entwickler diese nutzt ist natürlich jedem selbst überlassen, jedoch wäre zumidenst die Möglichkeit gegeben. Einen Zwang diese zu nutzen könnte man (so glaube ich zumindest) höchstens mit Generics in ModuleRequest verwirklichen.

Ich werd noch mal Google nach entsprechenden Themen und Threads durchsuchen, mal sehen auf was ich da so stoße.

-JavaZivi
 
S

Spacerat

Gast
Obwohl ich annehme, dass du das mit den Arrays bereits verstanden hast...
Also eine CloneNotSupportedException wird es schon mal nicht geben, weil du ja ausschliesslich Arrays clonst.
Ansonsten... Java ist auch eine Insel – 3.8 Arrays

Daraus folgt, dass die einzelnen Objekte im Request oder in der Response zwar immer noch gemeinsam genutzt werden, der Container (das Array) jedoch für jeden Nutzer ein anderes ist. Dem Original ist's also egal, ob ein Nutzer ein Objekt in seinem Array (warum auch immer) ändert.
 
J

JavaZivi

Gast
Ach so ist das gemeint, das also nur das Array geklont und nur die Referenzen kopiert werden, weshalb es dann also egal ist ob das original Array verändert wird. Zugegeben: nein, das wusste ich noch nicht. Ich habe es im Request bereits geändert, denke aber das ich es natürlich auch im Responsen ändern muss da ich ja nicht weis ob ein anderer Entwickler das Array z.B. mit einem anderen Thread ändert während im caller des Requests das Array verarbeitet wird.

Dann hätte ich ja jetzt alles bis auf die Sicherheitsprüfung zusammen :
- dynamisches Laden und Registrieren von Modulen
- initialisieren/starten/stoppen/destroyen von Modulen
- dynamisches De-Registrieren und Entladen von Modulen
- Kommunikation zwischen Modulen

Die Kommunikation zwischen Hauptanwendung und Modulen würde ich dann über die selbe Schnittstelle laufen lassen wie zwischen den Modulen untereinander. Für Modul->App würde ich Callback-Listener beim Modul über einen normalen ModulRequest registrieren. Ich denke das das soweit passen sollte, oder hat hier jemand begründete Einwände die es erforderlich machen würden eine gesonderte Schnittstelle für Modul->App zu nutzen ?

-JavaZivi
 
S

Spacerat

Gast
Ich habe es im Request bereits geändert, denke aber das ich es natürlich auch im Responsen ändern muss da ich ja nicht weis ob ein anderer Entwickler das Array z.B. mit einem anderen Thread ändert während im caller des Requests das Array verarbeitet wird.

Dann hätte ich ja jetzt alles bis auf die Sicherheitsprüfung zusammen :
- dynamisches Laden und Registrieren von Modulen
- initialisieren/starten/stoppen/destroyen von Modulen
- dynamisches De-Registrieren und Entladen von Modulen
- Kommunikation zwischen Modulen

Die Kommunikation zwischen Hauptanwendung und Modulen würde ich dann über die selbe Schnittstelle laufen lassen wie zwischen den Modulen untereinander. Für Modul->App würde ich Callback-Listener beim Modul über einen normalen ModulRequest registrieren. Ich denke das das soweit passen sollte, oder hat hier jemand begründete Einwände die es erforderlich machen würden eine gesonderte Schnittstelle für Modul->App zu nutzen ?

-JavaZivi

1. lol... gerade beim Responsearray wäre das Klonen wichtiger. Warum sollte ein Modul den Request ändern wollen? -> Nääähh, der Request gefällt mir net, da habbisch keen bock drauf, isch will das anders. :lol:

2. Wenn du für Modul->App noch Listener verwenden willst, wäre das bereits eine andere Schnittstelle. Die ist auch gar nicht so verkehrt, denn wenn ein Modul etwas an seinem Zustand aufgrund der Kommunikation mit anderen Modulen ändert, sollte die App auf jeden Fall davon erfahren. Dafür sind Events (genauer gesagt PropertyChangeEvents) um einiges besser geeignet. Andernfalls müsste die App ja über jeglichen "Funkverkehr" zwischen Modulen unterrichtet werden und ewig bei den beteiligten nachhaken, ob sich durch die Requests irgend etwas ändert. Man bedenke, dass dieses MessageSystem bisher noch keinerlei "Funkdisziplin" kennt, die bei Multithreading eingehalten werden müsste. Kurzgesagt: Der Messageport ist noch nicht ThreadSafe.
 
J

JavaZivi

Gast
1. lol... gerade beim Responsearray wäre das Klonen wichtiger. Warum sollte ein Modul den Request ändern wollen? -> Nääähh, der Request gefällt mir net, da habbisch keen bock drauf, isch will das anders. :lol:.
Shame on me, das hab ich wohl in deinem vorheringen Code-Posting total übersehen (/me bessert das mal eben sofort aus). Aber an sich klingt es witzig =D.
2. Wenn du für Modul->App noch Listener verwenden willst, wäre das bereits eine andere Schnittstelle. Die ist auch gar nicht so verkehrt, denn wenn ein Modul etwas an seinem Zustand aufgrund der Kommunikation mit anderen Modulen ändert, sollte die App auf jeden Fall davon erfahren. Dafür sind Events (genauer gesagt PropertyChangeEvents) um einiges besser geeignet. Andernfalls müsste die App ja über jeglichen "Funkverkehr" zwischen Modulen unterrichtet werden und ewig bei den beteiligten nachhaken, ob sich durch die Requests irgend etwas ändert. Man bedenke, dass dieses MessageSystem bisher noch keinerlei "Funkdisziplin" kennt, die bei Multithreading eingehalten werden müsste. Kurzgesagt: Der Messageport ist noch nicht ThreadSafe.
Ich würde jetzt nicht direkt PropertyChangeListener sondern eher in Richtung ModuleStateChangeListener nutzen, aber wie man ein solches Interface nennt ist ja dann doch eher nebensächlich. Theoretisch bräuchte man eigentlich noch nicht mal einen solchen Listener nutzen sondern dem Handler lediglich eine solche Methode hinzufügen sodass sich die eigentliche App dann beim Handler registrieren müsste und der ModuleHandler den call vom Module lediglich durchreicht (wobei man dann genau dort ebenfalls eine Zugriffsüberwachung einbauen könnte). Allerdings wäre dann natürlich die App wieder gezwungen eine entsprechende Methode zu implementieren, und diesen "Zwang" kann man halt über Interfaces sicherstellen. Das Ganze nimmt sehr gut und ziemlich schnell eine doch recht annehmbare Form an. Werde am Besten gleich alles entsprechend implementieren.

-JavaZivi
 
S

Spacerat

Gast
Ich würde jetzt nicht direkt PropertyChangeListener sondern eher in Richtung ModuleStateChangeListener nutzen, aber wie man ein solches Interface nennt ist ja dann doch eher nebensächlich. Theoretisch bräuchte man eigentlich noch nicht mal einen solchen Listener nutzen sondern dem Handler lediglich eine solche Methode hinzufügen sodass sich die eigentliche App dann beim Handler registrieren müsste und der ModuleHandler den call vom Module lediglich durchreicht (wobei man dann genau dort ebenfalls eine Zugriffsüberwachung einbauen könnte). Allerdings wäre dann natürlich die App wieder gezwungen eine entsprechende Methode zu implementieren, und diesen "Zwang" kann man halt über Interfaces sicherstellen. Das Ganze nimmt sehr gut und ziemlich schnell eine doch recht annehmbare Form an. Werde am Besten gleich alles entsprechend implementieren.

-JavaZivi
Klar, Namen sind Schall und Rauch. Eine PropertyChange-Funktionalität ist in Java aber bereits unter diesem Namen vorhanden. Man kann sie nutzen, muss sie halt nur noch erweitern.
Die Idee mit dem "Durchreichen" hatte ich allerdings auch schon. Ein Modul sendet einen Request an ein anderes Modul einen speziellen Status anzunehmen. Wie sollte denn die Anwendung nun mitbekommen, wie das Modul reagiert? Immerhin ist es Sache des Moduls den gewünschten Status anzunehmen oder den Request zu verweigern. Das Modul muss die App also auf jeden Fall informieren und zwar am besten noch bevor es ein Responseobjekt sendet (siehe VetoablePropertyChangeListener). So erfährt die App aus erster Hand, dass sich was geändert hat und das Modul ebenso aus erster Hand, wenn die App etwas dagegen hätte. Andernfalls müsste die App bei jedem Request wie gesagt nachhaken, wie diese Änderung zustande kommt (ungefähr so 'ne "why()"-Geschichte, wie sie in meiner Signatur steht). Das wird recht unperformant und es besteht die unmittelbare Gefahr eines Kreisbezugs.
 
J

JavaZivi

Gast
Ich bin jetzt leider die letzten Tage nicht wirklich dazu gekommen weiter zu implementieren, poste hier aber noch mal den aktuellen Source.

Java:
package modularity;
public interface Module
{
	void load(ModuleHandler moduleHandler);
	void init();
	void start();
	void stop();
	void destroy();
	void unload();
	ModuleResponse requestModuleResponse(ModuleRequest moduleRequest);
}
Java:
package modularity;
import java.io.*;
import java.net.*;
import java.util.*;
public class ModuleHandler
{
	private HashMap<String, Module> modules;
	private ArrayList<ModuleStateChangeListener> moduleStateChangeListeners;
	public ModuleHandler()
	{
		modules=new HashMap<String, Module>();
		moduleStateChangeListeners=new ArrayList<ModuleStateChangeListener>();
	}
	public void initModules() throws Throwable { for(String moduleName : modules.keySet()) { initModule(moduleName); } }
	public void initModule(String moduleName) throws Throwable { modules.get(moduleName).init(); }
	public void startModules() throws Throwable { for(String moduleName : modules.keySet()) { startModule(moduleName); } }
	public void startModule(String moduleName) throws Throwable { modules.get(moduleName).start(); }
	public void stopModules() throws Throwable { for(String moduleName : modules.keySet()) { stopModule(moduleName); } }
	public void stopModule(String moduleName) throws Throwable { modules.get(moduleName).stop(); }
	public void destroyModules() throws Throwable { for(String moduleName : modules.keySet()) { destroyModule(moduleName); } }
	public void destroyModule(String moduleName) throws Throwable { modules.get(moduleName).destroy(); }
	public void unloadModules() throws Throwable { for(String moduleName : modules.keySet()) { unloadModule(moduleName); } }
	public void unloadModule(String moduleName) throws Throwable
	{
		Module module=modules.get(moduleName);
		module.unload();
		modules.remove(moduleName);
		((URLClassLoader)module.getClass().getClassLoader()).close();
	}
	public boolean isModuleLoaded(String moduleName) { return modules.containsKey(moduleName); }
	public void addModule(String moduleName, Module module) { modules.put(moduleName, module); }
	public void registerModuleStateChangeListener(ModuleStateChangeListener moduleStateChangeListener) { moduleStateChangeListeners.add(moduleStateChangeListener); }
	public void removeModuleStateChangeListener(ModuleStateChangeListener moduleStateChangeListener) { moduleStateChangeListeners.remove(moduleStateChangeListener); }
	public ModuleResponse requestModuleResponse(ModuleRequest moduleRequest) throws Throwable
	{
		return modules.get(moduleRequest.getTargetModuleName()).requestModuleResponse(moduleRequest);
	}
	public void moduleStateChanged(ModuleStateChangeEvent moduleStateChangeEvent)
	{
		for(ModuleStateChangeListener moduleStateChangeListener : moduleStateChangeListeners) { moduleStateChangeListener.moduleStateChanged(moduleStateChangeEvent); }
	}
}
Java:
package modularity;
import java.io.*;
import java.net.*;
import java.util.jar.*;
public class ModuleLoader
{
	private ModuleHandler moduleHandler;
	public ModuleLoader(ModuleHandler moduleHandler)
	{
		this.moduleHandler=moduleHandler;
	}
	public void loadModules() throws Throwable
	{
		File moduleDir=new File(new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile(), "modules");
		File[] moduleFiles=moduleDir.listFiles(new FilenameFilter()
							{
								public boolean accept(File file, String name)
								{
									return name.endsWith(".module");
								}
							});
		for(File moduleFile : moduleFiles)
		{
			loadModule(moduleFile);
		}
	}
	public void loadModule(File moduleFile) throws Throwable
	{
		JarFile moduleJar=new JarFile(moduleFile);
		String moduleClassName=moduleJar.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
		moduleJar.close();
		URLClassLoader moduleLoader=new URLClassLoader(new URL[] { moduleFile.toURI().toURL() });
		Class<?> moduleClass=moduleLoader.loadClass(moduleClassName);
		Module module=(Module)moduleClass.newInstance();
		module.load(moduleHandler);
		moduleHandler.addModule(moduleClassName, module);
	}
}
Java:
package modularity;
public class ModuleRequest
{
	private Module sourceModule;
	private String targetModuleName;
	private Object[] requestData;
	public ModuleRequest(Module sourceModule, String targetModuleName, Object[] requestData)
	{
		this.sourceModule=sourceModule;
		this.targetModuleName=targetModuleName;
		this.requestData=requestData;
	}
	public Module getSourceModule() { return sourceModule; }
	public String getTargetModuleName() { return targetModuleName; }
	public Object[] getRequestData() { return requestData.clone(); }
}
Java:
package modularity;
public class ModuleResponse
{
	private Object[] responseData;
	public ModuleResponse(Object[] responseData) { this.responseData=responseData; }
	public Object[] getResponseData() { return responseData.clone(); }
}
Java:
package modularity;
import java.util.*;
public class ModuleStateChangeEvent extends EventObject
{
	private Object[] changedData;
	public ModuleStateChangeEvent(Module sourceModule, Object[] changedData)
	{
		super(sourceModule);
		this.changedData=changedData;
	}
	public Object[] getChangedData() { return changedData.clone(); }
}
Java:
package modularity;
import java.util.*;
public interface ModuleStateChangeListener extends EventListener
{
	void moduleStateChanged(ModuleStateChangeEvent event);
}

Ich weis das hier und da vielleicht noch das Exception-Handling fehlt, und einige Stellen würde ich gerne noch mit eigenen checked Exceptions behandeln, wüsste aber nicht wie ich die genau implementieren müsste.
Wenn jemand irgendwelche "Fehler" oder "logischen Schwachsinn" findet wäre ich über eine Anmerkung und vielleicht einen Denkanstoß dankbar.

-JavaZivi
 

Neue Themen


Oben