This is an automated email from the ASF dual-hosted git repository. ddekany pushed a commit to branch 2.3-gae in repository https://gitbox.apache.org/repos/asf/freemarker.git
commit 3afe04650f52672d99268e1f1d0937e25aa15765 Author: ddekany <[email protected]> AuthorDate: Sat Mar 2 18:50:29 2024 +0100 FREEMARKER-216: Fixed some IllegalAccessException-s appearing since Java 16 (JEP 396), because invoke public methods on public, but internal JDK classes --- .../freemarker/ext/beans/ClassIntrospector.java | 7 +-- .../ext/beans/ExecutableMemberSignature.java | 5 ++ .../ext/beans/NotExportedInternalPackageTest.java | 60 ++++++++++++++++++++++ freemarker-manual/src/main/docgen/en_US/book.xml | 15 ++++++ 4 files changed, 84 insertions(+), 3 deletions(-) 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 bc4a5ddd..66e75e03 100644 --- a/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java +++ b/freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java @@ -823,7 +823,8 @@ class ClassIntrospector { private static void discoverAccessibleMethods( Class<?> clazz, Map<ExecutableMemberSignature, List<Method>> accessibles) { - if (Modifier.isPublic(clazz.getModifiers())) { + if (Modifier.isPublic(clazz.getModifiers()) + && (_JavaVersions.JAVA_9 == null || _JavaVersions.JAVA_9.isAccessibleAccordingToModuleExports(clazz))) { try { Method[] methods = clazz.getMethods(); for (int i = 0; i < methods.length; i++) { @@ -860,8 +861,8 @@ class ClassIntrospector { } Class<?>[] interfaces = clazz.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - discoverAccessibleMethods(interfaces[i], accessibles); + for (Class<?> anInterface : interfaces) { + discoverAccessibleMethods(anInterface, accessibles); } Class<?> superclass = clazz.getSuperclass(); if (superclass != null) { diff --git a/freemarker-core/src/main/java/freemarker/ext/beans/ExecutableMemberSignature.java b/freemarker-core/src/main/java/freemarker/ext/beans/ExecutableMemberSignature.java index dfba6920..c9db1bc7 100644 --- a/freemarker-core/src/main/java/freemarker/ext/beans/ExecutableMemberSignature.java +++ b/freemarker-core/src/main/java/freemarker/ext/beans/ExecutableMemberSignature.java @@ -66,4 +66,9 @@ final class ExecutableMemberSignature { public int hashCode() { return name.hashCode() + args.length * 31; } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "(" + name + ", " + Arrays.toString(args) + ")"; + } } diff --git a/freemarker-core16/src/test/java/freemarker/ext/beans/NotExportedInternalPackageTest.java b/freemarker-core16/src/test/java/freemarker/ext/beans/NotExportedInternalPackageTest.java new file mode 100644 index 00000000..117544cc --- /dev/null +++ b/freemarker-core16/src/test/java/freemarker/ext/beans/NotExportedInternalPackageTest.java @@ -0,0 +1,60 @@ +/* + * 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.ext.beans; + +import static org.junit.Assert.*; + +import java.io.StringReader; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; + +import javax.xml.parsers.DocumentBuilderFactory; + +import org.hamcrest.Matchers; +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import freemarker.template.TemplateHashModel; +import freemarker.template.TemplateMethodModelEx; + +/** + * FREEMARKER-216: IllegalAccessException because of JEP 396 - Strongly Encapsulate JDK Internals by Default + */ +public class NotExportedInternalPackageTest { + @Test + public void java16InternalClassAvoidanceTest() throws Exception { + BeansWrapper bw = new BeansWrapper(); + + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder() + .parse(new InputSource(new StringReader("<a></a>"))); + TemplateHashModel documentModel = (TemplateHashModel) bw.wrap(document); + + Method internalClassMethod = document.getClass().getMethod("getDocumentElement"); + assertTrue(Modifier.isPublic(internalClassMethod.getModifiers())); + assertThat(internalClassMethod.getDeclaringClass().getName(), Matchers.startsWith("com.")); // Internal class + + TemplateMethodModelEx methodModel = (TemplateMethodModelEx) documentModel.get("getDocumentElement"); + assertNotNull(methodModel); + + assertNotNull(methodModel.exec(List.of())); // No IllegalAccessException + } +} diff --git a/freemarker-manual/src/main/docgen/en_US/book.xml b/freemarker-manual/src/main/docgen/en_US/book.xml index 56750583..17c0c34f 100644 --- a/freemarker-manual/src/main/docgen/en_US/book.xml +++ b/freemarker-manual/src/main/docgen/en_US/book.xml @@ -30213,6 +30213,21 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> </itemizedlist> </listitem> + <listitem> + <para><link + xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-216">FREEMARKER-216</link>: + Fixed <literal>IllegalAccessException</literal>-s on Java 16 and + later, when calling a public method that was last overridden by + a public class that's however JDK internal (see JEP 396). More + generally, we avoid this error if a class comes from the package + of a Java module that doesn't export that package to the module + where FreeMarker resides, by looking for an accessible public + method in a public class that the unaccessible one overrides + (maybe indirectly). If there's no such method, then the method + won't be visible (despite that both the method and the class is + public).</para> + </listitem> + <listitem> <para><link xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-219">FREEMARKER-219</link>:
