# Sockets, Threads & Plugins



## BigBoss (2. Jan 2012)

Hallo an Alle und erstmal ein gesundes neues Jahr.
Ich bin gerade dabei eine Pluginbasierte Client/Server Anwendung zu schreiben.
Beide Teile sollen aus einem Kernel bestehen der die komplette Kommunikation kapselt und den Plugins statische Methoden zur Verfügung stellt über welche diese Daten übers Netz schicken können.
Mein Problem dabei ist dass wenn ein Plugin Daten zum Server schickt und dieser antwortet diese in einem Thread ankommen und ich nicht an die Stelle komme wo ich die Daten weggeschickt habe.
Im Prinzip hatte ich mir das so vorgestellt das ich eine statische Methode im Kernel habe die so aussieht.

```
public static Object sendeDaten(Object obj)
{
Socket.OutPS.write(obj);
...
return Socket.IPS.read();
}
```
Das ist jetzt ziemlich Abstrakt.
Die Frage ist ob dies so realisierbar ist. Ich habe bis jetzt noch nichts gefunden wie ich aus einem Thread in den Rückgabewert einer anderen Methode springen kann.
Ich kann natürlich eine Variable definieren in welche ich den Wert schreibe und nach dem senden der Daten überprüfe wann endlich was in die Variable geschrieben wird und dies dann zurückgebe.
Den Weg finde ich aber nicht wirklich elegant und deshalb such ich nach einem anderen Weg.
Über ein paar Ideen würde ich mich sehr freuen.

Viele Grüße

BB


----------



## SlaterB (2. Jan 2012)

'in den Rückgabewert einer anderen Methode springen kann' und andere Formulierung sind zu undeutlich für meinen Geschmack,
eine konkrete Frage kann ich nicht erkennen,

allgemein geht es anscheinend um eine allgemeine Komponente für beliebige Kommunition, die den gemeinsamen Nenner Object hat,
das halte ich für nicht so verkehrt, wenn man andere Beispiele bedenkt:
ObjectInputStream verschiebt Objekte (das dürfte dein Socket.OutPS ja auch sein), 
alle Collections enthalten grundsätzlich Objekte, 
Hibernate liefert aus der DB wenn nicht normale Entitys, dann List<Object[]>, was auch sonst?

du könntest Interface für Höheres verwenden, du könntest zumindest auf das Interface Serializable einschränken,
mit Problemen, etwa dass das die Collections-Interface nicht Serializable implementieren


----------



## BigBoss (2. Jan 2012)

Es geht rein um die Kommunikation zwischen Server und Client.
Ich versuch mal mein Problem anderes zu beschreiben.
Mein Client besteht aus dem Kernel und vielen Plugins.
Jedes Plugin hat die möglichkeit über den Kernel Objekte zum Server zu schicken.
Jeder Client Kernel besitz genau eine Socketverbindung zum Server.
Über diese Verbindung muss alle Kommunikations laufen.
Wenn jetzt ein Plugin eine Anfrage an der Server schickt muss es warten bis der Server die Daten liefert.
Dies soll in der statischen Methode sendeDaten im Kernel oder in einer dadurch aufgerufenen Methode passieren.
Das empfangen aus dem Socket ist ja nun einmal ein Asyncroner Prozess der in einem eigenen Thread läuft. Und das Eingelesene Objekt aus dem Socket muss dann als Rückgabewert aus der Methode sendeDaten kommen damit das Plugin an der Stelle weiterarbeiten kann wo es die Daten versendet hat.
Und hier hab ich noch keine Idee wie man das auf Elegantem Weg lösen kann.
Ich hoffe so ist die Beschreibung etwas verständlicher.
VG
BB


----------



## SlaterB (2. Jan 2012)

also dazu kann ich jetzt auch nichts direkt elegantes nennen, 
wobei ich vermute dass es dazu endlos Theorie/ Frameworks gibt, 
vielleicht Java Message Service ? Wikipedia

aber um noch zu antworten wäre mein Vorschlag (wie immer) dass ganz bodenständig Schritt für Schritt zu programmieren,
zwischen Client und Server werden abschlossene sinnvoll definiere Nachrichten hin und her geschickt,
diese Nachrichten können zugeordnet werden, habe eine eindeutige Kommunikationsnummer oder was auch immer,
der Kernel empfängt die Antworten, informiert Observer, diese schauen nach welche Nachricht neu verfügbar ist,
wenn die eigene (Kommunikationsnummer beim Senden mitbekommen), dann wird sie abgeholt,
usw., nebenläufige Threads, Synchronisation, das sind alles Grundmechanismen


edit:
ach ja, wenn du irgendwas höheres wie RMI/ SIMON verwendest, dann musst du dir darüber keine Gedanken mehr machen,
allerdings sind das auch MB an APIs und evtl. keine Kontrolle mehr ob das über irgendeinen kleinen Socket geht, 
du willst das sicher selber implementieren?
ich wage die Wette dass ich mit meiner Beschreibung nicht weit von der Realität entfernt bin , 
was soll es da auch groß an Varianten geben


----------



## irgendjemand (2. Jan 2012)

als erstes würde ich dir erstmal raten dafür zu sorgen das "STATIC" aus der methoden signatur fliegt da du auf objekte zugreifst welche nur innerhalb einer instanz laufen ... aber außerhalb im static kontext so nicht verfügbar / NULL sind ...

ich nehme mal an das bei dir alles im endeffekt über [c]public static void main(String[] args)[/c] läuft ...
hier baue einfach eine self-instance ein ... sieht dann so aus


```
//package
//imports
public class KLASSENNAME //extends ... implements ...
{
	public static void main(String[] args) { (new KLASSENNAME()).start(); }
	private KLASSENNAME()
	{
		//CODE
	}
	private void start()
	{
		//CODE
	}
}
```

wenn du natürlich von Thread erbst oder Runnable implementierst würdest du mit start() natürlich den thread anstoßen ... hier sollte den start-code anderst lauten ...

für eine GUI würde das dann also heißen das du im konstruktor alles zusammen baust und dann in "start()" einfahc nur noch setVisible(true) callst ...


aber genug OT ...

ich persönlich habe schon so etwas programmiert wie du vorhast ...
daher kann ich dir bei der kommunikation helfen ...

da ich nicht weis auf welche art und weise deine plugin-architetkur arbeitet muss ich etwas abstrakter werden ...

das das ganze im allgemeinen a-sync abläuft ist klar ... desshalb solltest du auch nur EINEN thread haben der die daten einliest ...

das ganze läuft dann darauf hinaus das du ein protokoll brauchst welches einen ID-felder besitzt *mindestens PLUGIN-ID , METHODEN-ID und REQUEST-ID* ...
da du ja sicher beleibig plugins hinzufügen willst musst du dafür sorgend das im "kernel" die verbindung zwischen ID und plugin nicht statisch läuft ...
kann man durch entsprechendes design der interfaces erreichen ...

wenn du jetzt von einem plugin eine nachricht senden willst ... dann natürlich über die send-methode im kernel ... diese sollte falls möglich SYNCHRONIZED sein *zumindest wenn die plugins in eigenen threads laufen* ...

diese senden *damit die gegenstelle die daten eideutig idenzifizieren kann* deinem protokoll entsprechend auch die ID-header vorran ... und das war es ... hier wartest du NICHT auf eine antwort ... und hier kommt es auch ins spiel das deine plugins an sich a-sync laufen ...

im klartext : dein plugin muss so gebaut sein das der caller der dafür verantwortlich ist das überhaupt daten gesendet werden auch so lange in nem loop laufen muss bis die antwort da ist ... also in einem thread ...

wie genau funktioniert das nun ?

wenn die antwort eintrifft besitzt diese ja einen ID-header ... der wird dann der reihe nach abgearbeitet ...
die PLUGIN-ID verrät dir erstmal zu welchem plugin die daten gehören *egal ob a-sync request oder ein response ...
um dann noch den richtigen setter zu callen brauchst du noch die METHOD-ID ...
das ganze baust du dann so zusammen das in deinem reader-thread im kernel die daten dann so zerlegt werden das sie deinem interface entsprechend der signatur des setter entsprechen *am einfachsten wie bei reflections : [c]methode(Object... args)[/c] und als übergabe dann ein [c]Object[][/c]*
der setter setzt dann in instanz-variablen die werte und benachrichtigt den thread welcher auf diese antwort gewartet hat *gesteuert über die REQUEST-ID* ...

und DANN arbeitet der eigentlich thread den block weiter ab und returned dann erst an senen caller ...


du siehst also : das ganze wird ganz schön a-sync ... synchronisation , ein gut-überlegtes protokoll und eine entsprechend aufgebaut plugin-architektur sind hier wichtig ...

auch ich habe bestimmt einige monate damit verbracht bis meien lösung funktionierte ... und das sie nicht die beste ist weis ich auch ... aber mir fällt leider selbst nichts besseres mehr ein ...


----------



## BigBoss (2. Jan 2012)

@SlaterB
ja das stimmt soweit.
Ich will möglichst viel selber machen um die Zusammenhänge dahinter zu verstehen und um fehlern zukünftig ausm weg zu gehen.

@irgendjemand
Bei mir ist es etwas komplexer was den Start angeht.
Es gibt einen ClassLoader der alle Jars in einem Verzeichnis lädt und dann eine Startklasse welche ich übergebe oder auswählen kann startet.
Dies ist dann der ClientKernel oder ServerKernel.
Innerhalb dieses Kernels wird dann die Klasse Instanziiert welche die Netzwerkkommunikation steuert.
Im Client soll jedes Plugin ein eigener Thread sein, wobei es durchaus vorkommen kann dass das noch feiner werden muss.
Im Server müssen nur die Plugins einen eigenen Thread bekommen die permanente Aufgaben wahrnehmen.
Alle Plugins erben von einem Abstracten Server und Client Plugin und diese von einem Abstracten Plugin
Damit unterscheide ich welche Plugins wo gestartet werden können.
Das ist meine Klasse welche als Einzige direkt übertragen werden soll.

```
public class Nachricht implements Serializable
{
	private static final long serialVersionUID = 3739662583396188627L;
	private Object o;
	private Meldung meldung = new Meldung(Status.ok, "");
	private KommunikationsPartner empfaenger = null;
	private KommunikationsPartner sender = null;
	private String zielplugin = null;
	private String methode = "";
	private boolean antwort = false;
	private boolean warteaufantwort = true;
	private HashMap<String, Object> params = new HashMap<String, Object>();
	private Status status = null;
	private String nid;
...
}
```

deine Beschreibung der Kommunikation verstehe ich noch nicht so richtig.
Ich benutze ObjectImput/OutputStreams um meine Nachrichten Objekte zu Versenden.
Diese enthalten die Informationen wo das ganze hin muss.
Wie springst du wieder an die Stelle in deinen Plugins um dort weiterzuarbeiten wenn du die Daten vom Server hast?
Ich hoffe du verstehst mein Konzept dahinter ein wenig.
VG BB


----------



## SlaterB (2. Jan 2012)

alle Varianten sind denkbar, welche bereitet dir Schwierigkeiten welcher Art?
das üblichste dürfte doch sein dass das Plugin in einem eigenen Thread läuft und der Kernel auch,
das Plugin wartet dann die ganze Zeit oder kümmert sich um seinen Kram und schaut periodisch nach oder will vom Kernel informiert werden,
alles implementierbar, über jede Variante kann man ganze Seiten schreiben, aber alles Standard-Verfahren


----------



## irgenjemand (3. Jan 2012)

@TO
ich hab ja auch nicht behauptet das mein system "so super" wär ... geschweigedenn "überhaupt verwendbar" ...
es gibt viele dinge die mich selbst noch stören und an denen ich immer noch arbeite ... aber bei einige dingen fällt mir schlicht nichts besseres ein wesshalb ichs dann meist lieber lasse da es immer hin irgendwie doch funktioniert ...

was du mit "komplizierterem" start-up meinst verstehe ich nicht ...
das was ich da oben geschrieben habe war lediglich dazu da um aus dem static context von [c]main(String[])[/c] wegzukommen ... allen weiteren code kannst du dann gepflegt in den konstruktor oder die init/start methode packen ... ansonsten vielleicht mal etwas genauer erklären was du damit meinst ...

und das laden und starten der plugins ... klar weis ich das man da mit nem URLClassLoader über n verzeichnis geht und entsprechend seiner architektur dir JAR-files läd und instanzen der plugins erzeugt ... das hat aber rein garnichts damit zutun das du erstmal über self-instance raus kommst aus dem static context ... *oder greifst du von deinen plugins wirklich STATIC auf deinen pluginloader zu ? wenn ja -> nicht gerade das super konzept ...
du solltest wenn dann im plugin interface eine methode ala [c]loadPlugin(PluginLoader)[/c] definieren und diese nach instanzierung der klasse callen ... *entsprechend vorrausgesetzt das deine plugin-loader klasse das interface "PluginLoader" implementiert* ...

das erleichtert es dir auch gegen das loader-interface zu programmieren anstatt da mit irgendwelchen static-calls sich von einer klasse abhängig zu machen ...


----------

