How can I share non-OSGi libraries between bundles in an OSGi container? - osgi

I came across this question when trying to share the Struts packages among multiple bundles inside an OSGi container. I wanted to avoid both duplicating dependencies inside the bundles and introducing a new dependency between them (by having one bundle export its internal dependencies for the other to share).
It turns out the answer is very easy if you happen to use Maven, and if you aren't, you can still use the Maven solution to learn how to create the same result with some other mechanism.
I found this approach useful multiple times during my project, so I'll post an example here.

An additional path for those not so keen on maven, unable to port, or perfectly happy with ant/ivy
I've found the that easiest way to accomplish the stated task is to turn the non-OSGi library into an OSGi library by letting the manifest export every package and add on some approriate symbolic names / versions. I've been able to do this VERY easily with bnd via ant tasks (or even direct command line invocation). There are also repositories which contain "osgi-ified" version of many popular libraries. Some libraries (joda-time) are already shipping with correct OSGi manifests.

Using Maven, it is very easy to create an OSGi bundle from any library. However, I think the same result can be created with other mechanisms, too. The Maven solution helped me understand how it works.
Creating the bundle is done by creating a project which has the library as a dependency and then packaging the project using the maven-bundle-plugin from the Apache Felix project and specifying the library packages with the Export-Package instruction. I used this to share Google Protocol Buffers between bundles inside an OSGi container:
<?xml version="1.0" encoding="UTF-8" ?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.lib</groupId>
<artifactId>protobuf-dist</artifactId>
<version>2.1.0</version>
<name>Google Protocol Buffers OSGi Distribution</name>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>com.google.protobuf</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
If you want all transitive dependencies rolled into the bundle, too, use the bundleall goal of the plugin.
The plugin recognizes and honours existing OSGi manifests in the dependency.
You can also use the bundle plugin to just create the manifest and tell the jar packaging plugin (or the jar-with-dependencies builtin assembly) to use that manifest via the archive section. The plugin's page linked above shows how to do that.

Related

How to embed external jars dependency in OSGI bundles?

I am trying to convert my project into an OSGI application. I have few doubts. Suppose ModuleA in my application is dependent on external jars jarA and jarB. Now to make ModeuleA run, I am embedding both the jars using embed-dependency property of maven-bundle-plugin.
Now suppose I have another module ModuleB which is also dependent on jarA. So this module also embeds the jarA. My project ends up having jarA being embedded 2 times which will unnecessarily bloat the size of project.
Is there any way to tell OSGI to load jarA only once and provide it to both the modules.
If converting these jars to OSGI bundles is the only solution, I have few more questions:
What is the easiest way to convert jar to a bundle. BND tool looks like a good solution but I am not able to find proper documentation about it.
jarA will also have some dependent jars. So do I need to convert all the dependent jars to bundles also. My project has more than 100 jars. How can I automate this process.
Thanks in advance :)
There are actually to solutions to this, both a little bit different from what you are doing right now:
Build one "third party dependencies" bundle, which will embed all of the non OSGi dependencies your project has.
Convert every non OSGi dependency to a OSGi bundle.
Option 1 is easier to handle so I think most projects do this. I, personally, prefer option 2. We have a Maven "pom.xml" template that we use to convert those dependencies.
The "pom.xml" looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<library.groupId></library.groupId>
<library.artifactId></library.artifactId>
<library.version></library.version>
</properties>
<artifactId></artifactId>
<packaging>bundle</packaging>
<name></name>
<description>${library.groupId}:${library.artifactId}:${library.version}</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>*;resolution:=optional</Import-Package>
<Export-Package>*</Export-Package>
<Embed-Dependency>*;scope=compile|runtime;inline=true</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>${library.groupId}</groupId>
<artifactId>${library.artifactId}</artifactId>
<version>${library.version}</version>
</dependency>
</dependencies>
</project>
This does:
Add the non OSGi library as dependency
Tell maven-bundle-plugin to embed this dependency (transitive)
Tell maven-bundle-plugin to export all of the dependencies packages
I left some things blank that you have to set like library.groupId, library.artifactId and library.version. And somethings we need to tweak the configuration of the maven-bundle-plugin. But this is our starting point. Somethings for example, you do not want to export all packages etc.
If you really have 100+ dependencies that you need to convert you might be better of using this template and just adding all of your 100 dependencies as dependency and build one big bundle with all of them inside.
You can find the documentation for the maven-bundle-plugin here:
https://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html
At this point I also want to mention that there is a new bundle plugin that you might want to consider for this: bnd-maven-plugin.
See: https://github.com/bndtools/bnd/tree/master/maven/bnd-maven-plugin

How to deal with dependencies with "provided" scope in OSGi

There are lots of tutorials, which shows how to cope with dependencies of the OSGi project and how should they be converted to the bundle. After more than one day research, I have still not found how to deal with the dependencies with provided scope.
Let me give an example. I am currently using Dropbox (dropbox-core-sdk 3.0) and it has two dependencies (com.google.android and javax.servlet) with provided scope. When I use the techniques such as maven-bundle-plugin or bnd, it only downloads the artifacts and its transitive dependencies. However, I need also provided dependencies in order to be able to import my project to the OSGi container.
I am using maven-bundle-plugin and my pom.xml looks like:
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId};singleton:=true</Bundle-SymbolicName>
<Bundle-Version>${project.version}</Bundle-Version>
<Export-Package>*</Export-Package>
<Embed-Transitive>true</Embed-Transitive>
<Embed-Dependency>*</Embed-Dependency>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
even if Embed-Dependency says include everything, only the dependencies + transitive dependencies are in the jar. However, I want the provided-scoped jars are also in the jar.
Is there any way to download dependencies with provided scope. If not, how to deal with this situation?
I would have to defer to the Maven BND experts out there, but I don't think you can include provided dependencies through a Maven build. Since it is unlikely you will be using the Android components outside of your bundle, couldn't you just manually download the needed Jars and place them in your bundle (Bundle-Classpath)?
I think you can specify the scopes of the dependencies you want to embed. Be careful though that some dependencies like the OSGi spec jars should never be deployed.
In general you may only embed dependencies that are hidden inside the bundle. Any packages that are needed to talk to other bundles should better not deployed.
For example the servlet api is typically provided by the httpservice bundle you use.
Try this option:
<Embed-Dependency>*;scope=compile|provided</Embed-Dependency>

How to configure OSGI in IntelliJ when it's handled by Maven

I'm an OSGI newb.
I can really use any guidance I can get regarding IntelliJ IDEA / OSGI / Maven / Sling.
So the actual Felix plugin dies when I load it. Apparently it hasn't been maintained and is no longer compatible with the latest release by which I mean IntelliJ IDEA 13.
So I've configured the framework to felix-framework-4.2.1, and that seems to work fine. My greatest concern is that if I apply the OSGI facet to a bundle, the settings seem to indicate that it will change the bundle. Since we have this set up in Maven, I don't think we want this. The source of the facet seems to be the Osmorc plugin. When I used it before, there were complaints about some packages in maven that weren't OSGI enabled and the IDE wanted to point to a special Spring repository for OSGI enabled jar dependencies.
Since we are doing this in Maven, should I even bother with Osmorc? Is there a better way to manage OSGI in IntelliJ IDEA? It is handy knowing which packages are OSGI enabled but an error for that? Really? Specifically I am referring to "The package is not exported by the bundle dependencies" showing up on imports and annotations.
My personal observation with Intellij IDEA 13 is that the OSGI project inspector is slightly more aggressive when it comes to profiling your classes that utilize non-osgi exported classes. That being said, a way around this is by adjusting the inspector severity level. This enables you to use the same OSGI-based approach you were using in Intellij IDEA 12.
To do this, go into your project settings (on Mac: Command+,) and then navigate to the following node:
Inspections --> OSGI --> Package accessibility
Once selected, you'll be able to change the severity level from error to warning.
Performing this change is requisite on a few changes in your pom.xml:
<dependencies>
.
.
<dependency>
<groupId>com.pkg.name</groupId>
<artifactId>some-non-osgi-artifact</artifactId>
<version>0.1-EXAMPLE</version>
</dependency>
</dependencies>
<build>
<plugins>
.
.
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${maven-bundle-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>${project.version}</Bundle-Version>
<Export-Package>
you.know.what.goes.here
</Export-Package>
<Private-Package>you.know.what.goes.here</Private-Package>
<Import-Package>
*
</Import-Package>
<Embed-Dependency>some-non-osgi-artifact;scope=compile|runtime;inline=false</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
<Embed-StripGroup>true</Embed-StripGroup>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
Hope this helps,
Ajay
I think your best bet currently is to use the maven bundle plugin to manage your imports and exports. This means intellij will simply see your bundles as maven projects. Still the correct jars should result. I handle OSGi bundles the same way in eclipse and it works fine.
I also read on the OSGi dev mailing list that there is a bndtools for intellij planned but this will for sure take a while.

install osgi dependencies via maven bundle and sling plugins

I have an osgi-bundle which is created by using the maven-bundle-plugin:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
</instructions>
</configuration>
</plugin>
The bundle is installed via the maven-sling-plugin.
I have a fasterxml.jackson dependency which exists as an osgi-bundle in my .m2 repo and acts as a dependency in my project.
How can I make maven deploy this dependency as an osgi-bundle as well?
At the moment I have to install it manually in my osgi-container.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.2.2</version>
</dependency>
EDIT:
Simplification:
How do I make maven realize that an osgi-bundle which I refer to as a dependency in my pom.xml shall be installed in the osgi-container along with the bundles that depend upon it?
You'll probably find its best to package your bundle as part of your application along with the /content, /apps portions of your application in the /apps/myapp/install folder will cause the libraries to be installed as bundles by the jcr installer provider.
There are a couple of ways of automating packaging/deployment of applications using maven described in these articles:
http://www.cognifide.com/blogs/cq/maven-plugin-automating-deployments-of-crx-cq-applications/
http://mkalugin-cq.blogspot.co.uk/2012/11/how-to-use-maven-project-to-create.html
http://labs.sixdimensions.com/blog/dklco/2012-05-03/introducing-cq-deploy-maven-plugin-deploying-cq-projects
http://dev.day.com/docs/en/cq/current/core/how_to/how_to_use_the_vlttool/vlt-mavenplugin.html
Alternatively, you could just use the CRXDE to place the bundles and then CRX Package Manager to test the approach, them move on to automated packaging later.
If you don't want to repackage the two OSGI bundles, you can use the sling maven plugin to install the jackson bundle directly
mvn org.apache.sling:maven-sling-plugin:install-file -Dsling.file=jackson-databind-2.2.2.jar

What is the difference in Maven between dependency and plugin tags in pom.xml?

Created project with Spring, Hibernate & Maven. My question is what is the logic behind plugin versus dependency ?
Both plugins and dependencies are Jar files.
But the difference between them is, most of the work in maven is done using plugins; whereas dependency is just a Jar file which will be added to the classpath while executing the tasks.
For example, you use a compiler-plugin to compile the java files. You can't use compiler-plugin as a dependency since that will only add the plugin to the classpath, and will not trigger any compilation. The Jar files to be added to the classpath while compiling the file, will be specified as a dependency.
Same goes with your scenario. You have to use spring-plugin to execute some spring executables [ I'm not sure what spring-plugins are used for. I'm just taking a guess here ]. But you need dependencies to execute those executables. And Junit is tagged under dependency since it is used by surefire-plugin for executing unit-tests.
So, we can say, plugin is a Jar file which executes the task, and dependency is a Jar which provides the class files to execute the task.
Hope that answers your question!
Maven itself can be described as food processor which has many different units that can be used to accomplish different tasks. Those units are called plugins. For example, to compile your project maven uses maven-compiler-plugin, to run tests - maven-surefire-plugin and so on.
Dependency in terms of maven is a packaged piece of classes that your project depends on. It can be jar, war etc. For example, if you want to be able to write JUnit test, you'll have to use JUnit annotations and classes thus you have to declare that your project depends on JUnit.
Plugins and dependencies are very different things and these are complementary.
What plugins are ?
Plugins perform tasks for a Maven build. These are not packaged in the application.
These are the heart of Maven.
Any task executed by Maven is performed by plugins.
There are two categories of plugins : the build and the reporting plugins :
Build plugins will be executed during the build and they should be configured in the <build/> element from the POM.
Reporting plugins will be executed during the site generation and they should be configured in the <reporting/> element from the POM.
According to the maven goal specified in the command line (for example mvn clean, mvn clean package or mvn site) , a specific lifecyle will be used and a specific set of plugins goals will be executed.
There are three built-in build lifecycles: default, clean and site. The default lifecycle handles your project deployment, the clean lifecycle handles project cleaning, while the site lifecycle handles the creation of your project's site documentation.
A plugin goal may be bound to a specific phase of a specific lifecyle.
For example the maven-compiler-plugin binds by default the compile goal to the lifecycle phase: compile.
Most of maven plugins (both core plugins and third party plugins) favor convention over configuration. So these generally bound a plugin goal to a specific phase to make their usage simpler.
That is neater and less error prone :
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
than :
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
What dependencies are ?
Dependencies are Maven artifacts/components required for the project.
Concretely most of dependencies are jar (that is libraries) but these may also be other kinds of archives : war, ear, test-jar, ejb-client ... or still POM or BOM.
In a pom.xml, dependencies may be specified at multiple places : the <build><dependencies> part , the dependencies management part or still in a plugin declaration ! Indeed some plugins may need to have some dependencies in the classpath during their execution. That is not common but that may happen.
Here is an example from the documentation that shows that plugin and dependency may work together :
For instance, the Maven Antrun Plugin version 1.2 uses Ant version
1.6.5, if you want to use the latest Ant version when running this plugin, you need to add <dependencies> element like the following:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.2</version>
...
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
...
</project>
In Maven, dependencies are referenced in a specific format :
groupId:artifactId:packaging:classifier:version.
The classifier (that is optional) and the packaging (JAR by default) are not commonly specified. So the common format in the dependency declaration is rather : groupId:artifactId:version.
Here is an example of dependency declared in the <build><dependencies> part :
<build>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.14.Final</version>
</dependency>
<dependencies>
</build>
Dependency doesn't have a phase binding as plugins to address the "when" question.
But it has a counterpart : the scope.
Indeed declared dependencies are usable by the application at a specific time according to the scope we defined for these.
The scope is a central concept about how a dependency will be visible for the project.
The default scope is compile. That is the most commonly needed scope (convention over configuration again).
The compile scope means that the dependency is available in all classpaths of a project.
The scope defines in which classpaths the dependency should be added.
For example do we need it at compile and runtime, or only for tests compilation and execution ?
For example we previously defined Hibernate as a compile dependency as we need it everywhere : source compilation, test compilation, runtime and so for....
But we don't want that testing libraries may be packaged in the application or referenced in the source code. So we specify the test scope for them :
<build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
<scope>test</scope>
</dependency>
<dependencies>
</build>
One line answer - basic understanding
Plugin is a tool you use at the execution of your maven build
Dependency means kind of any library which you will use in your code
If you're coming from a front-end background like me, and are familiar with Grunt and npm, think of it like this:
First you would run, say, npm install grunt-contrib-copy --save-dev. This is like maven's <dependency></dependency>. It downloads the files needed to execute a build task.
Then you would configure the task in Gruntfile.js
copy: {
main: {
src: 'src/*',
dest: 'dest/',
},
}
This is like maven's <plugin>/<plugin>. You are telling the build tool what to do with the code downloaded by npm/<dependency></dependency>.
Of course this is not an exact analogy, but close enough to help wrap your head around it.
Plug-ins are used for adding functionalities to Maven itself (like adding eclipse support or SpringBoot support to Maven etc.). Dependencies are needed by your source code to pass any Maven phase (compile or test for example). In case of JUnit since the test code is basically part of your code base and you call JUnit specific commands inside test suites and those commands are not provided by Java SDK therefore JUnit must be present at the time Maven is in the test phase and this is handled by mentioning JUnit as a dependency in your pom.xml file.
In simple words:
Plugins are used to add some additonal features to the software/tools(like Maven). Maven will use the added plugins at the time of building when we use the build command.
Dependecies are used to add some addtional code to your source code, so a dependency will make some extra code (like Classes in Java) in the form of library available for your source code.
Maven at its heart is a plugin execution framework -- as per formal and standard compact definition. To make it more clear, the commands you use like maven-install/clean/compile/build etc for creating/executing jars, which we sometimes manually run too. So, the things which you want to run (or configure or execute) you basically put them in dependency tag of mavens pom and the answer so as to who will run these dependencies (required for environment setup) be the plugins.
javac (compiler) dependency.java (dependency)
A plugin is an extension to Maven, something used to produce your artifact (maven-jar-plugin for an example, is used to, you guess it, make a jar out of your compiled classes and resources).
A dependency is a library that is needed by the application you are building, at compile and/or test and/or runtime time.

Resources