(RMI)java.lang.ClassCastException: com.sun.proxy.$Proxy1 cannot be cast to Funktione

Hey,

ich habe ein Projekt bei dem Funktionen mit Antlr4 geparsed werden und dann damit gerechnet werden kann und die Funktionen integriert und differenziert werden können.

Im nächsten Schritt soll das jetzt auch über einen Dienst laufen:
Habe also das Interface:


String getName() throws java.rmi.RemoteException;

R execute(T task,A ... args) throws java.rmi.RemoteException;
}```

Dazu der Server, der das Interface implementiert: 
```public class DifferentiatorService extends java.rmi.server.UnicastRemoteObject
		implements Service<Funktionen, Double, Double> {

	/**
	 * 
	 */
	private static final long serialVersionUID = -3236697150408344006L;

	protected DifferentiatorService() throws RemoteException {

	}

	@Override
	public String getName() throws RemoteException {
		return "DifferentiatorService";

	}
        @Override
	public Double execute(Funktionen f, Double... args) throws RemoteException {
		Differentiator diff = new Differentiator();
		double result = diff.differentiate(f, args[0]);
		return result;
	}

	public static void main(String[] args) throws Exception {
		String url = "//localhost/DifferentiatorService";
		Registry registry = LocateRegistry.createRegistry(1099);
		DifferentiatorService service = new DifferentiatorService();
		Naming.rebind(url, service);
		System.out.printf("Service %s registered
", url);
	}

}

Der Client sieht so aus:

	
	public static void main(String[] args) throws RemoteException, MalformedURLException, IllegalArgumentException, NotBoundException, RecognitionException {
		execute();
	}
	
	public static Double execute() throws RemoteException, MalformedURLException, NotBoundException, IllegalArgumentException, RecognitionException {
		
		Service<Funktionen, Double, Double> service;
		String url = "//localhost/DifferentiatorService";
		Double r;
		String text = "f(x)=x^2";
		Script script = new Script();
		script.parse(text);
		Funktionen f = script.getFunction("f");
		service = (Service<Funktionen, Double, Double>) Naming.lookup(url);		
		r = service.execute(f, (double)2);
		return r;

	}

}```

Das Script liest erst eine Funktion ein und speichert die in einer Hashmap. Mit der getFunction() Methode hol ich mir dann ein Funktionsobjekt. 

Die Funktionen-Klasse: 
```public class Funktionen extends UnicastRemoteObject implements Function, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1234132759512350836L;

//viel Code
}```

Wenn ich jetzt versuche, die Funktion über RMI differenzieren zu lassen, kommt leider der schon im Titel erwähnte Fehler.

"Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy1 cannot be cast to Funktionen"

Im Stacktrace ist der letzte "Trace" in meinem Code diese Zeile im Client: 
```r = service.execute(f, (double)2);```
Danach nur noch RMI, was ich nicht wirklich nachvollziehen kann. 

Ich hab schon viel gegoogelt und es wird gesagt, dass man gg ein Interface programmieren soll, aber das habe ich ja eig. gemacht mit: 
```service = (Service<Funktionen, Double, Double>) Naming.lookup(url);	```

Wenn ich einen String übergebe, und das Parsen etc auf der Serverseite mache, dann klappt alles wunderbar. 

Hoffe mir kann da jemand helfen, sodass ich auch ein Funktionenobjekt übergeben kann.

Grüße

Ich stelle die Frage mal bewusst dämlich, vielleicht kommst du dann drauf : Was ist “Funktionen”?
Beachte das auch diese Klasse ein Remote-Interface braucht damit der RMI-Compiler auch dafür einen Stub erzeugen kann.

Kleiner Tipp aus Erfahrung : so toll RPC auch sein mag, nutze es nur wenn es wirklich nicht anders geht, ansonsten sollte es eigentlich immer möglich sei den Datenaustausch selbst erledigen zu können.

hey, danke für deine Antwort.

Die Frage verstehe ich nicht ganz :o Funktionen ist eine Klasse …
Hab nicht beachtet, dass das Interface “Function”, dass von Funktionen implementiert wird auch remote sein muss.
Hab also eingefügt:

    
    double eval(final double ... args) throws IllegalArgumentException, RecognitionException;
}```

Jetzt fliegt mir direkt die nächste Exception um die Ohren: 
java.rmi.server.ExportException: remote object implements illegal remote interface; nested exception is: 
	java.lang.IllegalArgumentException: illegal remote method encountered:

Fragt sich ob das ein Fort-oder Rückschritt ist? :D

Mit RMI hatte ich nur mal kurz experimentiert… müßte die Funktion nicht deklariert sein, eine “RemoteException” zu werfen? (Kann aber auch komplett falsch und sinnlos sein, sorry ggf.)

Es hat auf sich auf jeden Fall was geändert. Wenn ich in Function Interface die Methoden mit einem throws RemoteException “versehe” komme ich wieder zur ersten Exception, also:
“Exception in thread “main” java.lang.ClassCastException: com.sun.proxy.$Proxy1 cannot be cast to Funktionen”

liegt vielleicht an Generics, das Interface Service wird irgendwo so allgemein verarbeitet, dass der Proxy für den Rückgabewert auch nur allgemein Object oder so ist, nicht Funktionen

hier
Designing a Remote Interface (The Java
ist es zwar auch etwa generisch, aber zumindest ohne klassenweite Typen

mindestens testweise wechsle auf ein Interface mit konkreten Typen,
wenn das noch nicht hilft, baue Beispiele aus den Tutorials nach

sollen es wirklich so viele Service werden dass derart variabel?
vielleicht reicht die Generic-Variante aus dem Link, sofern diese bei dir funktioniert, also der Fehler bei <T,A,R> liegt


teste auch (evtl. wieder die ganze Generic-Variante) mit Rückgabewert String aus der API,
für den Fall dass es in die Richtung geht, dass auf Client und Server verschiedene Versionen der Klasse Funktionen existieren,

sollte mit bedachter serialVersionUID kaum sein, aber man weiß ja nie…,
anderes package würde schon einen Unterschied machen

es reicht bei String eine Dummy-Implementierung ‘return “test”’, am Anfang nie komplizierte Programmfunktionalität hineinbringen, RMI an sich testen, so einfach wie möglich, Schritt für Schritt mehr hinein,

auf Parameter etwa am Anfang ruhig auch verzichten


Funktionen, allgemein Mehrzahl, ist ein schrecklicher Klassenname, dann lieber FunkionImpl oder irgendwas

Hey, danke für die Antwort.
Werde ich morgen mal probieren, kann mich heute leider nicht mehr konzentrieren.

Der Fehler liegt meiner Vermutung nach aber nicht direkt an den Generics, denn das geht:

	
	public static void main(String[] args) throws RemoteException, MalformedURLException, IllegalArgumentException, NotBoundException, RecognitionException {
		execute();
	}
	
	public static Double execute() throws RemoteException, MalformedURLException, NotBoundException, IllegalArgumentException, RecognitionException {
		Service<String, Double, Double> service;
		String url = "//localhost/DifferentiatorService";
		Double r;
		String text = "f(x)=x^2";
		Script script = new Script();
		script.parse(text);
		Funktionen f = script.getFunction("f");
		service = (Service<String, Double, Double>) Naming.lookup(url);
		r = service.execute(text, 2.0);
		System.out.printf("returned: %s 
", r);
		return r;

	}

}

Deswegen siehts für mich fast so aus, als würde mit der “Funktionen”-Klasse was nicht stimmen(den Namen hab ich nicht gewählt, fande den Plural auch etwas komisch).
Werde mich morgen mal durch den Link durcharbeiten

Das war mir vorher auch klar, aber ich wollte halt eigentlich auf Code hinaus um halt feststellen zu können ob die Klasse halt korrekt Remote ist oder nicht.

Auch fehlt mir bisher in allen deinen Codes eine Package-Zeile. Sollten deine Klassen tatsächlich im default-package liegen solltest du das schnellstens ändern.
Auch ist es wichtig das man die Klassen die von beiden Seiten genutzt werden auch in einem common-package liegen hat und nicht im server- und client-package zwei Klassen. Das würde schon auf Grund des unterschiedlichen Package zu einer ClassCastException führen.

Was ggf noch wichtig sein könnte : wähle deine Interface- und Klassennamen mit bedacht um eventuelle Kollisionen mit bereits bestehenden Klasse der SE-API zu vermeiden. Wobei : auch innerhalb der SE-API gibt so den einen oder anderen Namens-Konflikt.

Danke, die Klassen liegen alle im gleichen (nicht default) Package.

Es war glaube ich ein Fehler, den Service mit Service<Funktionen, Double, Double> zu erstellen.
Also gegen die Klasse “Funktionen”.

Ich hab sowohl im Server als auch im Client als auf das Interface “Function” geändert. Also “Klasse implements Service<Function, Double, Double>” und den Service auch erstellt als:
Service<Function, Double, Double>
Jetzt kommt kein Proxy Fehler mehr(weil der korrekte Stub jetzt gecastet werden kann (??)), sondern folgende Exception:

    java.io.EOFException
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:229)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:162)
    at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
    at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
    at com.sun.proxy.$Proxy1.execute(Unknown Source)
    at de.lab4inf.wrb.DifferentiatorClient.execute(DifferentiatorClient.java:25)
    at de.lab4inf.wrb.DifferentiatorClient.main(DifferentiatorClient.java:13)
Caused by: java.io.EOFException
    at java.io.DataInputStream.readByte(DataInputStream.java:267)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:215)
    ... 6 more```
Hab schon viel gegoogelt, und bei den meisten hängt es wohl mit einem SecurityManager zusammen, den ich aber eh nichts nutze. 
Vllt weiß da einer weiter.

Grüße

Das ist keine Hilfe, allerdings bin ich schnell auf das Framework SIMON umgestiegen nach ein paar kleinere Versuche. Damit kam ich ziemlich gut zurecht, die Doku ist auch aussagekräftig.

Gut, steht ja deutlich da was das Problem ist : EOFException !
Irgendwo bricht also die Verbindung ab bzw werden falsche Daten übertragen. Da wäre vielleicht mal etwas mehr Code so drumrum interessant um zu gucken wie die RMI-Verbindung überhaupt so abläuft.

@Prototype
Mal von Simon abgesehen, wie gesagt : RPC sollte man nur nutzen wenn wirklich nötig.

Sind im Server und im Client wirklich die GLEICHEN .class-Files vervewendet worden?

Warum ist Funktionen ein UnicastRemoteObject? So wie ich die Sache verstanden hab, willst du das ja nur an die Gegenseite senden, um eben da eine Funktion auszuführen. Ist bei mir aber schon wieder Jahre aus, dass ich mit RMI programmiert hab.

Hey, sorry war eine Zeit lang nicht online. War eine Aufgabe für die Uni die inzwischen abgelaufen ist. Wir durften es dann doch mit Strings machen, also anstatt der Function einen String der zu-parsenden Funktion übergeben.

Es hatten wohl sämtliche Kommilitonen ähnliche Probleme. Woran es genau liegt, weiß ich immer noch nicht. Paar Kommilitonen meinten es liegt daran, dass man sämtliche Klassen Remote “machen” muss. Also es wird Antlr4 für das parsen verwendet und man sollte angeblich die ganzen Antlr generierten Klassen bzw die eigenen darauf-aufbauenden Klassen wie den Listener etc Remote machen. Ob das stimtm weiß ich nicht. Der Prof hatte es anscheinend selbst nicht ausprobiert mit Funktionen als Parameter und musste dann feststellen, dass er es so auf die schnelle auch nicht hinkriegt.

Na super … da ist man ja gleich richtig motiviert … hust NOT

Um mal vielleicht noch auf etwas hinzuweisen warum ich mehrfach gesagt habe : “RPC schön und gut … aber nur nutzen wenn nötig”
Ich habe mal das Beispiel aus dem Sun-Tutorial genommen : Trail: RMI (The Java
Weder habe ich verstanden was da abgeht noch wer wen wie callt und wo letzten Endes was berechnet wird … fakt ist : es gab einige Probleme.

Da ich einen Root-Server mit Domain habe konnte ich die Parameter entsprechend anpassen … und habe auch ein Ergebnis bekommen … ABER :

In der Server-Firewall muss erstmal der RMI-Registry-Port TCP/1099 freigegeben werden damit überhaupt ein lookup erfolgen kann. Gut, kann man nachvollziehen, denn ich muss ja auch bei jedem anderen Server die entsprechenden Ports freigeben.
Weiterhin : jede Instanz der Remote-VM belegt selbst auch noch einen Port, und der ist sogar noch random ! Ergo : jedes mal wenn ich die Remote-VM gestartet habe musste ich den dann neuen Port wieder in der Firewall freigeben bevor eine Verbindung hergestellt werden konnte und ich ein Ergebnis bekam.

Gut, ich habe auf meinem Server mit sudo natürlich root-Rechte um die Firewall anzupassen, aber wenn man vom Admin nur einen festen Port zugewiesen bekommt ist man aufgeworfen. Es gibt da zwar wohl noch was mit custom-Socket-Factories … aber soweit habe ich nicht mehr gelesen.

Sehr viel einfacher geht es halt wenn man sich direkt um die Verbindung und den Datenaustausch kümmert : so kann man beim Admin direkt nach einem bestimmten Port anfragen bzw seinen Code auf den vom Admin zugewiesenen Port ändern und hat keine Probleme mehr mit ständig random wechselnden Ports. Ein klarer Vorteil gegenüber RMI.

Was ich jetzt nicht nachvollziehen konnte, was aber wohl immer wieder auch mal als Argument gegen RMI kommt : der Server müsse eine Verbindung zum Client herstellen. Ich bin auch der Meinung sowas mal gelesen zu haben, aber der Fakt das ich in der NAT-Firewall meines Routers nichts freigeben musste lässt mich dann doch wieder dran zweifeln. Gleich ob nötig oder nicht : wenn man eine normale Client-Server-Struktur aufbaut (also mit direkt sebst mit Socket) weis man das man immer vom Client eine Verbindung zum Server aufbaut, also nur auf diesem der Port erreichbar sein muss, auch wenn man selbst hinter einem NAT sitzt.

Ich finds ja gut das versucht wurde euch RPC beizubringen, aber du siehst selbst : wenn man keine Ahnung von hat bekommt man nicht mal ein simples Hello World richtig ans laufen, von den möglichen Problemen bei der eigentlichen Netzwerkkommunikation mal ganz abgesehen.