{"id":385,"date":"2015-11-21T21:09:27","date_gmt":"2015-11-21T21:09:27","guid":{"rendered":"http:\/\/www.pnambic.com\/home\/?p=385"},"modified":"2015-12-08T22:42:44","modified_gmt":"2015-12-08T22:42:44","slug":"using-maven-to-bundle-non-osgi-third-party-libraries","status":"publish","type":"post","link":"http:\/\/www.pnambic.com\/home\/2015\/11\/21\/using-maven-to-bundle-non-osgi-third-party-libraries\/","title":{"rendered":"Using Maven to Bundle Non-OSGi Third-Party Libraries"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">One of the delights of modern software development is the rich availability of third-party libraries. Maven has been at the forefront on this, with a nearly endless supply of components at <a href=\"http:\/\/central.sonatype.org\/\">The Central Repository<\/a>.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Unfortunately, without special adaptations, many of the these libraries cannot used directly in an OSGi platform like Eclipse. \u00a0OSGi mandates specific information within the distribution JAR-files. \u00a0Although some libraries, like <a href=\"http:\/\/mvnrepository.com\/artifact\/com.google.guava\/guava\">Gauva<\/a>, include this data but there are many that do not. \u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><a href=\"http:\/\/google.github.io\/depan\/\">DepAn<\/a>, as an Eclipse RCP application, uses the OSGi model for plug-in components. \u00a0This provides excellent control for libraries that are packaged with OSGi metadata. However, of the 8 third-party libraries used by DepAn, \u00a06 of them are as simple JAR-files without any of the requisite metadata. This is only the start. Regardless of the technical challenges, easy integration of third-party graph analysis packages is an essential requirement for DepAn.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">With the Maven <tt>bundle<\/tt> &lt;packaging&gt; plugin, it is straightforward to encapsulate a third-party JAR-file for OSGi use. \u00a0Although this is a repackaging of the JAR-file, it does not modify the distribution JAR-file in any way. \u00a0The distribution JAR-file is embedded in an outer wrapper. The wrapper describes the OSGi access rules and places the original distribution JAR-file on the wrapper JAR-file\u2019s classpath. \u00a0Since the Eclipse OSGi implementation can access these embedded JAR-files, the capabilities of the encapsulated third party library are readily available.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Once these third-party libraries are packaged up, dependent modules can use them through the standard Maven process of including the wrapping artifact in the module\u2019s dependencies:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">You simply define the version in your build\u2019s parent:<\/span><\/p>\n<pre><span style=\"font-weight: 400;\"> \u00a0&lt;properties&gt;\r\n <\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;depan.jung.version&gt;0.0.1-SNAPSHOT&lt;\/depan.jung.version&gt;\r\n <\/span><span style=\"font-weight: 400;\"> \u00a0&lt;\/properties&gt;<\/span><\/pre>\n<pre><span style=\"font-weight: 400;\"> \u00a0&lt;dependencyManagement&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;dependencies&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0&lt;dependency&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;groupId&gt;com.pnambic.depan&lt;\/groupId&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;artifactId&gt;depan-jung-library&lt;\/artifactId&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;version&gt;${jung.version}&lt;\/version&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/dependency&gt;\r\n\r\n<\/span><span style=\"font-weight: 400;\">\u00a0 \u00a0&lt;\/dependencies&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0&lt;\/dependencyManagement&gt;<\/span><\/pre>\n<p><span style=\"font-weight: 400;\">And declare the dependency in each dependent module:<\/span><\/p>\n<pre><span style=\"font-weight: 400;\"> \u00a0&lt;dependencies&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;dependency&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0\u00a0\u00a0&lt;groupId&gt;com.pnambic.depan&lt;\/groupId&gt;\r\n<\/span><span style=\"font-weight: 400;\">\u00a0 \u00a0 \u00a0 &lt;artifactId&gt;depan-jung-library&lt;\/artifactId&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;\/dependency&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0&lt;\/dependencies&gt;<\/span><\/pre>\n<p><span style=\"font-weight: 400;\">Presto-Buildo. \u00a0The third-party libraries are available to other components in the OSGi framework without changes to the underlying distribution files. \u00a0There are some configuration tricks to enable this, and these are covered in the following section.<\/span><\/p>\n<h2>Bundling \u00a0JUNG libraries for OSGi usage<\/h2>\n<p><span style=\"font-weight: 400;\">To expand on this example, DepAn relies on the JUNG libraries for several graph layout strategies. \u00a0This third party actually consists of several JAR-files from UC Irvine and it in turn relies on numerous other\u00a0third-party libraries \u00a0The entire collection of JUNG JAR-files and their dependencies are packaged as as single OSGi compliant bundle.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The Maven Bundle Plugin <a href=\"http:\/\/felix.apache.org\/documentation\/subprojects\/apache-felix-maven-bundle-plugin-bnd.html\" target=\"_blank\">reference<\/a> provides lots of detail, and some of the more advanced feature can be really useful. \u00a0Most <tt>&lt;instruction&gt;<\/tt> elements get translated to OSGi <tt>MANIFEST.MF<\/tt> sections, but some provide guidance about the packaging process.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Define the Third-Party Library Dependencies<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">The Maven file to build the OSGi-compliant bundle is fairly straightforward. \u00a0Since the bundle components are specific to the product, I identify the version numbers of the included third-party libraries in the POM for the wrapper bundle.<\/span><\/p>\n<pre><span style=\"font-weight: 400;\"> \u00a0&lt;properties&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;collections-generic.version&gt;4.01&lt;\/collections-generic.version&gt;\r\n<\/span><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;jung.version&gt;2.0.1&lt;\/jung.version&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0&lt;\/properties&gt;<\/span><\/pre>\n<p><span style=\"font-weight: 400;\">The released wrapper bundle artifact should also be numbered as 2.0.1 (<tt>&lt;jung.version&gt;<\/tt>), but that has to be arranged separately.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The dependencies element lists all of the Maven artifact dependencies for JUNG.<\/span><\/p>\n<pre><span style=\"font-weight: 400;\"> \u00a0&lt;dependencies&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;groupId&gt;net.sourceforge.collections&lt;\/groupId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;artifactId&gt;collections-generic&lt;\/artifactId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;version&gt;${collections-generic.version}&lt;\/version&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;\/dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;groupId&gt;net.sf.jung&lt;\/groupId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;artifactId&gt;jung2&lt;\/artifactId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;version&gt;${jung.version}&lt;\/version&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;type&gt;pom&lt;\/type&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;\/dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;groupId&gt;net.sf.jung&lt;\/groupId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;artifactId&gt;jung-algorithms&lt;\/artifactId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;version&gt;${jung.version}&lt;\/version&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;\/dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;groupId&gt;net.sf.jung&lt;\/groupId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;artifactId&gt;jung-api&lt;\/artifactId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;version&gt;${jung.version}&lt;\/version&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;\/dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;groupId&gt;net.sf.jung&lt;\/groupId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;artifactId&gt;jung-graph-impl&lt;\/artifactId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;version&gt;${jung.version}&lt;\/version&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;\/dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0&lt;\/dependencies&gt;<\/span><\/pre>\n<p><span style=\"font-weight: 400;\">The precise list of dependencies will depend on the third-party library that you are trying to integration. \u00a0MavenCentral often has good dependency information, but some trial and error may be required before the full set of deployment components can be identified.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Configuring the Bundle Plugin<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">The next step is to configure the Maven bundle plugin to put the correct data in the target JAR-file\u2019s <tt>META_INF\/MANIFEST.MF<\/tt>.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The <tt>&lt;Embed-Dependency&gt;<\/tt> element does most of the work. \u00a0This causes the output JAR-file to include the JAR-files from the dependencies, and it adds each of the embedded JAR-files to the bundle\u2019s class path.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The other part that requires attention is the <tt>&lt;Export-Package&gt;<\/tt> element. \u00a0This should be as small a set of package names as possible. \u00a0For example, in DepAn\u2019s OSGi wrapper for JOGL, only 6 of the top-level API packages are exposed. \u00a0For JUNG, only a subset of all the packages are available to dependent modules.<\/span><\/p>\n<pre><span style=\"font-weight: 400;\"> \u00a0&lt;build&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;plugins&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;plugin&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0&lt;groupId&gt;org.apache.felix&lt;\/groupId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0&lt;artifactId&gt;maven-bundle-plugin&lt;\/artifactId&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0&lt;version&gt;${bundle.version}&lt;\/version&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0&lt;extensions&gt;true&lt;\/extensions&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0&lt;configuration&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;instructions&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;Bundle-SymbolicName&gt;${project.artifactId}&lt;\/Bundle-SymbolicName&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;Bundle-Vendor&gt;Pnambic Computing&lt;\/Bundle-Vendor&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;Embed-Dependency&gt;*&lt;\/Embed-Dependency&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;Export-Package&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0edu.uci.ics.jung.algorithms.importance,\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0edu.uci.ics.jung.algorithms.layout,\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0edu.uci.ics.jung.algorithms.util,\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0edu.uci.ics.jung.graph,\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0org.apache.commons.collections15,\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0org.apache.commons.collections15.map\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;\/Export-Package&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;_nouses&gt;true&lt;\/_nouses&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;_removeheaders&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0Embed-Dependency,\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0Embed-Artifacts,\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0Import-Package,\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0Private-Package\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;\/_removeheaders&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0&lt;\/instructions&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0 \u00a0&lt;\/configuration&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0 \u00a0&lt;\/plugin&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0 \u00a0&lt;\/plugins&gt;\r\n<\/span><span style=\"line-height: 1.5;\">\u00a0&lt;\/build&gt;<\/span><\/pre>\n<p><span style=\"font-weight: 400;\">Most configuration for the bundle &lt;packaging&gt; type is dependent on the third-party library that is being bundled, and is properly associated with the bundling pom.xml file itself. \u00a0The only item that is configured in the parent module is the version of the bundling plugin to use:<\/span><\/p>\n<pre><span style=\"font-weight: 400;\"> \u00a0\u00a0\u00a0&lt;bundle.version&gt;2.5.3&lt;\/bundle.version&gt;<\/span><\/pre>\n<h2><span style=\"font-weight: 400;\">Guidelines, Advice, and Alternatives<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">I recommend leaving the wrapper bundle\u2019s version number as <tt>-SNAPSHOT<\/tt> until you refine the <tt>&lt;Export-Package&gt;<\/tt> list. \u00a0Once that is as small as possible for your application, it might as well be released with the version number of the underlying third-party library <tt>${jung.version}<\/tt>. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">In strict release regimens, teams\u00a0might be consider adding their own customization and sequencing suffixes (e.g. <tt>-blix-201511a<\/tt>).<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the delights of modern software development is the rich availability of third-party libraries. Maven has been at the forefront on this, with a nearly endless supply of components at The Central Repository. Unfortunately, without special adaptations, many of the these libraries cannot used directly in an OSGi platform like Eclipse. \u00a0OSGi mandates specific<a class=\"moretag\" href=\"http:\/\/www.pnambic.com\/home\/2015\/11\/21\/using-maven-to-bundle-non-osgi-third-party-libraries\/\"> [&#8230;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[24],"tags":[],"class_list":["post-385","post","type-post","status-publish","format-standard","hentry","category-tutorial"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6YqIw-6d","_links":{"self":[{"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/posts\/385","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/comments?post=385"}],"version-history":[{"count":5,"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/posts\/385\/revisions"}],"predecessor-version":[{"id":535,"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/posts\/385\/revisions\/535"}],"wp:attachment":[{"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/media?parent=385"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/categories?post=385"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.pnambic.com\/home\/wp-json\/wp\/v2\/tags?post=385"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}