# RMI Angemeldetes Objekt nicht aktuell?



## Kai2 (29. Jun 2011)

Hallo,
ich bin durch zufall auf dieses Forum gestoßen und hoffe, ihr könnt mir helfen:
Ich bin gerade dabei eine Anwendung auf zwei Rechnern mittels RMI kommunizieren zu lassen. 
Auf einem Rechner melde ich ein Objekt a in der Registry an (wobei a INstanz der Klasse A). Dieses Objekt anhält eine Hashmap der Form (ID, b) wobei b eine Instand der Klasse B ist.
Sowohl die Interfaces von a und b sind von Remote abgeleitet, da ich auf dem andern Rechner keine Kopien von b haben möchte, sondern per Fernzugriff drauf zugreifen mag. 
Die Implementierungen erweitern UnicastRemoteObject und implementieren die Schnittstellen. 

So weit so gut.

Wenn ich mir nun von dem andern Rechner das Objekt a per Fernzugriff hole, sind in diesem alle bis dahin in der Hashmap gespeicherten Objekte b' vorhanden. Füge ich nun aber in die Hashmap neue Elemente während der Laufzeit ein und greife danach per Fernzugriff auf mein zuvor referenziertes Objekt a zu - so fehlen diesem diese Objekte in der Hashmap! 

Irgendwie kommt mir das so vor, als wäre auf dem Rechner, welcher a in der Regesitry anmeldet zuvor eine Kopie von a gemacht wurde, die nun referenziert wird und forlglich nicht aktuell ist, wenn Änderungen am wirklichen Objekt a vorgenommen werden. 
Wenn ich nach jeder Änderung an a am Server ein rebind mache, dann hat der Client die Aktuellen Daten. 

Das ist aber natürlich umständlich, da man ein rebind schnell mal vergisst, wenn man es überall machen muss, wo man am Server was an a ändert. Wieso ändere ich denn nicht autormatisch das Objekt in der Registry? Ist dieses jedesmal neu anmelden überhaupt sinnvoll oder mache ich hier etwas falsch?

Schonmal vielen Dank!

MfG Kai


----------



## tuxedo (30. Jun 2011)

Hui... Das ist ja echt fatal. Wäre jetzt echt davon ausgegangen dass sowas auch ohne rebind() geht. 

Zeig mal den Codeausschnitt der 'a' erzeugt und dann in der Registry anmeldet. Vielleicht ist da der Wurm drin.

Gruß
Alex


----------



## mvitz (30. Jun 2011)

Hab den Fall gerade mal nachgestellt und ist in der Tat so wie oben beschrieben:


```
package de.mvitz.jf.rmiaktuell;

import java.io.Serializable;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;

public final class Test {

    public interface A extends Remote, Serializable {
        Map<Integer, Integer> getMap() throws RemoteException;
    }
    @SuppressWarnings("serial")
    public static final class AImpl implements A {
        private final Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        public AImpl() {
            add();
        }
        void add() {
            int key = map.size() + 1;
            map.put(key, key);
        }
        @Override
        public Map<Integer, Integer> getMap() throws RemoteException {
            return map;
        }
    }
    
    private static final AImpl a = new AImpl();
    
    public static void main(String[] args) throws Exception {
        LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        Naming.bind("a", a);

        for(int i = 1; i < 11; i++) {
            execute(i > 4);
            Thread.sleep(1000);
        }
    }

    private static void execute(boolean withRebind) throws Exception {
        System.out.println("Server: " + a.getMap());
        System.out.println("Client: " + ((A) Naming.lookup("//localhost/a")).getMap());
        a.add();
        if (withRebind) {
            Naming.rebind("a", a);
        }
    }

}
```

Wird die Map denn vom Server, oder von anderen Clients erweitert? Sollte dies Serverseitig geschehen, müsste man um deine Klasse A eben eine "Serviceschicht" bauen, die sicherstellt, dass nach jedem "put" ein "rebind" aufruft.


----------



## tuxedo (30. Jun 2011)

Hmm, als einziges eventuelles Problem hab ich jetzt mal die statische Instanz der Klasse AImpl ausgemacht. Hast du mal probiert 'a' nicht-statisch in "main" zu erzeugen? Ändert das etwas am Verhalten? "static" bezieht sich ja erstmal auf den ClassLoader. Sind mehrere Classloader im Spiel (was bei RMI durchaus sein könnte), kann es da Probleme geben. 

Aber eventuell bist du ja nicht zwingend an RMI gebunden und möchtest mal SIMON ausprobieren?!
Dort sollte es keine derartigen Probleme geben.

- Alex


----------



## mvitz (30. Jun 2011)

Ja, habe vorher das ganze sogar in separaten Klassen gehabt und AImpl in der Main erzeugt. Das obige Listing ist nur so, da es so kürzer ist und hier nicht so viel Platz weg nimmt.


----------



## tuxedo (30. Jun 2011)

Hmm, okay. Auch wenn ich mir nicht so richtig vorstellen kann dass das wirklich nicht ohne ein rebind() geht (wäre ja wirklich sau-doof von RMI gelöst), ausschließen kann ich's nicht. 

Bleibt nur noch zu sagen: Mit SIMON geht das (und vieles andere) ohne Probleme.


----------



## Niki (30. Jun 2011)

wenn du die Klasse AImpl von UnicastRemoteObject ableitest funktionierts


----------



## tuxedo (30. Jun 2011)

Stimmt, im Beispiel hat er's nicht drin. Aber im Eingangspost hat er's geschrieben dass er das so gemacht hat:


> Die Implementierungen erweitern UnicastRemoteObject und implementieren die Schnittstellen.



... und da hat's ja angeblich auch nicht funktioniert.


----------



## mvitz (30. Jun 2011)

Korrekt, danke das hatte ich glatt überlesen. Folgendes funktioniert jetzt ohne Probleme

```
package de.mvitz.jf.rmiaktuell;

import java.io.Serializable;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

public final class Test {

    public interface B extends Remote, Serializable {
        int getNr() throws RemoteException;
    }
    public interface A extends Remote, Serializable {
        Map<Integer, B> getMap() throws RemoteException;
    }
    @SuppressWarnings("serial")
    public static final class BImpl extends UnicastRemoteObject implements B {
        private final int nr;
        public BImpl(int nr) throws RemoteException {
            this.nr = nr;
        }
        @Override
        public int getNr() throws RemoteException {
            return nr;
        }
    }
    @SuppressWarnings("serial")
    public static final class AImpl extends UnicastRemoteObject implements A {
        private final Map<Integer, B> map = new HashMap<Integer, B>();
        public AImpl() throws RemoteException {
            add();
        }
        void add() throws RemoteException {
            int key = map.size() + 1;
            map.put(key, new BImpl(key));
        }
        @Override
        public Map<Integer, B> getMap() {
            return map;
        }
    }
    
    private static AImpl a;
    
    public static void main(String[] args) throws Exception {
        a = new AImpl();
        
        LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        Naming.bind("a", a);

        for(int i = 1; i < 11; i++) {
            System.out.println("Server: " + asString(a.getMap()));
            System.out.println("Client: " + asString(((A) Naming.lookup("//localhost/a")).getMap()));
            a.add();
            Thread.sleep(1000);
        }
        System.exit(0);
    }
    private static String asString(Map<Integer, B> map) throws RemoteException {
        StringBuilder b = new StringBuilder("{");
        for (Entry<Integer, B> entry : map.entrySet()) {
            b.append(entry.getKey()).append("=").append(entry.getValue().getNr()).append(", ");
        }
        b.append("}");
        return b.toString();
    }
}
```

@Kai2: Du solltest evtl. mal ein vereinfachtes Beispiel posten, in dem man den Fehler nachvollziehen kann.


----------



## Kai2 (30. Jun 2011)

Hallo,
erstmal vielen Dank für die ganze Mühe! Ich hatte leider extrem viel Stress an Arbeit, daher komme ich erst jetzt zum posten. Ich habe heute denFehler selber entdeckt und behoben! Ich nutzte die OSGi Plattform und hab ausversehen im Activator einmal eine Dienstregistryinstanz beim Service erstellen kreiert und dann nochmal eine neue Instanz beim binden in die RMI-registry. Der Client hat quasi erst nach dem ersten rebind() auf die richtige Instanz zugegriffen. 
War nen blöder Fehler von mir... . Hab den aber durch die hunderte Zeilen an Code leider erst lange nicht entdeckt. Bis zum Glück jetzt  Euch trotzdem vielen Dank!

Ich bin nun ohnehin von der RMI Variante abgewichen, da ich wie erwähnt die OSGi-Plattform nutze. Habe da nun gelesen, dass es für diese schon fertige Erweiterungen in Hinblick verteilte Anwendungen/Netzwerkkommunikation gibt, so dass es quatsch wäre, RMI händisch anzuwenden. (siehe andere Post von mir in der OSGi-Rubrik)

MfG Kai


----------

