Converting ActiveMQ to Jakarta - Part 2

The ActiveMQ Jakarta Journey

Many Java developers are staring down an upgrade to their applications due to the impacts of the new Jakarta framework. I blogged an intro to Jakarta here to provide a primer.

Straight forward, right? Just change “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 that my experience is representative of what many enterprise Java-developers are also going through and wanted to share my experience and insights going through the process.  Lets 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 the HYTE platform.  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. ActiveMQ’s ability to be easily embedded in unit tests means many other open-source projects as a test dependency to certify those projects’ JMS integration capabilities.

While the Jakarta namespace change 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, Jetty upgrade and Spring upgrade did have breaking changes. ActiveMQ 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 test 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 APIs 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 backported 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 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 path 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 backports 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 I Took

Step 1: Begin JMS 2.0 support in the non-Jakarta releases. ActiveMQ 5.18.x series included the majority of JMS 2.0 features (see: ActiveMQ JMS 2.0 new features implementation approach).

Step 2: Add a transition client jar, so developer 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 capabiltiies is no longer blocked while waiting for the open-source project to complete the full Jakarta-based transition.

Step 3: Analysis of the ActiveMQ broker-side support for Jakarta

Step 4: Pay down technical debt.

Step 5: Modernize code base.

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 finalized.

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 feature that will benefit ActiveMQ users.

Getting over the big update to Jakarta sets up ActiveMQ for less impactful changes going forward.

Reference Information

ActiveMQ’s Jakarta Migration Stats

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

ActiveMQ Version Dependency Map

ActiveMQ Release Java JDK JMS Namespace Spring Jetty
ActiveMQ 6.0.x 17+ 3.1.0 jakarta.jms 6.x 11.x
ActiveMQ 5.18.x 11+ 2.0.1 javax.jms 5.3.x 9.4.x
ActiveMQ 5.17.x 11+ 1.1 javax.jms 5.3.x 9.4.x
ActiveMQ 5.16.x 8 1.1 javax.jms 4.3.x 9.4.x

Reference: ActiveMQ Jakarta and JMS 2.0 feature status page: https://activemq.apache.org/jms2

ActiveMQ Release Progression

ActiveMQ 5.18.x – JMS 2.0, pre-Jakarta release

  1. Added upgraded JEE API specification to JMS v2.0

  2. Added new transition module to v5.18.x:

    activemq-client-jakarta

ActiveMQ 6.0.x – JMS 3.1.0, Jakarta release

  1. Permanently dropped module: activemq-partition

  2. Upgraded Jakarta APIs:

    Jakarta Messaging

    Jakarta XML

    Jakarta Servlet

    Jakarta Transaction

  3. Upgrade key dependencies:

    Jetty v11

    Spring v6

    Java JDK 17+

    Maven modules

  4. Drop JEE API specs that do not have a Jakarta version:

    j2ee-management (interfaces re-implemented locally to ActiveMQ)

  5. Re-homed unmaintained test dependencies:

    stompjms Java STOMP client

    joram-jms-tests JMS test utilities

  6. Temporarily dropped dependencies:

    Jolokia (Jakarta support TBD)

    Apache Camel (Jakarta support coming with Apache Camel v4.x)

Matt Pavlovich

Matt Pavlovich, the Chief Technology Officer and Technical Practice Lead at HYTE Technologies, directs the HYTE Product Development Team. With a wealth of experience in the Open Source Software community, Matt is also a Committer on the Apache ActiveMQ project. Known for his technical prowess and leadership skills, Matt has successfully led numerous large-scale ActiveMQ implementations worldwide. Under his guidance, HYTE's services and tools enable accelerated Enterprise application development and enhance the supportability of middleware solutions.

Previous
Previous

Migrating ActiveMQ to Jakarta EE - JakartaOne Talk

Next
Next

Converting ActiveMQ to Jakarta - Part 1