Expand my Community achievements bar.

Upgrading from Java 11 to Java 21: A Guide for AEM Developers

Avatar

Level 10

2/2/25

Upgrading from Java 11 to Java 21: A Guide for AEM Developers

by @daniel-strmecki

 

Overview

As an AEM developer, staying current with Java versions ensures we can leverage the latest features, security patches, and performance improvements. With Java 21 being the latest Long-Term Support (LTS) release, it's a critical upgrade for enterprises and developers working on Adobe Experience Manager (AEM) projects. Java LTS versions are supported for a longer period than non-LTS releases, making them ideal for production environments where stability and security are paramount. For example, Java 21 was released in September 2023 and will receive Premier Support until September 2028 and Extended Support until September 2031.

In the latest release of Cloud Manager 2025.1.0, Adobe announced that they now support Java 21 in AEMaaCS. Java 21 will be enabled for all customers in February 2025, along with the rollout of a new SonarQube version. This article focuses on API features and enhancements introduced between Java 11 and Java 21 that are most relevant for developers. We'll briefly touch on the upgrade progress and then dive into new APIs and syntax improvements with practical examples. The main goal of the article is to inform developers of the latest API improvements, so we can start using them immediately after the upgrade.

 

Key Points

Learn how to upgrade your AEMaaCS from Java 11 to 21 and then be able to leverage the new API features and syntax improvements:
 
Pattern Matching

 

if (obj instanceof String str) {
    System.out.println(str.toUpperCase());
}

 

 

Switch Expressions

 

String result = switch (day) {
    case MONDAY, FRIDAY -> "Workday";
    default -> "Weekend";
};

 

 

Text Blocks

 

String html = """
              <html>
                  <head>
                      <title>Welcome Page</title>
                  </head>
                  <body>
                      <h1>Hello, World!</h1>
                      <p>This is an example of HTML in Java.</p>
                  </body>
              </html>
              """;

 

 

Records

 

public record User(String name, int age) {}

 

 

Sealed Classes

 

public sealed class Shape permits Circle, Rectangle, CustomShape {} 
public final class Circle extends Shape {}  // No further extension
public sealed class Rectangle extends Shape permits Square {}  // Can be extended only by Square
public non-sealed class CustomShape extends Shape {}  // Can be extended freely

 

 

Virtual Threads

 

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10).forEach(i ->
        executor.submit(() -> System.out.println(Thread.currentThread()))
    );
}

 

 

Scoped Values

 

private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();

public static void main(String[] args) {
    try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
        executor.submit(() -> ScopedValue.runWhere(USERNAME, "Daniel", () -> {
            processRequest();
        }));
        executor.submit(() -> ScopedValue.runWhere(USERNAME, "Ema", () -> {
            processRequest();
        }));
    }
}

private static void processRequest() {
    System.out.println("Processing request for user: " + USERNAME.get());
}

 

 

Structured Concurrency

 

 public static void main(String[] args) throws Exception {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        var task1 = scope.fork(() -> fetchData("Task 1", 2)); // Forking within the scope
        var task2 = scope.fork(() -> fetchData("Task 2", 3));

        scope.join();  // Wait until both tasks are done or one fails
        scope.throwIfFailed(); // Propagates exceptions if any

        System.out.println(task1.resultNow()); // Collect results
        System.out.println(task2.resultNow());
    }
}

 

 

Collections and Steams

 

public static void main(String[] args) {
    SequencedCollection<String> list = List.of("A", "B", "C");
    System.out.println(list.getFirst());  // A
    System.out.println(list.getLast());   // C
    System.out.println(list.reversed());  // [C, B, A]
}

 

Full Article

Read the full article on https://meticulous.digital/blog/f/upgrading-from-java-11-to-java-21-a-guide-for-aem-developers to find out more.


Q&A

Please use this thread to ask questions relating to this article

8 Comments

Avatar

Level 4

2/3/25

Thanks @daniel-strmecki for such an informative guide. It's really helpful.

Avatar

2/9/25

Thank you, @daniel-strmecki , for the incredibly informative guide. It's been very helpful.

Avatar

Level 2

2/13/25

Hi @daniel-strmecki ,

 

After upgrading to java 21 junit test cases are failing any suggestions.

ERROR

 

org.mockito.exceptions.base.MockitoException:

 

Mockito cannot mock this class: interface org.apache.sling.api.resource.ResourceResolver.

 

If you're not sure why you're getting this error, please report to the mailing list.

 

 

Java : 21

JVM vendor name : Oracle Corporation

JVM vendor version : 21.0.5+9-LTS-239

JVM name : Java HotSpot(TM) 64-Bit Server VM

JVM version : 21.0.5+9-LTS-239

JVM info : mixed mode, sharing

OS name : Windows 11

OS version : 10.0

 

 

You are seeing this disclaimer because Mockito is configured to create inlined mocks.

You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

 

Underlying exception : org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface org.apache.sling.api.resource.ResourceResolver, interface java.io.Closeable, interface java.lang.AutoCloseable]

Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface org.apache.sling.api.resource.ResourceResolver, interface java.io.Closeable, interface java.lang.AutoCloseable]

Caused by: java.lang.IllegalStateException:

 

Byte Buddy could not instrument all classes within the mock's type hierarchy

 

This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:

 - Compiled by older versions of scalac

 - Classes that are part of the Android distribution

Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 65

 

Avatar

Level 10

2/13/25

Hi @kirank84421130,

first of all, why would you use Mockito to mock ResourceResolver and not use the awesome reusable fakes from AEMMocks? Maybe it is a good time to do some test refactoring...

I don't expect the same problem on my project, but I would recommend to:

  • Check that Mockito and ByteBuddy dependencies are up-to-date
  • Try enabling inline mocking, which avoids subclassing issues: -Dmockito.mock-maker=inline

Good luck,

Daniel

Avatar

Level 2

2/13/25

Hi @daniel-strmecki ,

 

Thanks for the quick response.

I've updated mockito to recent versions.
POM

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<argLine>-Dmockito.mock-maker=inline</argLine>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.15.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.15.2</version>
<scope>test</scope>
</dependency>

 Error

Running AccountModelTest
[ERROR] Tests run: 3, Failures: 0, Errors: 3, Skipped: 0, Time elapsed: 0.419 s <<< FAILURE! - in AccountModelTest
[ERROR] testAccountModelProperties Time elapsed: 0.031 s <<< ERROR!
java.lang.AbstractMethodError: Receiver class org.apache.xerces.jaxp.DocumentBuilderFactoryImpl does not define or inherit an implementation of the resolved method 'abstract void setFeature(java.lang.String, boolean)' of abstract class javax.xml.parsers.DocumentBuilderFactory.

[ERROR] testGetExportedType Time elapsed: 0 s <<< ERROR!
java.lang.NoClassDefFoundError: Could not initialize class org.apache.sling.testing.mock.osgi.OsgiMetadataUtil
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.AbstractMethodError: Receiver class org.apache.xerces.jaxp.DocumentBuilderFactoryImpl does not define or inherit an implementation of the resolved method 'abstract void setFeature(java.lang.String, boolean)' of abstract class javax.xml.parsers.DocumentBuilderFactory. [in thread "main"]

[ERROR] getStringOutput Time elapsed: 0 s <<< ERROR!
java.lang.NoClassDefFoundError: Could not initialize class org.apache.sling.testing.mock.osgi.OsgiMetadataUtil
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.AbstractMethodError: Receiver class org.apache.xerces.jaxp.DocumentBuilderFactoryImpl does not define or inherit an implementation of the resolved method 'abstract void setFeature(java.lang.String, boolean)' of abstract class javax.xml.parsers.DocumentBuilderFactory. [in thread "main"]

Thanks
Kiran

Avatar

Level 10

2/13/25

Hi @kirank84421130,

you will need to make sure that the libraries you use in your project are compatible with Java 21. The exception you are getting is quite straightforward, suggesting an outdated or incompatible version of the Xerces library. Make sure you have the latest versions of Xerces and AEM Mocks. Also, it would be good to open a separate topic for this.

 

Good luck,

Daniel