Jakarta Upgrade Preview

I was able to complete an upgrade to Jakarta using the new Liferay tools and I'd like to share the experience with you...

Introduction

Since Liferay has published https://learn.liferay.com/w/dxp/development/tooling/liferay-workspace/upgrading-to-jakarta, I think that means it's okay for me to blog about it.

In my first blog about the Jakarta upgrade for Q3, I mentioned that there were going to be tool(s) from Liferay to help with the changes, and the linked page has the current details for what the tools are, although I expect it to change a bit before you can actually start upgrading to Q3+...

I used the tools with an internal DXP build on a Liferay Gradle Workspace with a lot of modules including a lot of classic JSP portlet modules, so it was a really good environment to let me put the tooling to the test.

So this blog is going to cover what I learned from doing a rather complicated upgrade, so it will be rather long as a result. I apologize in advance, but I can tell you that the length of the blog represents the effort it will take to update medium to large implementations with a lot of customizations.

So yeah, don't think this is going to be a weekend project. You're going to want to plan and schedule this as a real project that will require time and resources to complete. And it is a project that you'll have to undertake to upgrade to 2025.Q3 or any future version like 2026.Q1 LTS since Liferay will no longer be releasing a version that does not use the jakarta namespace.

Since the blog is so long, I'm injecting this little bit so you know what's coming:

TL;DR:
  • Start clean -> Commit everything and branch before running tools.
  • Use source-formatter.properties -> Exclude bundles and other dirs you don’t want touched.
  • Run tools separately -> Do JakartaTransform first, then Upgrade in a separate pass so you know what changed what.
  • Expect quirks -> Source Formatter is opinionated; be ready to tweak code style or check stack traces.
  • Third-party deps matter -> Short term: Eclipse Transformer can help. Long term: upgrade to real Jakarta versions.
  • Not everything is handled -> Watch for missed template updates, API signature changes, EhCache/log4j upgrades, etc.
  • Don’t rush production -> Test and report bugs now, but target the 2026.Q1 LTS release for stability.

Before getting into that, let's talk about the tool foundation because it's kind of important.

The Liferay Source Formatter

You may or may not be aware, but Liferay uses a tool called Source Formatter. It is an in-house developed and maintained tool that all Liferay engineers use on all the code they produce. It ensures that the code from the liferay-portal repository maintains the same format and adheres to a standard set of expectations (for example, it will flag use of if (value == null) and suggest if (Validator.isNull(value)) instead). It knows how to handle Java, JSP/JSPF, XML, YAML and all kinds of other files, so it kind of made sense to use as a starting point for a tool to help get past the Jakarta namespace change.

That said, the tool is generally very opinionated. This is important to keep in mind as we review the tools.

Now the tools themselves do not apply the full set of Liferay standard source formatting rules, so we don't have to worry about the tool totally reformatting all of our code, but where it does concern us is when it fails to parse valid code that falls outside of the Liferay rules that the tool never expected to encounter (because the Liferay engineers just don't do certain things).

Although the context hasn’t been introduced yet, this lays the foundation for the recommendations we’ll cover shortly.

Preparing To Upgrade

So there are some things that you should do before you just spin out to Liferay's Upgrading to Jakarta page and doing what it says there.

First and foremost, it goes without saying that you should be working from some kind of source code repository. Make sure all of your current code changes have been committed, that you have no local changes pending commit or publish. I would actually recommend creating a new branch for your changes. Even if you are working alone, you can isolate what the tools change and process as a pull request back to the primary repo, protecting your code base.

If you're not using a revision control system (some would say you're not a developer in that case), be sure to have a complete backup of your workspace folder.

Next, you have to take an extra step not covered in Liferay's doc; you have to create a source-formatter.properties file in the root of your workspace, and you're going to give it the following contents:

upgrade.to.liferay.version=7.4
upgrade.to.release.version=dxp-2025.q3.0
source.formatter.excludes=\
  bundles/**, \
  configs/**

The first line should always remain the same.

The upgrade.to.release.version will match the value you're actually testing the upgrade to. If you were to wait until Q4 after a few patch releases, for example, the value you might be using would be dxp-2025.q4.2 or something.

Whatever version you are using, be sure to delete your current bundles directory and use the blade gw initBundle command to download and populate the bundle for this version you are using. Do not try to use an older Tomcat 9-based bundle.

The source.formatter.excludes is going to be one that you'll likely be making the most changes to... This property holds a comma-separated list of path(s) in the workspace that should not be processed. There are just some directories that you don't want the source formatter to muck around with. For example, I've excluded the bundles directory because that will already be the target version, and I'm excluding configs because I don't want it making changes in there.

I actually stumbled upon this by accident; I had renamed my existing 2025.Q1 bundles directory to bundles-2025.q1 so I could have both the working version and the new Q3 version side by side, but the source formatter kept trying to process the backup bundles directory. This property helped keep it out of the folder where it was only breaking things. This property can help you if you have additional directories that should not be checked.

With this done, you're now ready to review (not put to use yet) the documentation...

Reviewing the Documentation

The Upgrading to Jakarta documentation, in my own opinion, got published too soon. I expect it to change because we have plans to use more applicable names to the processes and make them easier to use. If that happens, this page will likely be updated, but the same kinds of details will still apply.

Take a moment to swing over there and read that page completely. Don't do anything it says, just go read it, then come back here. Go ahead, I'll wait. And you will want to come back here, because I have some additions to it you'll want to hear.

Ah, you're back! Great! So what do you think? Sounds pretty straight-forward, right? But now for some updates. I'm going to be jumping around in this blog a bit as well, so it's best to just read along and gather all of the info you'll need to complete your upgrade...

Update Plugin Version Numbers

First, whatever version numbers get published in the final version of the documentation, they will likely be out of date. To find the correct versions, use these links:

Don't be fooled into trying that latest.release thing, it's a trap. Yes, it will give you the latest release of the plugins so you don't have to look them up, but it means any time a new version comes out, you're using it automagically which can lead to non-reproducible results, and that's never a good thing in a build process.

Use fixed versions and, when you need to, update to a newer version because it has better support or fixes a problem you're having or whatever, but do it because you want the change, not just because Liferay has released a new version.

Finally, for the liferay.workspace.target.platform.version, always use the latest available. If 2025.q3.4 is out, don't set it to 2025.q3.0, there's just no reason for it and leaves you open to bugs that may have been fixed.

Update the Source Formatter

The documentation, as listed, will have you making many changes across each of your build.gradle files to selectively add the sourceFormatter dependency.

An alternative to this is to open the build.gradle at the root of your workspace and add the following:

allprojects {
  afterEvaluate {
    pluginManager.withPlugin("com.liferay.source.formatter") {
      dependencies {
        sourceFormatter group: "com.liferay", 
          name: "com.liferay.source.formatter", version: "1.0.1527"
      }
    }
  }
}

Note here [at least at the time I'm writing this] that I used a newer version than the docs recommend...

This is the route I took to add the source formatter to my workspace. Worked out great because I had so many modules it would have been a lot of work to update each build.gradle file separately.

If you haven't already added the source-formatter.properties file that I mentioned above, now is a great time to take care of this too.

Running the Liferay Source Formatter

Okay, so I'm going to suggest that this section, well, you don't want to do it like it says...

Instead, break it into a two part run with separate commits, builds, etc.

Before explaining the process, let me explain why I'm making this recommendation...

So, first of all, there are actually two tools that run. From the doco you might see the category names are JakartaTransform and Upgrade. Each of these tools will make changes to your code, and unless you separate the runs you may have no idea which changed what source (in order to report bugs).

Note to Windows users: During testing of the documented instructions, we found that the blade command was failing to pass the -D arguments correctly. If you're on Windows, do not use the blade gw shortcut, instead use the direct path to the gradlew.bat script, i.e. ..\..\gradlew.bat formatSource ...

So this is the larger, more involved process I would recommend:

1. Run the JakartaTransform step.

To do this, you'll currently execute the command:

$ blade gw formatSource \
  -DformatSource.source.check.category.names="JakartaTransform" \
  -DformatSource.java.parser.enabled=false

Sometime in the near future (and after an upgrade to the workspace plugin), this command will be changed to simply:

$ blade gw upgradeJakarta
This form is not ready yet, but I'll update the blog when it is available...

This is going to sweep through your code changing references to things like "javax.portlet.Portlet" over to "jakarta.portlet.Portlet". It is more than just Java references like changing public class MyPortlet implements javax.portlet.Portlet kinds of things. It should touch all of those component annotations where "javax.portlet" (amongst others) are referenced, including the Language.properties file where the portlet name properties are referenced.

Actually, the tool didn't update the Language.properties for me as I expected. I opened a bug on this, though, and expect it to be working by the time you are migrating your code.

It should also update JSP tag libraries to the Jakarta equivalents, etc.

You might see a warning like:

> Task :formatSource
WARNING: Setting property "java.parser.enabled" to "false" may prevent certain 
  Java/JSP checks from working properly.

Don't freak out, this is expected. If you check the command line we used above, we're actually the ones that disabled the parser. It's important to do so to avoid a bunch of checks (and failures) that we likely don't want to get stuck in.

I would add/commit the changes locally (so you have the record of them). You could then attempt a clean build, but this will likely stumble based upon changes in APIs in the new Liferay version.

2. Run the Upgrade step.

To do this, you'll execute the command:

$ blade gw formatSource \
  -DformatSource.source.check.category.names="Upgrade" \
  -DformatSource.java.parser.enabled=false

This is going to make a completely different set of changes to your code, it is supposed to make API changes and things that it is capable of making, sometimes it is only going to offer suggestions because it is not capable of making changes directly itself.

Note: When the new blade gw upgradeJakarta command is released, it will combine steps 1 and 2. Your choice, you can use the combined command (Liferay will still support it) or separate by invoking as we did here (also supported).

Failed Runs of the Liferay Source Formatter

So the workspace I was working on, well it was not a Liferay-managed project or workspace, so it had never had the Source Formatter run on it and did not adhere to the typical Liferay formatting rules. So yeah, it was a lot like your projects ;-)

So when I would run the Upgrade, I'd get a lot of ugly failures such as:

> Task :modules:apps:payment:payment-service:formatSource FAILED
Parsing error at line "static": .../upgrade/modules/payment/payment-service/src/main/java/com/example/payment/util/PaymentContextFactory.java (SourceCheck:JavaClassParser)
java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Unable to format .../upgrade/modules/payment/payment-service/src/main/java/com/example/payment/util/PaymentContextFactory.java
        at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
        at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
        at com.liferay.source.formatter.SourceFormatter.format(SourceFormatter.java:416)
        at com.liferay.source.formatter.SourceFormatter.main(SourceFormatter.java:302)
Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Unable to format .../upgrade/modules/payment/payment-service/src/main/java/com/example/payment/util/PaymentContextFactory.java
        at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
        at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
        at com.liferay.source.formatter.processor.BaseSourceProcessor.format(BaseSourceProcessor.java:112)
        at com.liferay.source.formatter.SourceFormatter._runSourceProcessor(SourceFormatter.java:1327)
        at com.liferay.source.formatter.SourceFormatter.access$000(SourceFormatter.java:106)
        at com.liferay.source.formatter.SourceFormatter$1.call(SourceFormatter.java:402)
        at com.liferay.source.formatter.SourceFormatter$1.call(SourceFormatter.java:398)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
        at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.RuntimeException: Unable to format .../upgrade/modules/payment/payment-service/src/main/java/com/example/payment/util/PaymentContextFactory.java
        at com.liferay.source.formatter.processor.BaseSourceProcessor._performTask(BaseSourceProcessor.java:718)
        at com.liferay.source.formatter.processor.BaseSourceProcessor.access$000(BaseSourceProcessor.java:69)
        at com.liferay.source.formatter.processor.BaseSourceProcessor$1.call(BaseSourceProcessor.java:101)
        at com.liferay.source.formatter.processor.BaseSourceProcessor$1.call(BaseSourceProcessor.java:97)
        ... 4 more
Caused by: com.liferay.source.formatter.parser.ParseException: Parsing error at line "static"
        at com.liferay.source.formatter.parser.JavaClassParser._parseJavaClass(JavaClassParser.java:627)
        at com.liferay.source.formatter.parser.JavaClassParser.parseJavaClass(JavaClassParser.java:150)
        at com.liferay.source.formatter.check.UpgradeImportsCheck._getImportNames(UpgradeImportsCheck.java:82)
        at com.liferay.source.formatter.check.UpgradeImportsCheck._fixImports(UpgradeImportsCheck.java:203)
        at com.liferay.source.formatter.check.UpgradeImportsCheck.doProcess(UpgradeImportsCheck.java:42)
        at com.liferay.source.formatter.check.BaseFileCheck.process(BaseFileCheck.java:46)
        at com.liferay.source.formatter.check.util.SourceChecksUtil._processFileCheck(SourceChecksUtil.java:356)
        at com.liferay.source.formatter.check.util.SourceChecksUtil.processSourceChecks(SourceChecksUtil.java:111)
        at com.liferay.source.formatter.processor.BaseSourceProcessor._processSourceChecks(BaseSourceProcessor.java:729)
        at com.liferay.source.formatter.processor.BaseSourceProcessor.format(BaseSourceProcessor.java:311)
        at com.liferay.source.formatter.processor.BaseSourceProcessor.format(BaseSourceProcessor.java:268)
        at com.liferay.source.formatter.processor.BaseSourceProcessor._format(BaseSourceProcessor.java:601)
        at com.liferay.source.formatter.processor.BaseSourceProcessor._performTask(BaseSourceProcessor.java:703)
        ... 7 more

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':modules:payment:payment-service:formatSource'.
> Process 'command '~/.sdkman/candidates/java/21.0.8-zulu/zulu-21.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD FAILED in 2s
1 actionable task: 1 executed

Ick. And the code that failed, it pointed at this:

static
{
  planWeights.put("Free", 0);
  planWeights.put("Basic", 1);
  planWeights.put("Pro", 2);
  planWeights.put("Premium", 3);
}

Nothing wrong with this, right? It's perfectly valid Java code with a static initializer for a Map, so what's the big deal?

Remember when I said the Liferay Source Formatter is very opinionated? Well, as it turns out, it doesn't like the newline after static and before the block open. I changed it so it was static { and the tool ran just fine after that.

So, when you're running the tool, when you get these stack traces, you'll know the file where the problem is, but unless you have experience with the Liferay source formatting rules, it may be difficult to identify why the failure is happening. And no, this is not a support issue, so you're not going to be able to open a support ticket on it.

You can, however, swing on over to the Liferay Community Slack to ask for help. Just remember that we'll need the full output (like I shared above) and the source file and we should be able to help on making necessary changes so the tool will be able to parse the file successfully.

If you want to try and resolve yourself, the stack trace root cause is going to point to the source formatter code failure, and the source formatter code is part of the liferay-portal repository, so you can check the code and figure out what is failing. Often times its going to be easier just to reach out and ask for help, but that's going to be up to you.

Successful Runs of the Liferay Source Formatter

When the Upgrade category runs successfully, you may still end up with a failure:

> Task :modules:admin:tools-admin-web:formatSource FAILED
See LPD-44043, Liferay#Notification: .../upgrade/modules/apps/admin/tools-admin-web
  /src/main/resources/META-INF/resources/view.jsp (SourceCheck:UpgradeCatchAllCheck)
java.lang.Exception: Found 1 formatting issues:
1: See LPD-44043, Liferay#Notification: .../upgrade/modules/apps/admin/
  tools-admin-web/src/main/resources/META-INF/resources/view.jsp 
  (SourceCheck:UpgradeCatchAllCheck)

  at com.liferay.source.formatter.SourceFormatter.format
    (SourceFormatter.java:474)
  at com.liferay.source.formatter.SourceFormatter.main(SourceFormatter.java:302)

FAILURE: Build failed with an exception.

While it is a failure, it is considered a successful run of the Upgrade tool.

Note that when doing a complete workspace, the tool stops at the first failure. To get all errors at the same time, add --continue to the command line so it sweeps through all of your modules.

In this particular case, there was one failure which I highlighted above. It points to a ticket (this is going to change to a link) and, when you read the ticket, it should guide you on what was changed and what you need to change in your code. Use the file path you're given and the ticket details to find the point in the code that needs to change and make the necessary changes.

For a completely successful run of the tool, you'll go through each of the failures that were reported like above and made the necessary changes. When you re-run the tool again, it would complete without any failures.

As before, I would add/commit the changes that have been made to the local repository.

3. Handle Third-Party Dependencies

So the Replace Third-Party Dependencies section lays out three options:

  1. Substitute (upgrade) to a Jakarta-specific version.
  2. Maintain your own copy by editing the code to use jakarta.* instead of javax.*.
  3. Use the Eclipse Transformer to change the classes in the jar to reference jakarta.* instead of javax.* dependencies.

Now this list seems kind of incomplete to me, and I also kind of question the order (this is what the doco says is the order of preference).

First, there are important aspects to third party dependencies that these options gloss over:

  • Dependencies are rarely standalone unless they are simple. Many larger packages like Spring, POI, Guava, Bouncy Castle, etc tend to have large transitive graphs. You're not just upgrading the main package, but all of the transitive dependencies that go along with them.
     
  • Upgrading dependencies obviously includes upgrading. Your code may need to be changed to reflect changes in those upgraded dependencies.

So, back to the order...

#1 seems obvious, but it comes with a lot of pitfalls, right? I mean, you face both the aspects to go this path. Is it the preferred option? Yes, it's my preferred option too, but we have to understand that it comes with a lot of unknowns and risks. You'll need to take this into account in your upgrade planning when it comes to allocating time and resources.

#3 is smart too. Rather than changing any dependencies you have, you just tweak them to use jakarta instead of javax using the Eclipse Transformer. This obviously saves you from the problems that you might face with #1, although I wouldn't want to consider it a long term solution, right? First, you won't know if it really works until you try it, and that will only expose obvious failures, hidden bugs might be hard to spot. Second, because now you have morphed copies of third party dependencies (and possibly transitive dependencies again), you have extra build assets to maintain. Third, Liferay makes it pretty clear when they say directly in the documentation:

Note that Liferay cannot support applications with .jars that have been transformed this way.

Although that last part doesn't really mean much since Liferay doesn't support customizations anyway, and you can't lose something you weren't going to get in the first place.

#2 I think is just wrong. I mean, it basically advocates for forking a project and maintaining your own fork? There's no ROI on that, any support you might have gotten from the upstream project would be at risk, and you'd be constantly chasing them to keep up with updates, downloading their latest release, editing their code, building and publishing your artifact, ... #2 just feels dirty. Anyone with corporate development experience will know what I'm getting at here.

Finally, the list leaves off what I guess must be #4 - Contact the project and ask them for a Jakarta-friendly version. Maybe they already have one in the works. Maybe someone else has already provided a jakarta version? Maybe the project maintains both javax and jakarta versions? Getting a version from them could put you into item #1, but at least you're not transforming or forking your own copy.

My recommendations here:

  • For short term only, consider the Eclipse Transformer if it works. This keeps your migration to jakarta simpler by reducing the impact of the migration and you can look at a subsequent phase to deal with dependency upgrading.
     
  • For the long term, upgrading to the Jakarta versions of the dependencies (and transitive dependencies) will have the best outcome. You'll be using original maintained dependencies, and you'll be set to update versions as necessary going forward.

With the third-party dependencies handled (and any code changes necessary to take advantage of them out of the way), you're ready to move onto the next step, to complete the build.

4. Build

Since we applied the Jakarta namespace change and fixed all of our upgrades, we're now going to get a clean build, right?

Well, while I honestly hope that is the case for all of you, for some it will not.

There may still be build issues... API changes that weren't flagged, etc. In one case I found in early testing, the Upgrade step new that a constant was moved from one interface to another; it correctly changed to the new interface, but it didn't add the necessary import for the new interface.

And yes, I opened a bug report on this and I believe it has already been fixed, but it's one of the reasons I suggest testing the tools early so you can report bugs and have them fixed in the tools before you really need them.

As long as your code build cleanly before either of these tools were run, any issues after the tools run are going to fall into the following categories:

  • They are simply changes in that you have to make because there is no built-in Upgrade support to find or flag them. These will be on you.
     
  • They could be tool issues and, if they are, you can open Support tickets on them, although I wouldn't wait on Support to deliver a solution on them. I do suggest reporting them so they get fixed, but at the same time, you're trying to upgrade your code so report, fix yourself, and move on...

You want to get to a point, however, where in your modules directory you can do the blade gw clean deploy and all of your modules build cleanly and end up in your Q3+ bundle.

5. Upgrade your Database

You still need to upgrade your database, and this is going to involve the standard process, meaning setting up the tools/portal-tools-db-upgrade-client for your target database and completing the upgrade to your bundle version.

Of course you should follow my regular recommendations about database upgrades (have a full backup, make sure the environment you're upgrading has access to the actual D&M filestore, run the upgrade tool, on failure restore the DB, fix the error, wash rinse repeat until you get a clean run w/o failures, ...).

With a good upgrade and your code upgraded, now you're ready to launch Liferay!

6. Launch Liferay

Next step is to launch your shiny new upgraded environment, but don't worry, your fun isn't over.

You're going to use this opportunity to review all of the startup errors because you're going to work on resolving these. Some may be related to the upgrade, some are just broken code that was either already broken and being ignored or was already broken but not reporting itself.

I had to deal with some unresolved references that were actually caused by an OSGi reference loop; I'm sure it existed before the upgrade, but it was either being ignored before I got the code or wasn't reporting at all.

After you resolve all of your startup issues, next you're going to go around the site and fix other things that are broken, i.e. your old javascript no longer works the same way and needs to be updated.

Some of these issues you might feel should have been handled by one of the upgrade tools. For example, I had a bunch of broken FreeMarker templates because Liferay refactored com.liferay.portal.util.PropsUtil over to com.liferay.portal.kernel.util.PropsUtil. I felt that, well, I didn't move that class, Liferay did, and they should have fixed the templates in the database during the upgrade but they didn't. So I opened this up as an issue; whether it gets handled or not I have no idea. Like I said before, I reported the issue, but then I went in and fixed my templates manually so I could continue.

What Isn't Handled

So you are probably now thinking "These tools are great! There may be a few minor problems, but otherwise this upgrade is going to be a breeze!", but unfortunately this may not be true for you.

Depending upon the version that you're upgrading from, you may face some things that you have to upgrade that the tooling is just not going to handle for you.

This isn't necessarily a tooling issue; many of these things are really outside of the scope of the tools. I'm calling them out here not because I'm trying to point at problems in the tools, it's not that at all. I'm calling them out so we all realize that there will be things that we'll be responsible for; the tools will help us of course, but we still have to exert some effort to get over the line.

The database change for the FreeMarker template error I just shared? That was just one example.

Here are some things that I found during my upgrade, they may help you with yours...

1. Classes Found in Wrong Directory

When you’re compiling code and you get an error that reads:

error : Classes found in the wrong directory: 
  {META-INF/versions/9/module-info.class=module-info}

Edit the bnd.bnd file for the module and add the following directive:

-fixupmessages: Classes found in the wrong directory: ...;is:=ignore

This fixes a JDK-21 compilation issue and your build will now be successful.

2. EhCache 2 Configuration

At runtime, if you get an error like:

org.ehcache.xml.exceptions.XmlConfigurationException: Error parsing XML 
configuration at bundleresource://1557.fwk59789865/META-INF/module-multi-vm.xml

  at org.ehcache.xml.XmlConfiguration.<init>(XmlConfiguration.java:126) ~[?:?]
  at org.ehcache.xml.XmlConfiguration.<init>(XmlConfiguration.java:92) ~[?:?]
  at com.liferay.portal.cache.ehcache.internal.configurator.
    EhcachePortalCacheManagerConfigurator.getConfigurationObjectValuePair(
    EhcachePortalCacheManagerConfigurator.java:66) ~[?:?]

This means you have EhCache 2 configuration XML files. Liferay has upgraded to EhCache 3, so the old EhCache 2 XML files need to be upgraded.

3. module-log4j.xml Updates

If you have module-log4j.xml files that still use log4j 1 format, those should be updated to log4j2 format.

4. API No Such Method Errors

If you encounter an error for a previous API that used to be fine, but now requires a String as the first argument, this is for the new External Reference Code arguments.

Typically you can solve this by inserting a null, at the start of the argument list and that will resolve the simple API changes. Depending upon how far you are upgrading from, there may be other changes that you need to make.

Is That It?

No, but that's the list that I have so far. As you find and share other issues with me on Liferay's Community Slack, I'll come back here and add further cases.

Conclusion

So this was a fairly long blog, but let's cover what we learned...

First, we've seen the initial documentation for Liferay's tools to help with the Jakarta namespace migration.

Next, we talked about gaps in the doco and tried to fill those in, and I provided some recommendations along the way.

We spent quite a bit of time talking about Third Party Dependencies and how we're going to handle those during the migration.

And we closed with some cases of things where the tools will not handle the work for us, that we'll have some responsibility as well in the upgrade.

In my previous post about the Jakarta migration, I basically made the following recommendations:

  • When Q3 and the tools are available, start trying a Jakarta migration. I predicted that there would be issues that would need to be reported/fixed for the tools to do a good job migrating your code. In doing the migration of my complex project, I found this to be true. The tools did a lot of things really well, but there were some fringe cases that they failed on that I reported as bugs. You want to start the migration/upgrade to Q3 so you can report bugs so, when you are finally ready to do the jakarta migration, the tools will be ready to handle your code correctly.
     
  • I would not upgrade to Q3 or Q4 (unless there was a compelling feature you needed that was not available in earlier releases), to wait for the 2026.Q1 LTS release to upgrade and complete the Jakarta migration in production. I still stand by that recommendation. Based on the current status of the tools and the few things I had to handle manually, I was able to complete the Jakarta migration, but my project, as it existed, did not need any of the other features release in Q3 and while I don't know what's coming in Q4, I don't predict a compelling feature my existing project would take advantage of (of course that could change). So waiting for the 2026.Q1 LTS release still makes the most sense because that is the version that will give me the multiple years of support (where the Q3 and Q4 will only give 12 months of support).
     
  • And for JBoss folks, yes you're in a bind, you need the Liferay update to Q3 / Jakarta because your JBoss is EOL. You have two choices: Upgrade to Q3 and complete the Jakarta migration or, as I previously recommended, ride it out and wait for 2026.Q1 LTS. I was firmly behind the "ride it out" side in the previous blog, but now, based on the status of the tools and what not, now I would say that it's your call. I think you can be successful completing the Jakarta migration and upgrading to Q3 for your JBoss support (of course it is a new release with new features, the jakarta migration is new, so basically it's cutting edge and there may be some blood, but this is survivable).

I still feel pretty good about these recommendations for the most part, but remember, they are only my recommendations and they will vary from Liferay's official recommendations, and as always it is up to you whether to follow them or not, in whole or in part.

Otherwise, you should now be prepared to start the Jakarta migration once the release and the tools are ready to go. I'd encourage you to try the migration on your workspace(s) and let me know either below or on the Liferay Community Slack how it goes.