December 10, 2020
Estimated Post Reading Time ~

AEM 6.5 on Java 11 with Gradle

The recommended runtime environment for Adobe Experience Manager 6.5 (AEM) is Java 11. Let's see how to run it using Gradle AEM Plugin (GAP) and handle known discrepancies between Java 8 & 11.



In AEM instance setup using Gradle we presented how to set up AEM instances on Java 8 using GAP. Now, let's move a step forward and run AEM 6.5 on Oracle Java SE 11 JDK - 64bit.

Step by step:
  1. Handle the fact that the Common Annotations Module was removed from Java 11
  2. Expose extra packages in the system bundle (optional)
  3. Bypass known FELIX-6184 issue
  4. Open debug port (optional)
  5. Make reflection work (optional)
  6. Update Garbage Collector configuration (optional)
  7. Filter known errors in logs
Please see example GAP project running AEM 6.5 on Java 11.
1. Handle the fact that the Common Annotations Module was removed from Java 11

Since the Java Common Annotations Module got removed from Java 11 we need to add javax.annotation-api as a dependency, so that we're able to build our application on Java 11:

Maven
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> <scope>provided</scope> </dependency>

Gradle
compileOnly("javax.annotation:javax.annotation-api:1.3.2")

For details please refer to the mentioned PERFICIENT/digital blog post.
2. Expose extra packages in the system bundle (optional)

In some cases, we might need to expose extra packages in the system bundle. Especially, when upgrading from previous versions of AEM to AEM 6.5 and Java 11, in our application we might still depend on API available in Java 8 which is missing in 11. It might result in our bundle being Installed but not Active - some dependencies cannot be resolved (e.g. com.sun.org.apache.xpath.internal).

To expose extra packages we need to configure org.osgi.framework.system.packages.extra property in the AEM instance crx-quickstart/conf/sling.properties. It is possible to simply update this file manually, however, if you use GAP it is easy to automate.

Use GAP to override sling.properies
To achieve that, add a file with the new property under gradle directory in your project: 
src/aem/localInstance/common/crx-quickstart/conf/sling.properties
org.osgi.framework.system.packages.extra=com.sun.org.apache.xpath.internal;version\="{dollar}{felix.detect.java.version}"

Now, when creating a new instance with this configuration, GAP will merge extracted sling.properties with the one provided by you. System bundle will expose new packages and instance setup will remain fully automated and repeatable.

3. Bypass known FELIX-6184 issue
Similar solution can be applied to bypass known FELIX-6184 issue which pops up as "random" exceptions after reinstalling a bundle:

java.lang.NoClassDefFoundError: jdk/internal/reflect/ConstructorAccessorImpl at java.base/jdk.internal.misc.Unsafe.defineClass0(Native Method) at java.base/jdk.internal.misc.Unsafe.defineClass(Unsafe.java:1192) 
(...)

As described in the issue, we just need to add additional packages names for org.osgi.framework.bootdelegation property in the same sling.properties file:

org.osgi.framework.bootdelegation=sun.*,com.sun.*,jdk.internal.reflect,jdk.internal.reflect.*

Like in the previous section, we recommend using GAP to update instance sling.properties file.

4. Open debug port (optional)
Another interesting fact to remember: since Java 9, Java Debug Wire Protocol (JDWP) agent listens only on local network interface by default so remote connections will be rejected. This sounds sensible and secure. However, what to do when you need to debug a specific corner case related to content on your integration environment? A hostname of an asterisk (*) can be used to achieve the old behavior which is to bind the JDWP socket connector to all available interfaces. In this case, use following Java startup parameter: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:14502

It is simple to achieve that using GAP. We just need to specify debugAddress parameter in the gradle.properties:

instance.local-author.debugAddress=*

5. Make reflection work (optional)
Since Java 9 modules were introduced reflectively accessing internal Java APIs will fail if you won't allow it explicitly with --add-opens JVM option. A common case is dependency injection libraries like Guice that use the class loader’s internal API, which results in errors like the following:

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make ClassLoader.defineClass accessible: module java.base does not "opens java.lang" to unnamed module


Here is a handful of packages you might want to open for reflective access when running on AEM:
--add-opens=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED 
--add-opens=java.base/sun.net.www.protocol.jrt=ALL-UNNAMED 
--add-opens=java.naming/javax.naming.spi=ALL-UNNAMED 
--add-opens=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED 
--add-opens=java.base/java.lang=ALL-UNNAMED 
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED 
--add-opens=java.base/java.net=ALL-UNNAMED

As with the debug port example, you can use GAP to automate issuing those options at startup time extending gradle.properties file:

instance.local-author.jvmOpts=--add-opens=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED \ 
--add-opens=java.base/sun.net.www.protocol.jrt=ALL-UNNAMED \  
--add-opens=java.naming/javax.naming.spi=ALL-UNNAMED \ 
--add-opens=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED \ 
--add-opens=java.base/java.lang=ALL-UNNAMED \ 
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED \  
--add-opens=java.base/java.net=ALL-UNNAMED

6. Update Garbage Collector configuration (optional)
Sometimes you might want to select Parallel Garbage Collector for performance reasons. Again, just extend your list of JVM options with:

-XX:+UseParallelGC

7. Filter known errors in logs
Last but not least, how to make your logging transparent when running AEM on Java 11? Few things you might to consider:
  1. Hide Java 11 Nashorn deprecation warnings on runtime
  2. Filter out or fix "WARN with NPE in logs when opening Page Properties"
  3. Filter out "WARN EclipseJavaCompiler Using unsupported java version '11'"
1. Hide Java 11 Nashorn deprecation warnings on runtime
Nashorn Script Engine is deprecated in Java 11. Running it on AEM will generate WARNs in AEM logs. To suppress those warnings simply extend the list of JVM options with:

-Dnashorn.args=--no-deprecation-warning

2. Filter out or fix "WARN with NPE in logs when opening Page Properties"
Running AEM 6.5 and Java 11, when you open Page Properties in edit mode you might observe warnings containing NPE thrown from table_jsp.java:56. This seems to be a known issue. You can try to fix it or filter it out from your logs when using GAPs instanceTail task by extending your src/aem/instance/tail/incidentFilter.txt file with:

libs.granite.ui.components.coral.foundation.table Failed to wrap datasource for lookahead

See Filtering logs for more details.

3. Filter out "WARN EclipseJavaCompiler Using unsupported java version '11'"
There is another WARN related to SLING-8322. It is fixed in SLING but not released yet. A good candidate to filter it out with GAPs instanceTail:

org.apache.sling.commons.compiler.impl.EclipseJavaCompiler Using unsupported java version '11', assuming latest supported version '9'

Summary
Running AEM 6.5 on Java 11 might not be the simplest thing to do! It is good to have an understanding of what changed between Java 8 and 11 and how those changes affect your AEM instance. As always, it is good to automate. Here is the summary of changes you can make to GAPs configuration to be able to run it smoothly on Java 11:

1. build.gradle.kts - only if you use GAP to build your application (not covered in the example project):

dependencies { 
    compileOnly("javax.annotation:javax.annotation-api:1.3.2") 
}

2. src/aem/localInstance/common/crx-quickstart/conf/sling.properties
org.osgi.framework.system.packages.extra=com.sun.org.apache.xpath.internal;version\="{dollar}{felix.detect.java.version}" org.osgi.framework.bootdelegation=sun.*,com.sun.*,jdk.internal.reflect,jdk.internal.reflect.*

3. gradle.properties
instance.local-author.debugAddress=* instance.local-author.jvmOpts=
--add-opens=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED \  
--add-opens=java.base/sun.net.www.protocol.jrt=ALL-UNNAMED \  
--add-opens=java.naming/javax.naming.spi=ALL-UNNAMED \ 
--add-opens=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED \ 
--add-opens=java.base/java.lang=ALL-UNNAMED \ 
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED \  
--add-opens=java.base/java.net=ALL-UNNAMED \ -XX:+UseParallelGC \ 
-Dnashorn.args=--no-deprecation-warning

4. src/aem/instance/tail/incidentFilter.txt

libs.granite.ui.components.coral.foundation.table Failed to wrap datasource for lookahead org.apache.sling.commons.compiler.impl.EclipseJavaCompiler Using unsupported java version '11', assuming latest supported version '9'

You can find all those changes in one place in the example of GAP running AEM 6.5 on Java 11.


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.