6 Preparing for Migration
Tip:
Evaluate the Effort of Migration with Java Migration AnalysisOracle offers Java SE Subscribers and Oracle Cloud Infrastructure (OCI) users the Java Migration Analysis feature in the Java Management Service. This feature performs a comprehensive analysis of the effort involved in migrating to a new JDK version. It generates detailed reports highlighting the effort required and areas needing modification for migrating from an older JDK version to a newer one. During the migration process, you can utilize the analysis reports from Migration Analysis to identify classes and APIs that require changes. The reports specify line numbers in the source code where modifications are needed and highlight both mandatory and recommended changes.
The following sections will help you successfully migrate your application:
Run Your Program Before Recompiling
Try running your application on the latest JDK release (JDK 23). Most code and libraries should work on JDK 23 without any changes, but there may be some libraries that need to be upgraded.
Note:
Migrating is an iterative process. You’ll probably find it best to try running your program (this task) first, then complete these three tasks in parallel:
When you run your application, look for warnings from the JVM about obsolete VM options. If the VM fails to start, then look for Removed GC Options.
If your application starts successfully, look carefully at your tests and ensure that the behavior is the same as on the JDK version you have been using. For example, a few early adopters have noticed that their dates and currencies are formatted differently. See Migrate Away From the COMPAT Locale Data Provider.
- For detailed information about the new features and changes in JDK 23, see What's New in JDK 23- New Features and Enhancements.
- For detailed information about the new features and changes in JDK 22, see What's New in JDK 22 - New Features and Enhancements.
- For detailed information about the new features and changes in JDK 21, see What's New in JDK 21 - New Features and Enhancements.
- For detailed information about the new features and changes in JDK 20, see What's New in JDK 20 - New Features and Enhancements.
- For detailed information about the new features and changes in JDK 19, see What's New in JDK 19 - New Features and Enhancements.
- For detailed information about the new features and changes in JDK 18, see What's New in JDK 18 - New Features and Enhancements.
- For detailed information about the new features and changes in JDK 17, see What's New in JDK 17 - New Features and Enhancements.
- For detailed information about the new features and changes in JDK 16, see What's New in JDK 16 - New Features and Enhancements.
- For detailed information about the new features and changes in JDK 15, see What's New in JDK 15 - New Features and Enhancements.
- For detailed information about the new features and changes in JDK 14, see What's New in JDK 14 - New Features and Enhancements.
-
For detailed information about the new features and changes in JDK 13, see What's New in JDK 13 - New Features and Enhancements.
-
For detailed information about the new features and changes in JDK 12, see What's New in JDK 12 - New Features and Enhancements.
-
For detailed information about the new features and changes in JDK 11, see What’s New in JDK 11 - New Features and Enhancements.
-
For detailed information about the new features and changes in JDK 10, see What’s New in JDK 10.
-
For a comprehensive list of all of the new features of JDK 9, see What's New in JDK 9.
For detailed information about the changes in JDK 9, see JDK 9 Release Notes.
Even if your program appears to run successfully, you should complete the rest of the steps in this guide and review the list of issues.
Update Third-Party Libraries
For every tool and third-party library that you use, you may need to have an updated version that supports the latest JDK release.
Check the websites for your third-party libraries and your tool vendors for a version of each library or tool that’s designed to work on the latest JDK. If one exists, then download and install the new version.
If you use Maven or Gradle to build your application, then make sure to upgrade to a recent version that supports the latest JDK version.
If you use an IDE to develop your applications, then it might help in migrating the existing code. The NetBeans, Eclipse, and IntelliJ IDEs all have versions available that include support for the latest JDK.
You can see the status of the testing of many Free Open Source Software (FOSS) projects with OpenJDK builds at Quality Outreach on the OpenJDK wiki.
Compile Your Application if Needed
Compiling your code with the latest JDK compiler will ease migration to future releases since the code may depend on APIs and features, which have been identified as problematic. However, it is not strictly necessary.
If you need to compile your code with JDK 11 and later compilers, then take note of the following:
-
Use the new
--release
flag instead of the-source
and-target
options. See javac in Java Development Kit Tool Specifications.The supported values of
--release
are the current Java SE release and a limited number of previous releases, detailed in the command-line help.The
javac
can recognize and process class files of all previous JDKs, going all the way back to JDK 1.0.2 class files.See JEP 182: Policy for Retiring javac -source and -target Options.
-
If you use the
-source
and-target
options withjavac
, then check the values that you use.The supported
-source/-target
values are 23 (the default) till 9. The value 8 is deprecated and causes a warning.In JDK 8,
-source
and-target
values of 1.7/7 and earlier were deprecated, and caused a warning. In JDK 9 and above, those values cause an error.>javac -source 7 -target 7 Sample.java warning: [options] bootstrap class path is not set in conjunction with -source 7 not setting the bootstrap class path may lead to class files that cannot run on JDK 8 --release 7 is recommended instead of -source 7 -target 7 because it sets the bootstrap class path automatically error: Source option 7 is no longer supported. Use 8 or later. error: Target option 7 is no longer supported. Use 8 or later.
Note:
When using--release
, you cannot also use the--source
/-source
or--target
/-target
options. -
If you use the underscore character
(_)
as a one-character identifier in source code, then your code won’t compile in JDK 11 and later releases. It generates a warning in JDK 8, and an error, starting from JDK 9.As an example:
static Object _ = new Object();
This code generates the following error message from the compiler:
MyClass.java:2: error: as of release 9, '_' is a keyword, and may not be used as a legal identifier.
-
Critical internal JDK APIs such as sun.misc.Unsafe are still accessible in JDK 11 and later, but most of the JDK’s internal APIs are not accessible at compile time. You may get compilation errors that indicate that your application or its libraries are dependent on internal APIs.
To identify the dependencies, run the Java Dependency Analysis tool. See Run jdeps on Your Code. If possible, update your code to use the supported replacement APIs.
You may use the
--add-exports
and--add-opens
options as a temporary workaround to compile source code with references to JDK internal classes. See JEP 261: Module System and Strong Encapsulation in the JDK for more information about these options. -
You may see more deprecation warnings than previously.
Run jdeps on Your Code
Run the jdeps
tool on your application to see what packages and classes your applications and libraries depend on. If you use internal APIs, then jdeps
may suggest replacements to help you to update your code.
To look for dependencies on internal JDK APIs, run jdeps
with the -jdkinternals
option. For example, if you run jdeps
on a class that calls sun.misc.BASE64Encoder
, you’ll see:
>jdeps -jdkinternals Sample.class
Sample.class -> JDK removed internal API
Sample -> sun.misc.BASE64Encoder JDK internal API (JDK removed internal API)
Warning: JDK internal APIs are unsupported and private to JDK implementation that are
subject to be removed or changed incompatibly and could break your application.
Please modify your code to eliminate dependency on any JDK internal APIs.
For the most recent update on JDK internal API replacements, please check:
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
JDK Internal API Suggested Replacement
---------------- ---------------------
sun.misc.BASE64Encoder Use java.util.Base64 @since 1.8
If you use Maven, there’s a jdeps
plugin available.
For jdeps
syntax, see jdeps
in the Java Development Kit Tool Specifications.
Keep in mind that jdeps
is a static analysis tool, and static analysis of code might not provide a complete list of dependencies. If the code uses reflection to call an internal API, then jdeps
doesn’t warn you.
Migrate Away From the COMPAT Locale Data Provider
The COMPAT
locale data provider, which represents the locale data that is compatible with releases prior to JDK 9, has been removed in JDK 23.
COMPAT
. Do this by checking if the value COMPAT
is specified in the system property java.locale.providers
. If so, then test your applications with the latest JDK and the default CLDR
locale provider with respect to locale-related functions, such as the formatting and parsing of dates, times, and numbers. If your tests yield unexpected results, then try one of the following options:
Force Locale-Sensitive APIs to Use Legacy Locale Data at Startup
$ java -Djava.locale.providers=JRE,CLDR
COMPAT
can be used as a synonym for JRE
, for example, -Djava.locale.providers=COMPAT,CLDR
.
Note:
Forcing the use of legacy locale data must be treated as a temporary measure. In a release after JDK 9, only CLDR locale data will be available.Modify Your Code to Always Format and Parse Strings With the Same Patterns as Those in Legacy Locale Data
SimpleDateFormat
API to format Date objects. On JDK 8, the code might have obtained a SimpleDateFormat
as follows:SimpleDateFormat fmt
= (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.UK);
// prints "19-Mar-2024" on JDK 8 but "19 Mar 2024" on JDK 9
System.out.println(fmt.format(new Date()));
This solution can work well for small applications, or for large applications that store formats in singleton variables whose use is rigorously enforced across the codebase.
Create a Custom Locale Data Provider and Include It in the Application
A custom locale provider can override the CLDR
provider so that locale-sensitive APIs, when formatting and parsing strings, give priority to the patterns defined by the custom locale provider.
Note:
This option is complex because it involves changes to how the application is packaged and deployed. Before considering this option, investigate the previous option, Modify Your Code to Always Format and Parse Strings With the Same Patterns as Those in Legacy Locale Data. This option is more localized.package com.example.localization;
import java.text.*;
import java.text.spi.*;
import java.util.*;
public class HyphenatedUKDates extends DateFormatProvider {
@Override
public Locale[] getAvailableLocales() {
return new Locale[]{Locale.UK};
}
@Override
public DateFormat getDateInstance(int style, Locale locale) {
assert locale.equals(Locale.UK);
switch (style) {
case DateFormat.FULL:
return new SimpleDateFormat("EEEE, d MMMM yyyy");
case DateFormat.LONG:
return new SimpleDateFormat("dd MMMM yyyy");
case DateFormat.MEDIUM:
return new SimpleDateFormat("dd-MMM-yyyy");
case DateFormat.SHORT:
return new SimpleDateFormat("dd/MM/yy");
default:
throw new IllegalArgumentException("style not supported");
}
}
@Override
public DateFormat getDateTimeInstance(int dateStyle, int timeStyle,
Locale locale)
{
...
}
@Override
public DateFormat getTimeInstance(int style, Locale locale) {
...
}
}
The following is another example. The short month name for September differs between CLDR
and COMPAT
in the UK locale. The following SPI implementation addresses this incompatibility:
package spi;
import java.text.DateFormatSymbols;
import java.text.spi.DateFormatSymbolsProvider;
import java.util.Locale;
public class ShortMonthModifier extends DateFormatSymbolsProvider {
@Override
public DateFormatSymbols getInstance(Locale locale) {
assert locale.equals(Locale.UK);
return new DateFormatSymbols() {
@Override
public String[] getShortMonths() {
var ret = new DateFormatSymbols(Locale.UK).getShortMonths().clone();
ret[Calendar.SEPTEMBER] = "Sep";
return ret;
}
};
}
@Override
public Locale[] getAvailableLocales() {
return new Locale[]{Locale.UK};
}
}
Once you have implemented a custom locale data provider, package it as described in the section Packaging of Locale Sensitive Service Provider Implementations in the LocaleServiceProvider JavaDoc API documentation. Afterward, place it on the classpath and then run your applications with the -Djava.locale.providers=SPI,CLDR
command-line option.