2.3.21

Page Contents

Date of release: 2014-10-12

Note that since 2.3.21 is designed to be fully backward compatible with the previous 2.3.x releases, some of the improvements and fixes described below are only activated when you specifically ask for 2.3.21 "incompatible improvements" , because they could, with very small chance, break existing applications. If the dependent project is still actively developed, allowing 2.3.21 "incompatible improvements" is highly recommended. See how to set "incomplatible improvements" here.

Note that we have changed our proprietary BSD-style license to Apache License, Version 2.0. See the new license here.

Note that the minimum required Java version was increased from 1.2 to 1.4.

Changes on the FTL side

  • Improved ranges:

  • Added ranges with exclusive end: start..<end (also can be written as start..!end ). More...

    • Added length limited ranges: start..*length : For example, 10..*4 gives [10, 11, 12, 13] , 10..*-4 gives [10, 9, 8, 7] , and 10..*0 gives [] . When these kind of ranges are used for slicing, the slice will end without error if the end of the sliced sequence or string is reached before the specified range length was reached. Thus, for example, to take the first 10 characters from the string s , or less if s is shorter than 10 characters, you can use s[0..*10] . More...

    • Square bracket now accepts range values from any source, like <#assign r = 1..3> ${'foobar'[r]} will print "oob" . Earlier it has only supported ranges that were specified directly inside the square brackets, like 'foobar'[1..3] .

    • When slicing a sequence with a right-unbounded range, it's now allowed to have a range start index that's one higher than the last index of the sliced sequence. For example, ['x', 'y'][2..] is not an error anymore, but an empty sequence. (Of course, ['x', 'y'][3..] is still an error.)

    • someString?substring(from, toExclusive) and someString?substring(from) are now deprecated; use this slicing expression instead: someString[from..<toExclusive] and someString[from..] . A warning if you are processing XML: Since slicing expressions work both for sequences and strings, and XML nodes in FTL are typically both sequences and strings at the same time, there the equivalent expression is someXmlNode?string[from..<toExclusive] and exp?string[from..] , because without the ?string it would slice the node sequence instead of the text value of the node.

    • If the incompatible_improvements in the FreeMarker configuration is set to at least 2.3.21, right-unbounded ranges become readable (like #list -able). Earlier they could only be used for slicing, and behaved like empty sequences otherwise.

  • New built-in, ?url_path : This is the same as the url built-in, except that it doesn't escape slash ( / ) characters. This meant to be used for converting paths (like paths coming from the OS or some content repository) that use slash (not backslash!) to a path the can be inserted into the path part of an URL.

  • New built-ins for string manipulation:

  • someString?keep_before(substring[, flags]) : More...

    • someString?keep_after(substring[, flags]) : More...

    • someString?remove_beginning(substring) : More...

    • someString?remove_ending(substring) : More...

    • someString?ensure_starts_with(substring[, substitution[, flags]]) : More...

    • someString?ensure_ends_with(substring) : More...

  • someString?number now recognizes all XML Schema number formats, like NaN , INF , -INF , plus the Java-native formats Infinity and -Infinity .

  • If incompatible_improvements in the FreeMarker configuration is set to at least 2.3.21, someNumber?c will return "INF" , "-INF" and "NaN" for positive/negative infinity and IEEE floating point Not-a-Number, respectively. These are the XML Schema compatible representations of these special values. Earlier it has returned what java.text.DecimalFormat did with US locale, none of which was understood by any (common) computer language.

  • New built-in: someString?boolean . This is for example useful for converting "true" and "false" strings coming from XML to real boolean values. More...

  • Date/time/date-time related changes:

  • Added new kind of date_format / datetime_format / time_format setting values: XML Schema formats, starting with "xs" and ISO 8601:2004 formats, starting with "iso" . The format string can be continued with various space (or _ ) separated options, like h or m or s or ms for setting shown accuracy, nz or fz for setting time zone offset visibility, and u or, fu for using UTC time zone . For example, to use ISO 8601 with minute precision and without the zone offset being shown, set the datetime_format setting to "iso m nz" , so then the output will be like 2014-09-03T20:56 . More...

    • Because anything that's accepted as date_format / datetime_format / time_format setting value can also be used with the ?string and ?date / ?time / ?datetime build-ins, you can use the new formats like someDate?string.xs and someString?date.xs . (For the "xs" and "iso" formats, _ can be used instead of space, which means that, for example, you can write lastModified?string.iso_m_u instead of the more verbose lastModified?string["iso m u"] .)

    • That "iso" and "xs" are now possible date_format / datetime_format / time_format setting values also means that such values can now be parsed too via ?date / ?time / ?datetime . The main application is with processing XML DOM-s, as there values are coming in as strings, and now you can do something like order.confirmDate?date.xs to convert them to real dates.

    • The ?iso_... built-ins are now deprecated in favor of the new setting values described above. They can be set as the default date/time/date-time format, seamlessly fit into the formatting architecture (and thus can parse strings too), and has more/better options ( ms always shows 3 millisecond digits, fz for forcing showing time zone offset).

    • If the "incompatible improvements" configuration setting is at least 2.3.21, the ?iso_... built-ins won't show time zone offset for java.sql.Time values anymore. Most databases store time values that aren't in any time zone, but just store hour, minute, second, and decimal second field values, so showing the time zone doesn't make sense. (Notable exceptions are PostgreSQL "time with time zone" columns, where mzTime?string.iso_fz could be used.)

    • Added ?is_time , ?is_datetime , ?is_date_only (should be called ?is_date , but that was already taken) and ?is_unknown_date_like to check the exact type of a date-like value.

    • ?is_date is now a deprecated name, use ?is_date_like instead. This is because ?is_date sounds like it checks if the value is a date without time part, but actually it also returns true for time, date-time, and unknown date-like values.

    • Added ?date_if_unknown , ?time_if_unknown and ?datetime_if_unknown built-ins, which mark a date-like value with some of the sub-types: date without time, time, or date-time, respectively. However, if the value already holds this information, the built-in has no effect. That is, it will never convert the sub-type of a value, it only adds the sub-type if it was unknown.

    • Bug fixed: ISO 8601 dates (via ?iso_... and "iso" format settings) now use proleptic Gregorian calendar for the years before 1582, rather than Julian calendar. This is (indirectly) required by the standard, and it's also how the default Sun/Oracle Java XML Schema date/time/dateTime parser works.

  • Error message quality improvements (targeting frequent support requests and some error message bugs):

  • Fixed glitch where if an #if had and #else or #elseif , the #if / #else / #elseif wasn't hidden in the FTL stack trace when the error was inside its nested block.

    • Some new context sensitive hints in undefined variable exception error messages.

    • Fixed unclosed directive error messages at end of file where the wrong unclosed directive name was reported

    • Better type error messages when accessing XML data (applies when wrapped with freemarker.ext.dom ):

  • Trying to use node.noSuchChildNodes on a place where scalar value is expected will explain that the problem is that you had no matches in the constructing XML query.

    • Trying to use node.multipleSuchChildNodes on a place where scalar value is expected will explain that the problem is that you had multiple matches in the constructing XML query.

    • Trying to use node.exactlyOneChildNode as number, date/time/date-time or boolean will explain that values coming from XML are always strings (text), and must be converted explicitly via ?number , ?boolean , ?date.xs , etc.

    • Trying to use obj.someMethod without () on a place where method value is not expected will recommend calling the method.

    • Trying to use methods like obj.getFoo or obj.isFoo without () on a place where method value is not expected will recommend using the obj.foo form.

    • Messages are now much more readable when rendered in environments that don't obey to line-breaks. (This often happens in improperly implemented HTML error pages and logs viewers.)

    • Better FTL instruction stack traces:

  • Error messages now contain up to 10 lines of FTL stack trace (unless it's on the top of a full FTL stack trace), because the FTL stack trace wasn't printed at all when the exception was a cause exception in a Java stack trace, or when only the value of getMessage() was printed instead of a stack trace.

    • The FTL stack trace is now more self explanatory as it contains more text labels.

    • Stack frames that belong to nestings are now marked differently, and are filtered out when the stack trace wouldn't fit into the error message otherwise.

    • Bug fixed: ?substring has thrown low level java.lang.IndexOutOfBoundsException -s instead of more descriptive TemplateModelException -s with FTL stack trace.

    • Bug fixed: Slicing with ranges sometimes thrown low level java.lang.IndexOutOfBoundsException -s instead of more descriptive TemplateModelException -s with FTL stack trace.

    • Bug fixed [402]: Fixed misleading parser error message when a directive called without its required parameters (like <#list> ) was reported as unknown directive.

    • Bug fixed [222]: Poor quality error message when someString[someIndex] fails with string index out of bounds.

    • Bug fixed [27]: Not very good quality error messages when #import -ing a template whose parsing fails.

  • New include directive option, ignore_missing=boolean . When this is set to true , and the template to include is missing, the error will be silently ignored, and nothing will be included.

  • The setting directive can now set the output_encoding setting.

  • New special variable: .locale_object . This is like .locale , except that it's a java.util.Locale object, not a string. This is handy if you want to pass the current locale to Java methods.

  • If incompatible_improvements in the FreeMarker configuration is set to at least 2.3.21, hash literals that repeat keys now only have the key once with ?keys , and only has the last value associated to that key with ?values . This is consistent with the behavior of hash[key] and how maps work in Java.

  • Bug fixed: ?is_enumerable has returned true for Java methods get from Java objects, despite that those values aren't <#list ...> -able. (This is actually a historical quirk of BeansWrapper , not a bug in ?is_enumerable , but now ?is_enumerable is aware of this exceptional case.)

  • Bug fixed [257]: The result value of ?matches wasn't "reentrant". For example, you couldn't list the matches inside another listing where you are also listing exactly the same result value (stored in a common variable), as they would consume from the same iterator. Most importantly, even accessing the ?size of the same result value has terminated the outer listing of the same value.

  • Bug fixed [229]: If you set incompatible_improvements to 2.3.21 (or higher), unclosed comments ( <#-- ... ) and #noparse -s won't be silently closed at the end of template anymore, but cause a parsing error instead.

Changes on the Java side

  • Added new Configuration constructor, Configuration(Version incompatibleImprovements) . This deprecates the vague Configuration() constructor, and makes using setIncompatibleImprovements(Version) needless in most cases. See an example here...

  • When setting the incompatible_improvements setting (like with the constructor above) to 2.3.21, two setting defaults change:

  • The default of the object_wrapper setting ( Configuration.getObjectWrapper() ) changes from ObjectWrapper.DEFAULT_WRAPPER to another almost identical DefaultObjectWrapper singleton, returned by new DefaultObjectWrapperBuilder(Version).build() . The new default object wrapper's "incompatible improvements" version is set to the same as of the Configuration . (See later regarding the 2.3.21 "incompatible improvements" of BeansWrapper and DefaultObjectWrapper ). Furthermore, the new default object wrapper doesn't allow changing its settings; setter methods will throw IllegalStateException . (If anything tries to call setters on the old default in your application, that's a dangerous bug that won't remain hidden now. As the old default is a singleton too, potentially shared by independently developed components, most of them expects the out-of-the-box behavior from it (and the others are necessarily buggy). Also, then concurrency glitches can occur (and even pollute the class introspection cache) because the singleton is modified after publishing.)

    • The default of the template_loader setting ( Configuration.getTemplateLoader() }) changes to null , which means that FreeMarker will not find any templates. Earlier the default was a FileTemplateLoader that used the current directory as the root. This was dangerous and fragile as you usually don't have good control over what the current directory will be. Luckily, the old default almost never looked for the templates at the right place anyway, so pretty much all applications had to set template_loader , so it's unlikely that changing the default breaks your application.
  • New BeansWrapper , DefaultObjectWrapper and SimpleObjectWrapper constructor that takes a Version incompatibleImprovements argument. This has the same role as the incompatible_improvements setting of the Configuration , but it applies to the ObjectWrapper instead. (As ObjectWrapper -s are often shared among multiple Configuration -s, so they can't use that setting of the Configuration .) In new or actively developed projects it's recommended to use Configuration.VERSION_2_3_21 now. The constructor without the Version parameter is now deprecated.

  • Safer and more memory-efficient way of managing singletons of DefaultObjectWrapper -s and BeansWrapper -s that are possibly shared by independently developed subsystems:

  • Instead of new DefaultObjectWrapper(...) and new BeansWrapper(...) , from now on you should use new DefaultObjectWrapperBuilder(version).build() and new BeansWrapperBuilder(version).build() . (The builder objects have properties (configuration settings) like BeansWrapper has, which specify the properties of the objects created.) The created objects are singletons (VM-wide, or at least Web-Application-wide) and read-only (means, non-configurable, hence safe to share). The main benefit of using these factories instead of creating new instances is that it allows FreeMarker to share the class introspection caches (an internal part of BeansWrapper -s/ DefaultObjectWrapper -s that is expensive to populate) among the returned instances. This allow sharing the caches (and the object wrappers) between components that aren't aware of each other and use FreeMarker internally.

    • Deprecated the static fields ObjectWrapper.DEFAULT_WRAPPER , BEANS_WRAPPER and SIMPLE_WRAPPER , because these ObjectWrapper -s are configurable (not read-only), and thus dangerous to use as singletons (a badly behaving 3rd party component can mess them up). Use the factories described above instead. They are also more flexible, as you can specify the desired incompatible-improvements version for them and various other BeansWrapper settings.

    • Deprecated all SimpleHash , SimpleCollection and SimpleSequence constructors that didn't take an ObjectWrapper argument, as they have usually used ObjectWrapper.DEFAULT_WRAPPER as the default, which itself is deprecated.

    • BeansWrapper , DefaultObjectWrapper and SimpleObjectWrapper now implements the freemarker.template.utility.WriteProtectable interface with which the configuration properties of the object wrapper can be set permanently to read-only by calling writeProtect() . An attempt to call a setter on a such ObjectWrapper will immediately cause IllegalStateException . (This is what's used for the singletons returned by the getInstance methods too; see earlier).

  • The value of the time_zone setting, when you specify it with a String (in a java.util.Properties object, or via <#setting ...> ) can now be "JVM default" to use the JVM default time zone. The JVM default is the default value of that setting anyway, but now you can state this explicitly, or restore this value if it was overridden earlier.

  • Added new configuration setting, sql_date_and_time_time_zone ( Configurable.setSQLDateAndTimeTimeZone(TimeZone) ). When this is set to non- null , the time zone used when dealing with java.sql.Date and java.sql.Time values will be this time zone instead of the value of the time_zone FreeMarker configuration setting. This is useful because JDBC will, usually, construct the Java Date objects so that they will show the year-month-day and hour-minute-seconds values from the database "as is" if you render them using the JVM default time zone. As time zone conversions for SQL date-only and SQL time-only values doesn't make much sense (unlike for SQL timestamps), you should certainly set this setting to the JVM default time zone ( TimeZone.getDefault() , or if you configure FreeMarker via java.util.Properties , as property value "JVM default"). The default value is null for backward compatibility. For more details see the JavaDoc of Configurable.setSQLDateAndTimeTimeZone(TimeZone) .

  • When configuring FreeMarker with java.util.Properties (typically, when the configuration is stored in a .properties file), for the settings where you could specify a fully qualified class name (most notably for the object_wrapper setting) now you can also specify constructor arguments and JavaBean property assignments. For example, now you can write object_wrapper=com.example.MyObjectWrapper(1, 2, exposeFields=true, cacheSize=5000) that's nearly equivalent with this Java code: obj = new com.example.MyObjectWrapper(1, 2); obj.setExposeFields(true); obj.setCacheSize(5000); object_wrapper = obj; . If you are using this new syntax (i.e., if you have parentheses after the class name, even if they are empty), and there's a builder class for the requested class, that will be automatically used. For example, object_wrapper=DefaultObjectWrapper(2.3.21) will create a DefaultObjectWrapperBuilder to build the final instance, thus the object wrapper will be a singleton instead of a new instance. The new syntax will also look for a public static INSTANCE field if there are 0 arguments and property assignments. For more details see the Java API documentation of Configuration.setSetting .

  • Template not found exceptions now explain that the template path is interpreted by a template loader, and show the toString of the TemplateLoader . The out-of-the-box TemplateLoader implementations now have an overridden toString to show the actual base directory and such details. Custom TemplateLoader implementations are encouraged to override toString .

  • Added Configuration.setSharedVariables(Map/*<String, Object>*/) for setting the shared variables from Spring IoC and other IoC solutions. The already existing Configuration.setSharedVariable(String, Object) isn't a JavaBean property so it was hard to use for that. Furthermore, the order in which Configuration.setObjectWrapper and Configuration.setSharedVariables are called doesn't mater (unlike in the case of Configuration.setSharedVariable ), which is essential in most IoC solutions.

  • Mostly concerning tool (like IDE plugin) authors:

  • ParseException -s now also store the end-location of the error, not just its start-location. This is useful if you want to underline the error in the source code, not just point at it.

    • Configuration.getSupportedBuiltInDirectiveNames() can be used to return the names of directives supported by FreeMarker.

    • TemplateExceptions now expose the position of the error (template name, line, column, end line, end column) similarly to ParseException -s. Where applicable, they also expose the blamed expression in its canonical FTL source form; this is mostly useful for InvalidReferenceException -s.

  • The concurrent performance of overloaded method lookups (from the cache) was improved under Java 5 and later.

  • The Version instances that are "incompatible improvements" break points are now available via constants like: Configuration.VERSION_2_3_21 .

  • From now on, if you try to set the "incompatible improvements" to greater than the current FreeMarker version, or less than 2.3.0, an IllegalArgumentException will be thrown. Thus, new Configuration(someVersion) not only activates the fixes up to that version, but ensures that the application will not run in an environment with an older FreeMarker version. (On an older FreeMarker version the improvements that you have requested aren't implemented yet, so you should get an exception.)

  • Added new configuration setting, show_error_tips , defaults to true . Sets if tips should be shown in error messages of errors arising during template processing.

  • Instead of overriding BeansWrapper.finetuneMethodAppearance (now deprecated), now you can use BeansWrapper.setMethodAppearanceFineTuner(MethodAppearanceFineTuner) , so you don't need to extend the object wrapper class to customize this aspect.

  • Added Configuration.getCoreDirecticeNames() which returns the names of all directives that are provided by FreeMarker. This can useful for IDE-s.

  • template_loader was added as possible configuration setting Properties key.

  • The standard CacheStorage implementations now have a getSize() method for monitoring the cache size. MruCacheStorage also has getSoftSize() and getStrongSize() .

  • Various smaller improvements in configuration setting errors messages.

  • With incompatible improvements 2.3.21 only: Empty ranges return Constants.EMPTY_SEQUENCE instead of an empty SimpleSequence . This is in theory backward compatible, as the API only promises to give something that implements TemplateSequenceModel .

  • FreeMarker now requires Java version has changed from 1.2 to 1.4.

  • Bugs fixed and improvements in overloaded method selection/invocation, but only if you create the BeansWrapper / DefaultObjectWrapper with constructor parameter Configuration.VERSION_2_3_21 (or if you are using Properties to configure FreeMarker, you can do that like object_wrapper=BeansWrapper(2.3.21) ), or if you have a Configuration with similar incompatible_improvements 2.3.21 and you leave the object_wrapper setting on its default value. There's a little chance that because of these changes, a different overloaded method will be chosen than before, or even that ambiguity errors will arise where earlier they didn't (although the opposite is far more frequent), hence the fixes aren't automatically activated. But the fix mostly only effect calls that were failing or have chosen then wrong method earlier, so it's recommended to activate it for projects that are still actively developed. This fix includes numerous changes:

  • Earlier, null argument values has only matched overloaded methods where the corresponding parameter had Object type, not a subclass of it. That's clearly a bug. Now it considers all overloads where the parameter type is non-primitive, and just like the Java language, it choses the one with the most specific type among them. This is the most important fix, and also the most risky one regarding backward-compatibility. Like if you have m(Object o) and m(String s) in a Java class, earlier for a m(null) call in the template it has chosen m(Object o) , but now it will choose m(String s) instead (because String is also null -able and is more specific than Object ). Furthermore, if you also had m(File f) in the same class, now it will cause an ambiguity exception, since the specificity of File and String can't be compared (same rule as under Java language), while earlier that wasn't a problem as only m(Object o) was seen as applicable.

    • The behavior of numbers with overloaded method selection was heavily reworked:
  • If possible, it now always choses the overload where overflow and truncation to integer (like 1.5 to 1) is avoided. Among the methods where no such critical loss occurs, it choses the overload with the least risk of precision loss (unless other conditions with higher priority suggest otherwise). Earlier, the method selection was prone to do choices that led to overflow or precision loss, especially when the parameter was a literal with decimals.

    • Overloaded method call can now convert to non-primitive numerical types, like a Byte or byte value is automatically converted to Integer if the parameter type is Integer . (This has always worked for non-overloaded methods.) Earlier where such conversion was needed, the method wasn't seen seen applicable.

    • Method selection is now not only based on the type of the wrapped number, but also on its value. For example, a Long with value 1 is now seen as compatible with a method with parameter type int or short or byte , as 1 can be stored in those without loss. This is important as unlike in Java language, in FTL you doesn't have strict control over the numerical types (the type of the wrapped number, actually), as FTL has no type declarations. (If multiple compatible methods are available, it will still try to chose the one with the same or bigger numerical type.)

    • Conversion from/to BigInteger is now supported.

    • Method choice ambiguity errors now occur much less often. Ambiguities was and are resolved by selecting the compatible methods then choosing the one with the most specific parameter types among them. The changes are:

  • When comparing the overall specificity of two parameter lists: Earlier the parameter list seen as more specific was the one that had some parameters that won in specificity, and if both had such parameters then it was an ambiguity. Now it's enough if a method has more such parameters where it's a better match than the other has, or if the two methods are still equal, if it has the first better matching parameter. This can lead to choices that seem arbitrary (but are still deterministic), but as there's no automated way of discovering method selection ambiguities in templates (unlike in Java source code, where they will be detected during compilation), especially as overloaded selection has to rely on the runtime type of the values which even make proper testing hard, this was considered to be a better compromise than throwing an exception whenever the choice of the method is not obvious. Also note that in fact this mechanism is more complicated than just counting the "winner" parameter positions for each methods, as certain kind of wins are stronger than any number of the others: wins where the other possibility is risking of substantial mantissa precision loss are the strongest (like dropping decimals versus not to), wins where the primitive type wins over the boxed class is the weakest (like int versus Integer ), subclassing wins (like String versus Object ) are between these two.

    • When comparing the specificity of two parameters types at the same parameter position: The algorithm now considers a primitive type as more specific that its corresponding boxing class (like int is considered to be more specific than Integer ).

    • There was a bug with overloaded varargs methods of different parameter counts, where sometimes the last parameters of the compared methods was ignored, which is taking away a potential deciding factor and thus can lead to ambiguity error. Whether this happened depends on the order in which the Java reflection API has returned the methods, which is undocumented and known to change at least after some Java updates, breaking the application.

    • When comparing the specificity of two array types, until now they were seen as equal. Now the component types are compared, and then that with the less specific component type is preferred. For example, among f(String[]) and f(Object[]) , the last will always win. This might sounds controversial, but as we can't efficiently tell the common type of all the items in a sequence or List , and so we don't know if both arrays are indeed valid targets, we go for the safest choice.

    • FTL sequence values (like Java List -s or FTL [x, y, ...] constants) were not seen as applicable to a parameter with array type if there were multiple overloaded methods with the same number of parameters but with different types on the position of the array parameter. That is, if you had f(String[]) and f(String) in Java, then f(['foo', 'bar']) in the template reported no compatible overloads. Now it will choose f(String[]) . Note that if there's also an f(List) or even an f(Collection) , it will prefer those over arrays. (For consistency, this conversion will work even if the argument is a List that come directly from Java (as opposed to be created inside FTL), i.e., when it was wrapped then unwrapped to the original List object.) For a multidimensional array parameter type, this conversion works recursively, so you can pass in a sequence-of-sequences as the argument.

    • FTL sequence values that wrapped a Java array (when FreeMarker was also aware of that via AdapterTemplateModel or WrapperTemplateModel ) were not seen as applicable to a parameter with List type if there were multiple overloaded methods with the same number of parameters but with different types on the position of the array parameter. So this is pretty much like the issue described in the previous point, but for array to List conversion. The array to List conversion will be avoided if possible, but it will be attempted if there's no other alternative. Note that unlike with List to array conversions, here FreeMarker can't cope with multi-dimensional lists, that is, an array-of-arrays won't be converted to a List -of- List -s.

    • FTL string to Java char / Character conversion now works for overloaded method parameters; earlier it has worked for non-overloaded methods only. If the string length is 1, it will be seen as compatible with parameters with char or Character type.

    • Decreased the chance of choosing the wrong target Java type when unwrapping multi-typed FTL values: When unwrapping a parameter value that implements multiple FTL types (e.g. string and hash) but doesn't implement AdapterTemplateModel or WrapperTemplateModel , a decision has to be made if to what Java type the value should be unwrapped to (e.g. to String or to Map ). In earlier versions that decision was made based on the most specific common super type of the parameters types of the given parameter position. However, it's quite common that the common super type is too generic, usually Object . Now BeansWrapper stores "type flags" for each parameter position of overloaded methods, from which it can tell whether a potential target Java type occurs in any of the overloads on the given parameter position.

    • In many cases, less specific hint class was used for unwrapping than that was possible within the limitations of the applied hint generation algorithm. (The unwrapping hint has influence when there's an ambiguity regarding how to create a Java object form an FTL value. In the vast majority of the cases, a too generic hint has no effect.) (This is a highly technical topic. The way it works is that a single common unwrapping hint class is chosen for a given argument position shared by the overloads that has the same number of parameters, and that hint class has to be as specific as possible while it must fit all those parameter types. The issue with the too generic hints had several instances: (a) When the most specific common class/interface of two same-position parameter types was searched, if there was multiple common classes/interfaces that had no relationship (this is always at most one class and one or more unrelated interfaces), due to the ambiguity it has felt back to using Object as the unwrapping hint. Now if there's a non- Object class among them in such case, it will be chosen as the hint (i.e., we ignore the common interfaces). Otherwise if only a single interface remains by removing Cloneable , Serializable , and Comparable (in that order), that will be chosen. Only then it falls back to Object . (b) The common most specific class of a primitive type and the corresponding boxing class was sometimes Object instead of the boxing class. This has depended on Java's internal ordering of the methods, and so were quite unpredictable, like the result could change after upgrading Java under the application. (c) The common superclass of a numerical primitive value and a numerical non-primitive value was always Object , now if they are a primitive-boxing class pair, then it's the boxing class, otherwise it's Number . (d) If the varags parameter position was not the same in all the overloaded varargs methods, sometimes some varargs arguments where unwrapped with too generic hints. When this happened was unpredictable as it depended on Java's internal method ordering again.)

    • When unwrapping method call arguments before calling a Java method, if the argument was an AdapterTemplateModel and the target parameter type was primitive, AdapterTemplateModel.getAdaptedObject(Class hint) has received the primitive type of the target parameter (like int instead of Integer ) as the hint. This did not make sense since getAdaptedObject can only return Object -s, not primitive values. Yet, BeansWrapper has expected the returned value to be of the primitive type, otherwise it has discarded it. Exactly the same problem occurs with WrapperTemplateModel . Thus, ultimately, if the target parameter type was primitive and some of these interfaces were implemented, their return value was always discarded and FreeMarker has felt back to other means of unwrapping. Now BeansWrapper always passes in and expects the boxing type (like Integer ) instead of the primitive type.

  • Bug fixed [373]: These are three bugs actually, which can cause problems when FreeMarker re-loads a template because of a charset override with <#ftl encoding="..."> : First, if the template loader was a URLTemplateLoader , when TemplateLoader.getReader() was called for the second time, and the Reader returned for the first time was already used, it might returned an empty or corrupted template, depending on the backing URL implementation. Secondly, when FreeMarer has decided if a template file has to be re-loaded because its encoding specified with <#ftl encoding="..."> differs from the encoding used for loading the template first, it has used case-sensitive comparison, thus often re-loaded needlessly (like "UTF-8" and "utf-8" mean the same). Now this comparison is case-insensitive. Last not least, when retrying with the second charset, the TemplateCache has forgotten to close the first Reader , which can be a handle leak.

  • Bug fixed [411]: Invalid and redundant execution environment names from the OSGi bundle manifest were removed. It looks like this now: Bundle-RequiredExecutionEnvironment: J2SE-1.5, J2SE-1.4 . That is, we prefer (and compile against) 1.5, but only require 1.4.

  • Bug fixed [409]: SimpleHash 's internal Map is concurrently modified on a non-safe way when getting length-1 String key that exists but maps to null . This operation will accidentally add a Character key to the internal map, which is not a thread-safe operation.

  • Bug fixed [273]: TemplateLoader -s that use java.net.URLConnection -s should set URLConnection.useCaches to false , or else it won't detect template caches on certain configurations.

  • URLTemplateLoader and its subclasses and WebApplicationTemplateLoader now has a setURLConnectionUsesCaches(Boolean) method. It's recommended to set this property to false from its default backward compatible value, null . As FreeMarker has its own template cache with its own update delay setting ( template_update_delay , Configuration.setTemplateUpdateDelay(int) ), it shouldn't cause performance problems. The null value will leave the caching of the java.net.URLConnection on its default, which is usually true .

    • If incompatible_improvements is set to 2.3.21 (or higher) and templates are loaded through Configuration.getTemplate , and the TemplateLoader in use has URLConnectionUsesCaches left on null , it will behave as if it was set to false . Note that this incompatible_improvements trick only works if the template is loaded through Configuration.getTemplate (or TemplateCache ).
  • Bug fixed: When changing the properties of DefaultObjectWrapper or BeansWrapper that influenced class introspection results (like exposureLevel or exposeFields ), the introspection cache wasn't cleared, and thus returned stale information for the classes that were introspected before those properties were changed. Now changing such properties always clears the introspection caches.

  • Bug fixed: Constants used for empty sequence, empty hash, empty collection and empty iterator weren't Serializable .

  • Bug fixed [300]: Logger class availability check was incorrect in that it has checked the availability of logger libraries with the thread context class loader, then later it tried to link to them with the defining class loader of the FreeMarker classes. With some class loader setups (notably under Netbeans sometimes) this led to ClassDefNotFoundError -s that made FreeMarker unusable. (The check itself was also not very durable, as it didn't expected LinakeError -s, only ClassNotFoundException .)

  • Bug fixed: ClassUtil.forName , used inside FreeMarker everywhere to resolve class names to classes, if the thread context class loader is null , no longer tries to load with the bootstrap class loader before loading with the defining class loader of FreeMarker.

  • Bug fixed [311]: TemplateBooleanModel.TRUE and FALSE are now serializable

  • Bug fixed: Various issues in Version class, such as wrong hashCode , possible concurrency glitches, and acceptance of some malformed version strings.

  • Bug fixed [414]: Eclipse debug mode running was suspended during FreeMarker static initialization on the JRebel availability checked if JRebel wasn't available.

Other changes

  • The license has changed from our proprietary BSD-Style license to the well know "Apache License, Version 2.0". Furthermore, the copyright owner has changed from "Visigoth Software Society" (which was founded by Jonathan Revusky) to the three main FreeMarker 2 developers/contributors, "Attila Szegedi, Daniel Dekany, and Jonathan Revusky". See the new license here.

  • The required minimum Java version was raised from 1.2 to 1.4. FreeMarker will not work on Java 1.2 or 1.3.

  • Many smaller improvements and fixes in the Manual and API JavaDocs.