Liferay’s OSGi framework lets you build applications composed of multiple OSGi bundles (modules). For the framework to assemble the modules into a working system, the modules must resolve their Java package dependencies. In a perfect world, every Java library would be an OSGi module, but many libraries aren’t. So how do you resolve the packages your project needs from non-OSGi third party libraries?
Here is the main workflow for resolving third party Java library packages:
Option 1 - Find an OSGi module of the library: Projects, such as Eclipse Orbit and ServiceMix Bundles, convert hundreds of traditional Java libraries to OSGi modules. Their artifacts are available at these locations:
Deploying the module to Liferay’s OSGi framework lets you share it on the system. If you find a module for the library you need, deploy it. Then add a compile-only dependency for it in your project. When you deploy your project, the OSGi framework wires the dependency module to your project’s module or web application bundle (WAB). If you don’t find an OSGi module based on the Java library, follow Option 2.
Option 2 - Resolve the Java packages privately in your project: Copy required packages only from libraries into your project, if you can or embed libraries wholesale, if you must. The rest of this article shows you how to do these things.
The recommended package resolution workflow is next.
When you depend on a library JAR, much of the time you only need parts of it. Explicitly specifying only the Java packages you need makes your module more modular. This also keeps other modules that depend on your module from incorporating unneeded packages.
Here’s a configuration workflow for module projects that minimizes dependencies and Java package imports:
Add the library as a compile-only dependency (e.g.,
Copy only the library packages you need by specifying them in a conditional package instruction (
Conditional-Package) in your
bnd.bndfile. Here are some examples:
Conditional-Package: foo.common*adds packages your module uses such as
foo.common-webto your module’s class path.
Conditional-Package: foo.bar.*adds packages your module uses such as
foo.barand all its sub-packages (e.g.,
foo.bar.biz, etc.) to your module’s class path.
Deploy your project. If a class your module needs or class its dependencies need isn’t found, go back to main workflow Step 1 - Find an OSGi module version of the library to resolve it.
Important: Resolving packages by using compile-only dependencies and conditional package instructions assures you use only the packages you need and avoids unnecessary transitive dependencies. It’s recommended to use the steps up to this point, as much as possible, to resolve required packages.
If a library package you depend on requires non-class files (e.g., DLLs, descriptors) from the library, then you might need to embed the library wholesale in your module. This adds the entire library to your module’s classpath.
Next you’ll learn how to embed libraries in your module project.
You can use Gradle or Maven to embed libraries in your project. Below are examples for adding Apache Shiro using both build utilities.
Open your module’s
build.gradle file and add the library as a dependency in
compileInclude group: 'org.apache.shiro', name: 'shiro-core', version: '1.1.0'
compileInclude configuration is transitive.
compileInclude configuration embeds the artifact and all its dependencies
lib folder in the module’s JAR. Also, it adds the artifact JARs to the
Bundle-ClassPath manifest header.
compileInclude configuration does not download transitive
If your module requires such artifacts, add them as you would another third party library.
Note: If the library you’ve added as a dependency in your
file has transitive dependencies, you can reference them by name in an
-includeresource: instruction without having to add them explicitly to the
dependency list. See how it’s used in the Maven section next.
Follow these steps:
Open your project’s
pom.xmlfile and add the library as a dependency in the
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.1.0</version> <scope>provided</scope> </dependency>
Open your module’s
bnd.bndfile and add the library to an
This instruction adds the
shiro-core-[version].jarfile as an included resource in the module’s
META-INF/lib/shiro-core.jaris your module’s embedded library. The expression
[0-9]*helps the build tool match the library version to make available on the module’s class path. The
lib:=truedirective adds the embedded JAR to the module’s class path via the
Lastly, if after embedding a library you get unresolved imports when trying to deploy to Liferay, you might need to blacklist some imports:
* character represents all packages that the module refers to explicitly. Bnd detects the referenced packages.
Congratulations! Resolving all of your module’s package dependencies, especially those from traditional Java libraries, is a quite an accomplishment.