oo

Importing Packages

You often find yourself in a position of needing functionality provided by another module. To access this functionality, you must import packages from other modules into your module’s classpath. This requires that those other modules have already exported their packages containing the functionality you want. The OSGi framework wires the packages to the importing module’s classpath. The module JAR’s META-INF/MANIFEST.MF file uses the Import-Package OSGi header to import packages.

Import-Package: javax.portlet,com.liferay.portal.kernel.util

Import packages must sometimes be specified manually, but not always. Conveniently, Workspace-based module projects automatically detect required packages and add them to the module manifest’s package import list. Import packages must sometimes be specified manually.

There are two different package import scenarios:

Read below to explore how package imports are specified in these scenarios.

Automatic Package Imports

Workspace-based projects from the tutorial examples (see Module Projects) or created using Blade CLI or Liferay Developer Studio use Bnd. A Gradle plugin invokes Bnd, which can then read the Gradle dependencies and resolve the imports. When you build the project’s JAR, Bnd detects the packages the module uses, generates a META-INF/MANIFEST.MF file, and assigns the packages to an Import-Package header. In that sense, package import is automatic, because you must only define your dependencies in one place: the build script.

note

Liferay’s project templates use a third-party Gradle plugin to invoke Bnd.

The Gogo Command Sample’s build.gradle, for example, uses packages from com.liferay.portal.kernel and org.osgi.service.component.annotations. Here’s the sample’s build.gradle file:

dependencies {
    compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"
    compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations"
}

And here’s the Import-Package header that Bnd generates in sample JAR META-INF/MANIFEST.MF file:

Import-Package: com.liferay.portal.kernel.service;version="[4.3,5)"

The build file specifies dependencies. Bnd examines the module classpath to import packages the module uses. The examination includes all classes found in the classpath—even those from embedded third party library JARs.

note

For a plugin WAR project, Liferay’s WAB Generator detects packages used in the WAR’s JSPs, descriptor files, and classes (in WEB-INF/classes and embedded JARs). Also the WAB Generator searches the web.xml, liferay-web.xml, portlet.xml, liferay-portlet.xml, and liferay-hook.xml descriptor files. It adds package imports for classes that are neither found in the plugin’s WEB-INF/classes folder nor in its embedded JARs.

Manual Package Imports

If a module references a class in only the following places, you must manually add a package import.

  • Unrecognized descriptor file
  • Custom or unrecognized descriptor element or attribute
  • Reflection code
  • Classloader code

Here’s how to manually import the package:

  1. Open your module’s bnd.bnd file.

  2. Add the Import-Package header.

  3. Add the package to the header’s package list.

Import-Package: [... existing package list,][add the package here]
note

To manually import a package in a plugin WAR project, add an Import-Package header like the one above to the project’s WEB-INF/liferay-plugin-package.properties file.

Java API Packages

Packages for Java APIs, such as Java Portlet, aren’t semantically versioned but have Portable Java Contracts. Each API’s contract specifies the JSR it satisfies. Modules that use these APIs must specify requirements on the API contracts. The contract requirement defines the module’s relationship with the imported API packages. If the system you’re running does not provide the exact contract, the module does not resolve. Resolving a missing package is better than handling an incompatibility failure during execution.

Workspace-based projects specify Portable Java Contracts automatically! For example, if your module uses the Java Portlet API and you compile against the Java Portlet 2.0 artifact, a contract requirement for the package is added to your module’s manifest.

note

WAR projects don’t use Bnd and must specify contracts in their WEB-INF/liferay-plugin-package.properties file. For example, here’s the specified contract for JavaPortlet 2.0: Import-Package: javax.portlet Require-Capability: osgi.contract;filter:=(&(osgi.contract=JavaPortlet)(version=2.0))

Congratulations! Now you can import all kinds of packages for your modules to use.

Feature: