Install Apache FreeMarker manually
2.3-gaefreemarker.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.repository·2.3-gae·Indexed 22 days ago
https://github.com/apache/freemarkerA 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.
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.To develop on FreeMarker using IntelliJ IDEA:
settings.gradle.kts file in the root directory.File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle and ensure:freemarker/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml under Editor / Code style.freemarker/src/ide-settings/IntelliJ-IDEA/Editor-Inspections-FreeMarker.xml under Editor / Inspections.Editor / Copyright / Copyright Profiles using the header from existing Java files, then set it as the default project copyright.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>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:
prices.ods spreadsheet.toCsFreqSorted, toCsCostBoosts, and toCsContCosts definitions in config.fmpp.prices.csv using a comma (,) as the field separator.<freemarkerProjectDir>/build/getArgumentConversionPrice.java.OverloadedNumberUtil.java.OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE remains consistent with the values in the ODS and the cellValue multiplier defined in generator.ftl.FreeMarker uses Gradle for its build system.
gradle/wrapper/gradle-wrapper.jar to the project../gradlew jar (or gradlew.bat jar on Windows)../gradlew check (Use this instead of the test task to ensure all checks are performed)../gradlew javadoc and ./gradlew manualOffline../gradlew build (Note: For stable versions, you must configure signing or set freemarker.allowUnsignedReleaseBuild=true in gradle.properties)../gradlew jarThis project provides a test module (freemarker-test-graalvm-native) to verify Apache FreeMarker support for GraalVM native images.
GRAALVM_HOME environment variable.JAVA_HOME if GraalVM is your default JDK.Use the Gradle wrapper to compile the native image:
./gradlew :freemarker-test-graalvm-native:nativeCompileRun the generated executable.
Linux/macOS:
./freemarker-test-graalvm-native/build/native/nativeCompile/freemarker-test-graalvm-nativeWindows: Use backslashes in the path:
.\freemarker-test-graalvm-native\build\native\nativeCompile\freemarker-test-graalvm-nativeA 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-nativeFreeMarker 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.
?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.variable?default(fallbackValue)?exists: Returns a boolean (true or false) indicating whether the target exists and is not null.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.variable?has_content?if_exists: Returns the target if it exists, otherwise returns nothing (effectively suppressing the output if the variable is missing).variable?if_existsThese 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.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.
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)}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}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.
name (String): The name or path of the template to load.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.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>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:
TemplateDateModel.DATE), the offset is not included in the output as per ISO 8601 standards.<#-- 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)}