You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2024/06/09 21:16:01 UTC

(freemarker) branch java-impl-static-linking created (now 3b462a5f)

This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a change to branch java-impl-static-linking
in repository https://gitbox.apache.org/repos/asf/freemarker.git


      at 3b462a5f Statically linked JavaNImpl classes, utilizing "JEP 238: Multi-Release JAR Files"

This branch includes the following new commits:

     new 3b462a5f Statically linked JavaNImpl classes, utilizing "JEP 238: Multi-Release JAR Files"

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(freemarker) 01/01: Statically linked JavaNImpl classes, utilizing "JEP 238: Multi-Release JAR Files"

Posted by dd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch java-impl-static-linking
in repository https://gitbox.apache.org/repos/asf/freemarker.git

commit 3b462a5f78fe263ada4ab6609bb5cad2ef9e1231
Author: ddekany <dd...@apache.org>
AuthorDate: Tue Jun 4 09:05:52 2024 +0200

    Statically linked JavaNImpl classes, utilizing "JEP 238: Multi-Release JAR Files"
---
 build.gradle.kts                                   |  5 +-
 .../src/main/java/freemarker/core/_Java16.java     |  9 ++
 .../core/{_Java16.java => _Java16Impl.java}        | 24 +++++-
 .../src/main/java/freemarker/core/_Java9.java      |  9 ++
 .../core/{_Java16.java => _Java9Impl.java}         | 22 +++--
 .../main/java/freemarker/core/_JavaVersions.java   | 95 ----------------------
 .../freemarker/ext/beans/ClassIntrospector.java    |  9 +-
 .../ext/beans/ClassIntrospectorBuilder.java        |  9 +-
 .../src/main/java/freemarker/core/_Java16Impl.java | 13 ++-
 .../beans/Java16TestClassLoadingCorrectTest.java   | 23 +++---
 .../ext/beans/NotExportedInternalPackageTest.java  |  3 +
 .../src/main/java/freemarker/core/_Java9Impl.java  | 13 ++-
 .../test/java/freemarker/core/Java9ImplTest.java   | 10 +--
 .../core/Java9TestClassLoadingCorrectTest.java     | 22 ++---
 14 files changed, 109 insertions(+), 157 deletions(-)

diff --git a/build.gradle.kts b/build.gradle.kts
index 85309947..e2575431 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -20,7 +20,7 @@
 import java.io.FileOutputStream
 import java.nio.charset.StandardCharsets
 import java.nio.file.Files
-import java.util.Properties
+import java.util.*
 import java.util.stream.Collectors
 
 plugins {
@@ -144,6 +144,9 @@ tasks.sourcesJar.configure {
         into("META-INF")
     }
 
+    exclude("freemarker/core/_Java9Impl.java")
+    exclude("freemarker/core/_Java16Impl.java")
+
     // Depend on the createBuildInfo task and include the generated file
     dependsOn(createBuildInfo)
     from(buildInfoFile())
diff --git a/freemarker-core/src/main/java/freemarker/core/_Java16.java b/freemarker-core/src/main/java/freemarker/core/_Java16.java
index 19de70a3..396c83bb 100644
--- a/freemarker-core/src/main/java/freemarker/core/_Java16.java
+++ b/freemarker-core/src/main/java/freemarker/core/_Java16.java
@@ -26,6 +26,15 @@ import java.util.Set;
  * Used for accessing functionality that's only present in Java 16 or later.
  */
 public interface _Java16 {
+    /**
+     * Using "JEP 238: Multi-Release JAR Files", links to the proper version of the {@link _Java16Impl} class.
+     */
+    _Java16 INSTANCE = new _Java16Impl();
+
+    /**
+     * Tells if Java 16 features can be used in the current run-time environment.
+     */
+    boolean isSupported();
 
     boolean isRecord(Class<?> cl);
 
diff --git a/freemarker-core/src/main/java/freemarker/core/_Java16.java b/freemarker-core/src/main/java/freemarker/core/_Java16Impl.java
similarity index 55%
copy from freemarker-core/src/main/java/freemarker/core/_Java16.java
copy to freemarker-core/src/main/java/freemarker/core/_Java16Impl.java
index 19de70a3..5ae2f102 100644
--- a/freemarker-core/src/main/java/freemarker/core/_Java16.java
+++ b/freemarker-core/src/main/java/freemarker/core/_Java16Impl.java
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package freemarker.core;
 
 import java.lang.reflect.Method;
@@ -23,12 +24,27 @@ import java.util.Set;
 
 /**
  * Used internally only, might change without notice!
- * Used for accessing functionality that's only present in Java 16 or later.
+ * Pre-Java-16 implementation of {@link _Java16}.
  */
-public interface _Java16 {
+// We also have a Java 16 versions of this class in freemarker-core16, and we put all versions into
+// the jar artifact via "JEP 238: Multi-Release JAR Files".
+public final class _Java16Impl implements _Java16 {
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
 
-    boolean isRecord(Class<?> cl);
+    @Override
+    public boolean isRecord(Class<?> cl) {
+        throw newNoRecordSupportException();
+    }
 
-    Set<Method> getComponentAccessors(Class<?> recordClass);
+    @Override
+    public Set<Method> getComponentAccessors(Class<?> recordClass){
+        throw newNoRecordSupportException();
+    }
 
+    private UnsupportedOperationException newNoRecordSupportException() {
+        return new UnsupportedOperationException("Record support needs at least Java 16");
+    }
 }
diff --git a/freemarker-core/src/main/java/freemarker/core/_Java9.java b/freemarker-core/src/main/java/freemarker/core/_Java9.java
index 6f1a56ee..a446848f 100644
--- a/freemarker-core/src/main/java/freemarker/core/_Java9.java
+++ b/freemarker-core/src/main/java/freemarker/core/_Java9.java
@@ -23,6 +23,15 @@ package freemarker.core;
  * Used for accessing functionality that's only present in Java 9 or later.
  */
 public interface _Java9 {
+    /**
+     * Using "JEP 238: Multi-Release JAR Files", links to the proper version of the {@link _Java9Impl} class.
+     */
+    _Java9 INSTANCE = new _Java9Impl();
+
+    /**
+     * Tells if Java 9 features can be used in the current run-time environment.
+     */
+    boolean isSupported();
 
     boolean isAccessibleAccordingToModuleExports(Class<?> m);
 
diff --git a/freemarker-core/src/main/java/freemarker/core/_Java16.java b/freemarker-core/src/main/java/freemarker/core/_Java9Impl.java
similarity index 64%
copy from freemarker-core/src/main/java/freemarker/core/_Java16.java
copy to freemarker-core/src/main/java/freemarker/core/_Java9Impl.java
index 19de70a3..68b16761 100644
--- a/freemarker-core/src/main/java/freemarker/core/_Java16.java
+++ b/freemarker-core/src/main/java/freemarker/core/_Java9Impl.java
@@ -16,19 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package freemarker.core;
 
-import java.lang.reflect.Method;
-import java.util.Set;
+package freemarker.core;
 
 /**
  * Used internally only, might change without notice!
- * Used for accessing functionality that's only present in Java 16 or later.
+ * Pre-Java-9 implementation of {@link _Java9}.
  */
-public interface _Java16 {
-
-    boolean isRecord(Class<?> cl);
-
-    Set<Method> getComponentAccessors(Class<?> recordClass);
+// We also have a Java 9 versions of this class in freemarker-core9, and we put all versions into
+// the jar artifact via "JEP 238: Multi-Release JAR Files".
+public class _Java9Impl implements _Java9 {
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
 
+    @Override
+    public boolean isAccessibleAccordingToModuleExports(Class<?> m) {
+        throw new UnsupportedOperationException("Requires at least Java 9");
+    }
 }
diff --git a/freemarker-core/src/main/java/freemarker/core/_JavaVersions.java b/freemarker-core/src/main/java/freemarker/core/_JavaVersions.java
deleted file mode 100644
index 4a3957e4..00000000
--- a/freemarker-core/src/main/java/freemarker/core/_JavaVersions.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package freemarker.core;
-
-import freemarker.log.Logger;
-import freemarker.template.Version;
-import freemarker.template.utility.SecurityUtilities;
-
-/**
- * Used internally only, might change without notice!
- */
-public final class _JavaVersions {
-    
-    private _JavaVersions() {
-        // Not meant to be instantiated
-    }
-
-    /**
-     * {@code null} if Java 9 is not available, otherwise the object through with the Java 9 operations are available.
-     */
-    static public final _Java9 JAVA_9 = isAtLeast(9, "java.lang.Module")
-            ? tryLoadJavaSupportSingleton(9, _Java9.class)
-            : null;
-
-    /**
-     * {@code null} if Java 16 is not available, otherwise the object through with the Java 16 operations are available.
-     */
-    static public final _Java16 JAVA_16 = isAtLeast(16, "java.net.UnixDomainSocketAddress")
-            ? tryLoadJavaSupportSingleton(16, _Java16.class)
-            : null;
-
-    @SuppressWarnings("unchecked")
-    private static <T> T tryLoadJavaSupportSingleton(int javaVersion, Class<T> javaSupportInterface) {
-        String implClassName = "freemarker.core._Java" + javaVersion + "Impl";
-        try {
-            return (T) Class.forName(implClassName)
-                    .getField("INSTANCE")
-                    .get(null);
-        } catch (Exception e) {
-            try {
-                if (e instanceof ClassNotFoundException) {
-                    // Happens when we run JUnit tests
-                    Logger.getLogger("freemarker.runtime").warn(
-                            "Seems that the Java " + javaVersion + " support class (" + implClassName
-                                    + ") wasn't included in the build");
-                } else {
-                    Logger.getLogger("freemarker.runtime").error(
-                            "Failed to load Java " + javaVersion + " support class",
-                            e);
-                }
-            } catch (Exception e2) {
-                // Suppressed
-            }
-            return null;
-        }
-    }
-
-    private static boolean isAtLeast(int minimumMajorVersion, String proofClassPresence) {
-        boolean result = false;
-        String vStr = SecurityUtilities.getSystemProperty("java.version", null);
-        if (vStr != null) {
-            try {
-                Version v = new Version(vStr);
-                result = v.getMajor() >= minimumMajorVersion;
-            } catch (Exception e) {
-                // Ignore
-            }
-        } else {
-            try {
-                Class.forName(proofClassPresence);
-                result = true;
-            } catch (Exception e) {
-                // Ignore
-            }
-        }
-        return result;
-    }
-
-}
diff --git a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index 45f76de7..5e229937 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -48,7 +48,8 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import freemarker.core.BugException;
-import freemarker.core._JavaVersions;
+import freemarker.core._Java16;
+import freemarker.core._Java9;
 import freemarker.ext.beans.BeansWrapper.MethodAppearanceDecision;
 import freemarker.ext.beans.BeansWrapper.MethodAppearanceDecisionInput;
 import freemarker.ext.util.ModelCache;
@@ -200,7 +201,7 @@ class ClassIntrospector {
         this.defaultZeroArgumentNonVoidMethodPolicy = builder.getDefaultZeroArgumentNonVoidMethodPolicy();
         this.recordZeroArgumentNonVoidMethodPolicy = builder.getRecordZeroArgumentNonVoidMethodPolicy();
         this.recordAware = defaultZeroArgumentNonVoidMethodPolicy != recordZeroArgumentNonVoidMethodPolicy;
-        if (recordAware && _JavaVersions.JAVA_16 == null) {
+        if (recordAware && !_Java16.INSTANCE.isSupported()) {
             throw new IllegalArgumentException(
                     "defaultZeroArgumentNonVoidMethodPolicy != recordZeroArgumentNonVoidMethodPolicy, " +
                     "but record support is not available (as Java 16 support is not available).");
@@ -343,7 +344,7 @@ class ClassIntrospector {
             ClassMemberAccessPolicy effClassMemberAccessPolicy) throws IntrospectionException {
         BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
 
-        boolean treatClassAsRecord = recordAware && _JavaVersions.JAVA_16.isRecord(clazz);
+        boolean treatClassAsRecord = recordAware && _Java16.INSTANCE.isRecord(clazz);
         ZeroArgumentNonVoidMethodPolicy zeroArgumentNonVoidMethodPolicy = treatClassAsRecord
                 ? recordZeroArgumentNonVoidMethodPolicy
                 : defaultZeroArgumentNonVoidMethodPolicy;
@@ -835,7 +836,7 @@ class ClassIntrospector {
     private static void discoverAccessibleMethods(
             Class<?> clazz, Map<ExecutableMemberSignature, List<Method>> accessibles) {
         if (Modifier.isPublic(clazz.getModifiers())
-                && (_JavaVersions.JAVA_9 == null || _JavaVersions.JAVA_9.isAccessibleAccordingToModuleExports(clazz))) {
+                && (!_Java9.INSTANCE.isSupported() || _Java9.INSTANCE.isAccessibleAccordingToModuleExports(clazz))) {
             try {
                 Method[] methods = clazz.getMethods();
                 for (int i = 0; i < methods.length; i++) {
diff --git a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospectorBuilder.java b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospectorBuilder.java
index 7ff87072..141917cc 100644
--- a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospectorBuilder.java
+++ b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospectorBuilder.java
@@ -26,7 +26,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
-import freemarker.core._JavaVersions;
+import freemarker.core._Java16;
 import freemarker.template.Configuration;
 import freemarker.template.Version;
 import freemarker.template._TemplateAPI;
@@ -76,9 +76,10 @@ final class ClassIntrospectorBuilder implements Cloneable {
         this.incompatibleImprovements = normalizeIncompatibleImprovementsVersion(incompatibleImprovements);
         treatDefaultMethodsAsBeanMembers = incompatibleImprovements.intValue() >= _VersionInts.V_2_3_26;
         defaultZeroArgumentNonVoidMethodPolicy = ZeroArgumentNonVoidMethodPolicy.METHOD_ONLY;
-        recordZeroArgumentNonVoidMethodPolicy = incompatibleImprovements.intValue() >= _VersionInts.V_2_3_33 && _JavaVersions.JAVA_16 != null
-                ? ZeroArgumentNonVoidMethodPolicy.BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD
-                : defaultZeroArgumentNonVoidMethodPolicy;
+        recordZeroArgumentNonVoidMethodPolicy
+                = incompatibleImprovements.intValue() >= _VersionInts.V_2_3_33 && _Java16.INSTANCE.isSupported()
+                        ? ZeroArgumentNonVoidMethodPolicy.BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD
+                        : defaultZeroArgumentNonVoidMethodPolicy;
         memberAccessPolicy = DefaultMemberAccessPolicy.getInstance(this.incompatibleImprovements);
     }
 
diff --git a/freemarker-core16/src/main/java/freemarker/core/_Java16Impl.java b/freemarker-core16/src/main/java/freemarker/core/_Java16Impl.java
index ba1ba879..e498f8c3 100644
--- a/freemarker-core16/src/main/java/freemarker/core/_Java16Impl.java
+++ b/freemarker-core16/src/main/java/freemarker/core/_Java16Impl.java
@@ -25,16 +25,15 @@ import java.util.Set;
 
 /**
  * Used internally only, might change without notice!
- * Used for accessing functionality that's only present in Java 16 or later.
+ * Java 16 implementation of {@link _Java16}.
  */
-// Compile this against Java 16
+// We also have a pre-Java-16 versions of this class in freemarker-core, and we put all versions into
+// the jar artifact via "JEP 238: Multi-Release JAR Files".
 @SuppressWarnings("Since15") // For IntelliJ inspection
 public class _Java16Impl implements _Java16 {
-
-    public static final _Java16 INSTANCE = new _Java16Impl();
-
-    private _Java16Impl() {
-        // Not meant to be instantiated
+    @Override
+    public boolean isSupported() {
+        return true;
     }
 
     @Override
diff --git a/freemarker-core/src/main/java/freemarker/core/_Java16.java b/freemarker-core16/src/test/java/freemarker/ext/beans/Java16TestClassLoadingCorrectTest.java
similarity index 57%
copy from freemarker-core/src/main/java/freemarker/core/_Java16.java
copy to freemarker-core16/src/test/java/freemarker/ext/beans/Java16TestClassLoadingCorrectTest.java
index 19de70a3..d4f4f020 100644
--- a/freemarker-core/src/main/java/freemarker/core/_Java16.java
+++ b/freemarker-core16/src/test/java/freemarker/ext/beans/Java16TestClassLoadingCorrectTest.java
@@ -16,19 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package freemarker.core;
 
-import java.lang.reflect.Method;
-import java.util.Set;
+package freemarker.ext.beans;
 
-/**
- * Used internally only, might change without notice!
- * Used for accessing functionality that's only present in Java 16 or later.
- */
-public interface _Java16 {
+import static org.junit.Assert.*;
 
-    boolean isRecord(Class<?> cl);
+import org.junit.Test;
 
-    Set<Method> getComponentAccessors(Class<?> recordClass);
+import freemarker.core._Java16;
+import freemarker.core._Java16Impl;
 
+public class Java16TestClassLoadingCorrectTest {
+    @Test
+    public void java16Supported() {
+        // This can be a problem if the test is not ran from the jar, and the impl class from the core takes precedence
+        assertTrue(
+                "Multi-Release JAR selection of the proper " + _Java16Impl.class.getName() + " variant didn't happen in the Java 16 test environment",
+                _Java16.INSTANCE.isSupported());
+    }
 }
diff --git a/freemarker-core16/src/test/java/freemarker/ext/beans/NotExportedInternalPackageTest.java b/freemarker-core16/src/test/java/freemarker/ext/beans/NotExportedInternalPackageTest.java
index 117544cc..38a7f131 100644
--- a/freemarker-core16/src/test/java/freemarker/ext/beans/NotExportedInternalPackageTest.java
+++ b/freemarker-core16/src/test/java/freemarker/ext/beans/NotExportedInternalPackageTest.java
@@ -33,6 +33,7 @@ import org.junit.Test;
 import org.w3c.dom.Document;
 import org.xml.sax.InputSource;
 
+import freemarker.core._Java16;
 import freemarker.template.TemplateHashModel;
 import freemarker.template.TemplateMethodModelEx;
 
@@ -42,6 +43,8 @@ import freemarker.template.TemplateMethodModelEx;
 public class NotExportedInternalPackageTest {
     @Test
     public void java16InternalClassAvoidanceTest() throws Exception {
+        assertTrue(_Java16.INSTANCE.isSupported());
+
         BeansWrapper bw = new BeansWrapper();
 
         Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
diff --git a/freemarker-core9/src/main/java/freemarker/core/_Java9Impl.java b/freemarker-core9/src/main/java/freemarker/core/_Java9Impl.java
index 1f5182af..aabc49cc 100644
--- a/freemarker-core9/src/main/java/freemarker/core/_Java9Impl.java
+++ b/freemarker-core9/src/main/java/freemarker/core/_Java9Impl.java
@@ -22,18 +22,17 @@ import freemarker.ext.beans.BeansWrapper;
 
 /**
  * Used internally only, might change without notice!
- * Used for accessing functionality that's only present in Java 9 or later.
+ * Java 9 implementation of {@link _Java9}.
  */
-// Compile this against Java 9
+// We also have a pre-Java-9 versions of this class in freemarker-core, and we put all versions into
+// the jar artifact via "JEP 238: Multi-Release JAR Files".
 @SuppressWarnings("Since15") // For IntelliJ inspection
 public class _Java9Impl implements _Java9 {
-
-    public static final _Java9 INSTANCE = new _Java9Impl();
-
     private static final Module ACCESSOR_MODULE = BeansWrapper.class.getModule();
 
-    private _Java9Impl() {
-        // Not meant to be instantiated
+    @Override
+    public boolean isSupported() {
+        return true;
     }
 
     @Override
diff --git a/freemarker-core9/src/test/java/freemarker/core/Java9ImplTest.java b/freemarker-core9/src/test/java/freemarker/core/Java9ImplTest.java
index 9612d5ba..12807460 100644
--- a/freemarker-core9/src/test/java/freemarker/core/Java9ImplTest.java
+++ b/freemarker-core9/src/test/java/freemarker/core/Java9ImplTest.java
@@ -38,11 +38,11 @@ public class Java9ImplTest {
 
     @Test
     public void testIsAccessibleAccordingToModuleExports() throws Exception {
-        assertNotNull(_JavaVersions.JAVA_9);
-        assertTrue(_JavaVersions.JAVA_9.isAccessibleAccordingToModuleExports(Document.class));
-        assertFalse(_JavaVersions.JAVA_9.isAccessibleAccordingToModuleExports(getSomeInternalClass()));
-        assertTrue(_JavaVersions.JAVA_9.isAccessibleAccordingToModuleExports(String[].class));
-        assertTrue(_JavaVersions.JAVA_9.isAccessibleAccordingToModuleExports(int.class));
+        assertTrue(_Java9.INSTANCE.isSupported());
+        assertTrue(_Java9.INSTANCE.isAccessibleAccordingToModuleExports(Document.class));
+        assertFalse(_Java9.INSTANCE.isAccessibleAccordingToModuleExports(getSomeInternalClass()));
+        assertTrue(_Java9.INSTANCE.isAccessibleAccordingToModuleExports(String[].class));
+        assertTrue(_Java9.INSTANCE.isAccessibleAccordingToModuleExports(int.class));
     }
 
     private static Class<?> getSomeInternalClass() throws SAXException, IOException, ParserConfigurationException,
diff --git a/freemarker-core/src/main/java/freemarker/core/_Java16.java b/freemarker-core9/src/test/java/freemarker/core/Java9TestClassLoadingCorrectTest.java
similarity index 62%
copy from freemarker-core/src/main/java/freemarker/core/_Java16.java
copy to freemarker-core9/src/test/java/freemarker/core/Java9TestClassLoadingCorrectTest.java
index 19de70a3..5ef21763 100644
--- a/freemarker-core/src/main/java/freemarker/core/_Java16.java
+++ b/freemarker-core9/src/test/java/freemarker/core/Java9TestClassLoadingCorrectTest.java
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package freemarker.core;
-
-import java.lang.reflect.Method;
-import java.util.Set;
 
-/**
- * Used internally only, might change without notice!
- * Used for accessing functionality that's only present in Java 16 or later.
- */
-public interface _Java16 {
+package freemarker.core;
 
-    boolean isRecord(Class<?> cl);
+import static org.junit.Assert.*;
 
-    Set<Method> getComponentAccessors(Class<?> recordClass);
+import org.junit.Test;
 
+public class Java9TestClassLoadingCorrectTest {
+    @Test
+    public void java9Supported() {
+        // This can be a problem if the test is not ran from the jar, and the impl class from the core takes precedence
+        assertTrue(
+                "Multi-Release JAR selection of the proper " + _Java9Impl.class.getName() + " variant didn't happen in the Java 9 test environment",
+                _Java9.INSTANCE.isSupported());
+    }
 }