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 <[email protected]> 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()); + } }
