Allgemeine (unspezifische) equals-Frage

Added by JavaKru09 over 1 year ago

Hallo Alexander,

ich habe eine (vorerst ziemlich ungenaue) Frage in der Richtung, wie SIMON die equals-Methode bei Objekten handhabt, die über SimonInterfaces kommunizieren.

Szenario: Ein Client-Objekt ruft eine Methode des Servers auf und übergibt dabei sich selbst beziehungsweise sein SIMON-Interface als Parameter. Der Server auf der anderen Seite soll nun entscheiden, ob das aufrufende Objekt das ist, von dem er einen Aufruf erwartet.

Ich wollte zu diesem Zweck auf der Seite des Servers ein equals auf das Client-Interface loslassen, das von SimonRemote abgeleitet ist.

Meine Frage: Verwaltet SIMON die Interfaces im Hintergrund so, dass diese Art des equals klappen kann? Und wenn das so läuft, passiert das dann vollständig auf der Seite des Servers und innerhalb seiner Registry oder gibt es darüber hinaus noch einen Rückruf beim Client über das Netz?

class Server implements ServerInterface {

    // irgendwo vorher gesetzt
    ClientInterface erwartet = bla;

    public do(ClientInterface client) {
        if (client.equals(erwartet)) {
            // weiter geht's ...
        }
    }

}
Viele Grüße von

Christian


Replies (12)

RE: Allgemeine (unspezifische) equals-Frage - Added by achristian over 1 year ago

Meine Frage: Verwaltet SIMON die Interfaces im Hintergrund so, dass diese Art des equals klappen kann?

Sollte klappen, ja.

Und wenn das so läuft, passiert das dann vollständig auf der Seite des Servers und innerhalb seiner Registry oder gibt es darüber hinaus noch einen Rückruf beim Client über das Netz?

Da es sich hier im Objekte handelt, die nicht in der VM des Servers leben, sondern in der VM des/der Clients, hat ein Auruf von client.equals(...) einen Remotemethodenaufruf auf Clientseite zur Folge.
Hab eben festgestellt dass ich hierfür offenbar noch keinen JUnit-Test habe. Bin gerade dabei das nachzuholen und hab da auch gleich noch einen Bug entdeckt. Werde das schnellstmöglich fixen.

RE: Allgemeine (unspezifische) equals-Frage - Added by JavaKru09 over 1 year ago

Da es sich hier im Objekte handelt, die nicht in der VM des Servers leben, sondern in der VM des/der Clients, hat ein Auruf von client.equals(...) einen Remotemethodenaufruf auf Clientseite zur Folge.

Gibt es - auf der Seite der Server-VM - einen Weg, client.equals(...) und den resultierenden Rückruf zu umgehen?

Vielleicht kann man die Remote-Adresse und den Remote-Port eines Interfaces feststellen und sich dann das client.equals(...) sparen, wenn man von vornherein weiß, welcher Client (und damit: von wo) jetzt einen Aufruf starten sollte ...

RE: Allgemeine (unspezifische) equals-Frage - Added by achristian over 1 year ago

Ja, bin da gerade selbst am überlegen welche "equals" Strategie die beste ist.

Momentan basiert das (verbuggte) equals darauf, dass

  1. der Equals-Aufruf via Remote erfolgt
  2. im Endeffekt die String-Repräsentation beider Objekte verglichen wird

Mir ist klar dass 2) nicht der "optimale" Weg ist. Aber ein Remote-Objekt ist in erster Linie ein Proxy-Objekt (im Sinne von Reflection). Und da macht ein normaler equals-Vergleich wenig Sinn.
Die String-Repräsentation beinhaltet alle wichtigen Daten wie RemoteObjektName, Session-Informationen etc. Von daher hat das mit dem String soweit schon gepasst.

Allerdings zweifle ich gerade an dieser Implementierung. So langsam aber sicher 'krieg ich einen Knoten in meine Hirnwindungen ;-)

Muss das ganze mal sauber aufdröseln. Am besten, imdem ich hier meine geistlichen Ergüsse niederschreibe (das funktioniert oft am besten).

Okay, fangen wir also an:

Folgende Equals-Fälle gibt es:

  1. Client holt sich ein Remote-Objekt vom Server und will dieses RemoteObjekt mit "irgendwas" vergleichen: remoteObject.equals(irgendwasObjekt)
  2. Client holt sich ein Remote-Objekt vom Server und will dieses RemoteObjekt mit einem RemoteObjekt vergleichen: remoteObject.equals(anderesRemoteObjekt)
  3. Client hat ein Objekt, und will dieses mit einem RemoteObjekt das er vom Server hat vergleichen: irgendwasObjekt.equals(remoteObjekt)
  4. Client hat ein anderes RemoteObjekt, und will dieses mit einem RemoteObjekt das er vom Server hat vergleichen: anderesRemoteObjekt.equals(remoteObjekt)
  5. Server bekommt ein Callback vom Client und will dieses CallbackObjekt mit "irgendwas" vergleichen: clientCallback.equals(irgendwasObjekt)
  6. Server bekommt ein Callback vom Client und will dieses CallbackObjekt mit einem anderen CallbackObjekt vergleichen: clientCallback.equals(anderesCallbackObjekt)
  7. Server hat ein Objekt, und will dieses mit einem CallbackObjekt das er vom Client hat vergleichen: irgendwasObjekt.equals(clientCallback)
  8. Server hat ein anderes CallbackObjekt, und will dieses mit einem CallbackObjekt das er vom Client hat vergleichen: anderesCallbackObjekt.equals(clientCallback)

Dabei gilt es drei Arten zu unterscheiden:

  1. lokaler Vergleich mit Remoteobjekten: irgendwasObjekt.equals(remoteObjekt) bzw. irgendwasObjekt.equals(clientCallback)
  2. remote Vergleich mit Nicht-RemoteObjekten: remoteObject.equals(irgendwasObjekt) bzw. clientCallback.equals(irgendwasObjekt)
  3. Vergleich RemoteObjekt mit RemoteObjekt: remoteObject.equals(anderesRemoteObjekt)

Gegenüberstellung der Vergleichsarten

Art 1)

Hier wird die equals() Methode eines beliebigen Objekts benutzt. Wir gehen mal davon aus, dass es ein lokales POJO, und kein RemoteObjekt ist.
Deshalb wird hier wird lokal vergleichen. Kein Remote-Call notwendig. Die Implemenierung des Vergleichs hängt vom POJO ab.

Der Vergleich wird in den allermeisten Fällen wohl "false" ergeben. Ursache ist das Argument, welches in Simon-Proxy-Objekt ist und eigentlich nur zum delegieren der Aufrufe in die andere JVM dient.
Es können also weder Instanzvariablen, noch die Klasse selbst "anständig" verglichen werden. Die einzige Möglichkeit wäre hier die Methoden des RemoteObjekts zu Hilfe zu nehmen um somit Vergleichsparameter zu bekommen.
In den meisten Fällen wird es aber wohl wenig Sinn machen zwei komplett verschiedene Klassen miteinander zu vergleichen. Ergo: Meist "false". In Spezialfällen hier und da "true".

Art 2)

Hier wird, um den Vergleich durchzuführen, der Vergleich mittels SIMON zur anderen JVM delegiert. Wir gehen mal davon aus, dass das Argument in diesem Fall ein lokales POJO ist.

Man kann in diesem Fall wohl wie bei 1) von einem "false" im Standardfall ausgehen. Denn auch hier werden komplett unterschiedliche Klassen miteinander verglichen. Man müsste auch hier die equals-Methode speziell darauf auslegen solche Vergleiche anzustellen.

Art 3)

Hier wird, wie in 2) auch, der Vergleichs zur anderen JVM delegiert. Das Argument ist in erster Linie ein Simon-Proxy-Objekt.
Die Frage ist hier: Auf welcher Basis stellt man den Vergleich an? Über "toString()" erhält man einen "unique String" der das RemoteObjekt eindeutig identifiziert. Der String sieht so aus:

"[Proxy=" + remoteObjectName
          + "|invocationHandler=" + super.toString()
          + "|remote=" + dispatcher.invokeToString(session, remoteObjectName)
          + "|interfaces=" + Arrays.toString(remoteInterfaces)
          + "|sessionId=" + Utils.longToHexString(session.getId())
          + "]";

Zwei Remote-Objekte die identisch sind, sollten innerhalb einer JVM also den gleichen String erzeugen.

So, jetzt stellt sich die Frage:

Wie bringt man alle Szenarien auf einen Nenner? Bisher habe ich versucht das auf einen Nenner zu bringen. Aber ich bin nun sicher, dass das nicht geht.

Alles in allem kann man das eigentlich nur so lösen (wow, neue erkenntnis...):

Für Art 1): Keine Implementierung notwendig. Liegt an der implementierung von "equals" des POJOs selbst
Für Art 2): Hier muss das Argument einfach nur serialisierbar sein. In der anderen JVM wird dann die Equals-Methode des RmoteObjekts mit dem deserialisierten Argument aufgerufen
Für Art 3): Hier sind eigentlich alle Informationen lokal vorhanden. Das Simon-Proxy-Objekt, welches das Remote-Objekt auf Clientseite, bzw. das Callback-Objekt auf Serverseite darstellt, kann selbst prüfen ob das Argument ein SimonProxy ist, und wenn, dann gleich lokal den String-Vergleich starten und das Ergebnis zurückliefern.

Jo. Scheint so als hätte ich das "Problem" nun hinreichend genau analysiert. Gegenvorschläge?! Fragen?

Gruß
Alex

RE: Allgemeine (unspezifische) equals-Frage - Added by achristian over 1 year ago

Hab mal nen Bug eingetütet:

http://dev.root1.de/issues/89

Arbeite auch schon an der Lösung.

RE: Allgemeine (unspezifische) equals-Frage - Added by achristian over 1 year ago

Mir kam heute auf der fahrt ins Büro noch ein Gedanke:

Für Art 3): Hier sind eigentlich alle Informationen lokal vorhanden. Das Simon-Proxy-Objekt, welches das Remote-Objekt auf Clientseite, bzw. das Callback-Objekt auf Serverseite darstellt, kann selbst prüfen ob das Argument ein SimonProxy ist, und wenn, dann gleich lokal den String-Vergleich starten und das Ergebnis zurückliefern.

Das ist "falsch". Denn auch in diesem Fall wäre es ein Fehler die eventuell überschriebene remote-Equals-Methode NICHT aufzurufen und das ganze nur lokal abzufahren.
Muss da also nochmal ein bisschen drüber nachdenken wie ich das am besten realisiere/vergleiche

- Alex

RE: Allgemeine (unspezifische) equals-Frage - Added by achristian over 1 year ago

So, solved. Da ich die eventuell implementierte/überschriebene equals-Methode nicht übergehen kann/darf, wird nun jeder "equals(..)" Aufruf zur JVM transportiert in der das Objekt tatsächlich lebt.

Hab auch gleich nen Junit-Test dafür eingerichtet.

RE: Allgemeine (unspezifische) equals-Frage - Added by JavaKru09 over 1 year ago

Hallo Alexander,

Deine Reaktionszeiten sind wirklich beachtlich! Trotzdem bin ich mir für den folgenden Fall nicht sicher ...

5. Server bekommt ein Callback vom Client und will dieses CallbackObjekt mit "irgendwas" vergleichen: clientCallback.equals(irgendwasObjekt)

Mir ging es zunächst nur darum, dass der aufgerufene Server eben möglichst kein Equals ausführt, sondern nur an Hand der übergebenen Referenz prüft, ob diese einem bestimmten, beim Server unter diesem oder jenem Namen gespeicherten Interface gleicht.

Das wird sicher auch dadurch machbar sein, dass man ein equals() aufruft, was dann aber immer irgendwelche Objekte beziehungsweise Objektbäume hin- und herschieben muss. Dieses Verschicken kann sich ja über sehr viele (möglicherweise auch nicht-serialisierbare) Objekte erstrecken.

Ich suche eher nach einer Möglichkeit, das zu umgehen und folgendes zu implementieren:

class Server implements ServerInterface {

    // irgendwo vorher gesetzt
    ClientInterface erwarteterAufrufer = ... ;
    Registry simonRegistry = ... ;

    public do(ClientInterface aufrufer) {
        if (simonRegistry.denoteSameClient(aufrufer, erwarteterAufrufer )) {
            // weiter geht's ...
        }
    }

}

Damit will ich umgehen, dass SIMON anfängt, komplette Objektbäume durch das Netz zu blasen und nur kurz nachsieht, ob die beiden Interfaces mit dem selben Client-Objekt verbunden sind.

Gibt es da auch ohne equals() auf der Server-Seite eine Möglichkeit?

Christian

RE: Allgemeine (unspezifische) equals-Frage - Added by achristian over 1 year ago

Hallo Christian,

ich denke ich kann deine Bedenken etwas mildern:

Aktuell sieht es so aus, dass wenn man auf Serverseite ein Callback mit einem anderen, potentiell gleichen Callback mittels equals() vergleicht, das System erkennt: Aha, das Argument des Equals-Aufrufs ist ein Proxy-Objekt. In diesem Fall passiert nun folgendes: Das Proxy-Objekt wird nach seinem RemoteObjekt-Namen gefragt. Der Name, sowie die zugehörige Session werden in ein temporäres Container-Objekt gesteckt, welches dann serialisiert und zur gegenüberliegenden JVM geschickt. Dort entdeckt das System wieder: Oh, da kommt eine Equals-Anfrage und das Argument ist ein temporäres Container-Objekt. Ich muss also den Namen im Container-Objekt nehmen, und schauen welches Objekt dazu passt. Und dann wird equals() mit diesem dazu passenden Objekt aufgerufen.

Deine Angst, dass ganze Objektbäume verschickt werden, trifft also im Fall von "ich will remote-objekt mit remote-objekt vergleichen" nicht zu. Denn tatsächlich gibt es auf Serverseite dieses Objekt nicht. Es gibt nur eine Proxy-Instanz dieses Objekts (Stichwort InvocationHandler gepaar mit einem Java Proxy), welche mit Informationen wie dieses Objekt zu erreichen ist angereichert wurde.

Wenn du tatsächlich als Argument für equals ein "irgendwas, aber kein remote-objekt" vorsiehst: Nur dann wird das Objekt inklusive aller Felder und Super-Klassen und deren Felder serialisiert.

Aber so wie du schreibst geht es dir nicht explizit um "equals()" sondern: "Ist dieses Remote-Objekt mit diesem anderen Remote-Objekt identisch, sprich: Ist es ein und dasselbe Remote-Objekt".

Nun, das kann man auf zwei Arten lösen: Die Objekte haben getter() deren Rückgabewerte das Callback-Objekt identifizieren. Damit kannst du gleich zum Anfang beispielsweise eine HashMap füllen. Anhand dieser kannst du dann prima Vergleiche anstellen.

Aber gut, ich kann mir denken dass das nicht deinen Vorstellungen entspricht.
Hab deinen Vorschlag von wegen "denoteSameClient" aufgegriffen und einen Feature-Request daraus gemacht: http://dev.root1.de/issues/90

Hab das ganze auch schonmal experimentell ausprobiert: Funktioniert. Bis jetzt lebt die Methode in der Klasse "Simon" als static-helper Methode. Bin aber noch nicht sicher ob die Methode nicht besser in der Registry-Klasse aufgehoben ist.

Vermutlich ist die SIMON Klasse wirklich besser als die Registry klasse. Denn so kann auch ein Client, der eventuell mehrere Lookups auf eventuell verschiedene Server macht prüfen ob die ihm vorhandenen RemoteObjekte gleicher Herkunft sind.

- Alex

RE: Allgemeine (unspezifische) equals-Frage - Added by achristian about 1 year ago

Hast du die neue Methode schon ausprobiert? Funktioniert's wie gewünscht?

RE: Allgemeine (unspezifische) equals-Frage - Added by JavaKru09 about 1 year ago

Moin Alex,

Du bist wirklich sehr schnell, hast Du etwa schon eine neue Methode implementiert?

Zu meiner Schande muss ich gestehen: Ich habe das noch gar nicht weiter verfolgt. Das Projekt, in dessen Kontext ich auf diese Frage gestoßen bin, verwendet im Augenblick noch Random-Longs, um Clients zu identifiziere und einzelne Züge den Clients zuzuordnen.

Ich werde diese unelegante Form mittelbar wieder da rausnehmen, werkele zur Zeit aber noch an anderen Problemen rum. Mittelfristig werde ich diese Baustelle "Remote-Objekte an ihren Interfaces erkennen" weiter bearbeiten.

Sobald ich da was mache, komme ich mit Feedback.

Viele Grüße von

Christian

RE: Allgemeine (unspezifische) equals-Frage - Added by achristian 8 months ago

Nach etwas über einem halben Jahr:

Gibts schon Neuigkeiten?

Gruß
Alex

(1-12/12)