Blogs
Short explanation
If you want to convert a third party library of your portlets (modules) into a Bundle OSGi to be used by all modules, you could create a new module with your third party library and dependences and be used by your modules.
To explain this we will use JasperReport third party library. Supose that you have a module that uses JasperReport to generate PDF files. Probably you have these dependencies in the build.gradle file of your module:
/* POI */
compile group: 'org.apache.poi', name: 'poi', version: '3.15'
/* JasperReport */
compile group: 'net.sf.jasperreports', name: 'jasperreports', version: '5.6.1'
compile group: 'commons-digester', name: 'commons-digester', version: '2.1'
compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.0.1'
compile group: 'com.lowagie', name: 'itext', version: '2.1.7.js2'
Import-Package: \*;resolution:=optionalBundle-ClassPath:\.,\lib/poi-3.15.jar,\lib/jasperreports-5.6.1.jar,\lib/commons-digester-2.1.jar,\lib/groovy-all-2.0.1.jar,\lib/itext-2.1.7.js2.jarInclude-Resource:\lib/poi-3.15.jar=poi-3.15.jar,\lib/jasperreports-5.6.1.jar=jasperreports-5.6.1.jar,\lib/commons-digester-2.1.jar=commons-digester-2.1.jar,\lib/groovy-all-2.0.1.jar=groovy-all-2.0.1.jar,\lib/itext-2.1.7.js2.jar=itext-2.1.7.js2.jar-metatype: *
repositories {mavenCentral()maven{url "http://jasperreports.sourceforge.net/maven2/"}maven{url "http://jaspersoft.artifactoryonline.com/jaspersoft/third-party-ce-artifacts/"}}configurations {jasperreports {transitive = true}}dependencies {compileOnly group: "org.osgi", name: "org.osgi.core", version: "6.0.0"compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations", version: "1.3.0"/* JasperReport */compile group: 'net.sf.jasperreports', name: 'jasperreports', version: '5.6.1'compile group: 'commons-digester', name: 'commons-digester', version: '2.1'compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.0.1'compile group: 'com.lowagie', name: 'itext', version: '2.1.7.js2'compile group: 'org.apache.poi', name: 'poi', version: '3.7'}
Bundle-Name: jasperreportsBundle-SymbolicName: jasperreportsBundle-Version: 1.0.0Export-Package: *;-split-package:=merge-last;-noimport:=trueImport-Package: \*;resolution:=optionalBundle-ClassPath:\.,\lib/poi-3.7.jar,\lib/jasperreports-5.6.1.jar,\lib/commons-digester-2.1.jar,\lib/groovy-all-2.0.1.jar,\lib/itext-2.1.7.js2.jarInclude-Resource:\lib/poi-3.7.jar=poi-3.7.jar,\lib/jasperreports-5.6.1.jar=jasperreports-5.6.1.jar,\lib/commons-digester-2.1.jar=commons-digester-2.1.jar,\lib/groovy-all-2.0.1.jar=groovy-all-2.0.1.jar,\lib/itext-2.1.7.js2.jar=itext-2.1.7.js2.jar-metatype: *
compileOnly project(":modules:jasperreports")
Long explanation
- Pass your dependencies configuration from build.gradle file of your module to build.gradle file of the new library module. This will be necesary for download your third party library on your new library module.
repositories {mavenCentral()maven{url "http://jasperreports.sourceforge.net/maven2/"}maven{url "http://jaspersoft.artifactoryonline.com/jaspersoft/third-party-ce-artifacts/"}}configurations {jasperreports {transitive = true}}dependencies {compileOnly group: "org.osgi", name: "org.osgi.core", version: "6.0.0"compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations", version: "1.3.0"/* JasperReport */compile group: 'net.sf.jasperreports', name: 'jasperreports', version: '5.6.1'compile group: 'commons-digester', name: 'commons-digester', version: '2.1'compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.0.1'compile group: 'com.lowagie', name: 'itext', version: '2.1.7.js2'compile group: 'org.apache.poi', name: 'poi', version: '3.7'}
- Pass your dependencies configuration from bnd.bnd file of your module to bnd.bnd file of the new library module. This will be necesary for load your third party library on your new library module jar file.
Bundle-Name: jasperreportsBundle-SymbolicName: jasperreportsBundle-Version: 1.0.0Import-Package: \*;resolution:=optionalBundle-ClassPath:\.,\lib/poi-3.7.jar,\lib/jasperreports-5.6.1.jar,\lib/commons-digester-2.1.jar,\lib/groovy-all-2.0.1.jar,\lib/itext-2.1.7.js2.jarInclude-Resource:\lib/poi-3.7.jar=poi-3.7.jar,\lib/jasperreports-5.6.1.jar=jasperreports-5.6.1.jar,\lib/commons-digester-2.1.jar=commons-digester-2.1.jar,\lib/groovy-all-2.0.1.jar=groovy-all-2.0.1.jar,\lib/itext-2.1.7.js2.jar=itext-2.1.7.js2.jar-metatype: *
- Then you will need to add a little configuration to Export-Package parameter.
Export-Package: *;-split-package:=merge-last;-noimport:=true
-split-package:=merge-last directive on Export-Package allows fine grained control over what should be done with split packages, merging split packages but overwriting resources that come earlier in the classpath. That is, the last resource wins.
-noimport:=true directive on Export-Package disables automatically import packages by Export-Package directive.
When you build your new library module you will create a jar file with your third party library unzipped into your module and the configuration you need on MANIFEST.MF to use your library on other modules.
Then you will need add the dependency of your new library module into other module these use it:
compileOnly project(":modules:jasperreports")
You can use my example liferay workspace (https://github.com/ironcero/jasperreports-bundle-test).
Edited
As Miroslav said you could simplify this configuration changing the compile directives on your gradle file (build.gradle):
compileInclude group: 'org.apache.poi', name: 'poi', version: '3.7'
With compileInclude Gradle will copy the library jar file in lib folder inside the module jar file. I Built some diferent scenarios trying to understand the diferences between compileOnly, compile and compileInclude. In gradle for OSGi bundle I haven't found any diferences between compileOnly and compile:
For this build.gradle file:
repositories {mavenCentral()maven{url "http://jasperreports.sourceforge.net/maven2/"}maven{url "http://jaspersoft.artifactoryonline.com/jaspersoft/third-party-ce-artifacts/"}}configurations {jasperreports {transitive = true}}dependencies {compileOnly group: "org.osgi", name: "org.osgi.core", version: "6.0.0"compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations", version: "1.3.0"/* JasperReport */compileOnly group: 'net.sf.jasperreports', name: 'jasperreports', version: '5.6.1'compile group: 'commons-digester', name: 'commons-digester', version: '2.1'compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.0.1'compile group: 'com.lowagie', name: 'itext', version: '2.1.7.js2'compile group: 'org.apache.poi', name: 'poi', version: '3.7'// https://mvnrepository.com/artifact/org.apache.commons/commons-math3compileInclude group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'}
Where net.sf.jasperreports.jasperreports library is configured as compileOnly; org.codehaus.groovy.groovy-all is configured as compile; and org.apache.commons.commons-math3 is configured as compileInclude.
To complete the example, bnd.bnd was:
Bundle-Name: jasperreports
Bundle-SymbolicName: jasperreports
Bundle-Version: 1.0.0
-metatype: *
Result was that only commons-math3 library was included on lib filder inside the bundle jar file (logical). And this was MANIFEST.MF file:
Manifest-Version: 1.0Bnd-LastModified: 1516660109235Bundle-ClassPath: .,lib/commons-math3-3.6.1.jarBundle-ManifestVersion: 2Bundle-Name: jasperreportsBundle-SymbolicName: jasperreportsBundle-Version: 1.0.0Created-By: 1.8.0_73 (Oracle Corporation)Import-Package: net.sf.jasperreports.components.table,org.codehaus.groovy.ast.builder;version="[2.0,3)"Javac-Debug: onJavac-Deprecation: offJavac-Encoding: Cp1252Private-Package: jasperreports,jasperreports.api;version="1.0.0",lib,assets.org.apache.commons.math3.exception.util,assets.org.apache.commons.math3.random,org.apache.commons.math3,org.apache.commons.math3.analysis,org.apache.commons.math3.analysis.differentiation,org.apache.commons.math3.analysis.function,org.apache.commons.math3.analysis.integration,org.apache.commons.math3.analysis.integration.gauss,org.apache.commons.math3.analysis.interpolation,org.apache.commons.math3.analysis.polynomials,org.apache.commons.math3.analysis.solvers,org.apache.commons.math3.complex,org.apache.commons.math3.dfp,org.apache.commons.math3.distribution,org.apache.commons.math3.distribution.fitting,org.apache.commons.math3.exception,org.apache.commons.math3.exception.util,org.apache.commons.math3.filter,org.apache.commons.math3.fitting,org.apache.commons.math3.fitting.leastsquares,org.apache.commons.math3.fraction,org.apache.commons.math3.genetics,org.apache.commons.math3.geometry,org.apache.commons.math3.geometry.enclosing,org.apache.commons.math3.geometry.euclidean.oned,org.apache.commons.math3.geometry.euclidean.threed,org.apache.commons.math3.geometry.euclidean.twod,org.apache.commons.math3.geometry.euclidean.twod.hull,org.apache.commons.math3.geometry.hull,org.apache.commons.math3.geometry.partitioning,org.apache.commons.math3.geometry.partitioning.utilities,org.apache.commons.math3.geometry.spherical.oned,org.apache.commons.math3.geometry.spherical.twod,org.apache.commons.math3.linear,org.apache.commons.math3.ml.clustering,org.apache.commons.math3.ml.clustering.evaluation,org.apache.commons.math3.ml.distance,org.apache.commons.math3.ml.neuralnet,org.apache.commons.math3.ml.neuralnet.oned,org.apache.commons.math3.ml.neuralnet.sofm,org.apache.commons.math3.ml.neuralnet.sofm.util,org.apache.commons.math3.ml.neuralnet.twod,org.apache.commons.math3.ml.neuralnet.twod.util,org.apache.commons.math3.ode,org.apache.commons.math3.ode.events,org.apache.commons.math3.ode.nonstiff,org.apache.commons.math3.ode.sampling,org.apache.commons.math3.optim,org.apache.commons.math3.optim.linear,org.apache.commons.math3.optim.nonlinear.scalar,org.apache.commons.math3.optim.nonlinear.scalar.gradient,org.apache.commons.math3.optim.nonlinear.scalar.noderiv,org.apache.commons.math3.optim.nonlinear.vector,org.apache.commons.math3.optim.nonlinear.vector.jacobian,org.apache.commons.math3.optim.univariate,org.apache.commons.math3.optimization,org.apache.commons.math3.optimization.direct,org.apache.commons.math3.optimization.fitting,org.apache.commons.math3.optimization.general,org.apache.commons.math3.optimization.linear,org.apache.commons.math3.optimization.univariate,org.apache.commons.math3.primes,org.apache.commons.math3.random,org.apache.commons.math3.special,org.apache.commons.math3.stat,org.apache.commons.math3.stat.clustering,org.apache.commons.math3.stat.correlation,org.apache.commons.math3.stat.descriptive,org.apache.commons.math3.stat.descriptive.moment,org.apache.commons.math3.stat.descriptive.rank,org.apache.commons.math3.stat.descriptive.summary,org.apache.commons.math3.stat.inference,org.apache.commons.math3.stat.interval,org.apache.commons.math3.stat.ranking,org.apache.commons.math3.stat.regression,org.apache.commons.math3.transform,org.apache.commons.math3.utilRequire-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"Tool: Bnd-3.2.0.201605172007
You can see that all packages of commons-math3 library were included on Private-package section. However, in Import-Package section only were included the packages used on module source files (net.sf.jasperreports.components.table,org.codehaus.groovy.ast.builder).
The problem come when we want to export these libraries to used outside our modules. Then you will need to add Export-Package section on bnd.bnd file. If you only includes some package in this section, then all classes of first package will be copied in the classpath of bundle jar file independently of compile directive. I think that bnd doesn't know how to build the MANIFEST.MF in this case.
On the other hand, if you includes '*' in Export-Package section, then all classes of all packages includes on your bundle will be copied in the classpath of bundle jar file. If you includes '*' in Export-Package section and same library with compileInclude then all classes of library will be copied in classpath and in lib folder (duplicate).
Conclusion
At last I'm going to try to summarize all common scenarios:
- Add a third party library: If you only need to add one third party library to your bundle you can do easily adding the library with compileInclude in your build.gradle. You won't need anything else.
- Add a third party library that is already in OSGi container: If you only need to add one third party library that is exported by other bundle of your OSGi container you can do easily adding the library with compileOnly in your build.gradle. You won't need anything else.
- Add a third party library that you want to export to another bundle: If you need to include a library in your bundle to used in other bundle (to build a libraries bundle, for example), you will need to add the library to build.gradle with compileInclude and add all packages you need to Export-Package section on bnd.bnd. However if you need to export all package of third party libraries, I think that is better option include these library with compile in build.gradle and add '*' to Export-Package section on bnd.bnd.

