Referenz von Server auf Client löschen

Added by duschata about 1 year ago

Hallo Alle, hallo Alex,
ich habe einen einfaches Observer Pattern gebaut. Funktioniert auch erst mal prima, solange sich der Client nicht beim Server abmeldet. Schon der Versuch in der Liste mit indexOf nach dem Client Subscriber zu suchen führt zu einem Abbruch der Session. Ich hänge den Code einfach mal an, habe ihn auf ein sehr minimalistisches Beispiel runter gebrochen.

Viele Grüße und Danke für Tipps und Lösungen

Tom


Replies (13)

RE: Referenz von Server auf Client löschen - Added by achristian about 1 year ago

Ich werd's mir mal anschauen. Melde mich dann wieder...

- Alex

RE: Referenz von Server auf Client löschen - Added by achristian about 1 year ago

Welche SIMON Version benutzt du denn? Mit der 1.2.0-SNAPSHOT bekomm ich keinen Fehler mit deinem Sample-Code. Hab aber noch nicht in deinen Code reingeschaut. Werd' ich jetzt mal tun, bevor ich blind anfange andere Versionen zu testen.

- Alex

[update]
So, hatte doch total übersehen dass du nicht auf localhost verbindest. Da ich hier mit localhost getestet habe, hat sich die Anwendung ziemlich Zeit gegönnt deinen Rechner in meinem Netz zu finden (was letztendlich doch in einem Fehler geendet hat).

Okay, hab ich nun korrigiert. Test läuft erstmal via localhost.
Was mir auch noch gleich aufgefallen ist: Du release't das falsche Objekt auf Clientseite. Du musst das abgeholte Server-Objekt releasen, und nicht die Client-Klasse.
Mit diesen falschen Release geht der Reference-Counter in Simon nicht auf 0 runter und die Session bleibt mit samt der Netzwerkverbindung bestehen. Ergo wird auch kein Unreferenced etc. ausgelöst.
Killt man dann den Client, so wird die Netzwerkverbindung ebenfalls gekillt. Und das kriegt der Server recht zeitnah mit und loggt eine Exception:

14.03.2011 09:23:04 de.root1.simon.Dispatcher exceptionCaught
SCHWERWIEGEND: exception Caught. session=0x00000002. Exception
 java.io.IOException: An existing connection was forcibly closed by the remote host
...
...
...

Ich hab' aber immer noch das gefühl dass das dein Szenario noch nicht exakt trifft. Kannst mal die Fehlermeldung posten?
Ach ja, noch was: Es ist immer eine gute Idee in catch-Blöcken zumindest ein ex.printStackTrace(); hinzuschreiben. Dann weiß0 man wenigstens ob eine Exception auftritt. Den Catch-Block komplett leer lassen ist in den allerseltensten Fällen eine gute Idee.

[update2]

Auch nach intensiver Suche ist mir kein anderer Fehler wie das falsche releasen aufgefallen. Test läuft fehlerfrei.
Hab deinen Source man in ein Netbeans-Projekt gepackt, SIMON dazu getan und das ganze nochmal gezippt und hier angehängt. Mit Netbeans kannst du's direkt öffnen. Mit Eclipse sollte ein Import recht easy sein. Kannst es auch direkt in der Konsole laufen lassen (dank Netbeans' ANT Script).

Was mit design-technisch noch aufgefallen ist:

Dein Server bekommt es nicht unbedingt mit wenn der Client flöten geht. Wird ein Client abgeschossen bevor er seine Subscription entfernen kann, so würde die Liste mit Subscriber auf Serverseite mit jedem abgeschossenen Client anwachsen. Mit deiner aktuellen Implementiertung und Verwendung von "Unreferenced" kommst du hier nicht weiter.

Mein Tipp: Setze das Session-Pattern ein: http://dev.root1.de/projects/simon/wiki/Sample_session_pattern

"Unreferenced" funktioniert nur dann (bzw. macht nur dann Sinn), wenn du Callback-Objekte einsetzt. Ein Callback hast du ja schon: Der Server bekommt ein Callback vom Client. Geht der Server flöten, bekommt der Client das über die unreferenced-Methode des Callback Objekts mit.
Aber du brauchst das genau andersrum: Der Server muss wissen wann ein Client unsachgemäß verschwindet. Ergo: Der Client braucht ein Callback vom Server. Mit dem Session-Pattern-Sample ist das eigentlich kein Hexenwerk. Die Login-Methode kannst du ja anders nennen und umformen. Wichtig ist nur: Der Client erhält ein Callback-Objekt vom Server und dieses Callback-Objekt implementiert "Unreferenced". Dann wird der Server über jedes verschwinden eines Clients informiert.

- Alex

RE: Referenz von Server auf Client löschen - Added by duschata about 1 year ago

Hallo Alex,
danke erst mal. Was ich bestätigen kann ist, dass es mit Revision 538 geht. Vorher hatte ich 1.10 aus dem Dateien-Bereich. Ich schicke dir heute Abend mal die Fehlermeldungen, die bei mir so produziert werden...

Gruß Tom

[update]
Ups, das war jetzt asynchron. Hatte mir gerade die neuste Version aus dem svn gezogen... :-)

[update 1]
ich habe es mit dem Session Pattern schon vor einen paar Tagen ausprobiert. Da bekam ich den gleiche Fehler beim löschen aus der Liste...

RE: Referenz von Server auf Client löschen - Added by duschata about 1 year ago

Hallo Alex,
ich habe jetzt mal so deployed, dass der Fehler produziert wird. Wie gesagt, wenn ich die Version aus dem svn nehme, geht (bisher) alles gut.

Viele Grüße

Tom

RE: Referenz von Server auf Client löschen - Added by achristian about 1 year ago

Alles klar. Ich schau es mir morgen früh gleich an.

Gruß Alex

RE: Referenz von Server auf Client löschen - Added by achristian about 1 year ago

So, hab dein Sample nochmal runtergeladen...

Ich hätte jetzt eine SessionClosed Exception oder sowas erwartet.
Stattdessen krieg ich am Server:

2011-16-15 09:16::59.847 SEVERE  tid=12 de.root1.simon.Dispatcher.exceptionCaught: exception Caught. session=0x00000002. Exception
 org.apache.mina.filter.codec.ProtocolEncoderException: org.apache.mina.core.buffer.BufferDataException: java.io.NotSerializableException: de.root1.simon.SimonProxy
    at org.apache.mina.filter.codec.ProtocolCodecFilter.filterWrite(ProtocolCodecFilter.java:355)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.callPreviousFilterWrite(DefaultIoFilterChain.java:509)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$1400(DefaultIoFilterChain.java:46)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.filterWrite(DefaultIoFilterChain.java:808)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain$TailFilter.filterWrite(DefaultIoFilterChain.java:734)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.callPreviousFilterWrite(DefaultIoFilterChain.java:509)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.fireFilterWrite(DefaultIoFilterChain.java:501)
    at org.apache.mina.core.session.AbstractIoSession.write(AbstractIoSession.java:490)
    at org.apache.mina.core.session.AbstractIoSession.write(AbstractIoSession.java:435)
    at de.root1.simon.Dispatcher.invokeEquals(Dispatcher.java:460)
    at de.root1.simon.SimonProxy.remoteEquals(SimonProxy.java:260)
    at de.root1.simon.SimonProxy.invoke(SimonProxy.java:127)
    at $Proxy2.equals(Unknown Source)
    at java.util.ArrayList.remove(ArrayList.java:423)
    at de.toms_toy.simon.simple.server.RemotePublisherImpl.removeSubscriber(RemotePublisherImpl.java:46)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at de.root1.simon.ProcessMessageRunnable.processInvoke(ProcessMessageRunnable.java:444)
    at de.root1.simon.ProcessMessageRunnable.run(ProcessMessageRunnable.java:113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
...

und am Client:

2011-16-15 09:16::59.857 SEVERE  tid=11 de.root1.simon.Dispatcher.exceptionCaught: exception Caught. session=0x00000002. Exception
 java.lang.NullPointerException
    at de.root1.simon.LookupTable.unreference(LookupTable.java:338)
    at de.root1.simon.Dispatcher.sessionClosed(Dispatcher.java:811)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain$TailFilter.sessionClosed(DefaultIoFilterChain.java:665)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextSessionClosed(DefaultIoFilterChain.java:395)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$900(DefaultIoFilterChain.java:46)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.sessionClosed(DefaultIoFilterChain.java:781)
    at org.apache.mina.filter.codec.ProtocolCodecFilter.sessionClosed(ProtocolCodecFilter.java:387)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextSessionClosed(DefaultIoFilterChain.java:395)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$900(DefaultIoFilterChain.java:46)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.sessionClosed(DefaultIoFilterChain.java:781)
    at org.apache.mina.filter.logging.LoggingFilter.sessionClosed(LoggingFilter.java:211)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextSessionClosed(DefaultIoFilterChain.java:395)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.access$900(DefaultIoFilterChain.java:46)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.sessionClosed(DefaultIoFilterChain.java:781)
    at org.apache.mina.core.filterchain.IoFilterAdapter.sessionClosed(IoFilterAdapter.java:95)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextSessionClosed(DefaultIoFilterChain.java:395)
    at org.apache.mina.core.filterchain.DefaultIoFilterChain.fireSessionClosed(DefaultIoFilterChain.java:388)
    at org.apache.mina.core.service.IoServiceListenerSupport.fireSessionDestroyed(IoServiceListenerSupport.java:244)
    at org.apache.mina.core.polling.AbstractPollingIoProcessor.removeNow(AbstractPollingIoProcessor.java:580)
    at org.apache.mina.core.polling.AbstractPollingIoProcessor.removeSessions(AbstractPollingIoProcessor.java:540)
    at org.apache.mina.core.polling.AbstractPollingIoProcessor.access$600(AbstractPollingIoProcessor.java:67)
    at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:1087)
    at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)

... gefolgt von jede Menge logoutput der zeigt, dass die Antwort vom Server noch nicht da ist ....

Trifft das deine Beobachtung?

RE: Referenz von Server auf Client löschen - Added by duschata about 1 year ago

Hallo Alex,
ja, das sind die gleichen Fehlermeldungen, die ich auch bekomme. Merkwürdiger Weise gibt es ein ähnliches Verhalten, ohne dass der Subscriber removed wird. Ein einfacher Zugriff auf die Liste der Subscriber über subscribers.indexOf(remoteSubscriber) reicht schon aus.
Hoffe das hilft weiter, an den Quellen habe ich quasi nichts geändert (nur Verbindungshost = localhost, bzw. die Exceptions einzeln abgefangen +stacktrace eingefügt).

Viele Grüße

Tom

RE: Referenz von Server auf Client löschen - Added by achristian about 1 year ago

Alles klar. Dann kann ich der Sache nun auf den Grund gehen. Denke in 1.1.0 ist noch ein Fehler drin den ich in 1.2.0-SNAPSHOT schon gelöst hab.

RE: Referenz von Server auf Client löschen - Added by achristian about 1 year ago

Fehler gefunden...

Lass dein "RemoteSubscriber" Interface von "Serializeable" erben. Dann geht's auch mit 1.1.x

Muss nochmal genauer rein schauen und den Fix auch for 1.1.x anbieten.

- Alex

RE: Referenz von Server auf Client löschen - Added by achristian about 1 year ago

So, hab nochmal genauer rein geschaut...

Tatsächlich ist das ein Bug den ich bereits für 1.1.1-SNAPSHOT und 1.2.0-SNAPSHOT behoben habe:

http://dev.root1.de/projects/simon/repository/revisions/536

Da für 1.1.1 keine weiteren Fehler aufgetaucht sind, werde ich 1.1.1 wohl releasen und bei "Files" entsprechend zum Download anbieten. erledigt

Also entweder arbeitest du dann weiter mit dem 1.2.0-SNAPSHOT (aktuellen Snapshot gibts immer hier: http://nexus.root1.de/content/repositories/snapshots/de/root1/simon/1.2.0-SNAPSHOT/), oder du nimmst das 1.1.1. Release.

Gruß
Alex

RE: Referenz von Server auf Client löschen - Added by duschata about 1 year ago

Hallo Alex,
dann erst mal vielen Dank für deine Unterstützung. Ich werde wahrscheinlich im übernächsten Java-Aktuell einen Artikel in Form eines Workshops über RMI & Co schreiben und SIMON dabei natürlich lobend erwähnen. Ich lasse dir aber den Artikel noch vorher zukommen, damit du drüber schauen kannst. Falls du willst kannst du auch selbst etwas veröffentlichen, wir freuen uns immer über Beiträge.

http://www.ijug.eu/index.php?option=com_content&view=article&id=10

Viele Grüße Tom

[update 0]

Bei dem Snapshot 1.2.0 vom 2.1.2011 bekomme ich den gleichen Fehler. Auch RemoteSubsriber-(Implementierung) implements Serializable hilft bei mir nicht weiter. Revision 538 geht bei mir nach wie vor.

RE: Referenz von Server auf Client löschen - Added by achristian about 1 year ago

Hallo Alex,
dann erst mal vielen Dank für deine Unterstützung. Ich werde wahrscheinlich im übernächsten Java-Aktuell einen Artikel in Form eines Workshops über RMI & Co schreiben und SIMON dabei natürlich lobend erwähnen. Ich lasse dir aber den Artikel noch vorher zukommen, damit du drüber schauen kannst. Falls du willst kannst du auch selbst etwas veröffentlichen, wir freuen uns immer über Beiträge.

Klingt gut. Kannst den Artikel an meine eMailadresse schicken: alex[at]root1.de
Ich schau dann mal drüber. Wenn du das Thema SIMON RPC dann schon komplett erschlagen hast, werd' ich mal schauen ob ich noch ein anderes Thema parat habe, zu dem ich einen Artikel schreiben könnte.

[update 0]

Bei dem Snapshot 1.2.0 vom 2.1.2011 bekomme ich den gleichen Fehler. Auch RemoteSubsriber-(Implementierung) implements Serializable hilft bei mir nicht weiter. Revision 538 geht bei mir nach wie vor.

Hmm, bist du dir sicher dass die die richtige Version im Classpath hast? Konnte den Fehler mit 1.2.0-SNAPSHOT, 1.1.1-SNAPSHOT und jetzt final mit dem 1.1.1 Release nicht mehr reproduzieren, wohingegen 1.1.0 den Fehler 1a reproduziert.
Wenn du die Classpath-Sache ausschließen kannst: Bist du sicher dass der Exception-Stacktrace der gleiche ist?

Gruß
Alex

[update]
Wenn rev538 noch funktioniert, dann sollten die nachfolgenden Versionen auch noch funktionieren.
Hier der Diff zwischen 538 und 540: http://dev.root1.de/projects/simon/repository/diff?rev=540&rev_to=538

Aktuell ist 541. Von 540 zu 541 gabs nur eine Änderung: /branches/1.1.x wurde zu /tags/1.1.1 getagged.

(1-13/13)