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.
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.
Note: After adding Lombok to your project, ensure it is correctly set up in your IDE to avoid compilation issues. This typically involves running the Lombok installer or manually enabling it in the IDE settings.
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.
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:
Note: A new kernel-bom file has been introduced as part of this release in the commons repo which contains all the latest version changes to the spring-boot and other dependencies. Here spring-boot:3.2.3 is used.
Please refer to this file to learn about the dependencies and versions. this file is created to remove the repetitiveness in defining the dependencies. If you have other repositories that use repeated definitions of dependencies, create a new bom file for that specific repository that includes the kernel-bom in the dependencyManagement section then add the extra dependencies with appropriate versions, and then use the same bom file in your respective modules pom files.
Any module that needs the predefined versions for any of its dependencies should import the kernel-bom file into the module pom file’s dependency management section. Remove all the versions from the properties and dependencies section for which kernel-bom has already defined the version. Please refer to the example in the audit-service pom file. (You will need to include the latest link once tagging is done). If a module does not need any version from the kernel-bom, it's not needed to import it.
A module pom or bom file can import one or more bom files, and if there is a dependency referred from more than one bom file, it will be referred from the last bom file. Please refer here.
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.
Swagger UI update:
To upgrade to Swagger-UI version 3, make the following changes:
1. Update the Dependency
Add the springdoc-openapi-starter-webmvc-ui
dependency to your pom.xml file.
2. Remove Deprecated Dependency
Remove any reference to the springdoc-openapi-ui
dependency to prevent conflicts.
Note: If swagger-2 was used in the module already, change it to Swagger-3 and also make the above change.
Bouncy Castle Version update: Remove reference to an older version of the bouncycastle library and use the below one. The same is applied in kernel-bom and kernel-core. Please refer to the pom.xml file.
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.
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
Note: While doing a text replacement for the above packages, it might be accidentally renaming the properties having the same naming for example javax.persistence.jdbc.driver. Please make sure this does not happen. Please refer here to such fixes.
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
This required the addition of micrometer tracing dependencies and quartz scheduler dependency which is already included in kernel-core pom file.
Code changes were also necessary for the migration.
Import BraveAutoConfiguration.class
instead of using a component scan of org.springframework.cloud.sleuth.autoconfig.*
Use the AccessLogValve
class as the base class instead of ValveBase
for the SleuthValve
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
with LoadBalancerExchangeFilterFunction
, directly auto-wire ReactorLoadBalancerExchangeFilterFunction
in your implementation.
New Way to Set setMaxConnPerRoute
The method for setting setMaxConnPerRoute
in the HTTP connection manager now uses PoolingHttpClientConnectionManagerBuilder
.
For detailed implementation and examples, refer to this pull request.
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 the SecurityFilterChain
instance as a result of http.build()
Replace the existing deprecated way of HttpSecurity configurations with lambda based configuration. Please refer to this file present here.
HttpStatusCodeException.getStatusCode()
should be replaced with HttpStatus.valueOf(statusCodeException.getStatusCode().value())
Please refer to this link where all above stated changes have been made:
The existing bootstrap.properties
file continues to function only if the spring-cloud-starter-bootstrap dependency is added. This dependency is currently included in the kernel-core mentioned in the pom.xml file. If the dependency is not added, you will need to use application.properties instead.
Spring Batch Migration: Spring Batch has been migrated to version 5.x. The following changes need to be applied:
DB Changes:
The Map-based job repository is now deprecated. Therefore, any application running a batch job must have a database with Batch Job-related tables. If a database is not feasible, at least an in-memory database must be configured. Refer to the DB script for creating the tables.
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 if kernel-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
Annotation
The BatchConfigurer
and DefaultBatchConfigurer
classes are deprecated, and any references to them must be removed. For example, in the kernel-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.
Create a bean definition for PlatformTransactionManager
Please refer to the SaltGeneratorConfig.java file. Below is the relevant code snippet from the file:
JobBuilderFactory
and StepBuilderFactory
are deprecated. Instead, use JobBuilder
and StepBuilder
.
For reference, please refer to the SaltGeneratorJobConfig.java file.
Since JobExecutionListenerSupport
is deprecated in favor of the JobExecutionListener
interface, update the Batch Job Listener class to implement JobExecutionListener
instead of extending JobExecutionListenerSupport
.
For reference, see the relevant code in the BatchJobListener.java file.
The write
method parameter in the org.springframework.batch.item.ItemWriter
interface has been changed from List
to Chunk
.
For reference, please refer to the SaltWriter.java file.
Key Changes:
To obtain a stream from a Chunk
, use the StreamSupport
utility class as follows:
Similar to the List.of()
the method used for creating a List
, you can use the Chunk.of()
method to create a Chunk
.
References:
Spring Batch 5.0 Migration Guide: Spring Batch 5.0 Migration Guide
In @RestControllerAdvice
, if the response content type is not explicitly set to application/json
, it might default to returning an XML response. To prevent this, ensure the contentType
is specified in the ResponseEntity
as shown below:
In org.apache.commons.lang3.time.DateUtils
, null parameters will now throw a NullPointerException
instead of an IllegalArgumentException
. 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 the org.apache.commons.lang3.StringUtils.join
method, invalid arguments now throw an IllegalArgumentException
instead of an ArrayIndexOutOfBoundsException
.To prevent breaking functionality, handle this exception as shown in the StringUtils.java file.
Symmetric Algorithm AES/GCM/PKCS5Padding is no longer supported and it should be replaced with AES/GCM/NoPadding. Please refer to the CryptoUtil changes mentioned in the CryptoUtil.java file.
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 field private String langCode
, the non-native query should use langCode
instead of lang_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 with java.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 class org.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’s basePackages
attribute. The same applies to any exclusions. When using the exclusion attribute in the @ComponentScan
annotation, apply the AspectJ filter-type
to exclude a list of packages for convenience.
JUnit Test Dependency: To run the JUnit tests, the following dependency is required and has now been added to kernel-core
. For reference, see: kernel-core pom.xml
MVEL Dependency Update: If MVEL-related test cases are failing, update the MVEL dependency as per the kernel-applicanttype-api pom.xml file.
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 the spring-boot-bom
were incorrect, but this has now been corrected in kernel-bom
. The Mockito version is now correctly set to 3.4.3
.
For reference, please refer to the kernel-bom pom.xml file.
Power-Mockito related issues:
Mockito Core Version Issue:
If you encounter the following error:
Solution: Ensure that the mockito-core
version is set to 3.4.3
.
b. Maven Build Access Issues:
If the build fails due to access-related issues, add the following argLine
configuration in the maven-surefire-plugin
section of the pom.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, add javax.crypto.*
to @PowerMockitoIgnore
as shown below:
If you get the error PowerMockitoInjectingAnnotationEngine.injectMocks() is not accessible
because it is a private method, use mockito-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:
For reference, see the implementation in the file TestSecurityConfig.java (lines 34-38)
If you encounter the following error: 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 the itext-core
version from 7.2.0
to 7.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’s basePackages
attribute.
The same approach applies to any exclusions.
When using the exclusion
attribute in the @ComponentScan
annotation, use the ASPECTJ
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.
Note: Avoid Mixing JUnit 4 and JUnit 5
While this may not be directly related to Java migration, it is strongly recommended to use either JUnit 4 or JUnit 5 consistently throughout your test classes. Mixing the two versions can cause compatibility issues, leading to test failures or unexpected behavior.
Key Differences and Equivalents:
Assertions
JUnit 4: org.junit.Assert
JUnit 5: org.junit.jupiter.api.Assertions
Lifecycle Annotations
JUnit 4: @Before
JUnit 5: @BeforeEach
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.
Example:
Refer to the implementation here: application-default.properties (lines 469-470)
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.
Example:
Refer to the implementation here: application-default.properties (lines 337-338)
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.
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
If the new branch develop-java21
is not included in the GitHub workflow push trigger, ensure that it is added.
To see the changes made please refer to this commit.
Key Updates:
In the push trigger, the Java version was updated from:
java-version: 11
→ java-version: 21
For PR checks, any references to the kattu
repository in the GitHub workflow YAML files should now point to the master-java21
branch.
Please refer to the related commit here.
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.
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:
The registration-client
internally uses Derby as the local database. As part of the migration to Java 21, the Derby dialect has been updated from DerbyTenSevenDialect
to DerbyDialect
to ensure compatibility.
During the migration, issues were observed when converting request and response objects to String
or Map
with registration-processor
APIs. To address this:
The requestType
for some APIs was explicitly set to String
.
The response is now received as an Object
and converted to a Map
to handle exceptions.
Due to this change:
A non-migrated registration-client
will not work with a migrated registration-processor
.
However, a migrated registration-client
will work with a non-migrated registration-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 the registration-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:
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 the lib
folder. This approach avoids adding the dependency directly to the registration-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 the registration-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.
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:
Migrate the pre-reg service from Java 11 to Java 21. For more details, refer to the Pull Request #683 in the MOSIP repository.
Java 21 Jars:
Java 21 JARs can be taken from here.
Note:
Keycloak Role Removal: Please remove the INDIVIDUAL role from the mosip_prereg_client Available Roles in Keycloak.
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.