Introduction

So I wrote https://liferay.dev/blogs/-/blogs/extending-liferay-osgi-modules five years ago targeting Liferay 7.0.

A lot has changed since then. Liferay Gradle Workspace plugin has seen numerous updates. Gradle has seen an update or two as well.

Recently when asked to assist a client who wanted to extend Liferay's Journal Article service, I started by recommending they look at this old blog post for guidance.

However, I was a bit surprised when they came back after giving it a try and reported that the technique described in the blog was not working for them. I shouldn't have been surprised, given the changes that had happened since the post went up 5 years ago.

After reviewing the issue and finding a resolution, I figured it was time to come back and revisit the method for extending Liferay OSGi modules...

The Goal

The goal is usually pretty straight forward - you need to override a Liferay DXP class that is contained in a jar that is contained in an LPKG file, you can't replace it using an @Component override nor can you extend it because it is in an internal package.

For these cases, you need a Marketplace Override, or basically a jar to replace Liferay's module jar named in a specific way and dropped into the osgi/marketplace/override directory.

The challenge, of course, is how to build it...

For the current client, they wanted to override the com.liferay.journal.internal.upgrade.util.JournalArticleImageUpgradeHelper class from the com.liferay.journal.service module.

What follows is how we did it...

Creating the Module

Again, in a Liferay Workspace, we'll need a basic Gradle-based module.

In the src/main/java directory we created the overriding com.liferay.journal.internal.upgrade.util.JournalArticleImageUpgradeHelper class.

Following the old blog, we had the following build.gradle file:

dependencies {
   compileOnly group: "com.liferay.portal", name: "release.dxp.api"

   compile group: "com.liferay", name: "com.liferay.journal.service", version: "6.0.72"
}

jar.archiveName = 'com.liferay.journal.service.jar'

We were targeting Liferay DXP 7.2, and 6.0.72 was the version that was in the FixPack we were building for.

And like the old blog, we also had the following bnd.bnd file:

Bundle-Name: Liferay Journal Service
Bundle-SymbolicName: com.liferay.journal.service
Bundle-Version: 6.0.72
Liferay-Require-SchemaVersion: 3.4.0
Liferay-Service: true
-dsannotations-options: inherit

Include-Resource: @com.liferay.journal.service-6.0.72.jar

The Problem

So we followed the old blog and the module built and deployed correctly, but the overriding class was just not being used.

I opened up the built jar and found that the overriding class, although being compiled as part of the build process, was not being included correctly in the jar. Actually what I think happened, the Include-Resource header was doing just that, it was exploding and pulling in all of the files from the Liferay jar, basically replacing the custom one with the legacy one when building the jar.

So even though my project had files in the right places, they were getting stomped on by the legacy files when the jar was being built.

The Solution

Clearly the Include-Resource header was causing me problems. After conferring with my good friend Ray Auge (and per his guidance), I switched up to a directive in my bnd.bnd file:

Bundle-Name: Liferay Journal Service
Bundle-SymbolicName: com.liferay.journal.service
Bundle-Version: 6.0.72
Liferay-Require-SchemaVersion: 3.4.0
Liferay-Service: true
-dsannotations-options: inherit

-includeresource: @com.liferay.journal.service-6.0.72.jar!/!com/liferay/journal/internal/upgrade/util/JournalArticleImageUpgradeHelper.class

So here I've switched from Include-Resource header to -includeresource directive. This directive has significantly more configuration options on it and is more powerful than the header itself: https://bnd.bndtools.org/instructions/includeresource.html

Specifically, the include resource directive above reads: "From the exploded com.liferay.journal.service-6.0.72.jar dependency, include all files except excluding the JournalArticleImageUpgradeHelper.class".

The @ sign in front of the jar means it should be exploded and not just injected as-is.

Following the first ! character is the path to include, by using "/" alone we're saying that all files should be included.

The second ! marks the exclusion rule where we have specified the path to the class file that should not be pulled forward.

The documentation page for the -includeresource directive has lots of other details for different ways you can structure the arguments to get the outcome you need.

Conclusion

With this new -includeresource directive in the bnd.bnd file, when the jar was built almost all of the files from the legacy jar were included in the new module jar (all except for the class we didn't want to bring forward), and the custom class was in the module instead.

The new module jar, when dropped into the osgi/marketplace/override folder, injected the necessary override logic and the desired outcome was achieved.

Switching from the Include-Resource header to the -includeresource directive made all the difference. It's also notable that at Liferay the engineering team now exclusively uses the directive (well, you can see the header used in test modules, but otherwise it is all the directive).

Hopefully this will come in handy in case you also need to build a Liferay override module.

Blogs

@David In case of Multiple classes what will be the approach  For Example i have two classes  1. MFAPolicy.class 2. MFASystemConfiguration.class how to add that in include resource ?  Below is the code i have added that does-not work -includeresource: @com.liferay.multi.factor.authentication.web-1.0.24.jar!/com/liferay/multi/factor/authentication/web/internal/policy/MFAPolicy.class!/com/liferay/multi/factor/authentication/web/internal/system/configuration/MFASystemConfiguration.class