This website uses cookies to ensure you get the best experience. Learn More.
"There is nothing new under the sun" - King Solomon, Ecclesiastes 1
In one sense, this blog entry doesn't contain anything new. In another sense, since it brings pieces of prior entries together, I hope that it provides some new insight for portlet developers to overcome the "Unresolved requirement: Import-Package" deployment error.
Let's start out by referencing the following prior blog entries (ordered by newest to oldest):
Why does the "Unresolved requirement: Import-Package" deployment error occur? It's almost always because a portlet developer has embedded a dependency in a WAR or JAR artifact.
It's easy for us to understand how this can happen in a WAR module -- it simply means that we built a WAR with one or more JARs embedded in WEB-INF/lib using the "compile" scope with either Maven or Gradle. But Liferay portlet developers (particularly those that are using MVCPortlet in a JAR-based artifact) will sometimes use the "compileInclude" scope (see blog#3 by Dave) in order to embed a JAR within a JAR.
As a result, this can happen with MVCPortlet (Liferay's MVC Framework), FrameworkPortlet (PortletMVC4Spring), GenericFacesPortlet (Liferay Faces / JSF) or any other framework.
In order to experience the "Unresolved requirement: Import-Package" deployment error, let's follow a typical workflow for a JSF portlet developer. Let's start out by generating a new PrimeFaces project using a Maven archetype generation command found at liferayfaces.org...
mvn archetype:generate \ -DarchetypeGroupId=com.liferay.faces.archetype \ -DarchetypeArtifactId=com.liferay.faces.archetype.primefaces.portlet \ -DarchetypeVersion=8.0.0 \ -DgroupId=com.mycompany \ -DartifactId=com.mycompany.my.jsf.portlet
Let's add Apache POI and PrimeFaces Extensions to pom.xml, since they are very typical dependencies:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>org.primefaces.extensions</groupId> <artifactId>primefaces-extensions</artifactId> <version>10.0.0</version> </dependency>
mvn -P thin package
ls -1 target/com.mycompany.my.primefaces.portlet-1.0.0*/WEB-INF/lib
Directory Listing:
cp target/*.war $LIFERAY_HOME/deploy
This is the point at which you would encounter the "Unresolved requirement: Import-Package" deployment error:
com.liferay.portal.kernel.log.LogSanitizerException: org.osgi.framework.BundleException: Could not resolve module: com.mycompany.my.primefaces.portlet [1679]_ Unresolved requirement: Import-Package: org.apache.poi.xssf.usermodel; resolution:="optional"_ Unresolved requirement: Import-Package: com.google.gson_ [Sanitized] at org.eclipse.osgi.container.Module.start(Module.java:444) ~[org.eclipse.osgi.jar:?] at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:428) ~[org.eclipse.osgi.jar:?] at com.liferay.portal.file.install.internal.DirectoryWatcher._startBundle(DirectoryWatcher.java:1156) [bundleFile:?] at com.liferay.portal.file.install.internal.DirectoryWatcher._startBundles(DirectoryWatcher.java:1189) [bundleFile:?] at com.liferay.portal.file.install.internal.DirectoryWatcher._startAllBundles(DirectoryWatcher.java:1130) [bundleFile:?] at com.liferay.portal.file.install.internal.DirectoryWatcher._process(DirectoryWatcher.java:1041) [bundleFile:?] at com.liferay.portal.file.install.internal.DirectoryWatcher.run(DirectoryWatcher.java:221) [bundleFile:?]
There are two basic approaches to fixing this error, each with benefits and drawbacks:
Approach #1 (Globally / all artifacts): Follow my advice from blog#5, by adding com.google.gson to the module.framework.web.servlet.annotation.scanning.blacklist property.
com.google.gson
module.framework.web.servlet.annotation.scanning.blacklist
Potential Benefit: If you have multiple artifacts, this provides a centralized place that fixes the error for all of them.
Potential Benefit: The deployment process will get faster, because the WAB generator will bypass scanning of classes in all of the blacklisted package.
Potential Drawback: You might not have the privilege in your DevOps to set this property.
Potential Drawback: It's unlikely, but you might not want the package blacklisted for all deployed artifacts.
Potential Drawback: Every time you add a dependency, you will likely have to add packages, which affects all deployed artifacts (including Liferay OOTB ones!), which could affect your test scenarios.
Approach #2 (Individual artifact):
Add the package to the Import-Package directive in src/main/webapp/WEB-INF/liferay-plugin-package.properties as resolution:=optional:
Import-Package
resolution:=optional
Import-Package:\ com.google.gson;resolution:=optional,\ *
Potential Benefit: You are able to isolate the fix to a specific WAR artifact, rather than affecting all deployed artifacts.
Potential Drawback: You have to add this to every WAR artifact that encounters the problem for the package.
Drawback: It doesn't speed up deployment to the degree that Approach#1 does.
Whether you choose Approach #1 or Approach #2, you will find it very tedious to redeploy the artifact over-and-over in order to find the next problematic package.
By following Dave's advice from blog#6, you can discover ALL of the problematic packages and save yourself a lot of time by avoiding the rinse/repeat cycle of redeployment.
Whether you deploy a WAR artifact or a JAR artifact, ultimately a new OSGi bundle will appear in $LIFERAY_HOME/osgi/state. When you deploy a new module, it's normally the sub-directory with the highest integer value. Under there, you will find a "bundleFile" that contains the OSGi metadata you need.
bnd print --impexp ~/portals/liferay-dxp-7.4.13.u31/osgi/state/org.eclipse.osgi/1679/1/bundleFile
[IMPEXP] Import-Package com.google.gson com.google.i18n.phonenumbers com.ibm.uvm.tools {resolution:=optional} com.ibm.websphere.wsoc {resolution:=optional} com.liferay.faces.bridge.ext com.liferay.faces.bridge.impl com.liferay.petra.io.unsync com.liferay.petra.lang com.liferay.petra.string com.liferay.portal.kernel.bean com.liferay.portal.kernel.configuration com.liferay.portal.kernel.dao.jdbc com.liferay.portal.kernel.dao.orm com.liferay.portal.kernel.io com.liferay.portal.kernel.io.unsync com.liferay.portal.kernel.log com.liferay.portal.kernel.portlet com.liferay.portal.kernel.portlet.bridges.mvc com.liferay.portal.kernel.servlet com.liferay.portal.kernel.servlet.filters.invoker com.liferay.portal.kernel.template com.liferay.portal.kernel.util com.liferay.portal.kernel.xml com.liferay.portal.model {resolution:=optional} com.liferay.portal.osgi.web.servlet.jsp.compiler {resolution:=optional} com.liferay.portal.security.access.control {resolution:=optional} com.liferay.portal.security.auth {resolution:=optional} com.liferay.portal.security.permission {resolution:=optional} com.liferay.portal.service {resolution:=optional} com.liferay.portal.servlet {resolution:=optional} com.liferay.portal.servlet.filters.aggregate {resolution:=optional} com.liferay.portal.servlet.filters.dynamiccss {resolution:=optional} com.liferay.portal.spring.context {resolution:=optional} com.liferay.portal.theme {resolution:=optional} com.liferay.portal.util {resolution:=optional} com.liferay.portlet {resolution:=optional} com.liferay.taglib.aui com.liferay.taglib.portlet com.liferay.taglib.portletext com.liferay.taglib.security com.liferay.taglib.theme com.liferay.taglib.ui com.liferay.taglib.util com.sun.el {resolution:=optional} com.sun.el.lang {resolution:=optional} com.sun.el.parser {resolution:=optional} com.sun.el.stream {resolution:=optional} com.sun.el.util {resolution:=optional} com.sun.faces dev.morphia dev.morphia.query dev.morphia.query.experimental.filters dev.morphia.query.internal io.undertow.websockets.jsr {resolution:=optional} javax.annotation javax.crypto javax.crypto.spec javax.el javax.enterprise.context javax.faces javax.faces.application javax.faces.component javax.faces.component.behavior javax.faces.component.html javax.faces.component.visit javax.faces.context javax.faces.convert javax.faces.event javax.faces.lifecycle javax.faces.model javax.faces.render javax.faces.validator javax.faces.view.facelets javax.faces.webapp javax.imageio javax.imageio.metadata javax.imageio.stream javax.inject javax.portlet javax.portlet.faces javax.servlet javax.servlet.http javax.swing javax.swing.text javax.swing.text.html javax.websocket {resolution:=optional} javax.websocket.server {resolution:=optional} javax.xml.namespace javax.xml.parsers javax.xml.stream javax.xml.transform javax.xml.transform.dom javax.xml.transform.stream javax.xml.validation org.apache.commons.logging org.apache.logging.log4j.util.internal org.apache.naming.java {resolution:=optional} org.apache.tomcat.websocket.server {resolution:=optional} org.commonmark.node org.commonmark.parser org.commonmark.renderer.html org.dom4j org.dom4j.io org.eclipse.core.runtime {resolution:=optional, x-liferay-compatibility:=spring} org.osgi.framework org.osgi.framework.wiring org.owasp.html org.primefaces org.primefaces.behavior.ajax org.primefaces.behavior.base org.primefaces.clientwindow org.primefaces.component.api org.primefaces.component.breadcrumb org.primefaces.component.commandbutton org.primefaces.component.menu org.primefaces.component.outputpanel org.primefaces.component.row org.primefaces.config org.primefaces.context org.primefaces.event org.primefaces.expression org.primefaces.facelets org.primefaces.model org.primefaces.model.menu org.primefaces.renderkit org.primefaces.shaded.json org.primefaces.shaded.owasp.encoder org.primefaces.util org.w3c.dom org.xml.sax sun.misc sun.nio.ch weblogic.websocket.tyrus {resolution:=optional}
com.liferay
javax.annotation
javax.el
javax.enterprise.context
javax.inject
javax.portlet
com.sun.faces
javax.faces
javax.servlet
javax.servlet.http
javax.faces.webapp
javax.portlet.faces
org.primefaces
dev.morphia
com.google.gson;resolution:=optional,\ com.google.i18n.phonenumbers;resolution:=optional,\ com.lowagie.text.*;resolution:=optional,\ dev.morphia.*;resolution:=optional,\ org.apache.logging.log4j.util.internal;resolution:=optional,\ org.commonmark.*;resolution:=optional,\ org.owasp.html;resolution:=optional,\ org.primefaces.extensions.model.monacoeditor;resolution:=optional,\ sun.nio.ch;resolution:=optional,\
As indicated in the above NOTE, "thin" JSF portlets, have the ${osgi.import.package} variable in liferay-plugin-package.properties, so rather than modify that file directly, you would need to setup the Import-Package directive in pom.xml instead in order to take advantage of the variable substitution done by Maven:
${osgi.import.package}
<osgi.import.package> Import-Package:\ ${osgi.import.package.primefaces},\ com.google.gson;resolution:=optional,\ com.google.i18n.phonenumbers;resolution:=optional,\ com.lowagie.text.*;resolution:=optional,\ dev.morphia.*;resolution:=optional,\ org.apache.logging.log4j.util.internal;resolution:=optional,\ org.commonmark.*;resolution:=optional,\ org.owasp.html;resolution:=optional,\ org.primefaces.extensions.model.monacoeditor;resolution:=optional,\ sun.nio.ch;resolution:=optional,\ ${osgi.import.package.liferay.faces.bridge} </osgi.import.package>
The "Unresolved requirement: Import-Package" deployment error typically occurs due to embedded dependencies in a WAR or JAR module. It's possible to overcome the problem, but it takes a little bit of time. My recommendation is to use the bnd print --impexp command to determine which package(s) are causing the problem, so as to avoid repetitive redeploys which only reveal one problematic package at a time.
bnd print --impexp