# Session in RMI



## tuxedo (23. Jan 2012)

[abgespalten von http://www.java-forum.org/netzwerkprogrammierung/130702-designfrage-autorisiertem-filetransfer.html ]

Muss gestehen, ich hab jetzt nicht alles gelesen, nur die ersten paar Beiträge überflogen:

Die Sache mit der Übergabe der Session: Ist irgendwie nicht ganz OO-Style. Warum benutzt du kein Session-Pattern:

RMI Server bietet erstmal nur ein Remote-Interface mit einer Methode an:

```
public Session login(String user, String pass);
```

Bei erfolgreichem Login liefert der Server ein Session-Objekt (was auch ein Remote-Objekt ist) zurück. Jeder Login produziert so seine eigene Session. Das spart dir das dauernde durchreichen/schleifen der Session-ID.

Ein konkretes Beispiel (das mit RMi sehr ähnlich zu realisieren ist):

SIMON - Sample session pattern - root1.de - Software Engineering

Wenn du nicht an RMI gebunden bist, aber nicht auf RPC verzichten möchtest, hilft dir evtl. das hier weiter:

SIMON - Howto rawchannel - root1.de - Software Engineering

Vorteil gegenüber RMI ist: Mehr Up/Download Performance, da für RawChannels kein Reflection und Serialisierung zum Einsatz kommt. Bis auf ein wenig Protokolloverhead kommt da nix dazu.
Darauf kann man dann ein Streaming-Interface aufsetzen... 

Gruß
Alex


----------



## SlaterB (23. Jan 2012)

tuxedo hat gesagt.:


> Bei erfolgreichem Login liefert der Server ein Session-Objekt (was auch ein Remote-Objekt ist) zurück. Jeder Login produziert so seine eigene Session. Das spart dir das dauernde durchreichen/schleifen der Session-ID.


dein Beispiel zeigt (meiner Ansicht nach, kurze Durchsicht) nicht ganz was mit dem Session-Objekt jetzt genau gespart wird,
der interessante Teil der tatsächlichen einzelnen Aufrufe wird mit doSomething() ... weggelassen,
zudem ist das eine Methode in Session, das führt doch wohl kaum zu Remote-Aufrufen?

ich selber habe mir (in RMI direkt) eine nette Kette aus Wrappern gebaut,
für den Server gibt es ja sowieso schon Interface,
da kann am sich auf Client-Seite auch an einen lokalen Proxy wenden, welcher
daraus ein einheitliches Nachricht-Objekt baut, Methodenname als String, sonstige Informationen wie 
Client-Id, Session-Id automatisch rein, an Server geschickt, der dann alle Infos hat und die richtige Methode per Reflection aufruf

strenggenommen braucht man dann RMI gar nicht mehr bzw. eine wesentliche Funktionalität nachgebaut,
für die Übertragung der einen Standard-Nachricht ginge auch ein Socket mit Object-Streams 
so ist das eben bei generischer Verarbeitung


----------



## tuxedo (23. Jan 2012)

Hmm, okay. Bisher hatte ich noch kein Feedback bzgl "Info reicht nicht aus". Dann erläuter ich's mal von vorne:

Der Server erzeugt für jeden Login ein eigenes Session-Objekt, *welches wiederum ein Remote-Objekt ist*.
In die Session-Klasse steckt man nun alle Methoden, die man nur nach erfolgreichem Login ausführen darf. Fertig. Man muss dann, wenn man Methoden am Server aufrufen will, nicht jedesmal eine Session ID oder eine andere Authentifizierung mitschicken damit der Server überprüfen kann ob man das auch aufrufen darf oder nicht. Denn das ist schon mit dem Session-Objekt geschehen.

Exemplarisch gibt's in meinem Beispiel nur eine "doSomething()" Methode. 

Man könnte auch sagen: Session ist ein Remote-Objekt des Servers, an welches man nur gelangt, wenn man erfolgreich login() aufgerufen hat.



> ich selber habe mir (in RMI direkt) eine nette Kette aus Wrappern gebaut,
> für den Server gibt es ja sowieso schon Interface,
> da kann am sich auf Client-Seite auch an einen lokalen Proxy wenden, welcher
> daraus ein einheitliches Nachricht-Objekt baut, Methodenname als String, sonstige Informationen wie
> Client-Id, Session-Id automatisch rein, an Server geschickt, der dann alle Infos hat und die richtige Methode per Reflection aufruf



Sowas haben unser Inder auch fabriziert, und damit RMI "ad absurdum" geführt. RMI bietet ja schon Remote-methodenaufrufe an. Wieso sollte man dann auf RMI nochmal einen "RPC Mechanismus" drauf setzen?! Das macht die Sache an sich nochmal langsamer (Stichwort Serialisierung, Reflection, ...)

Fällt alles weg wenn du das Session-Pattern benutzt.



> strenggenommen braucht man dann RMI gar nicht mehr bzw. eine wesentliche Funktionalität nachgebaut,
> für die Übertragung der einen Standard-Nachricht ginge auch ein Socket mit Object-Streams
> so ist das eben bei generischer Verarbeitung



Wieso der Aufwand? RMI (und SIMON auch) funktioniert prima und ist effizient. Man muss nur verstanden haben, dass man Remote-Objekte frei erzeugen und dem "Gegenüber" geben kann. Stichwort Callback (wobei diese Bezeichnung etwas verwirrend ist wenn das Objekt auf Serverseite erzeugt und dem Client als Rückgabewert gegeben wird)

P.S. Natürlich kann man in die Session-Klasse auch Methoden packen die ein Client nicht ausführen darf (Stichwort Rolle/Gruppe). Dann wirft der Server eben beim aufruf durch den Client eine "AccessNotAllowedException" (o.ä.).


----------



## SlaterB (23. Jan 2012)

ok, mit diesen instanziierten Remote-Objekten bin ich immer skeptisch/ noch nicht vertraut

damit kann man aber nur statische Informationen verwalten, ein Objekt pro User, der Server kann zwar was eintragen, das sieht aber der Client nicht (ohne Remote-Abfrage) oder?

da hat mein Ansatz noch bisschen Mehrwert für mich, 
der Client liefert Standardinfos seiner aktuellen Auswahl/ Sicht welche der Server überprüft, 
etwa ob nicht inzwischen geändert, bzw. bei Anfragen berücksichtigt
(sehr spätes edit: auch viel zum Exception-Handling)


----------



## tuxedo (23. Jan 2012)

SlaterB hat gesagt.:


> hoffentlich letztes Posting von mir zu diesem Nebenthema:
> ok, mit diesen instanziierten Remote-Objekten bin ich immer skeptisch/ noch nicht vertraut



Die Skepsis versteh ich nicht ganz: Ist doch 0815/Standard RMI Mechanismus ?! Bin ehrlich gesagt auch etwas verwundert dass RMI Callbacks (das ist nix anderes) so wenig Erwähnung finden...



> damit kann man aber nur statische Informationen verwalten, ein Objekt pro User, der Server kann zwar was eintragen, das sieht aber der Client nicht (ohne Remote-Abfrage) oder?
> 
> da hat mein Ansatz noch bisschen Mehrwert für mich,
> der Client liefert Standardinfos seiner aktuellen Auswahl/ Sicht welche der Server überprüft,
> etwa ob nicht inzwischen geändert, bzw. bei Anfragen berücksichtigt



Wieso statisch? Das Session-Objekt kann ja auf darunterliegende Layer zugreifen. Nicht dass du meinst das Session-Objekt wäre ein simples Pojo mit gettern/settern das keinen Link zu anderen Schichten hat, und einfach nur "remote fähig" gemacht wurde... Nene ..

Und Änderungen kann der Server dem Client natürlich ebenfalls über Callbacks mitteilen: Dazu ist es am einfachsten man übergibt (wie in meinem Sample) bereits beim Login dem Server ein Client-seitiges Callback-Objekt. Das kann der Server dann nutzen um Änderungen aktiv dem Client mitzuteilen.

Wenn man's richtig macht, ist das so, als ob es kein RMI dazwischen gäbe, als wäre Client und Server eins. Damit hat man alle Möglichkeiten und keinerlei einschränkungen. 

Vielleicht sind auch zu viele Leute mit Remote-EJBs, wo's keinerlei Callback gibt, geimpft???!! Denn da muss man dann solche Krücken erfinden ...

- Alex


----------



## tuxedo (23. Jan 2012)

@Mods

Wäre vielleicht nützlich gewesen im ersten Beitrag des Splittings zu erwähnen wo die Diskussion entstand.. Sonst peilt das ja keiner wenn er diesen neuen Thread liest ...


----------



## SlaterB (23. Jan 2012)

ich hatte es zwar vergessen, aber ganz schön kurz gewartet 

mehr zu antworten habe allerdings auch gar nicht, Callback benutze ich auch nicht, ist alles ne Stufe höher


----------



## tuxedo (23. Jan 2012)

höher=mehr high level?

Callbacks sind nicht wirklich "low level"....

Vielleicht möchtest du dir nochmal das Sample ansehen:

SIMON - Sample session pattern - root1.de - Software Engineering

Speziell das "ClientCallbackInterface", sowie die Login-Methode:


```
public SessionInterface login(String user, ClientCallbackInterface clientCallback) {
         System.out.println("login. user="+user);
         clientCallback.callback("Login is in progress ...");
         System.out.flush();
         Session session = new Session(user, this);
         userSessions.add(session);
         clientCallback.callback("Session is created ... Now "+userSessions.size()+" users are online ...");
         System.out.println("Session created for user "+user+". Now "+userSessions.size()+" users are online ...");
         return session;
     }
```

Da sieht man recht schön wie das Callback-Objekt (man kann die Klasse/Instanz natürlich auch anders nennen) genutzt wird, um dem Client mitzuteilen, dass für ihn eine Session angelegt wurde.
Auch sieht man, dass die Session-Instanz mit User-Informationen angereichert wird. Das ist nützlich um bei Aufrufen auf dem Session-Objekt die Erlaubnis zu prüfen zu können. 

Und zuguter letzt, wird dem Client als Ergebnis seines erfolgreichen Logins (welchen man bei falschen Passwort [hier jedoch weggelassen] mit einer Exception ablehnen kann) das Session-Objekt gegeben.
Auch hier: Der Name ist nur Facade.. Es geht um das Prinzip. 

Bis hierhin ist alles "high level OO" ohne viel TamTam und "blackbox-magie". Die einzige "Magie" ist dass der Rückgabewert ein Remote-Objekt ist. Aber das ist dem Client (und sollte dem Entwickler auch) egal sein. Objekt ist Objekt. Ob nun im Hintergrund ein Proxy dasteht der den Aufruf über's Netz leitet oder nicht spielt da keine Rolle.

- Alex


----------



## SlaterB (23. Jan 2012)

nochmal Rückmeldung um es klarzustellen

> Callback benutze ich auch nicht, ist alles ne Stufe höher 
==
Callback und Remote-Objekte sind mir zu high-level


----------



## tuxedo (23. Jan 2012)

Zu high level? Gibt's sowas auch? :reflect:


----------



## NegroManus (23. Jan 2012)

Hey tuxedo!

Wow, danke. Der Hinweis mit dem Session-Pattern kommt wie gerufen.
Ich hatte nur mal in "Head First - Design Patterns" geschaut und halt das Dynamic Proxy Pattern als Weg gefunden, um Objektzugriffe zu kontrollieren. Ich habe sowieso noch nicht allzu viel Erfahrung im Softwareentwurf. ^^

Ich habe das Session-Pattern direkt mal implementiert und das nimmt mir echt einen Haufen Arbeit ab!
Man muss halt nur wissen wie. :applaus:


----------



## NegroManus (23. Jan 2012)

Okay, doch noch eine Frage zu SIMON - Sample session pattern - root1.de - Software Engineering

Warum hält die Session eine lokale Variable der ServerInterfaceImpl, die sie erzeugt hat?

Session:

```
private final ServerInterfaceImpl server;
```

Die wird in dem Beispiel nie verwendet und führt bei meinem Versuch zu einer ClassCastException...

```
java.lang.ClassCastException: cannot assign instance of $Proxy0 to field xx.Session.server of type xx.ServerInterfaceImpl in instance of xx.Session
	at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(Unknown Source)
	at java.io.ObjectStreamClass.setObjFieldValues(Unknown Source)
	at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
	at java.io.ObjectInputStream.readSerialData(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.readObject(Unknown Source)
	at sun.rmi.server.UnicastRef.unmarshalValue(Unknown Source)
	at sun.rmi.server.UnicastRef.invoke(Unknown Source)
	at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
	at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
	at $Proxy0.login(Unknown Source)
	at xx.Client.main(Client.java:22)
```

Client:
[JAVA=22]SessionInterface session = (SessionInterface) server.login("DonaldDuck", clientCallbackImpl);[/code]

[EDIT]Okay, mir fiel auf, dass ich ja die Methode, die diese lokale Variable verwendet, herausgelöscht habe, weil ich auf diesen Simon-Kram verzichtet habe.
Dann würde ich diese Variable weglassen und eine logout()-Methode ins Interface 
	
	
	
	





```
ServerInterface
```
 aufnehmen?[/EDIT]


----------



## tuxedo (23. Jan 2012)

> Warum hält die Session eine lokale Variable der ServerInterfaceImpl, die sie erzeugt hat?



Schau mal in die unreferenced() Methode .... Damit wird, beim "verschwinden" des Clients in der Server-Implementierung die UserListe auf den aktuellen Stand gebracht.

Diese "unreferenced" Technik gibt's auch bei RMI. Musst mal danach googeln. Kannst du fast 1:1 so übernehmen.




> ... weil ich auf diesen Simon-Kram verzichtet habe.



Das klingt jetzt irgendwie "herablassend" für SIMON :-(



> Dann würde ich diese Variable weglassen und eine logout()-Methode ins Interface ServerInterface aufnehmen?



Wenn du damit das Session-Objekt invalidieren kannst bzw. keine User-Liste aktualisieren musst: Jepp, ist eine Möglichkeit.

Zum Session-Pattern an sich:
Bin mir nicht sicher, aber in Bezug auf RMI bzw. RPC hab ich den Begriff "Session Pattern" noch nirgends gelesen. Wäre gut möglich dass ich den für diesen Zusammenhang gerade präge ;-)


----------



## NegroManus (23. Jan 2012)

Ich hatte auch nur aufm Handy deine Beiträge gelesen, mir das Beispiel aber nicht angeguckt. Kannte das Pattern auch vorher nicht.
Wusste aber dann, was du meinst, und konnte es auch ganz intuitiv erfolgreich implementieren.
Nur halt nicht mit dem erweiterten Kram (nicht abwertend gemeint! ^^) vom Simon. 
Also gut neues RMI-Pattern geprägt! :toll:

Genau, das mit dem unreferenced() fiel mir dann ja auch auf.
Werde ich morgen mal googlen. Und dann auch mal schauen, was für logout()-Möglichkeiten sich mir bieten.

Vielen Dank! 

[EDIT]Hast du, davon abgesehen, eine Ahnung, warum die Exception kommt?[/EDIT]


----------



## tuxedo (24. Jan 2012)

Ohne mehr Code zu sehen: Nein, keine Ahung warum die exception kommt. Irgendwas läuft da beim speichern der Server-Variable in der Session schief. Vermute da ist noch etwas "zu sehr SIMON-like" ...

Ach ja: Du bist dir über die Tatsache, dass Callbacks bei RMI - sofern man über's Internet verbindet - probleme machen bewusst? --> mehr dazu: RMI Callback-Problem

- Alex


----------



## NegroManus (24. Jan 2012)

Ja, callbacks funktionieren bei mir (noch) gar nicht.
Die callback()s werden auf Serverseite in den Log geschrieben statt auf Clientseite . ^^

Du promotest deinen Simon ja ziemlich stark.
Mal abgesehen davon, dass mein Server nie die Clientauslastung haben wird, die da getestet wird, finde ich diese Callbackfunktion schon nicht schlecht!
Muss mir deinen Freund wohl mal genauer anschauen.
Vor allem da die Hochschule meiner Heimatstadt sehr begeistert scheint. 


Zu dem RAW-Channel. Da ist ja alternativ noch der Filetransfer über die getFileBytes()-Methode beschrieben. Die ist aber wieder auf 2GB beschränkt (aufgrund RawChannelServerImpl: 
	
	
	
	





```
23: byte[] data = new byte[(int)f.length()];
```
) und wenn man die RawChannel-Alternative implementiert, sowieso überflüssig, oder?
Mit dem RawChannel bin ich aber nicht an solche Restriktionen gebunden, ist das richtig?

[EDIT]Weitere Designfrage:
Bei SIMON bleibt die Client-Server-Verbindung ja die ganze Zeit bestehen, bis man sie releast.
Aber wäre es ratsam, diese Verbindung während der gesamten Client-Login-Zeit aufrechtzuhalten? Oder ist es schlauer, diese nur immer wieder aufzubauen, wenn der Client tatsächlich etwas vom Server will und nicht einfach nur rumidlet?
[/EDIT]

Gruß
Max


----------



## tuxedo (24. Jan 2012)

> Ja, callbacks funktionieren bei mir (noch) gar nicht.
> Die callback()s werden auf Serverseite in den Log geschrieben statt auf Clientseite . ^^



Wie im Wiki ja erklärt wird: RMI baut für den Callback vom Server zurück zum Client eine NEUE Verbindung auf, was in der heute üblichen Infrastruktur, wo die meisten Clients über einen Router mit NAT am Internet hängen nicht so ohne weiteren Aufwand möglich ist. 



> Du promotest deinen Simon ja ziemlich stark.



"Stark" ist relativ. Hab SIMON ja aus der Not heraus die ich mir RMI hatte entwickelt. Und wieso sollten andere nicht von meiner Entwicklung ebenfalls profitieren? Das ist ja der Sinn von OpenSource...



> Mal abgesehen davon, dass mein Server nie die Clientauslastung haben wird, die da getestet wird, finde ich diese Callbackfunktion schon nicht schlecht!
> Muss mir deinen Freund wohl mal genauer anschauen.
> Vor allem da die Hochschule meiner Heimatstadt sehr begeistert scheint.



Naja, SIMON zeichnet sich ja nichtn ur durch NIO und die damit verbundene Performance aus. SIMON zu benutzen ist nicht ganz so ivasiv wie RMI. Beispiel: Bei RMI musst du jeden Remote-Call im Try/Catch stehen haben. Bei SIMON nicht.  



> Zu dem RAW-Channel. Da ist ja alternativ noch der Filetransfer über die getFileBytes()-Methode beschrieben. Die ist aber wieder auf 2GB beschränkt (aufgrund RawChannelServerImpl: 23: byte[] data = new byte[(int)f.length()]; ) und wenn man die RawChannel-Alternative implementiert, sowieso überflüssig, oder?
> Mit dem RawChannel bin ich aber nicht an solche Restriktionen gebunden, ist das richtig?



Korrekt. Ich hatte das nur der vollständigkeit halber mit aufgenommen. Mit RawChannels kannst du soviel übertragen wie du willst und bist auch performanter wie wenn du das mit Remote-Calls machst.

Gruß
Alex


----------



## tuxedo (24. Jan 2012)

> Weitere Designfrage:
> Bei SIMON bleibt die Client-Server-Verbindung ja die ganze Zeit bestehen, bis man sie releast.
> Aber wäre es ratsam, diese Verbindung während der gesamten Client-Login-Zeit aufrechtzuhalten? Oder ist es schlauer, diese nur immer wieder aufzubauen, wenn der Client tatsächlich etwas vom Server will und nicht einfach nur rumidlet?



Der Verbindungsaufbau braucht Zeit. Einmal aufbauen und aufgebaut lassen bis man mit allem fertig ist.
Das hat den Vorteil, dass du selbst keine eigene Session-Unterbrechungs-Erkennung bauen musst:

Wenn du immer wieder die Verbindung beendest weil du gerade "herumidlest", dann weiß der Server ja nicht wirklich ob du noch da bist oder nicht. Gut, der Client könnte sich explizit abmelden. Aber was ist mit Verbindungen die "abrauchen" (Jemand stolpert über's netzwerkkabel, WLAn Verbindung reisst ab, Mobilfunk-Verbindung ist auf einmal weg,. ...).

Hinzu kommt: Ein Client der wegen idle gerade seine Verbindung getrennt hat, kann keine Callbacks vom Server empfangen... :-(

SIMON schickt, wenn die Verbindung einen Idle-State erreicht kleine ping-pong Pakete um die Verbindung zu testen. Damit erkennt der Server weggefallene Clients, und der Client bekommt auch mit, wenn der Server dermaßen überlastet ist, dass er ein Ping-Paket nicht mehr "in time" mit einem Pong-Paket beantworten kann...

Summa sumarum: Es tut niemandem weh wenn die Verbindung herumidelt... Und wenn das doch zu viel Traffik verursachen sollte (ein Ping-Pong braucht nur wenige bytes): man kann einstellen wie oft Paket-Ping-Pong gespielt wird. 

- Alex


----------



## NegroManus (24. Jan 2012)

tuxedo hat gesagt.:


> "Stark" ist relativ. Hab SIMON ja aus der Not heraus die ich mir RMI hatte entwickelt. Und wieso sollten andere nicht von meiner Entwicklung ebenfalls profitieren? Das ist ja der Sinn von OpenSource...


Brauchst SIMON gegenüber mir gar nicht zu verteidigen. Ich kritisiere gar nicht.  Habe mir den Wiki durchgelesen und das klingt sehr interessant. Vll werd ich ja auch Fan. ^^



tuxedo hat gesagt.:


> Naja, SIMON zeichnet sich ja nichtn ur durch NIO und die damit verbundene Performance aus. SIMON zu benutzen ist nicht ganz so ivasiv wie RMI. Beispiel: Bei RMI musst du jeden Remote-Call im Try/Catch stehen haben. Bei SIMON nicht.


Und wenn ich diese Verbindungsabbrechungen auf Clientseite bemerken *will*?
Wenn die Verbindung nicht hergestellt werden kann oder abbricht, muss ich das ja iwie handlen.
Dazu hast du dann deine eigenen Exceptions kreiert?

Zu dem Verbindungshalten: Ich war mir nicht sicher, inwiefern das iwelche Ports/Ressource blockiert, die Verbindung zu halten. Danke für die Erklärung!


----------



## tuxedo (24. Jan 2012)

> Und wenn ich diese Verbindungsabbrechungen auf Clientseite bemerken will?



Stichwort: ClosedListener. Schau ins SIMON JavaDoc ...

Für Callback-Objekte vom Server (also dein Session-Objekt) gibts ja auch noch unreferenced()...



> Dazu hast du dann deine eigenen Exceptions kreiert?



Korrekt. Wäre "verwirrend" gewesen die RMI Exceptions zu mißbrauchen. jetzt gibt's diverse "Simon*Exception"'s, wie z.B. die SimonRemoteException ....

Zu dem Verbindungshalten: Ich war mir nicht sicher, inwiefern das iwelche Ports/Ressource blockiert, die Verbindung zu halten. Klar, der Port ist blockiert... Aber das stört ja nicht. Der Client bekommt für die Socketverbindung eh einen Port irgendwo über 1024. Und den Serversocket-Port kann man mehrfach nutzen (sonst würde ein Web- oder FTP-Server mit Port 80 bzw. 21 ein problem mit vielen Clients haben).

Von daher: Ja, Ressourcen werden gebraucht. Aber das stört keinen bzw. hat keine negativen Auswirkungen.

- Alex


----------



## NegroManus (24. Jan 2012)

Zwei Fragen bzgl. SIMON...

Threadpool/Dispatcher:
Verstehe ich das richtig, dass du beim Dispatcher einen (default: cached) Threadpool hast, dessen Threads die Anfragen entgegen nehmen? Jede Anfrage wird also von einem Thread gehandelt oder in die Queue eingereiht?


Dynamic Proxy vorschalten:
Bei RMI war es problemlos möglich, dass der Remote-Proxy (also eine Klasse, die ein Remote-Interface implementiert) vor dem Exportieren zum Stub noch in einen Dynamic Proxy mit InvocationHandler verpackt wurde, sodass man für alle RPCs leicht Logging betreiben kann.
Versuche ich das mit SIMON, kommt:

```
java.lang.IllegalArgumentException: Provided remote object is not marked with SimonRemote or Remote annotation!
	at de.root1.simon.Registry.bind(Registry.java:309)
```
Ich nehme an, das liegt daran, dass er die Markierung beim Proxy nicht findet...
Kann man irgendwie anders InvocationHandler nutzen?

Source:

```
// create the serverobject
	// LoginService
	LoginService loginService = new LoginServiceImpl();
	// Dynamic Proxy
	InvocationHandler h = new LogInvocationHandler(loginService);
	LoginService loginServiceP = (LoginService) Proxies.dynamicProxy(loginService, h);

	// create the server's registry ...
	Registry registry = Simon.createRegistry(22222);

	// ... where we can bind the serverobject to
	registry.bind("server", loginServiceP);
```

Vgl mit RMI.:

```
// Simple LoginService ...
LoginService loginService = new LoginServiceImpl();
// ... wrapped in a Dynamic Proxy
InvocationHandler h = new LogInvocationHandler(s);
LoginService loginServiceP = (LoginService) Proxies.dynamicProxy(loginService, h);

// ... then wrapped in a Remote Proxy (-> now a Remote Dynamic Proxy)
loginServiceP = (LoginService ) UnicastRemoteObject.exportObject(loginServiceP , 0);

// ... and bound (reg = RMI-Registry)
reg.rebind("servicex", loginServiceP);
```

mit  Proxies.dynamicProxy():

```
// Service ist nur eine Markierungsschnittstelle
public static <S extends Service> S dynamicProxy(S serviceImpl, InvocationHandler h)
{
	ClassLoader loader = serviceImpl.getClass().getClassLoader();
	Class<?>[] interfaces = serviceImpl.getClass().getInterfaces();
	return (S) Proxy.newProxyInstance(loader, interfaces, h);
}
```

und LogInvocationHandler:

```
public class LogInvocationHandler implements InvocationHandler
{
    private final Object source;
    public LogInvocationHandler(Object source)
    {
	this.source = source;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException
    {
	Log.log("in")
	Object returnObject = null;
	try
	{
	    returnObject = method.invoke(this.source, args);
	}
	catch (Exception e)
	{
	    e.printStackTrace();
	}
	
	Log.log("out")
	return returnObject;
    }
}
```


Bissl viel Code, ich hoffe, du verstehst trotzdem, was ich meine... ^^


----------



## tuxedo (25. Jan 2012)

Ja, die Problemursache ist mir klar. Muss mal schauen ob ich das nicht korrekt erkennen und dann auch verwenden kann.

Alternative: Du kannst in SIMON auch Nicht-Remote-Objekte als Remote markieren:

Simon (SIMON 1.2.0-SNAPSHOT 1.2.0-SNAPSHOT API)

Das ist in etwa so wie das exportierren von Objekten bei RMI.
Damit solltest du deinen Proxy als Remote markieren können. Ausprobiert hab ich's aber mit Proxy-Klassen bisher nicht.

Wenn es dir nur allgemein drum geht zu loggen was wie über die Remote-Schnittstelle aufgerufen wird: Das geht auch einfacher. Kann dafür einen extra Logger (java.utils.logging) bereit stellen.

- Alex

[EDIT]Bei den ganzen Invocationhandlern muss dir aber eins klar sein: Die Performance wird irgendwie darunter leiden...[/EDIT]


----------



## tuxedo (25. Jan 2012)

> Threadpool/Dispatcher:
> Verstehe ich das richtig, dass du beim Dispatcher einen (default: cached) Threadpool hast, dessen Threads die Anfragen entgegen nehmen? Jede Anfrage wird also von einem Thread gehandelt oder in die Queue eingereiht?



Fast komplett korrekt. Im Dispatcher kommen die Daten bereits dekodiert an. Dort werden sie in ein Runnable gesteckt, welches dann in den Pool geworfen wird. Per Default ist dieser "cached". Kann man aber einstellen.
Im Pool wird dann geschaut, was es für ein Datenpaket genau ist, und dann die notwendige Aktion unternommen.

So können viele Datenpakete vom Netz entgegen genommen werden. Die Ausführung ist dann durch den Pool entkoppelt. Das Ausführen blockiert also nicht den Empfang.


----------



## NegroManus (25. Jan 2012)

Genau. Logging aller Methodenaufrufe auf dem Session-Objekt würde mir erst einmal reichen. 
Die "ganzen" Invocationhandler sollten zunächst auch schlicht diesen Zweck haben. Wenn das einfacher geht, gerne!

Entkopplung von Requestannahme und -ausführung: Gut.


----------



## tuxedo (25. Jan 2012)

Sodele. Logger eingebaut.

Mit der aktuellen 1.2.0-SNAPSHOT (wird gerade gebaut --> SIMON - trunk #4 [Jenkins]) gibts den Logger namens "de.root1.simon.InvokeLogger". Wenn du da das Log-Level "FINE" einschaltest, siehst du auf welchem Remote-Objekt welche Methode mit welchen Argumenten aufgerufen wird.

- Alex


----------



## NegroManus (26. Jan 2012)

Sorry, vll bin ich doof...
Aber wolltest du nicht einen Logger aus java.util.logging zur Verfügung stellen?
So wie ich das sehe, hast du in ProcessMessageRunnable einen org.slf4j.Logger zur Verfügung gestellt, dessen Loglevel ich nicht verändern kann.

Ich weiß nicht, wie SLF4J arbeitet. Kann also sein, dass ich mich nur zu doof anstelle.


----------



## tuxedo (26. Jan 2012)

Es würde sich lohnen zu schauen was SLF4J genau ist :rtfm:

In wenigen Worten: SLF4J ist eine Logging-facade und kann mit java.utils.logging, log4j und diversen anderen Logger-Mechanismen umgehen. 
Im Fall von SIMON ist das java.utils.logging ...

passt also ...


----------



## NegroManus (26. Jan 2012)

Das habe ich auch gelesen, aber auf die Schnelle nicht gefunden, wie ich den Logger holen und sein Loglevel setzen kann.

Wäre lieb, wenn du mir das eben erklärst, bevor ich mich hier tiefer in die Materie einlesen muss.


----------



## tuxedo (26. Jan 2012)

Java TM Logging Overview

Oder genauer:

Java TM Logging Overview


----------



## NegroManus (26. Jan 2012)

Habe meinen Fehler gefunden.
Ich Dummerle hatte keinen Handler installiert. ^^

Thx anyway.


----------



## NegroManus (26. Jan 2012)

Ich habe die RawChannel-Übertragung mal im lokalen Netz ausprobiert:

Server: Desktop-PC, 192.168.2.111. LAN-Anbindung am Router (SimonRegistry @ Port 22222)
Client: Laptop, 192.168.2.102. W-LAN-Anbindung am Router
Übertragung: Client->Server. Immer 512Byte lesen und dann schreiben (180MB Filesize).

Ich lausche jetzt beim Senden und Empfang mit und beobachte, dass immer ca 200KB übertragen werden, dann passiert ein paar Sekunden lang gar nichts und dann geht es weiter. Es ist so recht langsam und ich weiß nicht warum... Komplette Abbrüche (wohl wegen Timeouts) traten dabei auch schon auf.

Andersherum funktioniert es gar nicht (Laptop: Server, Desktop: Client):

```
de.root1.simon.exceptions.EstablishConnectionFailed: Could not establish connection to Connection[/192.168.2.102:22222]. Maybe host or network is down?
	at de.root1.simon.AbstractLookup.buildSessionDispatcherContainer(AbstractLookup.java:313)
	at de.root1.simon.NameLookup.lookup(NameLookup.java:60)
	at xx.desktopclient.ClientMain.main(ClientMain.java:35)
```
bei
[JAVA=35]server = (LoginService) nameLookup.lookup(LoginService.BIND_NAME);[/code]

Kam dir so etwas schon mal unter?
Ping in beide Richtungen geht perfekt.

P.S. Lokal auf 127.0.0.1 funxt auch alles perfekt und schnell.


----------



## tuxedo (27. Jan 2012)

Wenn es auf locahost prima funktioniert, dann könnte es an folgendem liegen dass es übers Netzwerk nicht so doll funktioniert:

* Virenscanner:
ich hab schon mehrfach Virenscanner erlebt die sich auch ans Netzwerkinterface "hooken" und den Traffik mitsniffen um "böse Downloads oder Uploads" zu identifizieren. Selbst nach dem abschalten lief der Hook weiter. Da hatte nur die zeitweilige deinstallation oder der Wechsel zu einem anderen Virenscanner was gebracht.

* Laptop-Powermanagement:
Viele Laptops lassen ihre WLAN und auch LAN-Karten auf Sparflamme laufen wenn sie nicht am Netzteil hängen. Beim normalen Surfen fällt das nicht auf. Aber bei Up/Downloads im lokalen Netz wird's dann schnell gravierend. Konnte ich ohne weiteres mit meinem MacBook nachstellen. Also: Netzteil ran und Powermanagement erstmal aus und am besten über Kabel statt WLAN testen... Wenn das funktioniert kannst du dich an WLAN rantasten.

* Firewall:
Siehe Virenscanner. Gleiches Prinzip, ähnliche bis gleiche Auswirkung. 

* Die Implementierung an sich:
RawChannels ermöglichen das Senden von Datenpaketen. Die sollte man nicht zu groß wählen, aber auch nicht zu klein. In der Regel sind Werte von 4-8k ausreichend. Wählt man das ganze zu groß, dann "flutet" man den Zwischenspeicher, denn meistens gehen die Daten langsamer durchs Netzwerk als man die Daten auf den Socket schaufeln kann (100Mbit = max. ~10MByte/sek Netto ... 54MBit WLAN = ~3MByte/sek Netto).
Wählt man die Pakete zu klein, dann gibt's zu viele Schreib-Requests...
Man kann auch die Puffergröße am Netzwerksocket einstellen (Siehe SIMON API). Per Default ist hier 8k eingestellt und reicht normalerweise aus.

Generell gilt:

SIMON ist es egal ob LAN oder WLAN und welche Marke des Computers, der WLAN oder LAN Karte. Wenn jedoch andere Anwendungen sich in die Verbindung "rein hooken", oder Systemeinstellungen negativen Einfluss auf Netzwerkverbindungen nehmen, dann bricht die Geschwindigkeit systembedingt ein. Und das nicht nur bei SIMON, sondern bei allem anderen auch. Nur fällt's da u.U. nicht so stark auf, da man seltenst den Vergleich zu einer FTP-Verbindung oder ähnlichen sucht.. Schließlich will man ja die eigene Anwendung testen und nicht FTP.

Gruß
Alex

P.S. SIMON hat ein eigenes Support-Forum: SIMON - Forums - root1.de - Software Engineering
Wenhn du dort, statt hier postest, dann hat das den Vorteil, dass andere die mit SIMON ein Problem haben, die Infos zur Problemlösung an einer zentralen Stelle finden und nicht quer beet im Netz suchen müssen (nicht jeder kennt dieses Forum hier, leider).


----------

