Java 21 Migration Guide
Overview
This document is designed to be a comprehensive resource for users who have deployed the latest versions of the Modular Open Source Identity Platform (MOSIP) compatible with Java 11 and are preparing to upgrade their systems to Java 21. It provides a detailed, step-by-step migration process to facilitate a seamless and efficient transition. By adhering to the guidelines in this document, users can modernize their MOSIP environments to take full advantage of Java 21's improved performance, advanced security features, and enhanced functionality. The guide also emphasizes best practices to minimize disruptions, maintain system stability, and ensure compliance throughout the upgrade process.
1. Pre-requisites:
JDK 21: Ensure Java Development Kit (JDK) 21 is installed and configured in your system's environment variables.
Maven (Latest Version): To build and manage dependencies, use the latest version of Maven, such as 3.9.6.
Optional:
A modern IDE (e.g., Eclipse, IntelliJ IDEA, or others) is compatible with Java 21 to streamline coding and debugging.
Ensure the Lombok library version is compatible with your IDE. For instance, Lombok 1.18.30 works seamlessly with the latest IDE versions.
2. Backward compatibility:
Java applications compiled in older versions are compatible with Java 21 run time. To support running those application jars in Java 21, additional VM Arguments need to be added while running applications in Java 21.
Java applications compiled with older versions are compatible with the Java 21 runtime. However, to ensure these application JARs run correctly in Java 21, additional JVM arguments may need to be specified when running the applications.
The libraries must be API-compatible and should not rely on deprecated or removed APIs.
The libraries should not depend on older Spring Boot versions (before 3.x), as the newer Spring Boot versions introduce significant API changes. Failure to meet this requirement can lead to compile-time or runtime issues, such as errors during class loading, bean initialization, or method invocation.
The dependent libraries of any module that have a dependency on any other MOSIP library (such as kernel-core) or older Spring Boot version (older than 3.x) need to be migrated before migrating the specific module. This applies not only to the static dependencies mentioned in the POM file, but also to the dynamic dependencies loaded from the classpath such as Kernel Auth Adaptor, BioSDK client, or any such libraries.
3. Migration Changes in a module/repository:
I. POM file changes:
All POM versions of the modules and their dependency modules should be updated to reference the Java 21 migrated version.
Change the source and target compiler versions to 21:
Jacoco-plugin version needs to be updated to 0.8.11:
Unless there is a compelling reason for using a different version of the library than the version defined in kernel-bom, do not mention the version to that dependency, if done it will override the version with the specified one.
Remove any unused version properties from the pom.
2. Remove Deprecated Dependency
Remove any reference to the springdoc-openapi-ui
dependency to prevent conflicts.
Note:
Any exclusions specified for a library in the POM can be retained. However, the version mentioned in that dependency can be removed, allowing it to inherit the version defined in the kernel-bom or another POM file.
Always make it a practice to keep the versions in the properties instead of hardcoding.
Even if the version is changed in the properties of pom.xml, make sure those properties are used in those dependencies/plugins instead of hardcoding the version.
For example:
maven-javadoc-plugin dependency should refer to ${maven.javadoc.version} property in the POM file.
Check and remove any unused version properties. With the use of kernel-bom, the version does not need to be mentioned to the dependencies mostly, unless it needs to be overridden or a different version is used.
POM files should not include duplicate version properties, as this can lead to errors. For example, even if the version property is updated correctly in its first occurrence, subsequent occurrences may override it with an outdated or incorrect version. This behavior can go unnoticed and may cause unexpected errors or functionality issues. To avoid such problems, carefully review POM files to identify and remove any repeated version properties. This ensures consistent version management and prevents overriding conflicts.
II. Java file changes:
Below are the package changes that need to be applied in Java files.
javax.servlet.*
jakarta.servlet.*,
javax.annotation.*
jakarta.annotation.*,
javax.activation.*
jakarta.activation.*,
javax.persistence.*
jakarta.persistence.*,
javax.validation.*
jakarta.validation.*,
javax.mail.*
jakarta.mail.*,
org.apache.http.impl.client.*
org.apache.hc.client5.http.impl.classic.*,
org.apache.http.conn.ssl.SSLConnectionSocketFactory
org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory
Postgres Hibernate Dialect: Instead of using specific version dialects for PostgreSQL, such as org.hibernate.dialect.PostgreSQL95Dialect or org.hibernate.dialect.PostgreSQL92Dialect, it should be org.hibernate.dialect.PostgreSQLDialect. This is applicable for properties referred to in Java code and any properties file as well.
The Sleuth configuration has now been migrated to the Micrometer Tracer
Code changes were also necessary for the migration.
Import
BraveAutoConfiguration.class
instead of using a component scan oforg.springframework.cloud.sleuth.autoconfig.*
Use the
AccessLogValve
class as the base class instead ofValveBase
for theSleuthValve
class.Replace the following code:
tracer.newTrace()
→tracer.nextSpan()
span.context().traceIdString()
→span.context().traceId()
span.context().spanIdString()
→span.context().spanId()
Refer to the details below for the changes made.
HTTP Connection Manager changes:
The following changes have been introduced related to the HTTP Connection Manager:
Deprecation of Existing Configuration Methods
The previously existing configuration methods have been deprecated and removed.
New configuration methods must be used, utilizing updated classes such as:
ReactorLoadBalancerExchangeFilterFunction
PoolingHttpClientConnectionManagerBuilder
Direct Auto-Wiring of
ReactorLoadBalancerExchangeFilterFunction
Instead of using
LoadBalancerClient
withLoadBalancerExchangeFilterFunction
, directly auto-wireReactorLoadBalancerExchangeFilterFunction
in your implementation.
New Way to Set
setMaxConnPerRoute
The method for setting
setMaxConnPerRoute
in the HTTP connection manager now usesPoolingHttpClientConnectionManagerBuilder
.
Spring Security Changes: The Old way of extending WebSecurityConfigurationAdapter for SecurityConfig is deprecated, now it requires a new way of configuring the same using as mentioned below.
Remove
extends WebSecurityConfigurerAdapter
Replace
@EnableGlobalMethodSecurity(prePostEnabled = true)
with@EnableMethodSecurity
Replace the
.antMatchers()
method with.requestMatchers()
Replace the
@Override
on the configure method with@Bean
that returns theSecurityFilterChain
instance as a result ofhttp.build()
Spring Batch Migration: Spring Batch has been migrated to version 5.x. The following changes need to be applied:
DB Changes:
If a Spring datasource is already being used in the Spring Batch job application, the Batch Job-related tables must be created in that datasource (i.e., database) using the aforementioned DB script.
The existing Spring Batch Job tables have to be applied with the below upgrade script:
The rollback script for the above is given below:
POM Changes:
Add
spring-boot-starter-batch
if it does not already exist. The version will be 3.x ifkernel-bom
is used, as discussed in the previous sections.Add hibernate-validate dependency if it does not exist. The version will be 8.x if kernel-bom is used as discussed in the previous sections.
Java Code changes:
Remove
@EnableBatchProcessing
AnnotationThe
BatchConfigurer
andDefaultBatchConfigurer
classes are deprecated, and any references to them must be removed. For example, in thekernel-salt-generator
, these classes were used to implement a Map-based Job Repository. However, since the Map-based Job Repository is no longer supported and requires the use of database tables, the only option is to remove references to these classes and create the necessary batch job tables.JobBuilderFactory
andStepBuilderFactory
are deprecated. Instead, useJobBuilder
andStepBuilder
.Since
JobExecutionListenerSupport
is deprecated in favor of theJobExecutionListener
interface, update the Batch Job Listener class to implementJobExecutionListener
instead of extendingJobExecutionListenerSupport
.The
write
method parameter in theorg.springframework.batch.item.ItemWriter
interface has been changed fromList
toChunk
.Key Changes:
To obtain a stream from a
Chunk
, use theStreamSupport
utility class as follows:Similar to the
List.of()
the method used for creating aList
, you can use theChunk.of()
method to create aChunk
.
References:
In
@RestControllerAdvice
, if the response content type is not explicitly set toapplication/json
, it might default to returning an XML response. To prevent this, ensure thecontentType
is specified in theResponseEntity
as shown below:
In
org.apache.commons.lang3.time.DateUtils
, null parameters will now throw aNullPointerException
instead of anIllegalArgumentException
. To prevent breaking functionality, handle the exception as shown below.The impact is observed in utility methods for the following classes:
You can handle the
NullPointerException
appropriately by adding a null check or by using a try-catch block.In a JPA repository, for non-native query methods, the column names should be based on the entity’s field names rather than the actual column names. For example, if an entity has a column
lang_code
mapped to a fieldprivate String langCode
, the non-native query should uselangCode
instead oflang_code
.Since Joda Time is deprecated, the related date formatting classes have been removed from
spring-context
, which may cause compatibility issues. Therefore, replace Joda's Time-based logic withjava.time
-based logic to ensure compatibility and maintainability.Hibernate CriteriaBuilder: Hibernate’s CriteriaBuilder now cannot be reused for multiple CriteriaQuery instances using the createQuery method on the same builder instance. When we want to create multiple CriteriaQuery it should be associated with a new CriteriaBuilder instance, otherwise, it will throw an error saying java.lang.IllegalArgumentException: Already registered a copy: SqmBasicValuedSimplePath.
JPA Naming Strategy: The
SpringPhysicalNamingStrategy
class is no longer available in the latest Spring Boot jar. Therefore, any JPA configuration related to it should refer to the classorg.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
. For example:If component scanning does not work with the
scanBasePackages
attribute within the@SpringBootApplication
annotation, move that definition inside the@ComponentScan
annotation’sbasePackages
attribute. The same applies to any exclusions. When using the exclusion attribute in the@ComponentScan
annotation, apply the AspectJfilter-type
to exclude a list of packages for convenience.
III. JUnit Test Case related changes
Spring Boot Test Error Fix:
If you encounter the following error:
Add the exclusion in the Spring Boot Test application as shown below:
This will exclude the DataSourceAutoConfiguration.class
, resolving the issue.
Mockito Related errors: The
Mockito
dependencies in thespring-boot-bom
were incorrect, but this has now been corrected inkernel-bom
. The Mockito version is now correctly set to3.4.3
.Power-Mockito related issues:
Mockito Core Version Issue:
If you encounter the following error:
Solution: Ensure that the
mockito-core
version is set to3.4.3
.b. Maven Build Access Issues:
If the build fails due to access-related issues, add the following
argLine
configuration in themaven-surefire-plugin
section of thepom.xml
:
For example, if you are facing the following error:
then add the below command to the argument line.
If you encounter the
PBKDF2WithHmacSHA256 algorithm not supported
error, addjavax.crypto.*
to@PowerMockitoIgnore
as shown below:If you get the error
PowerMockitoInjectingAnnotationEngine.injectMocks() is not accessible
because it is a private method, usemockito-core version 3.11.2
in that module specifically.
Test Security Config: To configure test security, a
SecurityFilterChain
bean is required. The bean can be implemented as shown below:
If you encounter the following error:
Use the -e
flag with the mvn clean install
command to enable stack trace printing:
After running the command, scroll to the top of the errors to locate the real root cause, as this error is not the original issue.
Dependency Version Issue: In
kernel-pdfgenerator-itext
, downgrade theitext-core
version from7.2.0
to7.1.0
to fix test case issues.Error: MockMvc - 401 or 403 Status If using
mockMvc
results in errors like the below:Check the
TestSecurityConfig
to ensure that the URLs are permitted. Configure it as shown below to allow all requests during JUnit tests:Component Scanning Fix: If component scanning does not work with the
scanBasePackages
attribute within the@SpringBootApplication
annotation, follow these steps:Move the
scanBasePackages
definition to the@ComponentScan
annotation’sbasePackages
attribute.The same approach applies to any exclusions.
When using the
exclusion
attribute in the@ComponentScan
annotation, use theASPECTJ
filter type to conveniently exclude specific packages. Example:
Test Properties Not Loaded: If JUnit tests are not loading the test properties, ensure that the following annotation is added to the test class:
Replace application.properties
with the correct properties file name, if different.
IV. Configuration Changes
1. Explicitly Configuring Ant Path Matcher
In Spring MVC, the default path-matching strategy has changed to PathPatternParser
. This can cause failures when processing Ant-style patterns. To avoid such issues, explicitly configure the application to use AntPathMatcher
by setting the appropriate property in your configuration file.
For example:
2. Updated HTTP Header Size Property
The server.max-http-header-size
property is now deprecated. Use server.max-http-request-header-size
instead to configure the maximum HTTP request header size.
For example:
V. Artifactory Changes
The following steps can be followed to configure Artifactory during the Java migration for the modules:
If a dynamic dependency is migrated for a module that needs to be loaded from the artifactory (either directly downloaded as a jar file or packaged in a zip file), it can be created as a new entry to the artifactory pom.xml file by mentioning the version and its path which is different from an existing entry.
The idea is to keep the existing artifacts unchanged to have the existing services unaffected and only add new entries that are differentiated by the new version in the jar file or the containing folder. Finally, when all modules migration is done we can remove the old version artifact entries.
While deploying a migrated service, it is essential to update the Docker run command or Helm chart configuration to include the appropriate argument for loading the newer version of the dynamic library from the Artifactory server.
VI. Docker file changes
The following updates are done in the docker file.
FROM openjdk:11
FROM eclipse-temurin:21-jre-alpine
apt-get -y update
apk -q update
apt-get update -y
apk -q update
apt-get install -y
apk add -q
apk add -q unzip \
apk add -q unzip wget \
apt-get -y install
apk add -q
groupadd -g ${container_user_gid} ${container_user_group}
addgroup -g ${container_user_gid} ${container_user_group}
useradd -u ${container_user_uid} -g ${container_user_group} -s /bin/sh -m ${container_user}
adduser -s /bin/sh -u ${container_user_uid} -G ${container_user_group} -h /home/${container_user} --disabled-password ${container_user}
ARG container_user_uid=1001
ARG container_user_uid=1002
VII. Github Workflow changes:
If the new branch
develop-java21
is not included in the GitHub workflow push trigger, ensure that it is added.
Key Updates:
In the push trigger, the Java version was updated from:
java-version: 11
→java-version: 21
Note:
Please refer to the changes made for running the audit service in the following pull requests (PRs):
While these changes apply generally to most modules, specific dependencies or code used by other modules may require additional, module-specific modifications.
5. Additional Module-wise Migration Guides
I. Resident Service:
Perform the following additional steps if you encounter the issue mentioned below during migration:
Interceptor Issue: An empty interceptor is not working with Hibernate 6. To resolve this, you need to implement the
Interceptor
interface.
As part of the Java upgrade from an older version, the above method is deprecated. Therefore, use the following method instead:
II. Registration Client:
The
registration-client
internally uses Derby as the local database. As part of the migration to Java 21, the Derby dialect has been updated fromDerbyTenSevenDialect
toDerbyDialect
to ensure compatibility.During the migration, issues were observed when converting request and response objects to
String
orMap
withregistration-processor
APIs. To address this:The
requestType
for some APIs was explicitly set toString
.The response is now received as an
Object
and converted to aMap
to handle exceptions.
Due to this change:
A non-migrated
registration-client
will not work with a migratedregistration-processor
.However, a migrated
registration-client
will work with a non-migratedregistration-processor
, or both modules must be migrated together.As part of the Java migration, JavaFX has been upgraded to version 21.0.3 to support the latest features. When setting up the Java 21-migrated
registration-client
repository in your IDE, you will need to:Download the required JavaFX ZIP file.
While running or debugging the
Initialization.java
class to start theregistration-client
application, we pass certain VM arguments to ensure the application runs correctly. One of these arguments specifies the path to the OpenJFX ZIP file. You will need to update the path in the VM arguments to point to the latest JavaFX ZIP file that was downloaded in the previous step. Additionally, a few changes have been made to the existing VM arguments to support Java 21.
The updated VM arguments are listed below:
Running the Registration Client Downloader Docker image
The base image in the Dockerfile has been updated to
mosipdev/openjdk-21-jdk:latest
to support Java 21.The
registration-api-stub-impl
JAR dependency has been added to the Artifactory POM file. During the deployment of the registration client, this dependency is pulled from Artifactory and bundled with other JAR files in thelib
folder. This approach avoids adding the dependency directly to theregistration-services
POM file. If custom implementations related to document scanning or geo-positioning are required, they can be pulled from Artifactory and bundled without modifying theregistration-client
codebase.Since JavaFX has been migrated to version 21.0.3, the JavaFX-related files, specifically
zulu21.34.19-ca-fx-jre21.0.3-win_x64.zip
, have been added to Artifactory. This ZIP file is used when preparing the registration-client downloadable ZIP file.Modifications have been made to the
configure.sh
script to support the above two changes.
III. Pre-Registration:
Please refer to the below points for additional steps for the deployment of the pre-registration module:
Pre-registration Batch Job Upgrade Scripts for PostgreSQL DB:
Run the scripts in the respective pre-registration batch job tables.
Pre-reg Service Migration:
Java 21 Jars:
IV. Web-Sub Migration:
Please refer to the following points while migrating the Web-Sub Java version:
The Web-Sub repository contains a Java-based module called
kafka-admin-client
.As part of the migration process, the
websub/kafka-admin-client
module has been updated to Java 17, the latest version supported by Ballerina.Additionally, Ballerina has been upgraded to the latest version, 2201.9.0 (Swan Lake Update 9), to ensure compatibility with Java 17. This upgrade enables the module to leverage the new features and improvements introduced in both Java 17 and the updated Ballerina version.
Last updated
Was this helpful?