RMI Callback

carom

Mitglied
Hallo!

Wenn man per RMI Daten überträgt (die bekanntlich serialisierbar sein müssen), dann muss man ja aufpassen, ob im Objektgraphen nicht ausversehen noch andere Dinge mitserialisiert werden und man so unbewusst das halbe Clientprogramm verschickt. Ich stehe gerade auf dem Schlauch, gilt das auch für folgende Situation?

Mein RMI-Client soll per Callback antworten entgegennehmen und sie in einer Liste abspeichern. Dazu gibt es auf Clientseite eine Klasse, welche analog zur Serverseite nochmals von UnicastRemoteObject erbt und Remote implementiert, nennen wir sie ClientRemote.

Java:
public class ClientRemote extends UnicastRemoteObject implements Client {

	List<MyType> MyList = Collections.synchronizedList(new ArrayList<MyType>());
	
	protected ClientRemote() throws RemoteException {
		super();
	}
	
	@Override
	public void addToClientList(MyType xy) throws RemoteException {
		MyList.add(xy);
	}
}

1.)
Beim RMI-Server registriere ich mich so: server.register(new ClientRemote());
Der Server soll dann über das übergebene Objekt Einträge in die Liste des Clients vornehmen können.
Wird jedoch beim registrieren die Liste mitübertragen? Der Server soll von dieser ja nichts wissen, oder anders gesagt das würde unnötigen Traffic erzeugen.

2.)
Der Einfachheit halber besitzt der obige RemoteClient eine eigene Liste. Gerne würde ich den RemoteClient aber Einträge in eine Liste schreiben lassen, sie sich irgendwo anders im Client befindet. Gibt es da bei der Umsetzung etwas zu beachten?
Kann ich einfach per Setter-Methode eine Referenz auf die externe Liste übergeben?


vielen Dank!
 
Zuletzt bearbeitet:

r.w.

Bekanntes Mitglied
Hallo carom,

Java:
   server.register(new ClientRemote());

ich denke, wenn Du in der aktuellen Instanz des Clients etwas verändern möchtest,
solltest Du beim Registrieren des Callbacks kein neues Objekt, sondern dessen aktuelle
Instanz übergeben.

Wenn dies aus dem Client (ClientRemote) selbst geschieht, kannst Du das auch so machen:
Java:
   server.register((Client) this);
  // ...wobei "Client" der Type des Interfaces ist

Damit hat der Server keine Kenntnis von irgendwelchen Listen, die nicht im Interface
definiert sind. Das kannst Du aber auch direkt im Server selbst testen. ;-)
Wahrscheinlich wird die Methode "register(...)" sowieso einen Parameter vom Typen
des Client-Interfaces erwarten, wodurch dieser automatisch gecastet werden sollte.

Ich hoffe, dass hilft Dir weiter.

VG ROlf
 
T

tuxedo

Gast
UnicastRemoteObject's werden gar nicht serialisiert. Die verbleiben da wo sie sind. Lediglich dein Gegenüber bekommt eine Referenz auf die Instanz. Alle Methodenaufrufe auf diesem Objekt werden von deinem Gegenüber zur eigentlichen Instanz weitergeleitet.

Zu deinen Fragen:

1) Nein, der Server bekommt weder die Liste, noch das tatsächliche Objekt geschickt. Nur eine Referenz auf das Objekt. Mehr nicht.
2) So ist es. Der Server sieht nur das Interface der Klasse "ClientRemote". Ob die Implementierung der Klasse nun eine eigene, lokale Liste hat, oder sich einer Liste einer anderen Klasse bedient spielt da für RMI keine Rolle.
 

carom

Mitglied
Vielen Dank dir schon mal.

edit: danke auch an tuxedo! unterer Text relativiert sich jetzt etwas.

Muss kurz etwas ausholen: es handelt sich um ein etwas umfangreicheres Programm, nur ein kleiner Teil davon ist der Netzwerkpart. Das Programm drumherum verwendet eine Liste, welche es grafisch mit Swing darstellt. Genau zu dieser Liste aus dem Hauptprogramm möchte ich über den Callback vom RMI-Server nun Einträge hinzufügen lassen, so dass sie später darstellbar sind. Also um es nochmal klarer auszudrücken, die Liste gehört eigentlich garnicht zum RMI-Teil, sondern soll von diesem nur aktualisiert werden (sprich, Einträge hinzufügen).

Vielleicht liegt hier schon ein Problem. Wie könnte man das optimal umsetzen? Kann ich problemlos dem ClientRemote diese Liste des Hauptprogramms übergeben?

edit: die Liste des Hauptprogramms sollte dann wohl besser threadsafe sein, richtig? Denn wenn ich nicht falsch liege, dann wird die vom Server aufgerufene Callbackmethode auf dem Client implizit in einem neuen Thread ausgeführt.

Grüße
 
Zuletzt bearbeitet:
T

tuxedo

Gast
Zur Thread-Sache:

Vergiss die RMI-Internas mit Threading und Co. ...

Dich interessieren nur die Threads die DU in deiner Serveranwendung hast. Wenn da sichergestellt ist, dass es immer nur einen Thread gibt der die Methode deines RemoteClients benutzt die Zugriff auf die Liste hat, dann passt das. Wenn der Server aber selbst mehrerer Threads hat aus denen heraus er bei ein und demselben Client diese Methode aufruft, dann musst du dir gedanken um Threadsicherheit machen.

- Alex
 

carom

Mitglied
Gut dass du das nochmal erwähnst, hier habe ich etwas Nachholbedarf.

Deine Aussage ist für mich nachvollziehbar, wenn der Server die lokalen RemoteClient-Methoden nicht durch serverseitiges Threading mehrmals zu einem Zeitpunkt anspricht, dann greifen diese Methoden auch nicht parallel auf die Liste zu.

Allerdings sitzt die Liste ja im Gui-Thread (oder eben da, wo die Hauptanwendung läuft). Wenn ich jetzt aus der Gui heraus schreibend auf die Liste zugreifen würde, dann hätte ich aber unter Umständen Probleme, oder? Denn: Schreibzugriff durch das Gui und Schreibzugriff durch die RemoteClient-Methode, welche durch Server ausgeführt wird und sich implizit in einem anderen Thread befindet.

Noch am Rande: kann der Server die Methoden aus RemoteClient im Client überhaupt mehrmals gleichzeitig ausführen? Wenn man etwas googelt, dann stellt man fest, dass ein RMI-Server für jede zugreifende JVM nur einen neuen Thread bereitstellt, egal wie oft dieser zugreift. Und in meinem Fall ist mein RemoteClient ja auch eine Art Server, denn er erbt ja von UnicastRemoteObject.
 
T

tuxedo

Gast
Mit RMI ist es wie ohne RMI was das Threading betrifft: Wenn du von mehreren Stellen/Threads auf eine Liste zugreifst, dann solltest du sicher sein dass nix schief geht. Also den Listenzugriff passend synchronisieren.

Eine Liste kann nicht im GUI-Thread sitzen. Sie kann maximal in deiner GUI-Klasse sitzen und der GUI-Thread greift darauf zu :)

...welche durch Server ausgeführt wird und sich implizit in einem anderen Thread befindet.

Nicht nur implizit, sondern ziemlich exakt explizit. Sowohl Server als auch Client haben eine eigene JVM. Folglich ist dazwischen nix synchron, außer du machst es synchron.

Noch am Rande: kann der Server die Methoden aus RemoteClient im Client überhaupt mehrmals gleichzeitig ausführen? Wenn man etwas googelt, dann stellt man fest, dass ein RMI-Server für jede zugreifende JVM nur einen neuen Thread bereitstellt, egal wie oft dieser zugreift. Und in meinem Fall ist mein RemoteClient ja auch eine Art Server, denn er erbt ja von UnicastRemoteObject.

Klar geht das. Die Aussage die du da gefunden hast bezieht sich wohl auf die RMI internen Threads. Das RMI Protokoll sieht aber vor, dass Methodenaufrufe asynchron kommuniziert werden. Das heisst der Call wird abgesetzt und irgendwann später kann die Antwort (return) zurück kommen. Währenddessen kannst du weitere Methoden aufrufen.

Wäre ja schlimm wenn dem nicht so wäre. Dann würde man mit RMI gar nicht richtig nebenläufig programmieren können.


- Alex
 

carom

Mitglied
Nicht nur implizit, sondern ziemlich exakt explizit. Sowohl Server als auch Client haben eine eigene JVM. Folglich ist dazwischen nix synchron, außer du machst es synchron.

Mit implizit meinte ich, dass man es dem Code eigentlich ohne weitere Kenntnisse nicht ansehen kann, es werden keine Schlüsselwörter wie Thread, runnable etc. verwendet und trotzdem werden neue Threads erzeugt.. das war wohl etwas schwammig ausgedrückt., stimmt.



Es bleibt noch ein letztes Verständnisproblem:

Die ganze RMI-Logik liegt sozusagen im Model meiner Anwendung. Nehmen wir mal eine einfache Methode im Callback-Objekt an, welche vom RMI-Server lediglich einen String entgegen nimmt (receiveMessage(String msg) oder etwas ähnlich einfaches). Dieser String soll im View angezeigt werden. Normalerweise hängt ja das View per Observer-Pattern am Model.

An dieser Stelle hats noch nicht klick gemacht. Die Methode receiveMessage wird also vom RMI-Server aufgerufen und läuft in einem neuen Thread. Was wäre, wenn ich aus diesem Methodenaufruf heraus das notify ans View anstoße, um den String abzuholen? Würde die ganze notification dann ebenfalls noch in diesem neuen Thread ablaufen - und das View folglich in eben diesem Thread den Wert beim Model abholen?

Eigentlich sollen ja nur die kritischen RMI-Abschnitte in extra Threads laufen. Doch momentan kommt es mir so vor, als würde eine ganze Verkettung an Methodenaufrufen bis hin zum View in diesem neuen Thread ablaufen, wenn ich die notifys aus dem Callback-Objekt heraus starte. Wie kriege ich die Kommunikation zwischen Model und View wieder in den Gui-Thread zurück, oder ist das garnicht so tragisch?
 
T

tuxedo

Gast
An dieser Stelle hats noch nicht klick gemacht. Die Methode receiveMessage wird also vom RMI-Server aufgerufen und läuft in einem neuen Thread. Was wäre, wenn ich aus diesem Methodenaufruf heraus das notify ans View anstoße, um den String abzuholen? Würde die ganze notification dann ebenfalls noch in diesem neuen Thread ablaufen - und das View folglich in eben diesem Thread den Wert beim Model abholen?


Du siehst gerade den Wald vor lauter Bäumen nicht mehr. Vergiss mal die Sache mit den vielen Threads und RMI und Co. Stell dir vor es gäbe nur eine einzige Anwendung die ohne RMI arbeitet. Und diese eine Anwendung würde, aus einem x-beliebigen Event/Thread heraus die receiveMessage() Methode aufrufen. Java baut dafür einen Call-Stack auf. A ruft B auf. Wenn B noch dann noch C aufruft und C dann auch noch D, dann sieht der Stack so aus:

A -> B -> C -> D

Der A aufruf ist erst dann beendet (kehr zurück) wenn der D Aufruf auch beendet ist. AUSSER du bastelst dir SELBST einen Thread dazwischen der einen Teil des Callstacks abkoppelt. Tust du das NICHT, so läuft alles von A bis D in dem Thread ab, von dem aus A aufgerufen wird.
Um auf deine Notify-Geschichte zurück zu kommen:

Wenn A nun in Thread1 stattfindet in deinem Callstack nun an Stelle D ein NOTIFY ausgelöst wird, dann wird ein anderen Thread benachrichtigt dass er etwas zu tun hat. Nach dem Notify ist dein ganzer Callstack fertig und ThreadX, welcher "notifiziert" ( :D ) wurde, macht da weiter wo er aufgehört und sich schlafen gelegt hat.



Eigentlich sollen ja nur die kritischen RMI-Abschnitte in extra Threads laufen. Doch momentan kommt es mir so vor, als würde eine ganze Verkettung an Methodenaufrufen bis hin zum View in diesem neuen Thread ablaufen, wenn ich die notifys aus dem Callback-Objekt heraus starte. Wie kriege ich die Kommunikation zwischen Model und View wieder in den Gui-Thread zurück, oder ist das garnicht so tragisch?

Vergiss die RMI Threads. Das ist "blackbox magie" die dich nicht wirklich interessieren muss. Interessant ist nur: Dein Server, der das Callbackj-Objekt des Clients nutzt um beim Client einen Methodenaufruf abzusetzen, hat auch so schon Threads. Und wenn es nur der main-Thread ist. Und aus dem Thread heraus, aus dem er sich entscheidet die Remote-Methode beim Client aufzurufen (und das ist noch kein RMI Thread) baut sich der Callstack auf. Ich versuchs mal zu skizzieren:

[X-beliebiger Thread im Server, kann und muss kein RMI Thread sein] Aufrufwunsch Methode A -> [hier kommt RMI ins Spiel mit all seinen RMI Threads] -> [RMI Client nimmt den Aufruf in einem RMI Thread entgegen] -> tatsächlicher Aufruf von A -> Aufruf von B -> Aufruf von C -> Aufruf von D

Das alles läuft synchron von links nach rechts ab, und ist somit unabhängig von etwaigern RMI Threads dazwischen. Es fängt in einem x-beliebigen Thread an, planzt sich über's Netzwerk fort und am anderen Ende des Netzwerks macht ein RMI Thread stellvertretend weiter. Nichts desto trotz: Alles syncron und für dich völlig transparent. Das ganze verhält sich genau so, als ob du gar kein RMI benutzt hättest und auch keine RMI Threads da wären.
 

Ähnliche Java Themen


Oben