home / blog

Java class-loading woes

Another day, another OSGI headache.

JAXB within OSGI.

This won’t work by default. FYI: I’m more of a DOM / XPath fan, but some people insist on using JAXB.

JAXBContext jc = JAXBContext.newInstance("com.adamish.foo.jaxb");

This error is produced.

"com.adamish.foo.jaxb" doesn't contain ObjectFactory.class or jaxb.index

Workaround, manually pass in a class-loader to the overloaded newInstance() method.

ClassLoader cl = com.adamish.foo.jaxb.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("com.adamish.foo.jaxb", cl);

Resource loading within OSGI.

You may have existing code which loads resources via strings referring to resource names. This will work fine when the class doing the loader is in the same “context” as the resource. With OSGI this might not be the case.

In my nightmare I have resources in one bundle, referenced by XML which are loaded by code in another bundle.

// fine most of the time, but a nightmare with OSGI
public void foo() {
  getClass().getResource("/icons/foo.png"); 
}

Solutions?

One: Do the resource resolution from the same bundle as the resources. Another variant on this would be to pass an InputStream to foo() instead. This might not always be possible.

// in same bundle as resources, then call foo with resolved resource
URL url = getClass().getResource("/icons/foo.png"); 
foo(url);

public void foo(URL url) {
   // do stuff
}

Two: Pass in an optional class-loader from the context which has access to the resources.

foo(getClass().getClassLoader());

public void foo(ClassLoader cl) {
  cl.getResource("/icons/foo.png"); 
}
// default for backwards compatibility
public void foo() {
  foo(getClass().getClassLoader());
}

Three: Put the resources in a fragment bundle. Set the fragment host to the bundle containing the code that does the loading. Node there can only be one fragment bundle per bundle-host.

Other class-loading funnies.

The following two lines are not equivalent, I repeat, not equivalent. The first will successfully resolve foo.png (if it’s in the classpath). The second will not.

getClass().getResource("/foo.png");
getClass().getClassLoader().getResource("/foo.png");

The second getClassLoader() is a different classloader to the one used internally in getClass().getResource(…). It will only accept relative paths. The following _will_ work. Note the removal of the leading slash.

getClass().getClassLoader().getResource("foo.png");
This entry was posted in geek and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published.