A Systematic Approach for Java Software Upgrades
Old is gold, and this is true in the case of software systems too. But, you need to keep them updated. This writing guides you in upgrading software systems.
Join the DZone community and get the full member experience.
Join For FreeThe amount of business logic embedded in the form of code in software systems is so huge and valuable that you don’t always need to think in terms of replacing them with entirely new systems. Rather, software systems should facilitate evolution both in terms of functionality and in terms of technological upgrades.
This is truer in transactional systems, especially in domains including, but not limited to, finance, travel, transportation, logistics (TTL), and e-commerce. While much literature has been written about how to manage functional changes in software applications, technology upgrades are non-trivial, and many times, developers are left out to try and test their own intuition.
In this writing, we attempt to lay out how a systematic approach can bring more definitiveness not only to the engineering process for software upgrades but also to other aspects of it, especially in acting as an aid for the estimation process for upgrade projects. Though we use Java as a language paradigm for our discussions, all or many of the approaches are applicable to other programming language scenarios, too.
Setting the Context
Our organization is in the business of providing SaaS-based products worldwide for the TTL industry. The mainstream products use new-generation technology and architecture and can accommodate multi-tenancy in the same runtime process operated from the public cloud.
Our organization also provides services for customer-built and customer-operated solutions. At times, due to customer-specific needs and preferences and since the customer organization is large and has its own IT upgrade and lifecycle timelines, they have not been upgrading the underlying technology to match the industry’s pace.
We have been recently working on a Software upgrade project whose original version was written more than a decade ago. The application is being used for a subdomain of the Airline operations by one of the Airline companies. This application has been built using Java and the J2EE (Java 2 Enterprise Edition) technologies, leveraging Struts for the presentation tier, EJB (Enterprise Java Beans) 2.0 as the middle tier technology, and using Oracle RAC (Real Application Cluster) in the persistence layer.
There are more than a dozen other software libraries and components, most of them from the OSS (Open-Source Software) world. While the customer organization has already put up a roadmap for shifting business functionality using the latest software stack and architecture, the immediate requirement is to do all essential technology upgrades in the currently running codebase and keep the application running till the release of the new product.
Figure 1 lists the different technologies used by the application.
As you can see in Figure 1, the top portion of the list enumerates the main components and libraries, whereas the bottom portion of the list enumerates the various runtime and platform technologies used to host the application. Though Spring is listed as a component here, it’s used for a planetary and standalone module, not as a container for the main application.
As mentioned already, the objective is to do a technology refresh without any change in business functionality. A technology refresh is a structured initiative aimed at updating the software system to a newer version. This may involve upgrading the application, framework and components, operating system, database, or any other software component to improve functionality, security, and/or performance.
To rephrase, a technology refresh is not a complete trial-and-error method but instead a structured and systematic way of planning the target versions of the various software elements involved. The subsequent sections will walk you through one feasible approach we adopted for doing the same.
Java Upgrade
Java was created at Sun Microsystems, Inc., where James Gosling and team worked with the intention of creating a new language that would allow consumer electronic devices to communicate with each other.
Even though work on the language began in 1991, the early version of Java was released in 1995 and has come a long way since its inception, and with each release, it brings new features and improvements that enhance programming flexibility, capability, and performance.
Java 25, the next Long-Term-Support (LTS) version of the Java language and runtime platform, will be officially released in September 2025. Figure 2 illustrates the published roadmap for Java SE support.
As of today, the option is to target Java 21, which has extended support until September 2031. Remember that our objective is to do all essential technology upgrades in the currently running codebase and keep the application running till the release of the new product for which 2031 gives us a clear 5-year extended life for the application from today (2025), which should be sufficiently enough by which the new product will be cut over.
Replacements for Deprecated Modules With Java EE APIs
Java EE comprises the legacy of Java EE development, including Java EE Platform version 8. Even though the code is developed by Oracle, it is generally only available as history archives.
So, to adopt an active Enterprise Java development platform, you need to transition to the Eclipse Foundation Jakarta EE. Jakarta EE is an open-source, cloud-native Java platform for enterprise applications governed by the Eclipse Foundation, which is the successor to Java EE (Java Platform, Enterprise Edition) previously maintained by Oracle under the JCP (Java Community Process). In 2017, Oracle transferred Java EE to the Eclipse Foundation, leading to its rebranding as Jakarta EE.
During 2020, Jakarta EE 9 brought the Namespace changes (javax.* → jakarta.*). While EJB 3.0 (Java EE 5, 2006) uses the javax.ejb package, the later EJB 4.0 (Jakarta EE 9, 2020) uses the jakarta.ejb package as part of the Jakarta EE transition.
In Figure 1, you can see that EJB 2.0 is the current specification adopted, and the application has been deployed in the WildFly Application container 10.1.0. WildFly has progressively reduced support for many legacy technologies, including those for EJB 2.x. While it continues to support EJB 2.x MDBs (Message Driven Beans) and stateful and stateless session beans, it no longer supports CMP (Container Managed Persistence) or EJB 2.x entity beans.
Since the application depends on EJB 2.0, you need to consider using an older version of WildFly that still supports these features. This will make the upgrade project meaningless. The alternative is to update the application to use more recent versions of the EJB specification, such as EJB 3.x, which would enhance compatibility with the latest WildFly releases.
Approach for EJB Upgrade
Having thought about a sketch for the EJB upgrade, the next logical step is to think about how to go about in effecting the upgrade. In 2013, EJB specification 3.2 was released as a part of Java EE 7. This spec aligns with JPA 2.1. (Java Persistence API). For ease of development and to minimize boilerplate code, Hibernate is widely used as a JPA implementation.
As of this writing, Hibernate 6.6 is the latest stable version. However, you should limit your urge to target this version for the upgrade for the time being. Instead, you should target Hibernate 4.3 since it was the first version to provide complete support for JPA 2.1, ensuring easy integration with EJB 3.2 components. We will look at this again towards the end of our discussion.
Application Container Upgrade
As discussed earlier, we have a compelling need to target Java 21. The earliest version of WildFly that supports Java SE 21 is WildFly 30, which was released in October 2023. WildFly 35 is the current version. For a reason like the decision on the target version of Hibernate, we will tentatively fix the target version of WildFly to be WildFly 30 (Not WildFly 35), or some version greater than 30.
The analysis so far is based on previous experience and on information available from different websites of the library providers. As you can see, by deciding that we need to update the application to use more recent versions of the EJB specification, such as EJB 3.x, we are able to provide initial estimates for the upgrade project which could also accommodate the efforts required to do code changes to comply to EJB 3.x API and deployment schemas.
Now, there is the next question: “Why can’t we target the latest available versions for Hibernate, WildFly, etc.?”
How do we answer this question?
Container Version vs. Component Version Dilemma
In Figure 1, the top portion of the list enumerates the different components and libraries whereas the bottom portion of the list enumerates the various runtime and platform (aka container) technologies used to host the application.
We need to have a strategy to finalize the target versions of all main containers as well as component technologies. Let us understand the challenge here with the help of Figure 3.
Figure 3 is an attempt to represent a superimposed view of the compatibility of various versions of the components or libraries with that of the runtime container or platform. Remember, this illustration is just representative, so do the various numbering (time and versions), nothing to do with the actuals.
Ideally, we would want the upgrade to target the latest versions of Java and WildFly, which are two separate vertically thick lines tending towards the right extreme of the diagram, black color for WildFly and blue color for Java. In the left extreme, we have listed the different components and libraries to be upgraded row-wise. Towards the middle, we have represented the general availability of various versions (v1, v2, v3, etc. in lower X-Axis) of each library as we traverse through the timeline (2025, 2026, 2027, etc. in upper X-Axis).
As of today (2025), we have an urge to upgrade the application since all or many of the library versions are outdated and hence pose a risk. This is shown by the red vertical strip. The dark green represents the availability of production releases of higher versions and the light green indicates that there is also a published roadmap available for later versions.
Assuming our objective is to enhance the life of the currently running application for another 5 years (until 2030), if for some reason there are libraries with uncertainties (no release candidate available, nor even a published roadmap available) until 2030, then that poses a risk. Such risk areas are marked with an amber color in Figure 3. Suppose those libraries are mainstream OSS libraries with active community support. In that case, there is every reason to believe that those will be de-risked in the future (down the line 2 or 3 years) when the respective vendors or communities publish an extended roadmap.
Having understood this view, the next step is to assess the compatibility of various released versions of the components or libraries with that of the runtime container or platform. Assume that we are now at the start of the project's execution. Such an analysis during the start of the project will make the lines of Java and WildFly converge towards the left (again, just representative), indicative of the compatibility of the versions of Java and WildFly with those of each of the components and libraries.
From this, we need to choose the minimum of the versions of Java and/or WildFly that are compatible with all or most of the components and libraries. Even though we need to choose the minimum of the versions of Java and/or WildFly, our objective should still be to “maximize the versions of Java and/or WildFly compatible with all or most of the components and libraries.” To restate this with respect to Figure 3, our objective should be to “minimize the concaveness of Java and/or WildFly lines.”
For example, if we start with Java 21 and WildFly 30 as our objective for the upgrade, we can still try out and check if a higher version of WildFly can still be chosen. Similarly, though we targeted Hibernate 4.3 as a minimum version, we can now attempt to see if a higher version of Hibernate could be adopted and so do with other libraries too. Figure 4 illustrates how the upgrade objective looks like with the learnings we have gained so far.
You need to appreciate that while some decisions on the upgrade projects could be made even before we start the project execution, a few other activities are iterative, and hence, you need to provision enough slack in the effort estimation. This is evident from Figure 4, where a few libraries are still in the state “To be analyzed,” which will get sorted out as we progress with the upgrade project.
Let us now list out how the typical project execution will progress, as follows:
- Align the container runtime (WildFly) and programming platform (JEE, JSE) to versions supported until at least 2030.
- Target the upgrade of libraries and components to the latest versions compatible with the container runtime.
- Iterate: In case of library or component conflicts, target the next previous version (with a roadmap extending to or near 2030).
- Perform regression and functional validation.
- Conduct performance and endurance validations.
- Assess and (re)size the infrastructure as needed.
As you can see, we have now moved from a complete trial-and-error-based approach to a more structured and systematic approach to plan and execute the technology upgrade of our application.
Conclusion
Technology upgrades of software applications are not green field projects most of the time. You need to carry the baggage of technology and architecture choices, and you still need to de-risk the running application from security, legal, and related business risks.
A combination of previous experience and a structured approach can provide a great deal of definitiveness to the overall planning and execution of such upgrade projects. As mentioned earlier, many of the steps and processes discussed in this writing can be applied to projects in technologies other than Java, too.
Opinions expressed by DZone contributors are their own.
Comments