2.3.20

Date of release: 2013-06-27

If you are IDE/tools author, note these changes.

Changes on the FTL side

  • Error message quality improvements:

    • Many error messages are now more helpful, especially for users who are not experienced with FreeMarker. For example, some of the most common user mistakes are recognized and tips are shown to fix them, reducing support costs or the time employees spend to figure them out.

    • It's now ensured that the error location in the template is included in the message returned by TemplateException.getMessage(). The stack trace always showed this information anyway, but some users only see the "message", not the stack trace, and that often didn't contained the location.

    • The template language part of the stack trace is now more detailed, and easier to understand. This is especially helpful in applications that use a complex library of macros and functions.

    • Several smaller bugs were fixed that made the error information wrong or lacking.

    • The layout of the error messages is now more consistent, and generally easier to read.

  • Changes regarding boolean to string conversions and formatting:

    • ?c (computer-language formatting) now works with booleans too, always giving "true" or "false" regardless of the boolean_format. This way it's safe for generating JavaScript in a context where human-readable text is also rendered.

    • If the boolean_format setting is set to anything but the default "true,false" value, boolean values will be automatically converted to string where a string value is expected by the template language, instead of giving an error. This helps you spare those?string-s after boolean values. This is the same logic as with numbers and dates, which were always automatically converted to string, according the corresponding format setting. Except, the provided default boolean format is useless for automatic conversion (but it's still there for ?string, for backward compatibility), hence it must be set manually. (We certainly couldn't come up with a sensible default anyway, as for booleans it depends too much on the application, not to mention the localisation issues.)

      Exactly like with numbers and dates, automatic conversion doesn't happen in these cases:

      • Comparisons, i.e., someBoolean == 'true' is still an error

      • Method calls where the declared type of the parameter is String but the actual value is a boolean; still an error

      • When the boolean value is used as key in expr[key], it's still an error (there was no automatic conversion there for other types either, as numerical and string keys have different meaning)

      • The opposite direction, i.e., string to boolean conversion; won't happen

  • New built-ins for numbers: abs, is_nan, is_infinite. Like n?abs will give the absolute value of n.

  • New built-in for sequences: join. Like [1, 2, 3]?join(", ") will give the string "1, 2, 3".

  • If you set the incompatible_improvements setting (see here) to 2.3.20 or higher, ?html will escape apostrophe-quotes just like ?xhtml does. Utilizing this is highly recommended, because otherwise if interpolations are used inside attribute values that use apostrophe-quotation (<foo bar='${val}'>) instead of plain quotation mark (<foo bar="${val}">), they might produce HTML/XML that's not well-formed. Note that ?html didn't do this because long ago there was no cross-browser way of doing this, but it's not a real concern anymore. Also note that this will be the default behavior starting from 2.4.

  • Bug fix [390] (and other improvements): ?js_string and ?json_string didn't escape the u2028-u2029 line terminators (problem for JavaScript) and the u007F-u009F control characters (maybe a problem in JSON, depending on implementation). Furthermore, the escaping of \, <, and > become safer in that now they are escaped whenever it can't be guaranteed that they won't be part of <!, ]]> or </. Earlier they were only escaped when it was known that they are part of these patterns, thus it was possible to assemble these patterns from two adjacent interpolations. Additionally, from now on <? and --> also count as dangerous patterns, and will trigger < and > escaping.

  • Bug fixed: The following string built-ins didn't coerce the numerical, date (and now the boolean) left-values to string, instead they threw a type error: contains, index_of, last_index_of, left_pad, right_pad, matches, replace, split, new. The other string built-ins already did this conversion for a long time; this was an accidental inconsistency.

  • Bug fixed: With the default arithmetic engine, it's now supported to compare infinite (positive or negative) with 0, to decide its sign.

Changes on the Java side

  • BeansWrapper introspection cache improvements:

    • Added public API to BeansWrapper for clearing the class cache: clearClassIntrospecitonCache(), removeFromClassIntrospectionCache(Class)

    • Significantly improved multi-core performance:

      • Uses ConcurrentHashMap when running on Java 5 or later.

      • The cache won't block readers while introspecting a class after a cache miss

      • If multiple threads need to introspect the same class that's not in the cache yet, only one of them will do it, the others will wait for its results.

    • Bug fix [361]: There was a small chance of deadlock when class-reloading was detected. Locking was redesigned to prevent such oversights in the future.

    • The internal package-visible freemarker.ext.beans API was slightly changed as the result of internal cleanup. Nobody but the FreeMarker developers should define classes in that package, so it shouldn't break anything. But if somebody did some in-house hacks there, re-compile to see if it still works.

  • Nameless templates (those directly created with new Template(null, ...) instead of loaded through Configuration) couldn't include or import other templates, and thrown a NullPointerException when they tried to. Now they resolve relative paths as if they were in the template root directory.

  • Bug fix: Regular expression built-ins and some logger libraries (most importantly Log4J) were unavailable on the Google App Engine platform. This fix is only present in the GAE-compatible build, 2.3.20-gae.

  • Added new method to Configuration: CacheStorage getCacheStorage()

  • Added new methods to Environment to make comparisons among TemplateModel-s according the rules of the template language operators: applyEqualsOperator, applyEqualsOperatorLenient, applyLessThanOperator, applyLessThanOrEqualsOperator, applyGreaterThanOperator, applyWithGreaterThanOrEqualsOperator

  • Added new method, Environment.isInAttemptBlock() to check if we are within an #attempt block. This can be useful for TemplateExceptionHandler-s, as then they don't need to print the error to the output since #attempt will roll it back anyway. This is already utilized by the built-in TemplateExceptionHandler-s (DEBUG_HANDLER and HTML_DEBUG_HANDLER).

  • Added convenience constructor Template(String name, String sourceCode, Configuration cfg).

  • TemplateException-s and TemplateModelExcepton-s now can have Throwable cause, not just Exception (it was an old oversight that somehow wasn't fixed so far).

  • Parsing error messages under the JBoss Tools FreeMarker IDE now doesn't contain the usual location line, so that the actual error description is immediately visible in the Eclipse "Problems" view. (It's a "hack" in FreeMarler itself; it tries to detect if it runs under the plugin and then changes its behavior.)

  • Mostly concerning tool (like IDE plugin) authors:

    • The error message formats (what Throwable.getMessage() returns) were heavily changed for TemplateException-s, and somewhat for ParseException-s. It's unlikely that anybody depends on these, but if you tried to parse these messages, be aware of this.

    • Fixed bug where ParseException has contained 0 as line and column number for lexical errors. Now it contains the correct information.

    • Added ParseException.getEditorMessage(): As in IDE-s the error markers show the error location to the user already, the location should not be repeated in the error message. So in IDE-s you should use this method instead of getMessage(). (Under JBoss Tools: FreeMarker now tries to detect that it runs under the plugin, and then it already does this, except that it still shows the column number as that's missing from the error marker location.)

    • Added ParseException.getTemplateName()

    • Added Configuration.getSupportedBuiltInNames(). As new built-ins (expr?builtin_name) are very often added to new FreeMarker versions, auto-completion or syntax highlighting should use this set instead of a fixed set of a names.

    • The format returned by TemplateElement.getDescription() was heavily changed. It's what FTL stack traces and maybe some outline views (tree-views) show. It was always for human reading (and till now was too inconsistent for anything else), so it's unlikely that this breaks anything.

    • There were some smaller changes in freemarker.debug, and it's expected that there will be more, so it was marked as experimental. As far as we know, nobody used it, so it shouldn't break anything.

  • In experimental status only, but freemarker.jar is now an OSGi bundle (see freemarker.jar/META-INF/MANIFEST.MF). Depending on user feedback, the bundle description may change in the future. So please give feedback, especially if you are an OSGi expert!

  • Improved the HTML generated by HTML_DEBUG_HANDLER (the red-on-yellow-background error message). Most importantly, now it will word-wrap. Other changes are minor, like now it can break out of CDATA sections, or now it has less intense colors.

  • New Template method, getActualTagSyntax(): Tells if the template is using traditional or square-bracket syntax. As the syntax can be overridden in the template, also it's possibly decided by auto-detection, it wasn't trivial to it tell till now.

  • Added some utility methods that are useful for generating error messages in custom directives: ClassUtil.getFTLTypeDescription(TemplateModel), getShortClassName, getShortClassNameOfObject

  • Bug fix [364]: freemarker.template.EmptyMap (which is passed to TemplateDirectiveModel-s if there are no parameters) now allows remove(key), clear() and putAll(anyEmptyMap) as these do nothing anyway.

  • Bug fix [375]: NullPointerException on IBM J9 VM (not on the Sun/Oracle implementation) in BeansWrapper when the Java implementation legally returns null for some BeanInfo getters.

  • Bug fix: Cloning a Configuration didn't deep-clone the data structures storing the auto_imports and auto_includes settings, hence possibly leading to aliasing problems.

  • Bug fix [377]: After a failed method call the exception handler could fail in the rare occasion when targetObject.toString() fails, raising a runtime exception that not even the attempt directive will catch.

  • Bug fix [391]: If a template name has contained * that was not the only character in the path step, it threw NegativeArraySizeException instead of FileNotFoundException.

  • With the default arithmetic engine, performance optimizations of comparison operations when some of the numbers is 0, and when the sign of the numbers differ.

  • Some smaller fixes in TemplateElement.getCanonicalForm(), also some quality improvements there.

  • Bug fixes in classic_compatible mode (this mode is to help migrating from FreeMarker 1), thanks to Information Mosaic:

    • When a macro was called with a null/missing parameter value, it has caused error like in FreeMarker 2.3

    • When a hash literal contained reference to a null/missing variable, like in { 'a': missingVar }, it has caused an error like in 2.3

    • When a sequence literal contained reference to a null/missing variable, like in [1, missingVar], it has caused an error like in 2.3

    • When a the left-side of the . (dot) or [key] operator was null or missing variable, like in missingVar.subVar, it has caused an error like in 2.3

    • When BeanModel-s are tried to be treated as strings, for most subclasses it has failed with type error, like in 2.3. In FreeMarker 1 all BeanModel-s were TemplateScalarModel-s, so it should succeed. The fix for this only works where FreeMarker coerces to string (string built-ins on the left-side and concatenation (+) and interpolation (${...}) do that), otherwise unfortunately it will still fail.

    • The classic_compatible setting now accepts value 2 along true (alias 1) and false (alias 0). 2 means true but with emulating bugs in early 2.x classic-compatibility mode. Currently this only affects how booleans are converted to string; with 1 it's always "true"/"", but with 2 it's "true"/"false" for values wrapped by BeansWrapper as then Boolean.toString() prevails. Note that someBoolean?string will always consistently format the boolean according the boolean_format setting, just like in FreeMarker 2.3.

  • Bug fix [394]: When trying to access the optional Jython (or W3C DOM) classes has failed in DefaultObjectWrapper with an Error, rather than with an Exception, loading the DefaultObjectWrapper class itself has failed, instead of the Jython (or W3C DOM) support being disabled. From now in, it will survive any kind of Throwable there, and if the Throwable is not an ClassNotFoundException, it will also log the Throwable.

  • Improved the performance of (thisVarIsMissing.foo)!default and similar parenthetical existence operators and existence built-ins in the case when the null/missing variable is not the last step inside the parenthesis. In that case it's about 30 times faster now, measured on Java 6. In other cases (when all variables exists, or the the last step is missing) the performance is about the same (relatively fast) as before.

  • Added interface freemarker.cache.CacheStorageWithGetSize which allows querying the current number of cache entries in a CacheStorage that implements it. It's implemented by all out-of-the-box CacheStorage implementations. Also added getStrongSize() and getSoftSize() to MRUCacheStorage.

  • Version and build information changes:

    • The form of the nightly build version numbers has changed to be Maven/JSR 277 compliant. What earlier was 2.3.19mod is now something like 2.3.20-nightly_20130605T130506Z (note how the last points to the target release version instead of the last release version).

    • The form of the Release Candidate and Preview version numbers where changed to be Maven/JSP 277 compliant: 2.4pre2 is now 2.4.0-pre02, 2.4rc1 is now 2.4.0-rc01.

    • Added new static method to Configuration to query build date: getBuildDate(). This is also printed by the main command line class.

  • Various internal code cleanups.

Other changes

  • Many JavaDoc improvements, mostly in the documentation of the basic (most frequently used) classes.

  • FreeMarker source code has moved to GitHub (https://github.com/freemarker/freemarker), other project resources has remained on sourceforge.net.

  • Project structure cleanup, Ivy-based build script.