Understanding a ClassNotFoundException
or NoClassDefFoundError
in non-OSGi
environments is straightforward.
ClassNotFoundException
: thrown when looking up a class that isn’t on the classpath or using an invalid name to look up a class that isn’t on the runtime classpath.NoClassDefFoundError
: occurs when a compiled class references another class that isn’t on the runtime classpath.
In OSGi environments, however, there are additional cases where a
ClassNotFoundException
or NoClassDefFoundError
can occur. Here are four:
- The missing class belongs to a module dependency that’s an OSGi module.
- The missing class belongs to a module dependency that’s not an OSGi module.
- The missing class belongs to a global library, either at the Liferay DXP webapp scope or the application server scope.
- The missing class belongs to a Java runtime package.
This tutorial explains how to handle each case.
Case 1: The Missing Class Belongs to an OSGi Module
In this case, there are two possible causes:
-
The module doesn’t import the class’s package: For a module (or WAB) to consume another module’s exported class, the consuming module must import the exported package that contains the class. To do this, you add an
Import-Package
header in the consuming module’sbnd.bnd
file. If the consuming module tries to access the class without importing it, aClassNotFoundException
orNoClassDefFoundError
occurs.In the consuming module, make sure you import the correct package. First check the package name. If the package import is correct but you still get the exception or error, the class might no longer exist in the package.
-
The class no longer exists in the imported package: In OSGi runtime environments, modules can change and come and go. If you reference another module’s class that its developer removed, a
NoClassDefFoundError
orClassNotFoundException
occurs. Semantic Versioning guards against this scenario: removing a class from an exported package constitutes a new major version for that package. Neglecting to increment the package’s major version breaks dependent modules.For example, say a module that consumes the class
com.foo.Bar
specifies the package importcom.foo;version=[1.0.0, 2.0.0)
. The module usescom.foo
versions from1.0.0
up to (but not including)2.0.0
. The first part of the version number (the1
in1.0.0
) represents the major version. The consuming module doesn’t expect any major breaking changes, like a class removal. Removingcom.foo.Bar
fromcom.foo
without incrementing the package to a new major version (e.g.,2.0.0
) causes aClassNotFoundException
orNoClassDefFoundError
when other modules look up or reference that class.You have these options since the class no longer exists in the package:
-
Adapt to the new API. To learn how to do this, read the package’s/module’s Javadoc, release notes, and or formal documentation. You can also ask the author, or search forums.
-
Revert to the module version you used previously. Deployed module versions reside in
[Liferay_Home]/osgi/
. For details, see Backing up Liferay Installations.
Do what you think is best to get your module working properly.
-
Now you know how to resolve common situations involving ClassNotFoundException
or NoClassDefFoundError
. For additional information on NoClassDefFoundError
,
see OSGi Enroute’s article
What is NoClassDefFoundError?.
Case 2: The Missing Class Doesn’t Belong to an OSGi Module
In this case, you have two options:
-
Convert the dependency into an OSGi module so it can export the missing class. Converting a non-OSGi
JAR
file dependency into an OSGi module that you can deploy alongside your application is the ideal solution, so it should be your first choice. -
Embed the dependency in your module by embedding the dependency
JAR
file’s packages as private packages in your module. If you want to embed a non-OSGiJAR
file in your application, see the tutorial Adding Third Party Libraries to a Module.
Case 3: The Missing Class Belongs to a Global Library
In this case, you can configure Liferay DXP so the OSGi system module exports the missing class’s package. Then your module can import it. You should NOT, however, undertake this lightly. If Liferay intended to make a global library available for use by developers, the system module would already export this library! Still, if you must access a global library that’s not currently exported and can’t think of any other solution, you can consider adding the required package for export by the system module. There are two ways to do this:
-
In your
portal-ext.properties
file, use the propertymodule.framework.system.packages.extra
to specify the packages to export. -
If the package you need is from a Liferay DXP JAR, you might be able to add the module to the list of exported packages in
[LIFERAY_HOME]/osgi/core/com.liferay.portal.bootstrap.jar
’sMETA-INF/system.packages.extra.bnd
file. Try this option only if the first option doesn’t work.
If the package you need is from a Liferay DXP module, (i.e., it’s NOT
from a global library), you can add the package to that module’s bnd.bnd
exports. You should NOT, however, undertake this lightly. The package would
already be be exported if Liferay intended for it to be available.
Case 4: The Missing Class Belongs to a Java Runtime Package
rt.jar
(the JRE library) has non-public packages. If your module imports one
of them, configure Liferay DXP’s system bundle to export the package to the module
framework.
-
Add the current
module.framework.system.packages.extra
property setting to a[LIFERAY_HOME]/portal-ext.properties
file. Your server’s current setting is in the Liferay DXP web application’s/WEB-INF/lib/portal-impl.jar/portal.properties
file. -
In your
portal-ext.properties
file, append the required Java runtime package to the end of themodule.framework.system.packages.extra
property’s package list. -
Restart your server.
The package requirement resolves.
Related Topics
Backing up Liferay Installations