Search code examples
javaosgiclasspathapache-karafblueprint-osgi

adding resource files to karaf classpath


I am working on a project using karaf 4.0.5, and osgi. We have client side code to invoke a REST API, which requires to load 3 "*.properties" files. I have been given a client jar that I'm using to invoke the server side classes and methods (containing code that I cannot update). The required property files are present in the provided client jar, but their code still doesn't locate and load them.

On debugging my pax exam I found the below possible reasons for it not loading resource files from the jar.

  1. the code to load the files seems to attempt to load resources only from the Bundle Classloader, and
  2. it calls the "getResource()" method instead of the "getResourceAsStream()" method.

Alternatively, I tried adding the resources to a directory on my file system, and appending the classpath with the directory's location, as in:

"-cp .;C:/Users/abcUser/Desktop/resourceFolder/;"

(windows 7, classpath entry added as a VM argument while executing pax exam using junit 4+ from eclipse) -> this doesn't work either and its still unable to locate these files. What other options do I have so the Bundle Classloader locates these files?

Note: We already have a bunch of other *.cfg files whose contents are loaded into beans using blueprint and are registered in the containers, but that's not what I need to do here. During runtime, these files should be available to the BundleClassloader, and should be retrieved by the "getResource()" method.

Update: Following the below portion of the accepted answer, the properties files were successfully loaded by the application.

Another thing to check is whether the client code is actually using the bundle classloader when trying to load these resources. In some cases the code tries to be clever by using the Thread Context Classloader, which would need to be set appropriately before calling the client code.

The code from the client jar was exactly as guessed: the resource loading was happening using the Thread.currentThread().getContextClassLoader(). I was able to set the ContextLoader to the CustomAbstractProcessor's classloader and it now loads the properties files from that bundle's classpath!

ClassLoader previousCL = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(CustomAbstractProcessor.class.getClassLoader());
try {
    //REST call to server using classes and methods from provided client jar.
}
finally {
    Thread.currentThread().setContextClassLoader(previousCL);
}

Solution

  • I have been given a client jar that I'm using to invoke the server side classes and methods (containing code that I cannot update). The required property files are present in the provided client jar, but their code still doesn't locate and load them.

    If this client jar is running as an OSGi bundle then it should be able to find resources using its own class loader (the bundle class loader) if (and only if) the resources are on the bundle's classpath.

    The default classpath for an OSGi bundle is . i.e. the root of the bundle. This can be overridden using the Bundle-ClassPath manifest header and used to one or more locations in the bundle.

    • One possibility is that the client bundle has a different classpath set and the properties files are not on it.
    • Another possibility is that the properties files are on the classpath, but that the locations don't match what's expected, e.g. the code is looking for foo.properties and the file is at `/props/foo.properties'

    it calls the getResource() method instead of the getResourceAsStream() method.

    getResourceAsStream() is just a null-safe version of getResource().openStream().

    What other options do I have so the Bundle Classloader locates these files?

    Another thing to check is whether the client code is actually using the bundle classloader when trying to load these resources. In some cases the code tries to be clever by using the Thread Context Classloader, which would need to be set appropriately before calling the client code.