Search code examples
osgiclassloaderapache-felix

Loading Resources with the Context Loader fails with a NullPointerException


I'm just wondering why I cannot load a resource with the Thread context loader in Felix OSGi? Am I not supposed to touch the context loader, am I doing something wrong or is it a bug?

I've a super simple bundle with a simple Activator:

public class Activator implements BundleActivator {
    public void start(BundleContext context) throws Exception {
        System.out.println("Hello World!!");
        String resourcePath = "META-INF/mySuperDuperResource.txt";

        // works
        System.out.println(Activator.class.getClassLoader().getResource(resourcePath));
        // null-pointer exception
        System.out.println(Thread.currentThread().getContextClassLoader().getResource(resourcePath));

    }
    public void stop(BundleContext context) throws Exception {
        System.out.println("Goodbye World!!");
    }
}

Now loading the resource with with the class loader with the Activator.class.getClassLoader works. But not with the Thread.currentThread().getContextClassLoader(). There I get:

ERROR: Bundle info.gamlor.osgi [26] Unable to get module class path. (java.lang.NullPointerException)
java.lang.NullPointerException
        at org.apache.felix.framework.BundleRevisionImpl.calculateContentPath(BundleRevisionImpl.java:410)
        at org.apache.felix.framework.BundleRevisionImpl.initializeContentPath(BundleRevisionImpl.java:347)
        at org.apache.felix.framework.BundleRevisionImpl.getContentPath(BundleRevisionImpl.java:333)
        at org.apache.felix.framework.BundleRevisionImpl.getResourceLocal(BundleRevisionImpl.java:472)
        at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1432)
        at org.apache.felix.framework.BundleWiringImpl.getResourceByDelegation(BundleWiringImpl.java:1360)
        at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.getResource(BundleWiringImpl.java:2256)
        at info.gamlor.osgi.Activator.start(Activator.java:23)
        at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:641)
        at org.apache.felix.framework.Felix.activateBundle(Felix.java:1977)
        at org.apache.felix.framework.Felix.startBundle(Felix.java:1895)
        at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:944)
        ...
org.osgi.framework.BundleException: Activator start error in bundle info.gamlor.osgi [29].
        at org.apache.felix.framework.Felix.activateBundle(Felix.java:2027)
        at org.apache.felix.framework.Felix.startBundle(Felix.java:1895)
        at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:944)
        at org.apache.felix.gogo.command.Basic.start(Basic.java:729)
        ...
Caused by: java.lang.NullPointerException
        at org.apache.felix.framework.BundleRevisionImpl.getResourceLocal(BundleRevisionImpl.java:474)
        at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1432)
        at org.apache.felix.framework.BundleWiringImpl.getResourceByDelegation(BundleWiringImpl.java:1360)
        at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.getResource(BundleWiringImpl.java:2256)
        at info.gamlor.osgi.Activator.start(Activator.java:23)
        at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:641)
        at org.apache.felix.framework.Felix.activateBundle(Felix.java:1977)
        ... 32 more

Now when just set the thread context class loader it works just fine:

Thread.currentThread().setContextClassLoader(Activator.class.getClassLoader());

But that has a hacky feeling to it. Feels like that will bite me later.


Solution

  • I'm not sure why your surprised this happens. A thread's context classloader is, by default, set to the classloader of it's parent, which in the beginning is set to the system classloader. So, assuming you don't do anything special, the context classloader is the system classloader, which is not the same as your bundle's classloader, hence it can't find your resource.

    I agree that setting the context classloader has a hacky feel to it, but some libraries require this. I would do something like,

    ClassLoader previous = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    badlyBehavedLibraryCall();
    Thread.currentThread().setContextClassLoader(previous);