Converting ActiveMQ to Jakarta - Part 3 - Final
This is the final blog post in a series covering the conversion of Apache ActiveMQ to Jakarta EE and JDK 17 to share lessons learned and best practices with enterprise software developers to guide their migrations.
Advanced Technical Approach
Some Java frameworks have taken on the complexity of supporting both javax and jakarta package namespaces simultaneously. This approach makes sense for frameworks and platform services, such as Jetty and ActiveMQ, where the core development team needs to move the code base forward to support newer JDKs, while also providing a way for application developers to adopt Jakarta EE gradually. This simplifies the support for Open Source frameworks, as there are fewer releases to manage, and in the event of a security bug, being able to release one mainline branch vs having to go back and backport across past versions.
However, supporting both javax and jakarta namespaces simultaneously in a single application is complicated and time-consuming. Additionally, it opens additional scenarios that may lead to errors and security gaps for enterprise applications. This limits the ability to set up verification checks and source code scanning to block pre-Jakarta libraries from being used or accidentally pulled in through transitive dependencies. It creates a lot of ambiguity and reduces the effectiveness of DevOps teams in providing pre-approved SDKs to be used by enterprise developers. With the pitfalls outweighing the benefits, enterprise projects should not need to support both javax and jakarta namespaces simultaneously in most scenarios.
Special Consideration for Exception Handling for Remote Operations
The one caveat to this best practice for enterprise applications is there may be a need to support mapping exceptions between javax and jakarta package namespaces to support clients making remote calls to a service or API. The server-side either needs to be able to detect javax clients and translate, or a thin client-side wrapper is needed to handle any jakarta exceptions received by remote services. Apache ActiveMQ handles exception namespace mapping appropriately for all client release streams (starting with v6.1.0, v5.19.0, v5.18.4, v5.17.7, and v5.16.8), so no additional handling is required by applications when using Jakarta Messaging.
Jakarta EE Updates and Nothing Else
A key factor to ActiveMQ’s success was that the scope of change was limited to only what was necessary for the upgrade to Jakarta EE. The change of underlying frameworks naturally brought new minimum JDK version requirements and other changes as Jakarta EE specifications brought forward their own set of changes.
No protocol changes, no data format, or configuration changes were made to ActiveMQ to support backwards compatibility to javax clients and to support roll-forward and rollback during upgrades.
Developers should resist the urge to tackle other refactoring, data model, or business functionality changes when making the upgrade to Jakarta EE. These upgrades should be structured as technical debt-only releases to ensure the best outcomes.
Jakarta Migration Planning Guide
Team Impact – Organizing Changes for Code Review
For an enterprise taking on a similar migration of a large and established code base, I highly recommend following this next piece of advice to lower the time and level of effort.
Enforce an organizational policy that requires git commits related to package naming to be separated. There should be two types that are clearly labeled in the comments:
Java package import namespace-only changes
Code changes
Namespace-only changes involve updating the file from “import javax.” to “import jakarta.” These text changes may live in java code files, Spring xml, config properties or other non-Java code artifacts used by the application.
Code changes are updates required due to fixes, technical debt, supporting Jakarta EE specification changes, or framework API changes (such as Spring or Jetty).
By separating these changes, you will greatly reduce the time required to review and approve changes. Java package namespace-only changes will have hundreds to thousands of files changed, and thousands to tens of thousands of lines changed. For the most part, these changes can be approved quickly, without need for a deep code review.
The actual impacting code changes should impact fewer files and fewer lines of code change. The code reviews on these changes will require a closer look, and by reducing the scope, you will greatly reduce the time required for code reviews.
Practical Tips for Jakarta Migration
Drop end-of-life and deprecated modules from your code base.
Migrate or drop end-of-life and deprecated dependencies.
Upgrade code to use in-Java features where commons-* dependencies are no longer needed
Upgrade to current non-Jakarta affecting dependencies you may have been putting off (Log4j v2, JUnit v5, etc.)
Where possible, release JDK 17 changes first (Upgrade JDK using LTS versions 8 -> 11 -> 17)
Release a tech-debt update of your product or application. This allows for supporting two modern release streams—non-Jakarta and Jakarta.
Update frameworks to Jakarta EE versions.
Break-up commits to have import-only changes for faster review.
For complex in-house ‘framework’ type components, consider releasing support for both javax and jakarta at the same time.
Add support for client-side Jakarta EE module alongside existing modules in the javax release stream.
Break-up commits to have import-only changes for faster reviews
In Summary
Apache ActiveMQ was successful in its migration to Jakarta EE by tackling necessary technical debt and putting off the urge to incorporate too many changes. The transition was successful, and users were able to quickly adopt the ActiveMQ 6.x releases in their Jakarta EE projects.
Additionally, since the wire protocol, configuration, and data formats did not change, older javax applications (and non-Java applications) were able to work seamlessly through an upgrade.
This is an exciting time for Java developers as the ecosystem is rapidly adopting awesome new features and great language improvements. I’m interested in your feedback as you tackle Jakarta EE and JDK upgrades for projects of all sizes.
Reference Material
Estimated Level of Effort
Change Type | Estimated Effort |
---|---|
Namespace change from “import javax…” to “import jakarta..” | Low |
Upgrade to JDK 17 | Medium |
Update Maven tooling to align with JDK 17 | Medium |
Update and refactor code to use updated Jakarta specifications APIs | Medium |
Update and refactor code to use current dependencies that implement updated specification APIs | High |
Pay down technical debt | High |
Update and refactor code to drop any dependencies that are not current with Jakarta, JDK 17 or transitive dependencies | High |
Team impacts - Managing change across the enterprise | High |
ActiveMQ’s Jakarta Migration Metrics
The following statistics are provided as a reference for the level of effort required in migrating a medium-sized, mature Java project to Jakarta EE.
PRs | 1 |
Commits | 25 (The total commit count to get here, including squashed and re-worked count, is over 100) |
Files changed | 1,425 |
Lines added | 9,514 |
Lines removed | 8,091 |
Modules dropped | 2* (1 is the transition module, which got a relocation) |
Dependencies re-homed | 2 |
Frameworks dropped | 2 |
Deprecated J2EE specifications dropped | 1 |
PR work tasks | 28 |
CI build jobs | 80 |
Apache ActiveMQ 6.0.0 Jakarta Messaging 3.1.0 Release Summary
Permanently dropped module: activemq-partition (drop deprecated Apache ZooKeeper test dependency)
Jakarta APIs
Jakarta Messaging
Jakarta XML
Jakarta Servlet
Jakarta Transaction
Upgrade key dependencies:
Jetty v11
Spring v6
Java JDK 17+
Maven modules
Drop JEE API specs that do not have a Jakarta version:
j2ee-management (interfaces re-implemented locally to Apache ActiveMQ)
Re-homed test dependencies:
stompjms Java STOMP client
joram-jms-tests JMS test utilities
Temporarily dropped dependencies the did not have Jakarta support at the time.
Note: Both have been added back in as of ActiveMQ 6.1.x.
Jolokia
Apache Camel