Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id B964B200D10 for ; Sat, 26 Aug 2017 01:44:27 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id B7E8116D83A; Fri, 25 Aug 2017 23:44:27 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 92DF016D838 for ; Sat, 26 Aug 2017 01:44:26 +0200 (CEST) Received: (qmail 5256 invoked by uid 500); 25 Aug 2017 23:41:17 -0000 Mailing-List: contact notifications-help@freemarker.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@freemarker.incubator.apache.org Delivered-To: mailing list notifications@freemarker.incubator.apache.org Received: (qmail 5221 invoked by uid 99); 25 Aug 2017 23:41:15 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 25 Aug 2017 23:41:15 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id C4DA21A24C4 for ; Fri, 25 Aug 2017 23:41:14 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.222 X-Spam-Level: X-Spam-Status: No, score=-4.222 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id mvK2j-1XjS39 for ; Fri, 25 Aug 2017 23:41:11 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id EFB3A619BB for ; Fri, 25 Aug 2017 23:41:08 +0000 (UTC) Received: (qmail 99865 invoked by uid 99); 25 Aug 2017 23:38:16 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 25 Aug 2017 23:38:16 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id BCF0EF5F17; Fri, 25 Aug 2017 23:38:14 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: ddekany@apache.org To: notifications@freemarker.incubator.apache.org Date: Fri, 25 Aug 2017 23:38:19 -0000 Message-Id: <75206bb99bb049a9922a534090ea8013@git.apache.org> In-Reply-To: <7dba91de4b59436b81fed26ba1f9c888@git.apache.org> References: <7dba91de4b59436b81fed26ba1f9c888@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [6/6] incubator-freemarker git commit: Bug fixed: BeansWrapper and DefaultObjectWrapper, starting from Java 8, when the same JavaBeans property has both non-indexed read method (like String[] getFoo()) and indexed read method (like String getFoo(int inde archived-at: Fri, 25 Aug 2017 23:44:27 -0000 Bug fixed: BeansWrapper and DefaultObjectWrapper, starting from Java 8, when the same JavaBeans property has both non-indexed read method (like String[] getFoo()) and indexed read method (like String getFoo(int index)), has mistakenly used the indexed read method to access the property. This is a problem because then the array size was unknown, and thus the property has suddenly become unlistable on Java 8. To enable the fix (where it will use the non-indexed read method), you have to increase the value of the incompatibleImprovements constructor argument of the used DefaultObjectWrapper or BeansWrapper to 2.3.27. Note that if you leave the object_wrapper setting of the Configuration on its default, it's enough to increase the incompatibleImprovements setting of the Configuration to 2.3.27, as that's inherited by the default object_wrapper. Note that this bug haven't surfaced before Java 8, as then java.beans.Inrospector has only exposed the non-indexed method when both kind of read method was present. Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/a4a406a1 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/a4a406a1 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/a4a406a1 Branch: refs/heads/2.3-gae Commit: a4a406a1d2e7a994ea589a9e6efe5d7575f69da4 Parents: ce4b9e4 Author: ddekany Authored: Sat Aug 26 01:37:32 2017 +0200 Committer: ddekany Committed: Sat Aug 26 01:37:32 2017 +0200 ---------------------------------------------------------------------- .../java/freemarker/ext/beans/BeanModel.java | 16 ++++-- .../java/freemarker/ext/beans/BeansWrapper.java | 14 ++++- .../freemarker/ext/beans/SimpleMethodModel.java | 21 ++++--- .../java/freemarker/template/Configuration.java | 11 +++- .../java/freemarker/template/_TemplateAPI.java | 1 + src/manual/en_US/book.xml | 27 +++++++++ .../ext/beans/BeansWrapperMiscTest.java | 58 +++++++++++++++++++- .../template/DefaultObjectWrapperTest.java | 2 +- 8 files changed, 134 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a4a406a1/src/main/java/freemarker/ext/beans/BeanModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/beans/BeanModel.java b/src/main/java/freemarker/ext/beans/BeanModel.java index 8ec19ea..312ec98 100644 --- a/src/main/java/freemarker/ext/beans/BeanModel.java +++ b/src/main/java/freemarker/ext/beans/BeanModel.java @@ -50,6 +50,7 @@ import freemarker.template.TemplateModelException; import freemarker.template.TemplateModelIterator; import freemarker.template.TemplateModelWithAPISupport; import freemarker.template.TemplateScalarModel; +import freemarker.template._TemplateAPI; import freemarker.template.utility.StringUtil; /** @@ -219,10 +220,17 @@ implements TemplateModel resultModel = UNKNOWN; if (desc instanceof IndexedPropertyDescriptor) { - Method readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod(); - resultModel = cachedModel = - new SimpleMethodModel(object, readMethod, - ClassIntrospector.getArgTypes(classInfo, readMethod), wrapper); + IndexedPropertyDescriptor pd = (IndexedPropertyDescriptor) desc; + if (wrapper.getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_27 + && pd.getReadMethod() != null) { + resultModel = wrapper.invokeMethod(object, pd.getReadMethod(), null); + // cachedModel remains null, as we don't cache these + } else { + Method readMethod = pd.getIndexedReadMethod(); + resultModel = cachedModel = + new SimpleMethodModel(object, readMethod, + ClassIntrospector.getArgTypes(classInfo, readMethod), wrapper); + } } else if (desc instanceof PropertyDescriptor) { PropertyDescriptor pd = (PropertyDescriptor) desc; resultModel = wrapper.invokeMethod(object, pd.getReadMethod(), null); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a4a406a1/src/main/java/freemarker/ext/beans/BeansWrapper.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/beans/BeansWrapper.java b/src/main/java/freemarker/ext/beans/BeansWrapper.java index f108ad6..ea9158d 100644 --- a/src/main/java/freemarker/ext/beans/BeansWrapper.java +++ b/src/main/java/freemarker/ext/beans/BeansWrapper.java @@ -19,6 +19,7 @@ package freemarker.ext.beans; +import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; @@ -247,6 +248,16 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable { * {@code true}. Thus, Java 8 default methods (and the bean properties they define) are exposed, despite that * {@link java.beans.Introspector} (the official JavaBeans introspector) ignores them, at least as of Java 8. * + *
  • + *

    2.3.27 (or higher): + * If the same JavaBean property has both an indexed property reader (like {@code String getFoo(int)}) and + * a non-indexed property reader (like {@code String[] getFoo()}), and {@link Introspector} exposes both + * (which apparently only happens since Java 8), we will use the non-indexed property reader method, while + * before this improvement we have used the indexed property method. When using the indexed property reader, + * FreeMarker doesn't know the size of the array, so the value becomes unlistable. Before Java 8 this problem + * haven't surfaced, as {@link Introspector} has only exposed the non-indexed property reader method when both + * kind of read method was present. So this can be seen as a Java 8 compatibility fix. + *

  • * * *

    Note that the version will be normalized to the lowest version where the same incompatible @@ -844,7 +855,8 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable { if (incompatibleImprovements.intValue() < _TemplateAPI.VERSION_INT_2_3_0) { throw new IllegalArgumentException("Version must be at least 2.3.0."); } - return incompatibleImprovements.intValue() >= _TemplateAPI.VERSION_INT_2_3_26 ? Configuration.VERSION_2_3_26 + return incompatibleImprovements.intValue() >= _TemplateAPI.VERSION_INT_2_3_27 ? Configuration.VERSION_2_3_27 + : incompatibleImprovements.intValue() == _TemplateAPI.VERSION_INT_2_3_26 ? Configuration.VERSION_2_3_26 : is2324Bugfixed(incompatibleImprovements) ? Configuration.VERSION_2_3_24 : is2321Bugfixed(incompatibleImprovements) ? Configuration.VERSION_2_3_21 : Configuration.VERSION_2_3_0; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a4a406a1/src/main/java/freemarker/ext/beans/SimpleMethodModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/beans/SimpleMethodModel.java b/src/main/java/freemarker/ext/beans/SimpleMethodModel.java index e7e1ae1..9b8150b 100644 --- a/src/main/java/freemarker/ext/beans/SimpleMethodModel.java +++ b/src/main/java/freemarker/ext/beans/SimpleMethodModel.java @@ -24,13 +24,16 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.List; +import freemarker.core._DelayedFTLTypeDescription; +import freemarker.core._DelayedToString; +import freemarker.core._ErrorDescriptionBuilder; +import freemarker.core._TemplateModelException; import freemarker.core._UnexpectedTypeErrorExplainerTemplateModel; import freemarker.template.SimpleNumber; import freemarker.template.TemplateMethodModelEx; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; import freemarker.template.TemplateSequenceModel; -import freemarker.template.utility.ClassUtil; /** * A class that will wrap a reflected method call into a @@ -81,13 +84,15 @@ public final class SimpleMethodModel extends SimpleMethod } public int size() throws TemplateModelException { - throw new TemplateModelException( - "Getting the number of items or enumerating the items is not supported on this " - + ClassUtil.getFTLTypeDescription(this) + " value.\n" - + "(" - + "Hint 1: Maybe you wanted to call this method first and then do something with its return value. " - + "Hint 2: Getting items by intex possibly works, hence it's a \"+sequence\"." - + ")"); + throw new _TemplateModelException( + new _ErrorDescriptionBuilder( + "Getting the number of items or listing the items is not supported on this ", + new _DelayedFTLTypeDescription(this), " value, because this value wraps the following Java method, " + + "not a real listable value: ", new _DelayedToString(getMember())) + .tips( + "Maybe you should to call this method first and then do something with its return value.", + "obj.someMethod(i) and obj.someMethod[i] does the same for this method, hence it's a " + + "\"+sequence\".")); } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a4a406a1/src/main/java/freemarker/template/Configuration.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java index efa14ee..8b14da6 100644 --- a/src/main/java/freemarker/template/Configuration.java +++ b/src/main/java/freemarker/template/Configuration.java @@ -414,7 +414,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf /** FreeMarker version 2.3.26 (an {@link #Configuration(Version) incompatible improvements break-point}) */ public static final Version VERSION_2_3_26 = new Version(2, 3, 26); - /** FreeMarker version 2.3.26 (an {@link #Configuration(Version) incompatible improvements break-point}) */ + /** FreeMarker version 2.3.27 (an {@link #Configuration(Version) incompatible improvements break-point}) */ public static final Version VERSION_2_3_27 = new Version(2, 3, 27); /** The default of {@link #getIncompatibleImprovements()}, currently {@link #VERSION_2_3_0}. */ @@ -833,6 +833,15 @@ public class Configuration extends Configurable implements Cloneable, ParserConf * properties they define); see {@link BeansWrapper#BeansWrapper(Version)}. * * + *

  • + * 2.3.27 (or higher): + *

      + *
    • + * {@link BeansWrapper} and {@link DefaultObjectWrapper} now prefers the non-indexed JavaBean property + * read method over the indexed read method when Java 8 exposes both; + * see {@link BeansWrapper#BeansWrapper(Version)}. + *

    + *
  • * * * @throws IllegalArgumentException http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a4a406a1/src/main/java/freemarker/template/_TemplateAPI.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/template/_TemplateAPI.java b/src/main/java/freemarker/template/_TemplateAPI.java index 22cbc21..62960c8 100644 --- a/src/main/java/freemarker/template/_TemplateAPI.java +++ b/src/main/java/freemarker/template/_TemplateAPI.java @@ -48,6 +48,7 @@ public class _TemplateAPI { public static final int VERSION_INT_2_3_24 = Configuration.VERSION_2_3_24.intValue(); public static final int VERSION_INT_2_3_25 = Configuration.VERSION_2_3_25.intValue(); public static final int VERSION_INT_2_3_26 = Configuration.VERSION_2_3_26.intValue(); + public static final int VERSION_INT_2_3_27 = Configuration.VERSION_2_3_27.intValue(); public static final int VERSION_INT_2_4_0 = Version.intValueFor(2, 4, 0); public static void checkVersionNotNullAndSupported(Version incompatibleImprovements) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a4a406a1/src/manual/en_US/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index 4961955..809822b 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -26965,6 +26965,33 @@ TemplateModel x = env.getVariable("x"); // get variable x + Bug fixed: BeansWrapper and + DefaultObjectWrapper, starting from Java 8, + when the same JavaBeans property has both non-indexed read + method (like String[] getFoo()) and indexed + read method (like String getFoo(int index)), + has mistakenly used the indexed read method to access the + property. This is a problem because then the array size was + unknown, and thus the property has suddenly become unlistable on + Java 8. To enable the fix (where it will use the non-indexed + read method), you have to increase the value of the + incompatibleImprovements constructor argument + of the used DefaultObjectWrapper or + BeansWrapper to 2.3.27. Note that if you + leave the object_wrapper setting of the + Configuration on its default, it's enough to + increase the incompatibleImprovements + setting of the Configuration to + 2.3.27, as that's inherited by the default + object_wrapper. Note that this bug haven't + surfaced before Java 8, as then + java.beans.Inrospector has only exposed the + non-indexed method when both kind of read method was + present. + + + Bug fixed: When the TemplateExceptionHandler suppresses (i.e., doesn't re-throw) an exception, the actual = new ArrayList(); for (int i = _TemplateAPI.VERSION_INT_2_3_0; i <= Configuration.getVersion().intValue(); i++) {