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 33d5094b85160c7fcc94ef6c9708c83aca161230
Author: ddekany <[email protected]>
AuthorDate: Thu Nov 13 18:27:13 2025 +0100

    GraalVM native PR followup: Moved documentation from the README into the 
Manual. Also reworked it.
---
 README.md                                        | 139 ---------------------
 freemarker-manual/src/main/docgen/en_US/book.xml | 152 ++++++++++++++++++++++-
 2 files changed, 148 insertions(+), 143 deletions(-)

diff --git a/README.md b/README.md
index d1286692..9702df4b 100644
--- a/README.md
+++ b/README.md
@@ -155,7 +155,6 @@ issue `.\gradlew publish`. Note that for this the following 
Gradle properties mu
 (in `gradle.properties`, or pass them via `-P<name>=<value>` arguments):
 `freemarker.signMethod`, `freemarker.deploy.apache.user`, 
`freemarker.deploy.apache.password`.
 
-
 ### FreeMarker website related build tasks
 
 The website (the FreeMarker homepage) is build by the `freemarker-site` 
project, not this project (`freemaker`). Except,
@@ -253,141 +252,3 @@ Gradle project. After that, it's recommended to set these 
preferences (based on
   - Project -> Properties -> FindBugs -> [x] Run Automatically
   - There should 0 errors. But sometimes the plugin fails to take the
     @SuppressFBWarnings annotations into account; then use Project -> Clean. 
-
-### GraalVM Native Support
-
-Apache FreeMarker is compatible with Ahead-of-Time (AOT) compilation using 
GraalVM as of version 2.3.35. However, any custom Java objects or resources 
used in the data model must still be registered for reflection.
-
-Refer to the [GraalVM documentation](https://www.graalvm.org/latest/docs/) for 
more details, especially:
-
-- [Reflection in Native 
Image](https://www.graalvm.org/jdk21/reference-manual/native-image/dynamic-features/Reflection/)
-- [Accessing Resources in Native 
Image](https://www.graalvm.org/jdk21/reference-manual/native-image/dynamic-features/Resources/)
-
-**TIP:** You can find many configuration samples in the 
[graalvm-reachability-metadata](https://github.com/oracle/graalvm-reachability-metadata)
 repository.
-
-Here is a sample usage guide for ApacheFreeMarker + GraalVM.
-
-To run the sample in classic Just In Time Way, we only need :
-
-* FreeMarkerGraalVMSample.java
-* sample.ftl
-
-But for the Ahead Of Time application with GraalVM some additional 
configuration is required :
-
-* custom-reflect-config.json
-
-#### FreeMarkerGraalVMSample.java sample class
-
-```java
-import freemarker.log.Logger;
-import freemarker.template.Configuration;
-import freemarker.template.Template;
-import freemarker.template.TemplateException;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.HashMap;
-import java.util.Map;
-
-public class FreeMarkerGraalVMSample {
-
-    private final static Logger LOG = 
Logger.getLogger(FreeMarkerGraalVMSample.class.getName());
-
-    /* data model */
-    public class Data {
-        private String description;
-        public String getDescription() {
-            return description;
-        }
-        public void setDescription(String description) {
-            this.description = description;
-        }
-    }
-
-    private void handleTemplate(Writer writer, String templatePath, 
Map<String, Object> dataModel) throws IOException, TemplateException {
-        Configuration cfg = new Configuration( Configuration.VERSION_2_3_34 );
-        cfg.setClassForTemplateLoading( FreeMarkerGraalVMSample.class, 
"/templates" );
-        Template template = cfg.getTemplate( templatePath );
-        template.process( dataModel, writer );
-    }
-
-    public void runSample() {
-        try ( StringWriter writer = new StringWriter() ) {
-            Map<String, Object> dataModel = new HashMap<>();
-            Data data = new Data();
-            data.setDescription( "FreeMarkerGraalVMSample" );
-            dataModel.put("data", data);
-            handleTemplate( writer, "sample.ftl", dataModel );
-            LOG.info( writer.toString() );
-        } catch (Exception e) {
-            LOG.error( e.getMessage(), e );
-        }
-    }
-
-    public static void main(String[] args) {
-        FreeMarkerGraalVMSample sample = new FreeMarkerGraalVMSample();
-        sample.runSample();
-    }
-
-}
-```
-
-#### Apache FreeMarker template
-
-```ftl
-<freemarker-graalvm-sample>
-    <freemarker-version>${.version}</freemarker-version>
-    <description>${data.description}</description>
-</freemarker-graalvm-sample>
-```
-
-#### Reflection configuration, custom-reflect-config.json
-
-Refers to [Reflection in Native 
Image](https://www.graalvm.org/jdk21/reference-manual/native-image/dynamic-features/Reflection/)
 guide
-
-```json
-[{
-  "name" : "FreeMarkerGraalVMSample$Data",
-  "methods" : [ {
-    "name" : "<init>",
-    "parameterTypes" : [ ]
-  },{
-    "name" : "getDescription",
-    "parameterTypes" : [ ]
-  } ]
-}]
-```
-
-#### Build the native image
-
-```shell
-#!/bin/bash
-
-# setting up environment
-export BASEDIR=.
-export CP=./lib/freemarker-gae-2.3.35-SNAPSHOT.jar:.
-
-# just in time application build
-javac -cp ${CP} -d build ./src/FreeMarkerGraalVMSample.java
-
-# ahead of time application build
-#
-# -H:IncludeResources=^templates/.* 
-#      will make the templates available to the native-image
-#
-# -H:ReflectionConfigurationFiles=./config/custom-reflect-config.json
-#      will setup reflection custom configuration
-native-image \
-  -cp "${CP}:build" \
-  -H:Path=build \
-  -H:Class=FreeMarkerGraalVMSample \
-  -H:IncludeResources=^templates/.* \
-  -H:+UnlockExperimentalVMOptions \
-  -H:ReflectionConfigurationFiles=./config/custom-reflect-config.json \
-  --no-fallback \
-  --report-unsupported-elements-at-runtime
-
-# running the application
-./build/freemarkergraalvmsample
-```
\ No newline at end of file
diff --git a/freemarker-manual/src/main/docgen/en_US/book.xml 
b/freemarker-manual/src/main/docgen/en_US/book.xml
index 16803701..3c970135 100644
--- a/freemarker-manual/src/main/docgen/en_US/book.xml
+++ b/freemarker-manual/src/main/docgen/en_US/book.xml
@@ -20,10 +20,7 @@
 <book conformance="docgen" version="5.0" xml:lang="en"
       xmlns="http://docbook.org/ns/docbook";
       xmlns:xlink="http://www.w3.org/1999/xlink";
-      xmlns:ns5="http://www.w3.org/2000/svg";
-      xmlns:ns4="http://www.w3.org/1998/Math/MathML";
-      xmlns:ns3="http://www.w3.org/1999/xhtml";
-      xmlns:ns="http://docbook.org/ns/docbook";>
+>
   <info>
     <title>Apache FreeMarker Manual</title>
 
@@ -11568,6 +11565,134 @@ TemplateHashModel roundingModeEnums =
         </section>
       </section>
 
+      <section xml:id="pgui_misc_graalvm_native">
+        <title>GraalVM native support</title>
+
+        <para>Apache FreeMarker is compatible with Ahead-of-Time (AOT)
+        compilation using GraalVM as of version 2.3.35, but with two big
+        caveats:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>Custom Java classes that are exposed to the templates via
+            the data model must be explicitly listed in the GraalVM native
+            reflection configuration, or else the FreeMarker templates will
+            not see their members. That's because the templates are not
+            compiled, and has to be able to discover the exposed classes on
+            runtime via Java reflection.</para>
+          </listitem>
+
+          <listitem>
+            <para>If templates are stored as Java resources, the resources
+            also must be explicitly configured to be accessible on runtime, or
+            else FreeMarker will not find the templates.</para>
+          </listitem>
+        </itemizedlist>
+
+        <para>Let's say you have this class:</para>
+
+        <programlisting role="unspecified">package com.example;
+
+public class User {
+    private String name;
+    private String email;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+}</programlisting>
+
+        <para>Then you put an instance of that into the data model with name
+        <literal>user</literal>, and access it from a template like this,
+        which you put into
+        <literal>src/main/resources/templates</literal>:</para>
+
+        <programlisting role="template">Hello ${user.name}!
+
+Your e-mail address is: ${user.email}</programlisting>
+
+        <para>Then you configure FreeMarker to load the templates from the
+        class path:</para>
+
+        <programlisting role="unspecified">Configuration cfg = new 
Configuration(Configuration.VERSION_...);
+...
+cfg.setClassForTemplateLoading(this.getClass(), "/templates");</programlisting>
+
+        <para>Under normal Java, all this would just work. But under GraalVM
+        native, if it finds the template at all, the template processing will
+        fail with <literal>freemarker.core.InvalidReferenceException</literal>
+        saying that <quote>user.name has evaluated to null or missing</quote>.
+        That's because FreeMarker tries to discover the methods of the
+        <literal>User</literal> class via Java reflection, but such
+        information is by default not available in GraalVM native. You need to
+        add a reflection configuration file like this, which we could put for
+        example into
+        
<literal>src/main/graalvm-native-config/reflection-config.json</literal>:</para>
+
+        <programlisting role="unspecified">[
+  {
+    "name": "com.example.User",
+    "allPublicMethods": true
+  }
+]</programlisting>
+
+        <para>Then this has to be referred in the GraalVM native build. For
+        example in Gradle it would be something like this:</para>
+
+        <programlisting role="unspecified">graalvmNative {
+    binaries.all {
+        ...
+        
configurationFileDirectories.from(file("src/main/graalvm-native-config"))
+        resources.autodetect() // Needed to find the templates
+        ...
+    }
+}</programlisting>
+
+        <para>If you are running <literal>native-image</literal> form command
+        line instead, then the corresponding command-line options are (here we
+        assume that template are in
+        <literal>src/main/resources/templates</literal>):</para>
+
+        <itemizedlist>
+          <listitem>
+            
<para><literal>-H:ReflectionConfigurationFiles=./src/main/graalvm-native-config/reflection-config.json</literal></para>
+          </listitem>
+
+          <listitem>
+            <para><literal>-H:IncludeResources=^templates/.*</literal></para>
+          </listitem>
+        </itemizedlist>
+
+        <para>After these, the templates should now work.</para>
+
+        <note>
+          <para>This example was written for GraalVM 21. GraalVM and its
+          Gradle plugin is still evolving, so above can become outdated, or
+          not the best practice anymore. Feel free to report any such
+          issue!</para>
+        </note>
+
+        <para>You can also find a working example in the FreeMarker source
+        code; see the <literal>freemarker-test-graalvm-native</literal>
+        subproject there!</para>
+
+        <para>Please see the <link
+        xlink:href="https://www.graalvm.org/latest/docs/";>GraalVM
+        documentation</link> for more details!</para>
+      </section>
+
       <section xml:id="pgui_misc_servlet">
         <title>Using FreeMarker with servlets</title>
 
@@ -30459,6 +30584,25 @@ TemplateModel x = env.getVariable("x");  // get 
variable x</programlisting>
     <appendix role="changelog" xml:id="app_versions">
       <title>Version history</title>
 
+      <section xml:id="versions_2_3_35">
+        <title>2.3.35</title>
+
+        <para>Release date: [TODO]</para>
+
+        <section>
+          <title>Changes on the Java side</title>
+
+          <itemizedlist>
+            <listitem>
+              <para>Added GraalVM native (AOT) support. But it doesn't
+              <quote>just work</quote>, as you have to explicitly configure
+              reflection and resource access for your application; <link
+              linkend="pgui_misc_graalvm_native">see more here</link>!</para>
+            </listitem>
+          </itemizedlist>
+        </section>
+      </section>
+
       <section xml:id="versions_2_3_34">
         <title>2.3.34</title>
 

Reply via email to