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

arunpati pushed a commit to branch release24.09
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/release24.09 by this push:
     new f74f3df897 Fixed: Call to non-existing method in production run PDF 
reports (OFBIZ-13504) [release24.09] (#1828)
f74f3df897 is described below

commit f74f3df89776d5b80d5f8b20fe926889e8baaeb4
Author: toaditi <[email protected]>
AuthorDate: Wed Oct 7 18:32:26 2026 +0530

    Fixed: Call to non-existing method in production run PDF reports 
(OFBIZ-13504) [release24.09] (#1828)
    
    Backport of #1827 to `release24.09`.
    
    ## Credits
    
    Thanks to **Carsten Heinrigs** for reporting OFBIZ-13504 and for naming
    both the
    template and the method in the report.
    
    This follows on from **OFBIZ-10478** by **Jacques Le Roux** and
    **Pradhan Yash Sharma**,
    reviewed by **Michael Brohl** — a broad and worthwhile cleanup, in which
    this one method
    happened to have all of its callers in templates, where static analysis
    could not see them.
    
    ## The problem
    
    `UtilDateTime.toDateString(Date, String)` is reached from templates
    through
    `Static["org.apache.ofbiz.base.util.UtilDateTime"]`. FreeMarker resolves
    those members
    reflectively and so sees public methods only, so narrowing the
    two-argument overload to
    private left the templates unable to resolve it:
    
    ```
    Java method "static 
org.apache.ofbiz.base.util.UtilDateTime.toDateString(Date)"
    takes 1 argument, but 2 was given.
    ```
    
    The render does not throw — that message is written *into* the output
    stream. So the
    report still returns HTTP 200 and a structurally valid PDF, with a
    FreeMarker stack trace
    printed where the dates belong.
    
    `release24.09` carries the same modifier and the same seven call sites
    as trunk:
    `ProductionRun.fo.ftl` (4), `PRunsProductsAndOrder.fo.ftl:324`,
    `PRunsProductsStacks.fo.ftl:92` and `ContentTreeLookupList.ftl:85`.
    
    Restoring the overload to public keeps the rendered format unchanged.
    Dropping the format
    string in the templates instead would fall back to the single-argument
    overload and
    silently move the reports from `dd/MM/yyyy` to `MM/dd/yyyy`.
    
    This was discussed on the Jira issue, and **Carsten Heinrigs, who
    reported it, agreed that
    restoring the method is the better route**, adding that he values being
    able to specify the
    date format string. With the two-argument form private, a template that
    needs a particular
    format has no clean way to ask for one.
    
    ## Two differences from the trunk PR, both deliberate
    
    **1. The test is JUnit 4**, matching this branch, rather than the
    Jupiter version on trunk.
    
    **2. There is a one-line `build.gradle` change.** The `test` task here
    lists the JUnit
    classes it runs explicitly, and `FreeMarkerWorkerTests` was not among
    them — so the test
    already in that class was never executed. Without the added `include`,
    the new regression
    test would sit in the tree and never run, which would be worse than not
    adding it. It is
    called out separately here so it is easy to drop if you would rather
    leave the task list
    untouched.
    
    ## Verification
    
    The test asserts `09/03/2026`, a date whose `dd/MM/yyyy` and
    `MM/dd/yyyy` forms differ, so
    it also fails if the format string is dropped rather than the method
    restored. It was
    watched failing before the change and passing after, on this branch,
    against the
    FreeMarker 2.3.35 this branch now pins.
    
    `./gradlew test checkstyleMain codenarcMain codenarcTest javadoc` — all
    pass, 25 tests,
    0 failures, 0 errors.
    
    The trunk change in #1827 was additionally verified by creating a
    production run and
    fetching `PrintProductionRun` over HTTP before and after, which is what
    confirmed the
    "valid PDF containing a stack trace" behaviour described above. That
    runtime check was not
    repeated here, since the code path and the fix are identical.
---
 build.gradle                                       |  1 +
 .../org/apache/ofbiz/base/util/UtilDateTime.java   | 15 ++++++++++++--
 .../base/util/template/FreeMarkerWorkerTests.java  | 23 ++++++++++++++++++++++
 3 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/build.gradle b/build.gradle
index 41d54f0c53..7128ac12f8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -373,6 +373,7 @@ test {
     include 'org/apache/ofbiz/test/TestServices.class'
     include 
'org/apache/ofbiz/base/util/string/FlexibleStringExpanderBaseCodeTests.class'
     include 'org/apache/ofbiz/base/util/FileUtilTests.class'
+    include 'org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.class'
     jvmArgs "-javaagent:${classpath.find { it.name.contains('jmockit') 
}.absolutePath}"
 }
 
diff --git 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilDateTime.java 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilDateTime.java
index 9ced64d53a..169c9f6bca 100644
--- a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilDateTime.java
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilDateTime.java
@@ -582,11 +582,22 @@ public final class UtilDateTime {
     }
 
     /**
-     * Makes a date String in the given from a Date
+     * Makes a date String in the given format from a Date.
+     *
+     * <p>This overload has no callers in Java code. It is reached from 
FreeMarker templates
+     * through {@code Static["org.apache.ofbiz.base.util.UtilDateTime"]}, 
which resolves members
+     * reflectively and therefore only sees public ones, so it must stay 
public. Narrowing it to
+     * private broke the production run reports (OFBIZ-13504); the failure is 
quiet, because
+     * FreeMarker writes the resolution error into the rendered output rather 
than throwing.
+     * Templates rely on passing an explicit pattern here: the single argument 
overload is fixed
+     * to {@code MM/dd/yyyy}, so dropping the pattern silently changes the 
rendered date order.</p>
+     *
      * @param date The Date
+     * @param format The date format pattern, as understood by {@link 
SimpleDateFormat}; the locale
+     *      default format is used when null
      * @return A date String in the given format
      */
-    private static String toDateString(java.util.Date date, String format) {
+    public static String toDateString(java.util.Date date, String format) {
         if (date == null) {
             return "";
         }
diff --git 
a/framework/base/src/test/java/org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.java
 
b/framework/base/src/test/java/org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.java
index 27ef6050d3..e421391f67 100644
--- 
a/framework/base/src/test/java/org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.java
+++ 
b/framework/base/src/test/java/org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.java
@@ -21,6 +21,7 @@ package org.apache.ofbiz.base.util.template;
 import static org.junit.Assert.assertEquals;
 
 import java.io.StringWriter;
+import java.sql.Timestamp;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -41,4 +42,26 @@ public class FreeMarkerWorkerTests {
         FreeMarkerWorker.renderTemplateFromString("template1", "Hello 
${name}", context, out, 0, false);
         assertEquals("Hello World!", out.toString());
     }
+
+    /**
+     * OFBIZ-13504: report templates format dates through
+     * {@code Static["...UtilDateTime"].toDateString(date, format)}. 
FreeMarker resolves
+     * {@code Static[...]} members reflectively against public methods only, 
so the two-argument
+     * overload must stay publicly visible for those templates to render.
+     *
+     * <p>The date below is deliberately the 9th of the 3rd month: {@code 
dd/MM/yyyy} yields
+     * 09/03/2026 while {@code MM/dd/yyyy} yields 03/09/2026. The assertion 
therefore also fails if
+     * the explicit format is dropped in favour of the single-argument 
overload, which silently
+     * switches the reports to US date order.</p>
+     */
+    @Test
+    public void renderTemplateCallingToDateStringWithExplicitFormat() throws 
Exception {
+        StringWriter out = new StringWriter();
+        Map<String, Object> context = new HashMap<>();
+        context.put("aDate", Timestamp.valueOf("2026-03-09 14:30:00"));
+        FreeMarkerWorker.renderTemplateFromString("templateToDateString",
+                
"${Static[\"org.apache.ofbiz.base.util.UtilDateTime\"].toDateString(aDate, 
\"dd/MM/yyyy\")}",
+                context, out, 0, false);
+        assertEquals("09/03/2026", out.toString());
+    }
 }

Reply via email to