Converting ActiveMQ to Jakarta (Part II)
In this article, learn more about migrating a mature code base to Jakarta based on the impacts of the new Jakarta framework.
Join the DZone community and get the full member experience.
Join For FreeMany Java developers are staring down an upgrade to their applications due to the impacts of the new Jakarta framework. In Part I, I blogged an intro to Jakarta (DZone article, and original post) to provide a primer.
Straight forward, right? Just change all import javax.foo to import jakarta.foo and ship it! No? Oh dang.
Having recently gone through this exercise with a large and well-established open-source project that is at the core of the HYTE platform, I’m here to share. I believe my experience is representative of what many enterprise Java developers are also going through and wanted to share lessons learned and insights going through the process. Let's dive in.
Background
ActiveMQ is the most widely used messaging broker in the world. ActiveMQ is the engine that powers HYTE MQ which is the engine of HYTE’s Messaging Platform as a Service. The ActiveMQ code base is mature, with over 30 Maven modules, and covers a range of many of the most widely used Jakarta EE APIs.
This Isn’t “Hello World”
Jumping straight to Jakarta was not a realistic option for ActiveMQ as it is widely adopted for messaging and integration infrastructure. A key feature of ActiveMQ is its ability to be easily embedded in unit tests which has led to many other open-source projects using it as a test dependency to certify those projects’ JMS integration capabilities.
While the Jakarta namespace changes itself is very minor, the heavy lifting for a large existing Java-based code base is going to come from paying dues on any outstanding technical debt and then upgrading ActiveMQ to adopt changes in the specification.
ActiveMQ’s Technical Debt
Upgrading Supported JMS Version
Before making the namespace transition, ActiveMQ needed to modernize the supported JMS API version and upgrade support from the JMS 1.1 version to the JMS 2.0 version of the JMS API specification. This revision change is a decent-sized jump as it added a new set of API classes that allow for a simplified coding interface and runtime exceptions vs checked exceptions.
Fortunately, the JMS 2.0 API specification upgrade did not break or remove anything from the JMS 1.1 specification. This enabled an upgrade approach that could be readily followed in enterprise code bases as well. I’ll blog in more detail about this in a separate post.
The Servlet specification did have breaking changes, as did Spring v5 to Spring v6. This meant that ActiveMQ’s code using these APIs and frameworks needed to be refactored.
Upgrading Frameworks
Large Java-based code bases will often rely on many other frameworks—especially in unit and integration tests. This presents a big challenge in converting a mature code base because not all frameworks are currently actively maintained or have a Jakarta-supported release. For example, some popular tools, such as Jolokia, do not have Jakarta-based releases. Others are in a chicken-and-egg problem (such as Apache Camel), where they have dependencies (usually for unit tests or integration tests) that need to have Jakarta-based releases before they can, in turn, provide a Jakarta-based release themselves.
Upgrading Jetty Version
Frameworks that made releases to support Jakarta did so appropriately in a major version upgrade. For example, ActiveMQ was using Jetty 9. To get a Jetty version that supported Jakarta namespaces, ActiveMQ needed to upgrade to Jetty v11. However, Jetty did not *only* change Jakarta namespaces between v9.x, v10.x and v11.x. Along the way, internal Jetty APIs changed. This means ActiveMQ needs to adopt those API changes at the same time as the Jakarta namespace change. If I had one do-over, it would be to adopt Jetty v10 in ActiveMQ 5.18.x and then level-set those API changes first. This would have reduced the scope and differences between the ActiveMQ 5.18.x and 6.0.x releases. Additionally, this would make any source tree management of back-ported patches easier.
Jetty Did Everything Right
ActiveMQ 5.x needed to make 2 major version jumps in Jetty releases to get to Jakarta. This is an example of a larger-than-expected effort where enterprise users will see the scope of Jakarta migrations keep creeping scope higher and taking longer than expected. In ActiveMQ’s case, the components that use Jetty are not critical paths for most users. The Jetty-based components support HTTP and web socket transports for clients. These are less frequently used and require fewer patches. Had the ActiveMQ components that required Jetty been in a critical path, or anticipated a high number of back-ports going forward, having an ActiveMQ 5.x release make the upgrade to Jetty 10 before starting the Jakarta transition would have been the better move.
Curveball
While the ActiveMQ Jakarta migration work was in progress Jetty made the move to release a v12 that supported both javax.* and jakarta.* namespaces. Had Jetty 12.x been available at the start of the migration process, the decision would have been a no-brainer and both ActiveMQ 5.x and ActiveMQ 6.x would have moved to Jetty 12. This still may happen down the road.
The Path Taken
Step 1
Implement JMS 2.0 support in the non-Jakarta releases. ActiveMQ 5.18.x release includes the majority of JMS 2.0 features (see: Apache ActiveMQ JMS / Jakarta status page).
Step 2
Add a transitional client jar, so developers can begin to integrate their Jakarta-based client-side applications with the non-Jakarta-based ActiveMQ server side. The activemq-client-jakarta module was created as a simple repackaging of the standard activemq-client (which uses javax.jms namespace), but using the jakarta.jms namespace. This is a big win! Now, any application using ActiveMQ as a JMS client that needs Jakarta-based client capabilities is no longer blocked while waiting for the open-source project to complete the full Jakarta-based transition. Spring Boot’s v3.x ActiveMQ starter was updated to use the activemq-client-jakarta to provide transitional support to users for a few months until the full Jakarta-based ActiveMQ release was completed.
Step 3
Analysis of the ActiveMQ broker-side support for Jakarta
Step 4
Refactor ActiveMQ for required changes. Pay down technical debt. Spring and Jetty changes. Remove and replace unmaintained dependencies.
Step 5
Modernize code base. Update language features for Java JDK 17, align Maven dependencies, etc.
Step 6
Release Jakarta-based ActiveMQ 6.0.0
Transition Approach Scope Creep
As users began to adopt the activemq-client-jakarta library released with Active 5.18.x, it became apparent that many users needed more than just the simple client jar to support Jakarta in their application. Applications following HYTE best practices would also need a Jakarta supported activemq-jms-pool project for connections to be pooled and refreshed over time. Addiitonally, users on legacy J2EE platforms needed the activemq-ra and activemq-rar modules to support Jakarta.
Additionally, many users use the ability to quickly embed an ActiveMQ broker in a unit test to run integration and smoke tests. Without a Jakarta-enabled broker, their application test suite would not pass. At this point, it became apparent that the best way forward was to get the full Jakarta release out the door.
While the approach of creating a transitional client was designed to lessen the impact to end-users, it ended up having unforeseen impacts that would have delayed the full Jakarta-support had the ActiveMQ community gone down that path to support Jakarta libraries for activemq-jms-pool, activemq-ra, and activemq-rar.
Modernization Will Continue
ActiveMQ 6.x may introduce javax-based modules (ie activemq-client-javax and activemq-jms-pool-javax) to provide backward compatibility to javax.jms users and allow them to stay aligned with the going forward features, but those plans have not been formalized. For now, javax.jms users can use activemq-client from ActiveMQ 5.18.x and connect with an ActiveMQ 6.x broker and vice-versa.
Jakarta and Java JDK releases will continue to iterate and provide value to coders. HYTE will continue to modernize ActiveMQ to adopt updates and language features that will benefit ActiveMQ users.
Getting over the big update to Jakarta sets up ActiveMQ for less impactful changes going forward.
Published at DZone with permission of Matt Pavlovich. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments