SIMON - Simple Invocation of Methods Over Network

Kleiner Tipp
wenn du nach dem Kommentar von Simon.createRegistry(int port) “Creates a registry (listening on all interfaces) with the scope of a global LookupTable” einen Punkt machst nimmt er das Beispiel nicht mit in die Übersicht sondern erst unten wenn man auf die Methode geklickt hat.

Hehe, ja, das ist mir vorhin auch aufgefallen.

Anfangs gab es nur die globale Registry und man hatte gar keinen direkten Zugriff auf das Registry Objekt.

Aber dann kam der Request, in einer Java Anwendung mehrere Server laufen zu haben, auf unterschiedlichen Ports. Deshalb die neue Methode die die Registry zurück gibt. Damit kann man dann an den entsprechenden Server seine Objekte binden.

Bei der „globalen“ Registry kann man sich das Registry-Objekt sparen und direkt Simon.bind() aufrufen, denn Simon weiß ja welche Registry gemeint ist.

Allerdings hast du recht: Der Konsistenz wegen sollte auch hier das direkte aufrufen von Bind an der globalen Registry möglich sein.

Und in dem zusammenhang hab ich auch das Mögliche „Problem“ entdeckt das ich noch verifizieren und ggf. fixen muss.

Wenn dir irgendwas „seltsames“ auffällt: Reporten.

Und wenn etwas „ganz“ krass läuft: Wie in den Beispielprojekten im SVN den Debug-Output einschalten …

  • Alex

Jo du wirst so schnell nicht schlafen können :smiley:
Ich bin gerade dabei alles von RMI auf SIMON umzubauen, weil ich denke/hoffe dass es einfacher ist als wenn ich mir da jetzt mit Streams was zusammen bastelle.

Was mir immer wieder auffällt beim umstellen des Codes, vielleicht wäre es brauchbar wenn es eine Exception oder so gibt, sobald man eine Anwendung mit einem Namen binden will, der schon vergeben ist. Wie es bei RMI auch ist (mit rebind gibts dann keine).

Da ist was dran. Sollte auch einfach zu fixen sein. Werd ich auch gleich in die JUnit Tests einbauen.

Btw: das doppelte erstellen einer individuellen Registry ist jetzt mit einer korrekten exception erledigt. Fangen muss man die „Superexception“ IOException. Genauer wird dann eine BindException geworfen. Ist natürlich auch praktisch wenn der Port von einer anderen Anwendung schon belegt ist.

  • Alex

fixed: mehrfaches binden ein und desselben objekts wirft eine NameBindingException
changed: LookupFailedException ist ab sofort eine compile-time-exception
added: rebind() methode in der Registry Klasse

aber jetzt ab in den feierabend …commite das zeug dann morgen früh.

Wie ist denn das eigentlich, räumt dein SIMON die Registry auch mal auf wenn einige nicht mehr erreichbar sind oder bleiben sie wie bei RMI ewig enthalten?

Was verstehst du unter aufräumen? Callbackobjekte werden von Simon freigegeben wenn die Verbindung zum Client beendet/unterbrochen wird.

PLötzlich verschwundene Clients werden ebenfalls entdeckt und man kann sogar drauf reagieren. Das “entdecken” basiert auf dem Ping-Pong prinzip. Client und Server tauschen selbstständig in einem festgelegten Intervall (kann man ändern) Mini-Nachrichten aus um zu sehen ob der andere noch da ist. Dadurch lässt sich auch eine RoundTripTime errechnen, welche ich für das Statistik-Feature benutze (ist noch nicht fertig).

  • Alex

Ah genau das meinte ich, weil bei RMI ist mir aufgefallen das Server die nicht mehr da sind (einfach abgeschaltet ohne abzumelden) ewig enthalten sind

Ich hab gerade noch eine etwas blöde Sache gefunden. Ich hab mir eine individuelle Registry erstellt, jetzt muss ich die aber überall durchreichen/erreichbar machen weil mit Simon.bind wird gesagt das es keine globale Registry gibt.

Ah genau das meinte ich, weil bei RMI ist mir aufgefallen das Server die nicht mehr da sind (einfach abgeschaltet ohne abzumelden) ewig enthalten sind

?? Ich glaub du verwechselst was: Wenn ein Server stirbt, stirbt dessen Registry mit. Weil Server==Registry.

Was ich gemein habe ist:

Du hast einen Server. Ein Client ruft dort beispielsweise login() auf und übergibt ein Callback-Objekt. Mit dem Objekt kann der Server nun Methoden beinm Client aufrufen. Stirbt jetzt ein Client unverhofft weg, erkennt der Server das und entfernt das Callback-Objekt aus seiner Liste mit bekannten Remote Objekten. Damit deine Serverlogik auch noch mitbekommt wann soetwas passiert, gibt es das SimonUnreferenced Interface. Wird das in dem Callback-Objekt implementiert, ruft der Server in dem Moment wo er das Objekt abräumt, vorher noch diese Methode auf. Da drin kannst du dann alles weitere veranlassen um aus deiner Serverlogik den Client zu streichen oder andere Clients zu benachrichtigen oder sonstwas.

Ich hab gerade noch eine etwas blöde Sache gefunden. Ich hab mir eine individuelle Registry erstellt, jetzt muss ich die aber überall durchreichen/erreichbar machen weil mit Simon.bind wird gesagt das es keine globale Registry gibt.

Logisch. Das ist eben das „Problem“ wenn man mehrere Registrys und somit mehrere Server in einer einzigen Java-Anwendung haben kann.

Es gibt jetzt 2 Möglichkeiten: Du baust dir in der Stelle deines Codes wo die Registry erzeugt wird eine statische „bind“ Methode ein die alle bind() aufrufe an die erstellte Registry weiterleitet,

oder

Ich bastle eine weitere createRegistry Methode, die eine globalw Registry erzeugt, und es erlaubt das Netzwerkinterface in Form einer IP mit anzugeben (macht ja durchaus Sinn).

Nur würde das nichts helfen wenn du mehr als eine Registry erzeugst… Da müsstest du wieder selbst mit statischen Methoden (oder eben mit durchreichen) ein Workaround basteln.

  • Alex

Ich steh gerade vor einem kleinen Problem, ich will meinen Server abschalten und “fahre” alle Sachen runter. Nur irgendwie bleiben immer 3 SIMON Threads aktiv. Unabhängig davon ob ich Simon.stopRegistry() oder Simon.shutdownRegistry() aufrufe.
Dadurch beendet sich dann mein Programm nicht.
Die Threads sind Simon.Dispatcher, Simon._Dispatcer.DGC und Simon._Dispatcher.DGC.PingWorkerPool.Nr#1

Außerdem gibt es eine Exception nach einigen Sekunden

27.08.2008 14:22:17 de.root1.simon.Dispatcher run
SCHWERWIEGEND: General Exception: e=java.util.concurrent.RejectedExecutionException msg=null
java.util.concurrent.RejectedExecutionException
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1477)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:384)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:856)
	at de.root1.simon.Dispatcher.handleRead(Dispatcher.java:305)
	at de.root1.simon.Dispatcher.run(Dispatcher.java:205)
	at java.lang.Thread.run(Thread.java:595)

[QUOTE=EagleEye]Ich steh gerade vor einem kleinen Problem, ich will meinen Server abschalten und “fahre” alle Sachen runter. Nur irgendwie bleiben immer 3 SIMON Threads aktiv. Unabhängig davon ob ich Simon.stopRegistry() oder Simon.shutdownRegistry() aufrufe.
Dadurch beendet sich dann mein Programm nicht.
Die Threads sind Simon.Dispatcher, Simon._Dispatcer.DGC und Simon._Dispatcher.DGC.PingWorkerPool.Nr#1

Außerdem gibt es eine Exception nach einigen Sekunden

27.08.2008 14:22:17 de.root1.simon.Dispatcher run
SCHWERWIEGEND: General Exception: e=java.util.concurrent.RejectedExecutionException msg=null
java.util.concurrent.RejectedExecutionException
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1477)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:384)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:856)
	at de.root1.simon.Dispatcher.handleRead(Dispatcher.java:305)
	at de.root1.simon.Dispatcher.run(Dispatcher.java:205)
	at java.lang.Thread.run(Thread.java:595)

[/QUOTE]

Hmm, strange

Da brauch ich den Debug-Output.
Schau mal im SVN, da gibts parallel zu SIMON im Trunk noch “simon_testserver”. Genau wie dort, erstellst du dir im Serverhauptverzeichnis ein Ordner “log” und “config”. In “config” legst du eine File namens “simon_logging.properties” an, die du mit folgendem Text füllst:


############################################################
#  	Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler 
# classes.  These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
#handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers.  For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= ALL

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = log/simon_debug.log
#java.util.logging.FileHandler.limit = 5000000
java.util.logging.FileHandler.count = 2
java.util.logging.FileHandler.formatter = de.root1.simon.utils.ConsoleLogFormatter
java.util.logging.FileHandler.level = FINEST



# Limit the message that are printed on the console to INFO and above.

# Diese Funktion hat gegenüber den Facilities vorrang bzgl des Loglevels. 
# Feiner als hier eingestellt lässt sich dann in der Console nicht loggen
java.util.logging.ConsoleHandler.level = FINEST
java.util.logging.ConsoleHandler.formatter = de.root1.simon.utils.ConsoleLogFormatter


############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################


# Benutzerdefinierte Log-Levels für die einzelnen Packages

# Stelle das Package auf das Level "FINEST"
de.root1.simon.level = FINEST


# INFO:
# The Level class defines seven levels of logging enlightenment :
#
#   * FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE
#   * ALL and OFF are defined values as well

Im Servercode solltest du dann VOR dem erzeugen der Registry folgende Zeile aufrufen:

Utils.DEBUG = true;

Das bedarf dann logischerweise auch einem

import de.root1.simon.utils.Utils;

Dann reproduzierst du das Problem nochmal und lässt mir den Inhalt des “log” Verzeichnisses zukommen (evtl zippen, die Files können je nach Aufwand des Servers ziemlich groß werden).

Zum Problem mit den Threads: Ich schau mal woran das liegt.

  • Alex

[update]

bzgl. den Threads: siehe: http://root1.de/mantis/view.php?id=20

–> fixed

So hier ist die Logdatei, danach war schluss weil ich es dann hart gekillt hab

Das ging ja fix. Werd’s mir gleich anschauen. Hast du das update meines letzten post gesehen?

Gruß
Alex

[update]
Kam die Exception in Verbindung mit den noch offenen Threads? Weil, ist schon “seltsam”:

Der fehler trat da auf, wo der Dispatcher versucht das lesen des nächsten Pakets an einen Thread zu übergeben, welcher dann im ThreadPool läuft. Und das hinzufügen in diesen Threadpool ist fehlgeschlagen.

Ah, das könnte sich überschnitten haben. Werde da einfach das ganze mit einer Abfrage “umgehen”.

  • Alex

[update]

sollte auch behiben sein: http://root1.de/mantis/view.php?id=21

[update2]

bzgl. dem erstellen einer globalen registry die zu einer speziellen IP gebunden ist: http://root1.de/mantis/view.php?id=22

ne das Update hast du gemacht da hatte ich schon gepostet :wink:
Ja die Exception kommt nur wenn ich den Server ins Bett schicken will :smiley:

Aber nicht das du denkst du kannst dich ausruhen :wink:
Ich hab schon die nächste komische Sache :smiley:

Mein Client verbindet sich zum Watchdog und dann starte ich den Watchdog neu. Der Client merkt das, weil ich hab einen Timer laufen der alle paar Sekunden einen ping (Methode) zum Server schickt und wenn das nicht klappt versucht er sich neu zu verbinden. Leider klappt das nicht weil mir dann immer Exceptions um die Ohren fliegen.

de.root1.simon.exceptions.ConnectionException: Connection is broken!
	at de.root1.simon.Dispatcher.cancelWaitingMonitors(Dispatcher.java:886)
	at de.root1.simon.Dispatcher.processPendingSelectorChanges(Dispatcher.java:273)
	at de.root1.simon.Dispatcher.run(Dispatcher.java:160)
	at java.lang.Thread.run(Thread.java:595)

Für den Fall das du sie brauchst ist wieder ne Debugausgabe dran (Anfang Server ist da, Server ist weg und am Ende ist er wieder da)

Ich kümmer mich gleich wieder drum…

Bzgl. Issue #22 werde ich Methoden ein wenig umbennenen müssen…

Grund:

Es gibt 2 Arten von Registrys:

  1. individuelle
  2. Globale

Von ersteren kann es mehr als eine geben, deshalb gibts hier auch einen Rückgabewert in Form der erzeugten Registry.

Vom zweiten gibts exakt eine.

Bisher gibts dafür folgende Methoden:

zu 1)
public static Registry createRegistry(InetAddress address, int port) throws IOException

zu 2)
public static void createRegistry(int port) throws UnknownHostException, IllegalStateException, IOException{

Zu 2) müsste jetzt noch eine Methode kommen, wo man eine InetAddress angeben kann. Aber das beisst sich mit der Methodensignatur von 1).

Jetzt ist’s natürlich doof, dass schon einige SIMON verwenden, und das ganze “inkompatibel” wird wenn ich Methodennamen einfach umbenenne.

Würde jetzt die alten Methodennamen gerne mit “@deprecated” versehen und auf neue Methodennamen mappen.

Nur, wie halte ich die zwei Registryarten jetzt ausseinander?

createGlobalRegistry und createRegistry?

“Global” gefüllt mir irgendwie nicht. Das passt nicht so ganz. Fällt dir was schickes ein?

  • Alex

wie wäre es mit einer einfachen setGlobalRegistry Methode?
damit umgehst du das alles

Also,

Leider klappt das nicht weil mir dann immer Exceptions um die Ohren fliegen.

Das ist doch „normal“. Was erwartest du denn wenn du versucht auf dem nicht vorhandenen Server etwas aufzurufen? Jede Remote-Methode muss ja „@throws SimonRemoteException“ drin haben. Und die „ConnectionException“ erbt von dieser „Überexception“ und wird geworfen wenn der Server nicht mehr da ist.

Das einzigste was ich machen könnte (weiß grad nicht wie das bei RMI ist), ich könnte SimonRemoteException nicht mehr als „Runtimeexception“ formulieren, sondern als Exceptiopn die man schon zur Compilezeit explizit fangen muss (also so, dass sich Eclipse solange beschwert bis ein try/catch drum rum ist der diese SimonRemoteException fängt).

  • Alex

[update]

wie wäre es mit einer einfachen setGlobalRegistry Methode? damit umgehst du das alles

Hmm, die globale registry hab ich eigentlich deshlab, damit man sich die „arbeit“ mit der individuellen Registry sparen kann.Mal schauen, vielleicht kann ich da was vom RMI abschauen…