2.3.27 (incubating at Apache)

Release date: 2017-11-03

This is a stable, final release. The "incubating" suffix is required by the Apache Software Foundation until the project becomes a fully accepted (graduated) Apache project.

Changes on the FTL side

  • New directive: continue (sf.net #79, FREEMARKER-37). This can be used inside the list directive to skip to the next iteration (similarly as in Java). See more...

  • Added alternative syntaxes for the && (logical "and") operator: \and and &&. These are to work around issues in applications where the template must be valid XML (&& is not valid XML/HTML, at most places), or where the template entered is stored after XML or HTML escaping. Note that lonely &, and and without \ is not recognized for backward compatibility.

  • New built-in, sequence (FREEMARKER-73). This can be used to work around situations where a listable value lacks some features that you need in the template (like it can't be listed twice, it can't tell its size, etc.), and you can't modify the data-model to fix the problem. See more...

  • Bug fixed (FREEMARKER-70): The usage of loop variable built-ins, like loopVar?index, was disallowed by the parser inside interpolations that are inside a string literal expression (as in <#list 1..3 as loopVar>${'${loopVar?index}'}</#list>), saying that there's no loop variable in scope with loopVar name.

  • Bug fixed: Comments were not allowed by the parser between the switch tag and the first case tag.

  • Bug fixed (FREEMARKER-71): When using exp?eval, if the expression inside the evaluated string throws an exception, the cause exception of that exception was lost.

Changes on the Java side

  • Added new configuration setting (FREEMARKER-48), wrap_unchecked_exceptions (Configurable.setWrapUncheckedExceptions(boolean)). When this is true, unchecked exceptions thrown during evaluating an expression or during executing a custom directive will be wrapped into a TemplateException-s. The advantage of that is that thus the exception will include the location in the template (not just the Java stack trace), and the TemplateExceptionHandler will be invoked for it as well. When this setting is false (which is the default for backward compatibility), the the unchecked exception will bubble up and thrown by Template.process, just as in earlier versions. (Note that plain Java methods called from templates have always wrapped the thrown exception into TemplateException, regardless of this setting.)

  • Added new configuration setting, attempt_exception_reporter (Configurable.setAttemptExceptionReporter(AttemptExceptionReporter)), to allow the customization of how the exceptions handled (and thus suppressed) by the attempt directive are reported. The default AttemptExceptionReporter logs the exception as an error, just as it was in earlier versions, though now the error message will indicate that the exception was thrown inside an attempt directive block.

  • Added new BeansWrapper setting, preferIndexedReadMethod. This was added to address a Java 8 compatibility problem; see the bug fix entry below for more information.

  • When incomplatible_improvements is set to 2.3.27 (or higher), the following unchecked exceptions (but not their subclasses) will be wrapped into TemplateException-s when thrown during evaluating expressions or calling directives: NullPointerException, ClassCastException, IndexOutOfBoundsException, and InvocationTargetException. The goal of this is the same as of setting the wrap_unchecked_exceptions setting to true (see that earlier), but this is more backward compatible, as it avoids wrapping unchecked exceptions that some application is likely to catch specifically (like application-specific unchecked exceptions). (This is related to FREEMARKER-48.)

  • Bug fixed: Starting from Java 8, when the same JavaBeans property has both non-indexed read method (like String[] getFoos()) and indexed read method (like String getFoos(int index)), BeansWrapper and DefaultObjectWrapper have 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 (that is, <#list myObject.foos as foo> fails). To enable the fix (where it will use the non-indexed read method), you should 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. In case increasing the incompatibleImprovements is not an option (because of the other changes it brings), you can instead set the preferIndexedReadMethod property of the object wrapper to false. 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 (affects Java 8 and later): Regardless of the value of the preferIndexedReadMethod setting (see previous point), if one of the indexed read method and the non-indexed read method is inaccessible (i.e., it's declared in a non-public type, and wasn't inherited by a public type), while the other read method is accessible, we will use the accessible one. Earlier, if there was an indexed read method but it was inaccessible, we have given up, and that bean property wasn't visible. Such properties will now be visible again, just as before Java 8. (Before Java 8 java.beans.Inrospector has only exposed the non-indexed read method in this case, so we didn't have this problem.)

  • Bug fixed: On OpenJDK 9 (but not on earlier versions, nor on Oracle Java 9 (tested with "build 9+181")), when you try to use the DOM-based XML support (freemarker.ext.dom.NodeModel), unless you happen to have Apache Xalan in the class path, the NodeModel constructor will fail with IllegalAccessError because "java.xml does not export com.sun.org.apache.xml.internal.utils". Note that while the exception is not thrown anymore in 2.3.27, FreeMarker can't use the XPath support included in OpenJDK 9, and so templates that try to use XPath expressions (like doc['//foo']) will still fail, unless 3rd party XPath support is present.

  • Bug fixed: When the TemplateExceptionHandler suppresses (i.e., doesn't re-throw) an exception, the attempt directive won't log it anymore. (To be precise, the AttemptExceptionReporter won't be invoked for it anymore; the default one logs as error.)

  • Bug fixed (part of FREEMARKER-48): When an arithmetic exception has occurred in an expression (typically division by zero), the template processing has thrown the ArithmeticException as is, without packaging it into a TemplateException. Thus, the error location in the template wasn't visible in the exception.

  • When logging error due to an error in an attempt directive block, the log message now indicates that the error was inside an attempt block.

  • Bug fixed (FREEMARKER-52): When setting the output_format from Properties or the setSetting(String, String) API, the XHTMLOutputFormat abbreviation wasn't recognized (for example in a .properties file, output_format=XHTMLOutputFormat didn't work, only output_format=freemarker.core.XHTMLOutputFormat() did).

  • Bug fixed: When setting the new_builtin_resolver from Properties or the setSetting(String, String) API, it didn't recognize the camel case form of the allowed_classes and trusted_templates keywords, and throw exception for them. Now allowedClasses and trustedTemplates can be used as well.

  • Bug fixed: JSP support haven't tried using the thread context class-loader to load the TLD, instead, it has only used the defining class loader of the FreeMarker classes. This can cause problem in the rare case where freemarker.jar is installed on higher scope than the web application (like on the Servlet container level), but the web application contains the TLD.

  • Constants.EMPTY_HASH and GeneralPurposeNothing (the value of missingVar!) now implements TemplateHashModelEx2. Earlier they were only a TemplateHashModelEx-s.


  • Somewhat less synchronization when accessing JavaBean properties (FREEMARKER-80). The problem was that the java.beans.PropertyDescriptor.getReadMethod method is synchronized, and freemarer.ext.beans.BeanModel has called it each time a property was read. Now that method is only called when the class is first introspected.

  • Improved/fixed TemplateTransformModel behavior (this is a legacy interface that's not used much in user code):

    • Writer TemplateTransformModel.getWriter(Writer out, Map args) can now return the out parameter as is, as FreeMarker now recognizes that it's the same object and so won't call close() on it after the end tag.

    • When incomplatible_improvements is set to 2.3.27 (or higher), and the returned Writer implements TransformControl, exceptions that are used internally for flow control (for <#return>, <#break>, etc.) won't be passed to TransformControl.onError(Throwable) anymore. Earlier, if onError didn't rethrow the exception (though almost all implementation does), you couldn't use said directives inside the transformed block.

  • Added workaround against "IllegalStateException: zip file closed" and "ZipException: ZipFile closed" issues (caused by bugs outside of FreeMarker) when loading resources included in the FreeMarker jar (see freemarker.template.utility.ClassUtil.loadProperties).