Last week we had an intro webinar to OSGi and using it with Vaadin. OSGi helps you to build modular applications where you can easily add, enable and disable modules at runtime. Most developers who have worked with OSGi projects know a minor pain point in OSGi development. There is a huge set of libraries that don’t support OSGi out of the box. The common workaround among OSGi users is to repackage JAR files with an OSGi compatible MANIFEST.MF file. But in an ideal world, all your libraries, including your Vaadin Add-ons, would already be OSGi bundles that you could just drop into your container.
Making your add-ons truly “OSGi compatible” is not that big a task actually. It is practically just a small set of metadata you need to add to your JAR file. So, even though you wouldn’t use OSGI yourself, it might be a large step for the mankind, or at least for the Vaadin community, if you would do the OSGi dance when building the next release of your Vaadin Add-on. Instructions presented here can be adapted to other Java libraries as well.
Things to keep in mind when aiming for OSGi compliance
Minimum manifest header requirements
OSGi compatible jar files, bundles, contain their metadata in the META-INF/MANIFEST.MF file. There are quite many headers that can be defined, but most of them are optional. It needs to contain at least the following bundle headers. The Bundle-Version is optional, but we highly recommend to add it there.
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Version: 1.2.3.qualifier Bundle-SymbolicName: org.my.vaadinaddon
In addition to the required headers above, you most often also define which external packages are needed (“Import-Package”) and which packages are provided for others (“Export-Package”).
Importing vs. requiring packages
Carefully decide, whether you should use an import package or require a bundle. If you are using the maven bundle plugin, this will happen pretty much automatically, but you must be careful if you are creating your MANIFEST.MF manually.
There is a major rule to do this right: if you expect that different vendors may provide an implementation for an interface, then use “import package”. Most often this may become an issue with logging libraries like org.slf4j. It would be a mistake to add a require bundle header to the manifest file. Then your bundle would require the implementation from org.slf4j. But if you just specify an import package header, then different implementations for org.slf4j, for example logback for slf4j, may be used.
Splitting code to different jar files
If a library is modularized into different jar-files, there are some things to be aware of. First one is to avoid “split packages”. If the same Java package exists in more than one bundle and becomes exported by them, OSGi will not work properly. There are ways to handle this, but it is a better habit to avoid splitting packages altogether. Just use a unique root package name for every jar file.
Cyclic dependencies are another thing that might cause headache for OSGi bundles. In a Java SE environment, you will load classes from the classpath. There it is not an issue if a jar-A needs to load classes from a jar-B and the jar-B loads classes from the jar-A. But in OSGi environment this will not work. This means that the jar-B has a dependency to the jar-A and the jar-A back to the jar-B and that’s called a “cyclic dependency” - try to avoid those.
Making an existing Vaadin add-on OSGi compatible
For Maven based add-ons, adding "OSGi support" is usually quite handy. There is a build plugin that mostly automates the process based on what you have in your dependency list and in your project sources. Let’s go through the required steps we did to “OSGify” the V-Leaflet add-on.
The first thing to do is to add the maven-bundle-plugin to your build and instruct it to export the "public" stuff from your add-on:
<plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.5.3</version> <extensions>true</extensions> <configuration> <instructions> <Export-Package>org.vaadin.addon.leaflet.*</Export-Package> </instructions> </configuration> </plugin>
You should also configure the packaging type of your artifact to "bundle". The actual artifact will still be a good old jar file, so rest assured that this will not affect non-OSGI users. Add the following line to the top of your pom.xml:
If your add-on has no static manifest file and you are making it "Directory compatible" by configuring manifest during build, you'll also need to make the automatic OSGi header generation a bit more "eager". Otherwise that stuff is overridden by the rules you have already defined for the jar plugin. An easy way to do this is to complement your build with the following execution rule:
<plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.5.3</version> <extensions>true</extensions> <configuration> <instructions> <instructions> <Export-Package>org.vaadin.addon.leaflet.*</Export-Package> </instructions> </configuration> <executions> <execution> <id>manifest</id> <phase>process-classes</phase> <goals> <goal>manifest</goal> </goals> </execution> </executions> </plugin>
In an ideal world that would be all and it actually is for most server side add-ons. But due to a compromise we have done in add-on packaging, to simplify GWT compilation of Vaadin add-ons, you most often want to optimize the setup a bit and exclude some client side only stuff that is only needed for the GWT compilation. To exclude those packages from the export list, add them to an Export-Package rule - prefixed with exclamation mark:
Even more essentially, you'll want to exclude a bunch of GWT and Vaadin client side related imports. Excluding everything below com.google.gwt and com.vaadin.client is most often enough, but if you have used a third party GWT library as a dependency, like in this case (org.peimari.gleaflet), exclude that one also. An essential step is to add ",*" to the end of your exclusion list to still import everything relevant. This is what was added for the V-Leaflet add-on:
If you are not trying the add-on in a real OSGi environment yourself, you could at least check that the MANIFEST.MF inside your jar file looks somewhat sane. A bit better method is to load it into some simple container like Karaf and verify there are no errors. The best option is to create an automated integration test that validates your bundle. Tycho and Pax-Exam projects provide tools for setting up integration tests. We will set up some wikipages in the upcoming weeks to provide some best practices for this kind of setup for Vaadin add-ons.
Florian Pirchner is founder of Lunifera GmbH in Austria. He has been working with OSGi and Vaadin for several years. It is a perfect match to create modular and flexible business applications. And integration with IoT technologies like MQTT (Eclipse Paho) makes the technology stack a marriage made in heaven. You can follow him on Twitter – @piflo