RMI ClassNotFoundException

bluma

Mitglied
Hallo alle zusammen,

ich baue zurzeit eine Client-/Server-Anwendung und habe einige Probleme mit RMI. Mein Server stellt Dienste bereit die ich mit dem Client nutzen will. Ich habe bereits eine Funktion über den Proxy aufgerufen (teste lokal) und alles hat bestens funktioniert. Der Rückgabewert der Funktion war ein Array, daher gab es da noch keine Probleme mit dem serialisieren. Wenn ich jedoch eine andere Methode mit einem eigens definiertem Objekttypen als Rückgabewert aufrufe, kommt folgender Fehler:

Code:
java.lang.reflect.UndeclaredThrowableException
	at $Proxy0.getPerson(Unknown Source)
	at de.tetra.keymanager.core.client.handlers.CallPersonEditor.execute(CallPersonEditor.java:47)
	at org.eclipse.ui.internal.handlers.HandlerProxy.execute(HandlerProxy.java:240)
	at org.eclipse.core.commands.Command.executeWithChecks(Command.java:475)
	at org.eclipse.ui.internal.handlers.HandlerService.executeCommand(HandlerService.java:174)
	at org.eclipse.ui.internal.handlers.SlaveHandlerService.executeCommand(SlaveHandlerService.java:253)
	at org.eclipse.ui.internal.handlers.SlaveHandlerService.executeCommand(SlaveHandlerService.java:253)
	at de.tetra.keymanager.core.client.views.ViewPersons$1.doubleClick(ViewPersons.java:88)
	at org.eclipse.jface.viewers.StructuredViewer$1.run(StructuredViewer.java:799)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:37)
	at org.eclipse.core.runtime.Platform.run(Platform.java:857)
	at org.eclipse.ui.internal.JFaceUtil$1.run(JFaceUtil.java:46)
	at org.eclipse.jface.util.SafeRunnable.run(SafeRunnable.java:199)
	at org.eclipse.jface.viewers.StructuredViewer.fireDoubleClick(StructuredViewer.java:797)
	at org.eclipse.jface.viewers.StructuredViewer.handleDoubleSelect(StructuredViewer.java:1061)
	at org.eclipse.jface.viewers.StructuredViewer$4.widgetDefaultSelected(StructuredViewer.java:1173)
	at org.eclipse.jface.util.OpenStrategy.fireDefaultSelectionEvent(OpenStrategy.java:237)
	at org.eclipse.jface.util.OpenStrategy.access$0(OpenStrategy.java:234)
	at org.eclipse.jface.util.OpenStrategy$1.handleEvent(OpenStrategy.java:295)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:938)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3682)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3293)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2389)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2353)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2219)
	at org.eclipse.ui.internal.Workbench$4.run(Workbench.java:466)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:289)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:461)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at client2.Application.start(Application.java:29)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:169)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:106)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:76)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:363)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:176)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:508)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:447)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1173)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1148)
Caused by: [b]java.rmi.UnexpectedException: unexpected exception; nested exception is: 
	java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
	java.lang.ClassNotFoundException: server.classes.Person[/b]
	at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
	at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
	... 44 more
Caused by: java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
	java.lang.ClassNotFoundException: server.classes.Person
	at sun.rmi.server.UnicastRef.invoke(Unknown Source)
	... 46 more
Caused by: java.lang.ClassNotFoundException: server.classes.Person
	at java.net.URLClassLoader$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Unknown Source)
	at sun.rmi.server.LoaderHandler.loadClass(Unknown Source)
	at sun.rmi.server.LoaderHandler.loadClass(Unknown Source)
	at java.rmi.server.RMIClassLoader$2.loadClass(Unknown Source)
	at java.rmi.server.RMIClassLoader.loadClass(Unknown Source)
	at sun.rmi.server.MarshalInputStream.resolveClass(Unknown Source)
	at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
	at java.io.ObjectInputStream.readClassDesc(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.readObject(Unknown Source)
	at sun.rmi.server.UnicastRef.unmarshalValue(Unknown Source)
	... 47 more

Es soll also ein Objekt vom Typ Person zurückgegeben werden. Ich habe den Server bereits kontrolliert und er nimmt den Aufruf in der Fassade entgegen, besorgt das Objekt und leitet es dann wieder zurück. Der Fehler besteht also auf Seite des Clients. Nun ist mir nicht ganz klar ob ich richtig liege: Auf Clientseite wird die Klasse Person nicht gefunden um das Objekt wieder neu zu bauen - richtig ? Man kann ja die class-Dateien entweder direkt in einem Verzeichnis haben oder per Classloader nachladen lassen. Beides habe ich versucht, beides ist gescheitert. Auf Seite des Servers liegt die Person.java-Datei im Paket server.classes . Warum gibt er bei der Fehlermeldung java.lang.ClassNotFoundException: server.classes.Person ebenfalls die Paketnamen mit an und nicht lediglich den Klassennamen ?

Ich komme hier nicht weiter und habe leider auch nichts hilfreiches im Netz darüber gefunden. Es wäre toll wenn ihr ein paar Tipps für mich übrig hättet.

Viele Grüße
bluma
 
Zuletzt bearbeitet:

Wildcard

Top Contributor
Das Package ist Teil des Klassennamens. Die Klasse heißt also server.classes.Person und der Client braucht exakt diese Klasse in seinem Classpath.
Du solltest ein eigenes Jar erstellen das alle Klassen enthält die sowohl der Server als auch der Client benötigen und dieses jar auf beiden seiten einbinden.
 

bluma

Mitglied
Das Package ist Teil des Klassennamens. Die Klasse heißt also server.classes.Person und der Client braucht exakt diese Klasse in seinem Classpath.
Du solltest ein eigenes Jar erstellen das alle Klassen enthält die sowohl der Server als auch der Client benötigen und dieses jar auf beiden seiten einbinden.


Hi, ich hatte vorher keine *.jar draus gemacht, sondern direkt auf das Verzeichnis mit den *.class-Dateien verwiesen. Dafür habe ich jetzt mal in Eclipse die Aufrufparameter um -Djava.rmi.server.codebase="D:\server_classes.jar" erweitert und eine *.jar erstellt. Wenn das funktioniert, werde ich die *.jar-Datei auf einen Webserver auslagern. Leider gibt es nach wie vor denselben Fehler - habe die *.jar aus Eclipse heraus generiert indem ich auf das package server.classes geklickt habe un dann ->export->jar-file. Wenn ich die *.jar aufmache und mir den inhalt ansehe, ist auch das drin was ich erwartet hatte.

META-INF
server
--> classes
--------> ....
--------> .... (*.class-Dateien (und *.java-Dateien...habe ich auch einfach mal reingepackt aus irgendeinem Grund)
--------> ....

Ich kann mir nicht erklären, warum die Klasse nicht gefunden wird..

VG bluma
 
Zuletzt bearbeitet:

Wildcard

Top Contributor
Dein Stacktrace deutet darauf hin das du ein Eclipse Plugin schreibst.
Wie bindest du die jar in dein Eclipse Plugin ein?
 

bluma

Mitglied
Dein Stacktrace deutet darauf hin das du ein Eclipse Plugin schreibst.
Wie bindest du die jar in dein Eclipse Plugin ein?

Hallo Wildcard,

ich habe gerade in meinem bin-directory meines Client-Programms die Class Dateien unter server/classes/... reinkopiert und siehe da - es geht theoretisch. Natürlich ist das ziemlich unsauber und wird im weiteren Verlauf gegen eine elegantere Variante ersetzt. Zur Zeit versuche ich die Jar-Datei über Programmargumente reinzuladen. Also mit -Djava.rmi.server.codebase="file:/D:\\server_classes.jar" (funktioniert ja aber nicht) - wobei ich diese stelle später dann gerne gegen eine HTTP-Adresse ersetzen würde.
 

Wildcard

Top Contributor
Warum willst du das tun? Objekte die du sowohl auf Client als auch auf Server Seite brauchst müssen auch auf beiden Seiten vorliegen. Du musst doch auch dagegen kompilieren wenn du etwas sinnvolles damit machen willst.
Mach aus diesen Klassen ein eigenes OSGi Bundle und der client macht dann ein import package oder require bundle statement.
 

Wildcard

Top Contributor
Ich habe mir das nochmal durchgelesen und es lag ein Missverständnis vor, also nochmal auf Anfang:
Du verwendest den ObjectInputStream ja nicht direkt sondern über RMI und du verwendest anscheinend keine generierten Stubs auf dem Client, sondern hast nur die Interfaces, richtig?
Die Sache mit der Codebase könnte problematisch werden da die OSGi Classloader da wohl nicht mitspielen werden.
Hast du es mal mit generierten Stubs versucht die im Bundle Classpath des Clients liegen?
 

bluma

Mitglied
Ich habe mir das nochmal durchgelesen und es lag ein Missverständnis vor, also nochmal auf Anfang:
Du verwendest den ObjectInputStream ja nicht direkt sondern über RMI und du verwendest anscheinend keine generierten Stubs auf dem Client, sondern hast nur die Interfaces, richtig?
Die Sache mit der Codebase könnte problematisch werden da die OSGi Classloader da wohl nicht mitspielen werden.
Hast du es mal mit generierten Stubs versucht die im Bundle Classpath des Clients liegen?


Hi, ja ich habe die Interfaces auf dem Client vorliegen. Bzgl. des Stubs dachte ich, dass diese ab Java 5 nicht mehr benötigt werden. Momentan sieht es so aus: Server hat Interfaces + Klassen, Client hat nur die Interfaces. Da der Client ja aber die Objekte nach dem deserialisieren wieder zusammenbasteln muss, braucht er ja mehr informationen als nur die interfaces - daher habe ich ihm noch die *.class-Dateien des Servers mitgegeben mit denen er sich das basteln kann. Wenn ich die direkt im client-bin-verzeichnis ablade, schafft er es auch selbst sich die zu suchen und zu benutzen. Nur bei externe quelle gibts probleme. Bzgl. Eclipse-Plugin: Ja, es ist richtig, dass ich RCP verwende - jedoch bezieht sich das mehr auf die GUI als auf die kompletten OSGi-Features. Was würde denn in einem generierten Stub drinstecken ? Dahingehend habe ich mich noch nicht genügend beschäftig.

VG bluma
 

Wildcard

Top Contributor
Richtig, ab Java 5 ist es nicht mehr nötig Stubs zu generieren, aber ich bin nicht sicher ob das innerhalb eines OSGi Frameworks funktionieren kann, daher der Vorschlag den 'alten Weg' zu gehen und die Stubs zu generieren.

Wenn ich die direkt im client-bin-verzeichnis ablade, schafft er es auch selbst sich die zu suchen und zu benutzen. Nur bei externe quelle gibts probleme
Wie gesagt, du kannst aus den Implementierungen ein eigenes Bundle machen und das Bundle in den Eclipse RCP stecken.

Ja, es ist richtig, dass ich RCP verwende - jedoch bezieht sich das mehr auf die GUI als auf die kompletten OSGi-Features.
Das spielt keine Rolle, alle Bundles laufen in Equinox OSGi und verwenden daher nicht die Standard Java Classloader, sondern die OSGi Bundle Classloader und die sehen nur die Klassen die du explizit per Require Bundle oder Import Package in der Manifest.MF anforderst.
 

bluma

Mitglied
Hallo Wildcard, das OSGi einen eigenen Classloader besitzt und ich über den regulären Java-Weg keine Klassen reinladen kann, war mir neu und trägt einen entscheidenden Teil dazu bei, dass mein Problem bald gelöst wird. Dann ist mir nun auch klar, warum jegliche Versuche Klassen zu laden fehlgeschlagen sind. Ich frage mich momentan nur noch wie ich aus den Class-Dateien ein Bundle erstellen kann. Habe bislang mit Eclipse nur die Möglichkeit gefunden ein vorhandenes RCP-Projekt zu einem Bundle zu machen. Mein Server-Programm ist jedoch keine RCP-Anwendung. Weißt du wie das geht ?

VG bluma
 

Wildcard

Top Contributor
Ein Bundle unterscheidet sich von einem regulären Bundle nur durch ein erweitertes Manifest. Mach dein Server Projekt ebenfalls zu einem Plugin Projekt, erstelle aber keinen Activator. OSGi Bundles lassen sich als reguläre jar verwenden solange du keine OSGi Klassen darin verwendest.
 

bluma

Mitglied
Ein Bundle unterscheidet sich von einem regulären Bundle nur durch ein erweitertes Manifest. Mach dein Server Projekt ebenfalls zu einem Plugin Projekt, erstelle aber keinen Activator. OSGi Bundles lassen sich als reguläre jar verwenden solange du keine OSGi Klassen darin verwendest.

Hallo Wildcard, ich habe mein Server-Projekt nun als Plugin angelegt und bin nach diesem Tutorial hier vorgegangen: Plug-in basierte Entwicklung mit OSGi . Also habe das Projekt als required Bundle angegeben und bei der Run Configuration bei den Plugins zusätzlich das Server-Bundle gemarkert. Leider werden die Klassen nach wie vor nicht gefunden. Habe dann spaßeshalber mal ein Plugin-Projekt MIT Activator angelegt und in die start-/stop-Methoden ein Konsolenausgabe integriert. Die kam beim nächsten Ablauf des Programms nicht zu Tage, so dass ich denke, dass das Bundle wohl gar nicht geladen wird - obwohl ich es angegeben habe..
 

Ähnliche Java Themen


Oben