Apache FreeMarker Documentation

repository·2.3-gae·Indexed 22 days ago

https://github.com/apache/freemarker

A Java-based template engine used to generate text output such as HTML or source code. It is designed for embedding into Java applications, particularly those following the MVC pattern. Documentation covers installation via Maven or manual JAR placement, building from source with Gradle, GraalVM native image support, and usage of template built-ins for date formatting (ISO 8601), existence handling (null/missing values), and optional template loading.

Tokens
2.9K
Snippets
7
Records
12
Agent score
78%

What's inside Apache FreeMarker

  1. Install Apache FreeMarker manually

    2.3-gae
    If you are not using a dependency manager, copy freemarker.jar to a location accessible by your Java application's ClassLoader. For web applications, a common practice is to place the JAR in the WEB-INF/lib directory.
  2. Set up IntelliJ IDEA for FreeMarker development

    2.3-gae

    To develop on FreeMarker using IntelliJ IDEA:

    1. Open Project: Select the settings.gradle.kts file in the root directory.
    2. Gradle Configuration: If the build fails, go to File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle and ensure:
      • Gradle JVM: JDK 17 (or higher)
      • Build and run using: "Gradle"
      • Run tests using: "Gradle"
    3. Code Style: Import freemarker/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml under Editor / Code style.
    4. Inspections: Import freemarker/src/ide-settings/IntelliJ-IDEA/Editor-Inspections-FreeMarker.xml under Editor / Inspections.
    5. Copyright: Create an "ASL2" profile under Editor / Copyright / Copyright Profiles using the header from existing Java files, then set it as the default project copyright.
  3. Install Apache FreeMarker via Maven

    2.3-gae

    To use FreeMarker in a Maven project, add the following dependency.

    Warning: Ensure you use the org.freemarker groupId. Using the older freemarker groupId may result in multiple versions of the library being present on the classpath, causing unpredictable runtime behavior.

      <!--
      Attention: Be sure nothing pulls in an old dependency with groupId
      "freemarker" (without the "org."), because then you will end up with
      two freemarker.jar-s and unpredictable behavior on runtime!
      -->
      <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker-gae</artifactId>
        <version>{version}</version>
      </dependency>
  4. Generate OverloadedNumberUtil methods from spreadsheet data

    2.3-gae

    This process uses FMPP to generate source code for freemarker.ext.beans.OverloadedNumberUtil methods based on data defined in a LibreOffice spreadsheet (prices.ods). This is used to synchronize Java utility methods with pricing data.

    Workflow:

    1. Modify Data: Edit the prices.ods spreadsheet.
    2. Update Configuration: If new types were added to the spreadsheet, update the toCsFreqSorted, toCsCostBoosts, and toCsContCosts definitions in config.fmpp.
    3. Export Data: Save the spreadsheet as prices.csv using a comma (,) as the field separator.
    4. Run Generator: Execute FMPP from the project directory. This generates the file <freemarkerProjectDir>/build/getArgumentConversionPrice.java.
    5. Integrate Code: Copy the generated content into OverloadedNumberUtil.java.
    6. Verify Constants: Ensure that the value of OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE remains consistent with the values in the ODS and the cellValue multiplier defined in generator.ftl.
  5. Build Apache FreeMarker from source

    2.3-gae

    FreeMarker uses Gradle for its build system.

    Prerequisites

    • JDK Versions: You must have JDK 8, JDK 16, and JDK 17 installed and visible to Gradle. Different parts of the source code target different Java versions.
    • Default Java: Your default Java version (used by Gradle) should be at least JDK 17.
    • Gradle Wrapper: If building from an official source release (rather than a Git checkout), you must manually add gradle/wrapper/gradle-wrapper.jar to the project.

    Common Build Tasks

    • Build the JAR: ./gradlew jar (or gradlew.bat jar on Windows).
    • Run Tests: ./gradlew check (Use this instead of the test task to ensure all checks are performed).
    • Generate Offline Docs: ./gradlew javadoc and ./gradlew manualOffline.
    • Build Distribution Artifacts: ./gradlew build (Note: For stable versions, you must configure signing or set freemarker.allowUnsignedReleaseBuild=true in gradle.properties).
    ./gradlew jar
  6. Build and run FreeMarker GraalVM native image

    2.3-gae

    This project provides a test module (freemarker-test-graalvm-native) to verify Apache FreeMarker support for GraalVM native images.

    Prerequisites

    1. GraalVM: Install GraalVM 21 or higher.
    2. Environment Variables:
      • If GraalVM is installed in a non-standard location, you must set the GRAALVM_HOME environment variable.
      • Alternatively, set JAVA_HOME if GraalVM is your default JDK.

    Build Steps

    Use the Gradle wrapper to compile the native image:

    ./gradlew :freemarker-test-graalvm-native:nativeCompile

    Execution

    Run the generated executable.

    Linux/macOS:

    ./freemarker-test-graalvm-native/build/native/nativeCompile/freemarker-test-graalvm-native

    Windows: Use backslashes in the path:

    .\freemarker-test-graalvm-native\build\native\nativeCompile\freemarker-test-graalvm-native

    Expected Output

    A successful run should output demo information and a rendered HTML snippet similar to:

    INFO: name : FreeMarker Native Demo, version : 2.3.35-nightly
    Jan 15, 2025 4:28:19 PM freemarker.log._JULLoggerFactory$JULLogger info
    INFO: result :
    <html
        <head>
            <title>Hello : FreeMarker GraalVM Native Demo</title>
        </head>
        <body>
            <h1>Hello : FreeMarker GraalVM Native Demo</h1>
            <p>Test template for Apache FreeMarker GraalVM native support (2.3.35-nightly)</p>
        </body>
    </html>
    # Build the test project native image
    ./gradlew :freemarker-test-graalvm-native:nativeCompile 
    
    # Run the native executable
    ./freemarker-test-graalvm-native/build/native/nativeCompile/freemarker-test-graalvm-native
  7. Handle missing or null values with FreeMarker existence built-ins

    2.3-gae

    FreeMarker provides several built-in functions (denoted by the ? operator) to safely handle variables that might be null or undefined. These prevent InvalidReferenceException errors during template processing.

    Key Existence Built-ins

    • ?default: Provides a fallback value if the target is missing or null. It accepts one or more arguments and returns the first non-null argument.
      • Usage: variable?default(fallbackValue)
    • ?exists: Returns a boolean (true or false) indicating whether the target exists and is not null.
      • Usage: variable?exists
    • ?has_content: Returns a boolean indicating whether the target exists and is not "empty". For strings, this means not being empty or just whitespace; for sequences/collections, this means having at least one element.
      • Usage: variable?has_content
    • ?if_exists: Returns the target if it exists, otherwise returns nothing (effectively suppressing the output if the variable is missing).
      • Usage: variable?if_exists

    String-to-Null Conversion Built-ins

    These built-ins are used to treat specific string states (like being blank or empty) as null values:

    • ?blank_to_null: If the target is null or consists only of whitespace, it returns null. Otherwise, it returns the original model.
    • ?trim_to_null: Trims the target string. If the resulting string is empty, it returns null. Otherwise, it returns the trimmed string.
    • ?empty_to_null: If the target string is empty, it returns null. Otherwise, it returns the original model.
  8. Configure XPath dependencies for OpenJDK 9+

    2.3-gae

    If you are running on OpenJDK 9 or later and your templates use XPath queries via freemarker.ext.dom, you must explicitly add Apache Xalan as a dependency. This is because OpenJDK no longer includes the XPath support that FreeMarker's freemarker.ext.dom relies on.

    Note: This is not required if you are using Oracle Java 9 or if FreeMarker is configured to use Jaxen for XPath.

  9. Use ?default to provide fallback values

    2.3-gae

    The ?default built-in allows you to specify one or more fallback values. FreeMarker will iterate through the arguments provided and return the first one that is not null. If all provided arguments are null, the built-in returns null.

    Note: You must provide at least one argument to ?default.

    <#-- If user_name is missing, use 'Guest' -->
    Hello, ${user_name?default('Guest')}!
    
    <#-- Multiple fallbacks -->
    ${val?default(fallback1, fallback2, fallback3)}
  10. Format dates to ISO 8601 UTC or Local using ?iso_utc and ?iso_local

    2.3-gae

    FreeMarker provides specialized built-ins for quick ISO 8601 formatting without explicitly passing a time zone argument:

    • ?iso_utc: Formats the date/time using the UTC time zone.
    • ?iso_local: Formats the date/time using the environment's default time zone (or the SQL time zone if configured).

    These built-ins are useful when you want to ensure a specific standard (UTC) or follow the server's local settings without manual time zone management.

    <#-- Format as UTC -->
    ${myDateTime?iso_utc}
    
    <#-- Format using local time zone -->
    ${myDateTime?iso_local}
  11. Use the .get_optional_template built-in

    2.3-gae

    The .get_optional_template(name, options) built-in allows you to attempt to load a template without causing an error if the file is missing. It returns a hash containing information about the template's existence and methods to interact with it.

    Arguments

    1. name (String): The name or path of the template to load.
    2. options (Hash, optional): A hash of configuration options:
      • encoding (String): The character encoding to use.
      • parse (Boolean): Whether to parse the template. Defaults to true.

    Return Value

    The built-in returns a hash with the following keys:

    • exists (Boolean): true if the template was found and loaded, false otherwise.
    • include (Directive, only if exists is true): A directive that performs an <#include> of the loaded template. It accepts no parameters, loop variables, or nested content.
    • import (Method, only if exists is true): A method that imports the loaded template. It accepts no parameters.

    If the template does not exist, the returned hash will only contain exists: false. This allows you to use the default value operator (!) to provide a fallback, for example: <@optTemp.include!myDefaultMacro />.

    <#-- Example usage -->
    <#assign optTemp = .get_optional_template("missing_file.ftl", {"parse": true})>
    
    <#if optTemp.exists>
      <@optTemp.include />
    <#else>
      <p>Template not found.</p>
    </#if>
  12. Format dates to ISO 8601 using ?iso(timeZone)

    2.3-gae

    The ?iso(timeZone) built-in converts a date/time value into an ISO 8601 formatted string.

    Arguments:

    • timeZone: A single argument that must be either a java.util.TimeZone object or a string representing a valid time zone name.

    Behavior:

    • If the left-hand value is a date-only type (TemplateDateModel.DATE), the offset is not included in the output as per ISO 8601 standards.
    • If the time zone string is not recognized, FreeMarker throws an error indicating the time zone name is invalid.
    <#-- Example usage of ?iso with a time zone string -->
    ${myDate?iso("UTC")}
    
    <#-- Example usage with a java.util.TimeZone object (if passed in the data model) -->
    ${myDate?iso(myTimeZoneObject)}