☕ 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:
Update the base platform version to 2.7.0.
Add Java 21 support while retaining Java 8 compatibility where possible.
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
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