Search code examples
javaspringmavenosgiosgi-bundle

"cannot be cast to org.osgi.framework.BundleActivator" when declaratively starting OSGi bundle


There's this kind of legacy Spring based java application that runs within a container. I'm trying to provide some OSGi plugin functionality by embedding Apache Felix within this already-existing application. I'm declaratively starting the framework, by following this:

https://dotcms.com/blog/post/navigating-osgi-extending-your-software-to-embed-an-osgi-framework

In case the link doesn't work, what I'm doing is importing Felix as a maven dependency, using the ServiceLoader to get a reference to the framework factory, starting a framework, and loading/starting all bundles in a specific directory.

The bundle is created via the Felix Bundle plugin, with the following configuration:

            <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <extensions>true</extensions>
            <configuration>
                <instructions>
                    <Export-Package>com.example</Export-Package>
                    <Import-Package>!*</Import-Package>
                    <Bundle-Name>${project.description}</Bundle-Name>
                    <Bundle-Activator>com.example.Activator</Bundle-Activator>
                    <Embed-Transitive>true</Embed-Transitive>
                </instructions>
            </configuration>

As for the dependencies:

<dependencies>
    <dependency>
        <groupId>org.apache.felix</groupId>
        <artifactId>org.apache.felix.framework</artifactId>
        <version>6.0.1</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

So as you can see, it doesn't import anything (it's the only bundle in the whole application, the servlet itself is not an OSGi bundle). Since the application is not an OSGi bundle, I cannot export the framework classes from it, Felix must be embedded in the bundle itself.

I cannot get the bundle to start. This is what I get (stacktrace has been redacted a bit):

14:43:52,981 ERROR [con.example.Plugin] (Initialization Thread) Failed to start bundle com.example: org.osgi.framework.BundleException: Activator start error in bundle com.example.Plugin[2].
at org.apache.felix.framework.Felix.activateBundle(Felix.java:2448)
at org.apache.felix.framework.Felix.startBundle(Felix.java:2304)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:998)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:984)

Caused by: java.lang.ClassCastException: com.example.Activator cannot be cast to org.osgi.framework.BundleActivator
    at org.apache.felix.framework.Felix.createBundleActivator(Felix.java:4744)
    at org.apache.felix.framework.Felix.activateBundle(Felix.java:2379)
    ... 35 more

I've managed to reduce this to the possible fact that there are two instances of the BundleActivator interface: one in the bundle's classloader and another one in the Frameworks' ModuleClassLoader. At least I think that's what's going on.

How do people usually work around this? I've tried creating another bundle that just exports the framework for the initial bundle to import, but that one suffers from the same error when it starts. Refactoring the spring app to be overall OSGi capable is out the question (that's why I'm using Felix and not Equinox).


Solution

  • Bundles must not have their own internal copy of the OSGi Framework classes (i.e. the package org.osgi.framework and its subpackages). They must import those packages from the System Bundle.

    The cause of the ClassCastException is that in Java the identity of a class is the combination of its fully-qualified name and the ClassLoader that defined it. If you define the BundleActivator type in multiple ClassLoaders — which is what happens when you have copies of it inside bundles — then they are considered different types.