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 2020/06/15 00:16:09 UTC

[freemarker] branch 2.3-gae updated (2ef07c2 -> eedc075)

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

ddekany pushed a change to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git.


    from 2ef07c2  Manual: Recommending #import instead of #include in the documentation of #include. Improved #import documentation.
     new d921f5d  [FREEMARKER-145] Fixed bug where methods with "overloaded" return type may become inaccessible on Java 9+, if some overriding subclasses are not public. (This is because java.beans.Introspector behavior has changed with Java 9.)
     new 09f2472  [FREEMARKER-133] Fixed bug where FreeMarker sometimes tries to expose public methods that are defined or overridden in a non-public class, if the non-public class was then extended by a public class. Calling such method just ends up with IllegalAccessException, but they shouldn't be exposed on the first place, and furthermore can sometimes hide the callable version of the method.
     new eedc075  Updated some test dependencies.

The 3 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.


Summary of changes:
 ivy.xml                                            |   4 +-
 .../freemarker/ext/beans/ClassIntrospector.java    |  65 ++++----
 .../java/freemarker/ext/beans/_MethodUtil.java     |  82 +++++++++++
 src/manual/en_US/book.xml                          |  41 +++++-
 .../beans/Java9InstrospectorBugWorkaround.java}    |  11 +-
 .../java/freemarker/ext/beans/MethodUtilTest2.java | 164 +++++++++++++++++++++
 6 files changed, 331 insertions(+), 36 deletions(-)
 copy src/test/java/freemarker/{manual/WithArgsExamples.java => ext/beans/Java9InstrospectorBugWorkaround.java} (73%)
 create mode 100644 src/test/java/freemarker/ext/beans/MethodUtilTest2.java


[freemarker] 02/03: [FREEMARKER-133] Fixed bug where FreeMarker sometimes tries to expose public methods that are defined or overridden in a non-public class, if the non-public class was then extended by a public class. Calling such method just ends up with IllegalAccessException, but they shouldn't be exposed on the first place, and furthermore can sometimes hide the callable version of the method.

Posted by dd...@apache.org.
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 09f247247faa894cddcfa8b36c1d7149106b73be
Author: ddekany <dd...@apache.org>
AuthorDate: Mon Jun 15 01:38:10 2020 +0200

    [FREEMARKER-133] Fixed bug where FreeMarker sometimes tries to expose public methods that are defined or overridden in a non-public class, if the non-public class was then extended by a public class. Calling such method just ends up with IllegalAccessException, but they shouldn't be exposed on the first place, and furthermore can sometimes hide the callable version of the method.
---
 .../freemarker/ext/beans/ClassIntrospector.java    | 38 ++++++++++++----------
 src/manual/en_US/book.xml                          | 17 ++++++++++
 2 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/src/main/java/freemarker/ext/beans/ClassIntrospector.java b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index 32053f2..36b2511 100644
--- a/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -775,25 +775,27 @@ class ClassIntrospector {
                 Method[] methods = clazz.getMethods();
                 for (int i = 0; i < methods.length; i++) {
                     Method method = methods[i];
-                    ExecutableMemberSignature sig = new ExecutableMemberSignature(method);
-                    // Contrary to intuition, a class can actually have several
-                    // different methods with same signature *but* different
-                    // return types. These can't be constructed using Java the
-                    // language, as this is illegal on source code level, but
-                    // the compiler can emit synthetic methods as part of
-                    // generic type reification that will have same signature
-                    // yet different return type than an existing explicitly
-                    // declared method. Consider:
-                    // public interface I<T> { T m(); }
-                    // public class C implements I<Integer> { Integer m() { return 42; } }
-                    // C.class will have both "Object m()" and "Integer m()" methods.
-                    List<Method> methodList = accessibles.get(sig);
-                    if (methodList == null) {
-                     // TODO Collection.singletonList is more efficient, though read only.
-                        methodList = new LinkedList<>();
-                        accessibles.put(sig, methodList);
+                    if (Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
+                        ExecutableMemberSignature sig = new ExecutableMemberSignature(method);
+                        // Contrary to intuition, a class can actually have several
+                        // different methods with same signature *but* different
+                        // return types. These can't be constructed using Java the
+                        // language, as this is illegal on source code level, but
+                        // the compiler can emit synthetic methods as part of
+                        // generic type reification that will have same signature
+                        // yet different return type than an existing explicitly
+                        // declared method. Consider:
+                        // public interface I<T> { T m(); }
+                        // public class C implements I<Integer> { Integer m() { return 42; } }
+                        // C.class will have both "Object m()" and "Integer m()" methods.
+                        List<Method> methodList = accessibles.get(sig);
+                        if (methodList == null) {
+                            // TODO Collection.singletonList is more efficient, though read only.
+                            methodList = new LinkedList<>();
+                            accessibles.put(sig, methodList);
+                        }
+                        methodList.add(method);
                     }
-                    methodList.add(method);
                 }
                 return;
             } catch (SecurityException e) {
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index c779e01..29dce43 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -29329,6 +29329,23 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
               <literal>java.beans.Introspector</literal> behavior has changed
               with Java 9.)</para>
             </listitem>
+
+            <listitem>
+              <para><link
+              xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-133">FREEMARKER-133</link>:
+              Fixed bug where FreeMarker sometimes tries to expose public
+              methods that are defined or overridden in a non-public class, if
+              the non-public class was then extended by a public class.
+              Calling such method just ends up with
+              <literal>IllegalAccessException</literal>, but they shouldn't be
+              exposed on the first place. Furthermore, such a wrong method
+              sometimes replaces the good version of the method, which would
+              be callable. When this glitch happens is somewhat unpredictable,
+              as it also depends on what methods
+              <literal>java.beans.Introspector</literal> exposes (which at
+              very least can depend on the Java version), and in what
+              order.</para>
+            </listitem>
           </itemizedlist>
         </section>
       </section>


[freemarker] 01/03: [FREEMARKER-145] Fixed bug where methods with "overloaded" return type may become inaccessible on Java 9+, if some overriding subclasses are not public. (This is because java.beans.Introspector behavior has changed with Java 9.)

Posted by dd...@apache.org.
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 d921f5dd833d190adf8d1c67fed8d26114c4816f
Author: ddekany <dd...@apache.org>
AuthorDate: Mon Jun 15 00:14:35 2020 +0200

    [FREEMARKER-145] Fixed bug where methods with "overloaded" return type may become inaccessible on Java 9+, if some overriding subclasses are not public. (This is because java.beans.Introspector behavior has changed with Java 9.)
---
 .../freemarker/ext/beans/ClassIntrospector.java    |  27 ++--
 .../java/freemarker/ext/beans/_MethodUtil.java     |  82 +++++++++++
 src/manual/en_US/book.xml                          |  24 ++-
 .../ext/beans/Java9InstrospectorBugWorkaround.java |  38 +++++
 .../java/freemarker/ext/beans/MethodUtilTest2.java | 164 +++++++++++++++++++++
 5 files changed, 323 insertions(+), 12 deletions(-)

diff --git a/src/main/java/freemarker/ext/beans/ClassIntrospector.java b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index e0c5135..32053f2 100644
--- a/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -814,21 +814,26 @@ class ClassIntrospector {
         }
     }
 
+    // This is needed as java.bean.Introspector sometimes gives back a method that's actually not accessible,
+    // as it's an override of an accessible method in a non-public subclass. While that's still a public method, calling
+    // it directly via reflection will throw java.lang.IllegalAccessException, and we are supposed to call the overidden
+    // accessible method instead. Like, we migth get two PropertyDescriptor-s for the same property name, and only one
+    // will have a reader method that we can actually call. So we have to find that method here.
+    // Furthermore, the return type of the inaccisable method is possibly different (more specific) than the return type
+    // of the overidden accessible method. Also Introspector behavior changed with Java 9, as earlier in such case the
+    // Introspector returned all variants of the method (so the accessible one was amongst them at least), while in
+    // Java 9 it apparently always returns one variant only, but that's sometimes (not sure if it's predictable) the
+    // inaccessbile one.
     private static Method getMatchingAccessibleMethod(Method m, Map<ExecutableMemberSignature, List<Method>> accessibles) {
         if (m == null) {
             return null;
         }
-        ExecutableMemberSignature sig = new ExecutableMemberSignature(m);
-        List<Method> ams = accessibles.get(sig);
-        if (ams == null) {
-            return null;
-        }
-        for (Method am : ams) {
-            if (am.getReturnType() == m.getReturnType()) {
-                return am;
-            }
-        }
-        return null;
+        List<Method> ams = accessibles.get(new ExecutableMemberSignature(m));
+        // Certainly we could return any of the accessbiles, as Java reflection will call the correct override of the
+        // method anyway. There's an ambiguity when the return type is "overloaded", but in practice it probably doesn't
+        // matter which variant we call. Though, technically, they could do totaly different things. So, to avoid any
+        // corner cases that cause problems after an upgrade, we make an effort to give same result as before 2.3.31.
+        return ams != null ? _MethodUtil.getMethodWithClosestNonSubReturnType(m.getReturnType(), ams) : null;
     }
 
     private static Method getFirstAccessibleMethod(
diff --git a/src/main/java/freemarker/ext/beans/_MethodUtil.java b/src/main/java/freemarker/ext/beans/_MethodUtil.java
index 1540853..99a733e 100644
--- a/src/main/java/freemarker/ext/beans/_MethodUtil.java
+++ b/src/main/java/freemarker/ext/beans/_MethodUtil.java
@@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -458,4 +459,85 @@ public final class _MethodUtil {
         return getInheritableFieldAnnotation(superClass, fieldName, false, annotationClass);
     }
 
+    public static Method getMethodWithClosestNonSubReturnType(
+            Class<?> returnType, Collection<Method> methods) {
+        // Exact match:
+        for (Method method : methods) {
+            if (method.getReturnType() == returnType) {
+                return method;
+            }
+        }
+
+        if (returnType == Object.class || returnType.isPrimitive()) {
+            // We can't go wider than these types, so we give up. Note that void is primitive.
+            return null;
+        }
+
+        // Super-class match, which we prefer over Interface match, except if the match is just Object:
+        Class<?> superClass = returnType.getSuperclass();
+        while (superClass != null && superClass != Object.class) {
+            for (Method method : methods) {
+                if (method.getReturnType() == superClass) {
+                    return method;
+                }
+            }
+            superClass = superClass.getSuperclass();
+        }
+
+        // Interface match:
+        Method result = getMethodWithClosestNonSubInterfaceReturnType(returnType, methods);
+        if (result != null) {
+            return result;
+        }
+
+        // As the returnType is non-primitive, Object return type will do, as a last resort:
+        for (Method method : methods) {
+            if (method.getReturnType() == Object.class) {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+    private static Method getMethodWithClosestNonSubInterfaceReturnType(
+            Class<?> returnType, Collection<Method> methods) {
+        HashSet<Class<?>> nullResultReturnTypeInterfaces = new HashSet<>();
+        do {
+            Method result
+                    = getMethodWithClosestNonSubInterfaceReturnType(returnType, methods, nullResultReturnTypeInterfaces);
+            if (result != null) {
+                return result;
+            }
+            // As Class.getInterfaces() doesn't return the inherited interfaces, we have to do this.
+            returnType = returnType.getSuperclass();
+        } while (returnType != null);
+        return null;
+    }
+
+    private static Method getMethodWithClosestNonSubInterfaceReturnType(
+            Class<?> returnType, Collection<Method> methods, Set<Class<?>> nullResultReturnTypeInterfaces) {
+        boolean returnTypeIsInterface = returnType.isInterface();
+        if (returnTypeIsInterface) {
+            if (nullResultReturnTypeInterfaces.contains(returnType)) {
+                return null;
+            }
+            for (Method method : methods) {
+                if (method.getReturnType() == returnType) {
+                    return method;
+                }
+            }
+        }
+        for (Class<?> subInterface : returnType.getInterfaces()) {
+            Method result = getMethodWithClosestNonSubInterfaceReturnType(subInterface, methods, nullResultReturnTypeInterfaces);
+            if (result != null) {
+                return result;
+            }
+        }
+        if (returnTypeIsInterface) {
+            nullResultReturnTypeInterfaces.add(returnType);
+        }
+        return null;
+    }
+
 }
\ No newline at end of file
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 1041ca7..c779e01 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -29,7 +29,7 @@
 
     <titleabbrev>Manual</titleabbrev>
 
-    <productname>Freemarker 2.3.30</productname>
+    <productname>Freemarker 2.3.31</productname>
   </info>
 
   <preface role="index.html" xml:id="preface">
@@ -29311,6 +29311,28 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
     <appendix xml:id="app_versions">
       <title>Version history</title>
 
+      <section xml:id="versions_2_3_31">
+        <title>2.3.31</title>
+
+        <para>Release date: FIXME</para>
+
+        <section>
+          <title>Changes on the Java side</title>
+
+          <itemizedlist>
+            <listitem>
+              <para><link
+              xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-145">FREEMARKER-145</link>:
+              Fixed bug where methods with "overloaded" return type may become
+              inaccessible on Java 9+, if some overriding subclasses are not
+              public. (This is because
+              <literal>java.beans.Introspector</literal> behavior has changed
+              with Java 9.)</para>
+            </listitem>
+          </itemizedlist>
+        </section>
+      </section>
+
       <section xml:id="versions_2_3_30">
         <title>2.3.30</title>
 
diff --git a/src/test/java/freemarker/ext/beans/Java9InstrospectorBugWorkaround.java b/src/test/java/freemarker/ext/beans/Java9InstrospectorBugWorkaround.java
new file mode 100644
index 0000000..05fa695
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/Java9InstrospectorBugWorkaround.java
@@ -0,0 +1,38 @@
+/*
+ * 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 java.io.IOException;
+import java.nio.file.Paths;
+
+import org.junit.Test;
+
+import freemarker.template.TemplateException;
+import freemarker.test.TemplateTest;
+
+public class Java9InstrospectorBugWorkaround extends TemplateTest {
+
+    @Test
+    public void test() throws IOException, TemplateException {
+        addToDataModel("path", Paths.get("foo", "bar"));
+        assertOutput("<#assign _ = path.parent>", "");
+    }
+
+}
diff --git a/src/test/java/freemarker/ext/beans/MethodUtilTest2.java b/src/test/java/freemarker/ext/beans/MethodUtilTest2.java
new file mode 100644
index 0000000..9723613
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/MethodUtilTest2.java
@@ -0,0 +1,164 @@
+/*
+ * 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 freemarker.ext.beans._MethodUtil.*;
+import static org.junit.Assert.*;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Test;
+
+public class MethodUtilTest2 {
+
+    @Test
+    public void testGetMethodWithClosestNonSubReturnType1() {
+        List<Method> methods = getMethods(ObjectM.class, ListM.class, CollectionM.class);
+        assertEquals(getMethod(ObjectM.class), getMethodWithClosestNonSubReturnType(Object.class, methods));
+        assertEquals(getMethod(CollectionM.class), getMethodWithClosestNonSubReturnType(Collection.class, methods));
+        assertEquals(getMethod(ListM.class), getMethodWithClosestNonSubReturnType(List.class, methods));
+        assertEquals(getMethod(ListM.class), getMethodWithClosestNonSubReturnType(ArrayList.class, methods));
+        assertEquals(getMethod(ObjectM.class), getMethodWithClosestNonSubReturnType(String.class, methods));
+        assertNull(getMethodWithClosestNonSubReturnType(int.class, methods));
+        assertNull(getMethodWithClosestNonSubReturnType(void.class, methods));
+    }
+
+    @Test
+    public void testGetMethodWithClosestNonSubReturnType2() {
+        List<Method> methods = getMethods(ListM.class, CollectionM.class);
+        assertNull(getMethodWithClosestNonSubReturnType(Object.class, methods));
+        assertEquals(getMethod(CollectionM.class), getMethodWithClosestNonSubReturnType(Collection.class, methods));
+        assertEquals(getMethod(ListM.class), getMethodWithClosestNonSubReturnType(List.class, methods));
+        assertEquals(getMethod(ListM.class), getMethodWithClosestNonSubReturnType(ArrayList.class, methods));
+        assertNull(getMethodWithClosestNonSubReturnType(String.class, methods));
+        assertNull(getMethodWithClosestNonSubReturnType(int.class, methods));
+        assertNull(getMethodWithClosestNonSubReturnType(void.class, methods));
+    }
+
+    @Test
+    public void testGetMethodWithClosestNonSubReturnType3() {
+        List<Method> methods = getMethods(ObjectM.class, SerializableM.class);
+        assertEquals(getMethod(SerializableM.class), getMethodWithClosestNonSubReturnType(String.class, methods));
+        assertEquals(getMethod(SerializableM.class), getMethodWithClosestNonSubReturnType(Serializable.class, methods));
+        assertEquals(getMethod(ObjectM.class), getMethodWithClosestNonSubReturnType(List.class, methods));
+        assertNull(getMethodWithClosestNonSubReturnType(int.class, methods));
+        assertNull(getMethodWithClosestNonSubReturnType(void.class, methods));
+    }
+
+    @Test
+    public void testGetMethodWithClosestNonSubReturnType4() {
+        List<Method> methods = getMethods(ReturnType1M.class, ReturnType2M.class, ObjectM.class);
+        assertEquals(getMethod(ReturnType2M.class), getMethodWithClosestNonSubReturnType(ReturnType3.class, methods));
+        assertEquals(getMethod(ReturnType2M.class), getMethodWithClosestNonSubReturnType(ReturnType2.class, methods));
+        assertEquals(getMethod(ReturnType1M.class), getMethodWithClosestNonSubReturnType(ReturnType1.class, methods));
+        assertEquals(getMethod(ObjectM.class), getMethodWithClosestNonSubReturnType(Serializable.class, methods));
+    }
+
+    @Test
+    public void testGetMethodWithClosestNonSubReturnType5() {
+        List<Method> methods = getMethods(SerializableM.class, ReturnType1M.class);
+        assertEquals(getMethod(ReturnType1M.class), getMethodWithClosestNonSubReturnType(ReturnType3.class, methods));
+    }
+
+    @Test
+    public void testGetMethodWithClosestNonSubReturnType6() {
+        List<Method> methods = getMethods(SerializableM.class);
+        assertEquals(getMethod(SerializableM.class), getMethodWithClosestNonSubReturnType(ReturnType3.class, methods));
+    }
+
+    @Test
+    public void testGetMethodWithClosestNonSubReturnType7() {
+        List<Method> methods = getMethods(IntM.class, VoidM.class, ObjectM.class, CollectionM.class);
+        assertEquals(getMethod(IntM.class), getMethodWithClosestNonSubReturnType(int.class, methods));
+        assertEquals(getMethod(VoidM.class), getMethodWithClosestNonSubReturnType(void.class, methods));
+        assertNull(getMethodWithClosestNonSubReturnType(long.class, methods));
+        assertEquals(getMethod(ObjectM.class), getMethodWithClosestNonSubReturnType(Long.class, methods));
+        assertEquals(getMethod(CollectionM.class), getMethodWithClosestNonSubReturnType(List.class, methods));
+    }
+
+    private static List<Method> getMethods(Class<?>... methodHolders) {
+        List<Method> result = new ArrayList<>();
+        for (Class<?> methodHolder : methodHolders) {
+            result.add(getMethod(methodHolder));
+        }
+        return result;
+    }
+
+    private static Method getMethod(Class<?> methodHolder) {
+        try {
+            return methodHolder.getMethod("m");
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static class ObjectM {
+        public Object m() { return null; }
+    }
+
+    public static class CollectionM {
+        public Collection<?> m() { return null; }
+    }
+
+    public static class ListM {
+        public List<?> m() { return null; }
+    }
+
+    public static class SerializableM {
+        public Serializable m() { return null; }
+    }
+
+    public static class StringM {
+        public String m() { return null; }
+    }
+
+    public static class ReturnType1M {
+        public ReturnType1 m() { return null; }
+    }
+
+    public static class ReturnType2M {
+        public ReturnType2 m() { return null; }
+    }
+
+    public static class ReturnType3M {
+        public ReturnType3 m() { return null; }
+    }
+
+    public static class IntM {
+        public int m() { return 0; }
+    }
+
+    public static class LongM {
+        public long m() { return 0L; }
+    }
+
+    public static class VoidM {
+        public void m() { }
+    }
+
+    public static class ReturnType1 { }
+    public static class ReturnType2 extends ReturnType1 implements Serializable { }
+    public static class ReturnType3 extends ReturnType2 { }
+
+}
\ No newline at end of file


[freemarker] 03/03: Updated some test dependencies.

Posted by dd...@apache.org.
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 eedc075ddfa5b3db89c1215e531958b40382d7d5
Author: ddekany <dd...@apache.org>
AuthorDate: Mon Jun 15 01:44:36 2020 +0200

    Updated some test dependencies.
---
 ivy.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ivy.xml b/ivy.xml
index 0d54d11..251ec1b 100644
--- a/ivy.xml
+++ b/ivy.xml
@@ -138,8 +138,8 @@
 
     <dependency org="ch.qos.logback" name="logback-classic" rev="1.1.2" conf="test->default" />
 
-    <dependency org="commons-io" name="commons-io" rev="2.2" conf="test->default" />
-    <dependency org="com.google.guava" name="guava-jdk5" rev="17.0" conf="test->default" />
+    <dependency org="commons-io" name="commons-io" rev="2.7" conf="test->default" />
+    <dependency org="com.google.guava" name="guava" rev="29.0-jre" conf="test->default" />
 
     <dependency org="org.eclipse.jetty" name="jetty-server" rev="&jetty.version;" conf="test->default" />
     <dependency org="org.eclipse.jetty" name="jetty-webapp" rev="&jetty.version;" conf="test->default" />