Using Vaadin’s ClassResource in OSGi environments is pretty straightforward. But you have to know a few basics about the OSGi class loader:
In OSGi, every bundle has its own classloader. And all resources contained in a certain bundle are loaded by this bundle’s classloader only.
For instance, we try to load an image based on Vaadin’s ClassResource.
new Image("Bundle2 exported", new ClassResource(org.my.SampleExported.class, "ImageExported.png"))
The ClassResource internally will call
whenever ClassResource#getStream() is called.
The OSGi classloader takes over
After this, the OSGi classloader comes into play. It uses a defined strategy to look for the resource:
- If the package starts with “java”, then the request is forwarded to the parent classloader
- If the package is in the list of “boot delegation”, the the request is forwarded to the parent classloader
- The request is forwarded to “class loading hooks”
- The classloader checks whether there is a “import package” for “org.my”.
- If so, then the resource is loaded from this imported package (meaning that the request is forwarded to a different bundle classloader). If a package could be found, the lookup will terminate.
- If not, proceed with 5.
- The classloader checks all “required bundles” whether the resource can be found there.
- The classloader checks the “local” resources contained in the classloaders bundle.
- The classloader checks the “fragments classpath” resources contained in the fragments.
- The classloader checks for “dynamic imports”.
How not to: Something that will never work
Assume we have two bundles.
Bundle A – contains the Vaadin-Servlet, the resource handler and all that stuff required to run Vaadin in OSGi.
Bundle B – just another bundle. “Bundle A” does not have a dependency to “Bundle B”.
Bundle B contains an image and it should be loaded by a ClassResource. If you prepare a ClassResource for the image contained in “Bundle B”, but you do not pass a class contained in “Bundle B” to the ClassResource, OSGi is not able to load the image.
new Image("", new ClassResource("ImageInBundleB.png"))
The reason is that ClassResource internally will use the classloader of the UI class. And this one does not have a dependency to “Bundle B”. And so the resource can not be loaded because it is not visible to the classloader.
Using a BundleResource
I prepared a BundleResource prototype. It is licensed under the Apache License and is available at the Lunifera github repo.
new Image("", new BundleResource(theBundle, "resources/ImageInBundleB.png"))
The path to the resource is the path relative to the bundle. In this sample, the “ImageInBundleB.png” is located in a folder called “resources”.
Feel free to use the BundleResource for your own projects.
If a resource could not be found by the “ClassResource”, one just has to debug the OSGi classloader. If you are using Equinox, take a look at
org.eclipse.osgi.internal.loader.BundleLoader. It is responsible to load the resource.