☕ Migrating the OpenMRS Backend to Java 21

☕ Migrating the OpenMRS Backend to Java 21

Background

The current OpenMRS Reference Application runs smoothly on the Java 21 runtime. However, most of our community-supported modules cannot be compiled with Java 21.

As we plan for the next major release—OpenMRS Platform 3.0.0—we've identified that upgrading to Spring 6 and Hibernate 6 requires us to drop support for Java 8 and adopt a Java version 17 or higher. Since migrating to any newer Java version involves similar effort, and Java 21 is the latest Long-Term Support (LTS) version, we've decided it's best to move directly to Java 21.

After discussions with the community, we've outlined the following migration plan:

OpenMRS Core

  • Add support for Java 21 while continuing to support Java 8, ensuring backward compatibility during the transition.

OpenMRS Modules

Each module will be reviewed individually to determine whether to:

  • Update the module to support Java 21, or

  • Deprecate the module and migrate its functionality to a more modern or consolidated alternative.

For modules that will be updated to support Java 21, the following steps will be taken:

  1. Update the base platform version to 2.7.0.

  2. Add Java 21 support while retaining Java 8 compatibility where possible.

  3. With the release of Platform 3.0.0, perform a full migration to Java 21 and create a Java 8-compatible branch (2.8.x) for legacy support.

Migration Guide for modules

We hope that this migration guide will help you solve some issues you will face while adding support for Java 21. However, as each module is different

Step 1: Update the Module to Platform 2.7.0

Java 21 support begins with OpenMRS Platform 2.7.0, so the first step is to update your module’s platform version to 2.7.0. After updating, try compiling the module using Java 8. You may encounter a few errors — here’s how to address some common ones.

Common Issues and Solutions

1. Compile Errors Due to Removed Deprecations

Some previously deprecated classes and interfaces may have been removed as part of the upgrade. To find replacements, check the OpenMRS documentation or core repository.

Example:
If you're using: org.openmrs.module.web.extension.AdministrationSectionExt

This class has been moved from core to the legacyui module. To resolve this, add the following dependency in your module's pom.xml:

<dependency> <groupId>org.openmrs.module</groupId> <artifactId>legacyui-omod</artifactId> <version>${legacyUiVersion}</version> <scope>provided</scope> </dependency>

2. org.hibernate.AssertionFailure: Unable to locate referenced entity mapping in Tests

If you encounter this error during testing, it likely means that Hibernate is unable to detect certain entity classes. You need to explicitly register the OpenMRS packages in your module’s TestingApplicationContext.xml by adding them to the sessionFactory bean configuration.

<bean id="sessionFactory" class="org.openmrs.api.db.hibernate.HibernateSessionFactoryBean"> <property name="configLocations"> <list> <value>classpath:hibernate.cfg.xml</value> <value>classpath:test-hibernate.cfg.xml</value> </list> </property> <property name="mappingJarLocations"> <ref bean="mappingJarResources" /> </property> <!-- default properties must be set in the hibernate.default.properties --> <property name="packagesToScan"> <list> <value>org.openmrs</value> </list> </property> </bean>

 

3. Getting a prompt for a username and password when running some tests.

In some cases, you may be prompted for a username and password when running tests, especially when the test class uses @DirtiesContext

 

Screenshot from 2025-05-02 11-56-33.png

 

To avoid this prompt, add @SkipBaseSetup to the test class. With @SkipBaseSetup, you may also need to manually load INITIAL_XML_DATASET_PACKAGE_PATH and EXAMPLE_XML_DATASET_PACKAGE_PATH for some tests.

 

@Before public void setupDatabase() throws Exception { initializeInMemoryDatabase(); authenticate(); executeDataSet(INITIAL_XML_DATASET_PACKAGE_PATH); executeDataSet(EXAMPLE_XML_DATASET_PACKAGE_PATH); executeDataSet(XML_DATASET_PACKAGE_PATH); }

 

4. Tests are not running

If mvn clean install completes without running any tests, it may be due to the use of maven-parent-openmrs-module in your pom.xml. To fix this, remove the <parent> block referencing the parent module.

// Remove this <parent> <groupId>org.openmrs.maven.parents</groupId> <artifactId>maven-parent-openmrs-module</artifactId> <version>1.1.1</version> </parent>

 

Step 2: Adding support for Java 21

 

Switch your Java version to 21 and try compiling the module. There are a few known issues when adding support for Java 21.

Source option X is no longer supported. Use 8 or later

This happens when the maven-compiler-plugin is configured with a source or target version lower than Java 8. Update your pom.xml to set the compiler plugin to use Java 8.

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <target>1.8</target> <source>1.8</source> </configuration> </plugin>

 

java.lang.reflect.InaccessibleObjectException errors

These errors occur due to strong runtime encapsulation introduced in modern Java versions (Java 17+).

Avoid using argLine or --add-opens flags to bypass these errors, it's a temporary workaround and not a proper solution.

  • Update outdated dependencies to versions that support modern Java.

  • If a dependency is deprecated or no longer maintained, consider replacing it.

Example:
Replace PowerMock (which relies on deep reflection) with Mockito.mockStatic, which is supported in recent versions of Mockito.

 

 

Resources

 

Epic: https://openmrs.atlassian.net/jira/polaris/projects/OMRS/ideas/view/3970978?selectedIssue=OMRS-341

Talk Discussion: https://talk.openmrs.org/t/planning-java-21-migration-for-openmrs-modules/45713?u=wikumc