ClassCastException

BDiegelmann

Grünschnabel
Hallo zusammen,

ich möchte eine Referenz eines Objektes über ein OSGI Service holen. Jedoch bekomme ich beim Zuweisen immer eine ClassCastException. Ich blicks nicht woran es liegt.
Code:
try
          {
            
            Object o = context.getService(serviceRef);
            if (o instanceof IsoTP)
              System.out.println("instance of!!");
            System.out.println(o.getClass());
            tp = (IsoTP) context.getService(serviceRef);
            
          }

Registriert hab ich den Service folgendermaßen:

Code:
tpServiceReg = context.registerService(IsoTP.class.getName(), tp, null);

Und das was rauskommt ist das:

Code:
Server is started.
fw>$INFO 240 Service export: edag.can.controller.CANController
INFO 240 CANController-Bundle gestartet
INFO 241 ISO-TP Bundle gestartet
INFO 241 CANController Service referenziert.
DEBUG 240 ServiceEvent.REGISTERED
in if tpRef == null
edag.can.transport.IsoTP@4845aa
class edag.can.transport.IsoTP
java.lang.ClassCastException: edag.can.transport.IsoTP
        at edag.can.controller.peak.Activator.serviceChanged(Activator.java:99)
        at com.prosyst.mbs.impl.framework.EventsManager.serviceChanged(EventsMan
ager.java:681)
        at com.prosyst.mbs.impl.framework.BundleContextImpl.registerService(Bund
leContextImpl.java:299)
        at com.prosyst.mbs.impl.framework.BundleContextImpl.registerService(Bund
leContextImpl.java:267)
        at com.prosyst.mbs.impl.framework.BundleContextImpl.registerService(Bund
leContextImpl.java:321)
        at edag.can.transport.Activator.start(Activator.java:46)
        at com.prosyst.mbs.impl.framework.BundleImpl.startIt(BundleImpl.java:306
3)
        at com.prosyst.mbs.impl.framework.BundleImpl.simpleStart(BundleImpl.java
:991)
        at com.prosyst.mbs.impl.framework.BundleImpl.start0(BundleImpl.java:904)

        at com.prosyst.mbs.impl.framework.BundleImpl.start(BundleImpl.java:814)
        at com.prosyst.mbs.impl.framework.BundleImpl.start(BundleImpl.java:592)
        at com.prosyst.mbs.impl.services.pmp.Administration.startBundle(Administ
ration.java:193)
        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 com.prosyst.mbs.impl.services.pmp.InvocationThread.run(InvocationThre
ad.java:89)
        at com.prosyst.util.impl.tpt.threadpool.Executor.run(Executor.java:136)

Obwohl die ganzen Debug-Ausgaben die ich da rein gebaut habe richtig zu sein scheinen, es nicht.
Kann mir da jemand auf die Sprünge helfen?
 
Deine if-Abfrage umfasst nur noch eine Zeile. Liegts vllt daran oder war das nur das kürzen fürs Forum?
 
Hallo,

kann es sein, dass du hier unterschiedliche ClassLoader verwendest? In der JVM ist nur das Tupel aus (Class,ClassLoader) einedeutig. Sprich es kann passieren dass eine Klasse mit dem gleichen namen von zwei unterschiedlichen ClassLoadern geladen wurde... diese Klassen kann man nicht aufeinander Casten... Vergleich doch mal an was getClass().getClassLoader() an der Klasse sagt die du vom Service zurück bekommst und die die du "lokal" an der Hand hast.

Gruß Tom
 
Danke, für die schnelle Antwort, ich hab das mit dem Classloader überprüft, und so wie ich die Sache sehe scheinen die überein zu stimmen.
Ich kann natürlich den Classloader der tp Objektes nicht ausgeben, da ja keine Instanz zugewiesen wurde. Dies wird ja erst mit getService() erledigt.
Ich hab aber den Classloader ausgegeben, nachdem ich es zum ersten mal erstellt habe und dann nochmal, nachdem vom Objekt o nach getService(). Bin mir nicht sicher ob das so richtig ist.
Was aber nicht übereinstimmt sind die Classloader des Activators des Bundels das den Service referenzieren soll und der Classloader der zu referenzierenden Klasse.
Ist das vieleicht das Problem?


Was mich aber verwundert ist der Ablauf. Es wird ein Event gesendet obwohl der Service erst nach diesem Event registriert wird. Kann man in der Ausgabe unten sehen. Die Klasse Transport registriert den Service erst nachdem der Fehler auftritt. Gibts da vieleicht einen Zusammenhang?

Code:
INFO 258 Service export: edag.can.controller.CANController
INFO 258 CANController-Bundle gestartet
INFO 259 ISO-TP Bundle gestartet
INFO 259 CANController Service referenziert.
DEBUG 258 ServiceEvent.REGISTERED
edag.can.transport.IsoTP@502819
Classloader vom CanActivator: com.prosyst.mbs.impl.framework.DefaultClassProvide
r@29c58e
class edag.can.transport.IsoTP
Classloader in CANController: com.prosyst.mbs.impl.framework.DefaultClassProvide
r@a4488
java.lang.ClassCastException: edag.can.transport.IsoTP
        at edag.can.controller.peak.Activator.serviceChanged(Activator.java:110)

        at com.prosyst.mbs.impl.framework.EventsManager.serviceChanged(EventsMan
ager.java:681)
        at com.prosyst.mbs.impl.framework.BundleContextImpl.registerService(Bund
leContextImpl.java:299)
        at com.prosyst.mbs.impl.framework.BundleContextImpl.registerService(Bund
leContextImpl.java:267)
        at com.prosyst.mbs.impl.framework.BundleContextImpl.registerService(Bund
leContextImpl.java:321)
        at edag.can.transport.Activator.start(Activator.java:47)
        at com.prosyst.mbs.impl.framework.BundleImpl.startIt(BundleImpl.java:306
3)
        at com.prosyst.mbs.impl.framework.BundleImpl.simpleStart(BundleImpl.java
:991)
        at com.prosyst.mbs.impl.framework.BundleImpl.start0(BundleImpl.java:904)

        at com.prosyst.mbs.impl.framework.BundleImpl.start(BundleImpl.java:814)
        at com.prosyst.mbs.impl.framework.StartupWatchdog.run(StartupWatchdog.ja
va:139)
In Start of Transport: class edag.can.transport.IsoTP

In Start of Transport:  Classloader in ISO-TP: com.prosyst.mbs.impl.framework.De
faultClassProvider@a4488
Classloader von Activator Transport: com.prosyst.mbs.impl.framework.DefaultClass
Provider@a4488
DEBUG 258 ServiceEvent.REGISTERED
Server is started.
fw>$DEBUG 258 ServiceEvent.REGISTERED
DEBUG 258 ServiceEvent.REGISTERED
 
Zuletzt bearbeitet:
Kann das Problem noch ein bisschen eingrenzen.

Der Fehler tritt immer dann auf wenn zwei Bundles sich gegenseitig referenzieren. Also wenn Bundle A einen Service von B nutzen will und B einen Service von A.
Wenn ich jetzt zum Beispiel ein Bundle C habe was die Services von A und B referenziert gibt es keine Probleme. Nur wenn A und B gegenseitig Services referenzieren.

Hat da OSGi ein Mechanismus vorgesehen wie man das machen kann? Ich komm da einfach nicht weiter.

Code:
class A {
B b;
b.useService();

}

class B {
A a;
a.useService();
}

Wie mach ich das ohne die ClassloaderException ?

P.S.: Hab in der Spec. nochmal gestöbert. Da stehts auch drin. Frag mich nur wie das funktionieren soll. Ich hab doch gar kein Einfluss auf den Classloader?
Bundles and classloaders
Each bundle installed in the Framework that is resolved must have a classloader
associated with it (Frameworks may have multiple classloaders per
bundle). This classloader provides each bundle with its own namespace, to
avoid name conflicts, and allows package sharing with other bundles. The
bundle's classloader must find classes and resources in the bundle by searching
the bundle's classpath as specified by the Bundle-Classpath header in the
bundle's manifest. See Bundle Classpath on page 20 for more information on
this header.
Bundles collaborate by sharing objects that are an instance of a mutually
agreed class (or interface). This class must be loaded from the same classloader
for both bundles, otherwise, using the shared object will result in a ClassCast-
Exception. Therefore, the Framework must ensure that all importers of a class
in an exported package use the same classloader to load that class or interface.
OSGi Service-Platform Release 2 17-282
Framework Specification Version 1.1 The Bundle Namespace
For example, a bundle may register a service object under a class com.acme.C
with the Framework service registry. It is crucial that the bundle that created
the service object ("Bundle A") and the one retrieving it from the service registry
("Bundle B") share the same class com.acme.C of which the service object
must be an instance. If Bundle A and Bundle B used different classloaders to
load class com.acme.C, Bundle B's attempt to cast the service object to its version
of class com.acme.C would result in a ClassCastException.
A bundle's classloader also sets the ProtectionDomain object for classes loaded
from the bundle, as well as participates in requests to load native libraries
selected by the Bundle-NativeCode manifest header. The classloader for a bundle
is created during the process of resolving the bundle.
 
Zuletzt bearbeitet:
Zurück