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 asstart..!end
). More... -
Added length limited ranges:
start..*length
: For example,10..*4
gives[10, 11, 12, 13]
,10..*-4
gives[10, 9, 8, 7]
, and10..*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 strings
, or less ifs
is shorter than 10 characters, you can uses[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)
andsomeString?substring(from)
are now deprecated; use this slicing expression instead:someString[from..<toExclusive]
andsomeString[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 issomeXmlNode?string[from..<toExclusive]
andexp?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 theurl
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, likeNaN
,INF
,-INF
, plus the Java-native formatsInfinity
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 whatjava.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, likeh
orm
ors
orms
for setting shown accuracy,nz
orfz
for setting time zone offset visibility, andu
or,fu
for using UTC time zone . For example, to use ISO 8601 with minute precision and without the zone offset being shown, set thedatetime_format
setting to"iso m nz"
, so then the output will be like2014-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 likesomeDate?string.xs
andsomeString?date.xs
. (For the"xs"
and"iso"
formats,_
can be used instead of space, which means that, for example, you can writelastModified?string.iso_m_u
instead of the more verboselastModified?string["iso m u"]
.) -
That
"iso"
and"xs"
are now possibledate_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 likeorder.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 forjava.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, wheremzTime?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 returnstrue
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
orobj.isFoo
without()
on a place where method value is not expected will recommend using theobj.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 leveljava.lang.IndexOutOfBoundsException
-s instead of more descriptiveTemplateModelException
-s with FTL stack trace. -
Bug fixed: Slicing with ranges sometimes thrown low level
java.lang.IndexOutOfBoundsException
-s instead of more descriptiveTemplateModelException
-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 totrue
, and the template to include is missing, the error will be silently ignored, and nothing will be included. -
The
setting
directive can now set theoutput_encoding
setting. -
New special variable:
.locale_object
. This is like.locale
, except that it's ajava.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 ofhash[key]
and how maps work in Java. -
Bug fixed:
?is_enumerable
has returnedtrue
for Java methods get from Java objects, despite that those values aren't<#list ...>
-able. (This is actually a historical quirk ofBeansWrapper
, 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 vagueConfiguration()
constructor, and makes usingsetIncompatibleImprovements(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 fromObjectWrapper.DEFAULT_WRAPPER
to another almost identicalDefaultObjectWrapper
singleton, returned bynew DefaultObjectWrapperBuilder(Version).build()
. The new default object wrapper's "incompatible improvements" version is set to the same as of theConfiguration
. (See later regarding the 2.3.21 "incompatible improvements" ofBeansWrapper
andDefaultObjectWrapper
). Furthermore, the new default object wrapper doesn't allow changing its settings; setter methods will throwIllegalStateException
. (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 tonull
, which means that FreeMarker will not find any templates. Earlier the default was aFileTemplateLoader
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 settemplate_loader
, so it's unlikely that changing the default breaks your application.
-
-
New
BeansWrapper
,DefaultObjectWrapper
andSimpleObjectWrapper
constructor that takes aVersion
incompatibleImprovements
argument. This has the same role as theincompatible_improvements
setting of theConfiguration
, but it applies to theObjectWrapper
instead. (AsObjectWrapper
-s are often shared among multipleConfiguration
-s, so they can't use that setting of theConfiguration
.) In new or actively developed projects it's recommended to useConfiguration.VERSION_2_3_21
now. The constructor without theVersion
parameter is now deprecated. -
Safer and more memory-efficient way of managing singletons of
DefaultObjectWrapper
-s andBeansWrapper
-s that are possibly shared by independently developed subsystems:-
Instead of
new DefaultObjectWrapper(...)
andnew BeansWrapper(...)
, from now on you should usenew DefaultObjectWrapperBuilder(version).build()
andnew BeansWrapperBuilder(version).build()
. (The builder objects have properties (configuration settings) likeBeansWrapper
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 ofBeansWrapper
-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
andSIMPLE_WRAPPER
, because theseObjectWrapper
-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 otherBeansWrapper
settings. -
Deprecated all
SimpleHash
,SimpleCollection
andSimpleSequence
constructors that didn't take anObjectWrapper
argument, as they have usually usedObjectWrapper.DEFAULT_WRAPPER
as the default, which itself is deprecated. -
BeansWrapper
,DefaultObjectWrapper
andSimpleObjectWrapper
now implements thefreemarker.template.utility.WriteProtectable
interface with which the configuration properties of the object wrapper can be set permanently to read-only by callingwriteProtect()
. An attempt to call a setter on a suchObjectWrapper
will immediately causeIllegalStateException
. (This is what's used for the singletons returned by thegetInstance
methods too; see earlier).
-
-
The value of the
time_zone
setting, when you specify it with aString
(in ajava.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 withjava.sql.Date
andjava.sql.Time
values will be this time zone instead of the value of thetime_zone
FreeMarker configuration setting. This is useful because JDBC will, usually, construct the JavaDate
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 viajava.util.Properties
, as property value "JVM default"). The default value isnull
for backward compatibility. For more details see the JavaDoc ofConfigurable.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 theobject_wrapper
setting) now you can also specify constructor arguments and JavaBean property assignments. For example, now you can writeobject_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 aDefaultObjectWrapperBuilder
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 staticINSTANCE
field if there are 0 arguments and property assignments. For more details see the Java API documentation ofConfiguration.setSetting
. -
Template not found exceptions now explain that the template path is interpreted by a template loader, and show the
toString
of theTemplateLoader
. The out-of-the-boxTemplateLoader
implementations now have an overriddentoString
to show the actual base directory and such details. CustomTemplateLoader
implementations are encouraged to overridetoString
. -
Added
Configuration.setSharedVariables(Map/*<String, Object>*/)
for setting the shared variables from Spring IoC and other IoC solutions. The already existingConfiguration.setSharedVariable(String, Object)
isn't a JavaBean property so it was hard to use for that. Furthermore, the order in whichConfiguration.setObjectWrapper
andConfiguration.setSharedVariables
are called doesn't mater (unlike in the case ofConfiguration.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 toParseException
-s. Where applicable, they also expose the blamed expression in its canonical FTL source form; this is mostly useful forInvalidReferenceException
-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 totrue
. 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 useBeansWrapper.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 settingProperties
key. -
The standard
CacheStorage
implementations now have agetSize()
method for monitoring the cache size.MruCacheStorage
also hasgetSoftSize()
andgetStrongSize()
. -
Various smaller improvements in configuration setting errors messages.
-
With incompatible improvements 2.3.21 only: Empty ranges return
Constants.EMPTY_SEQUENCE
instead of an emptySimpleSequence
. This is in theory backward compatible, as the API only promises to give something that implementsTemplateSequenceModel
. -
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 parameterConfiguration.VERSION_2_3_21
(or if you are usingProperties
to configure FreeMarker, you can do that likeobject_wrapper=BeansWrapper(2.3.21)
), or if you have aConfiguration
with similarincompatible_improvements
2.3.21 and you leave theobject_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 hadObject
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 havem(Object o)
andm(String s)
in a Java class, earlier for am(null)
call in the template it has chosenm(Object o)
, but now it will choosem(String s)
instead (becauseString
is alsonull
-able and is more specific thanObject
). Furthermore, if you also hadm(File f)
in the same class, now it will cause an ambiguity exception, since the specificity ofFile
andString
can't be compared (same rule as under Java language), while earlier that wasn't a problem as onlym(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
orbyte
value is automatically converted toInteger
if the parameter type isInteger
. (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 value1
is now seen as compatible with a method with parameter typeint
orshort
orbyte
, as1
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
versusInteger
), subclassing wins (likeString
versusObject
) 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 thanInteger
). -
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[])
andf(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 orList
, an 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 hadf(String[])
andf(String)
in Java, thenf(['foo', 'bar'])
in the template reported no compatible overloads. Now it will choosef(String[])
. Note that if there's also anf(List)
or even anf(Collection)
, it will prefer those over arrays. (For consistency, this conversion will work even if the argument is aList
that come directly from Java (as opposed to be created inside FTL), i.e., when it was wrapped then unwrapped to the originalList
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
orWrapperTemplateModel
) were not seen as applicable to a parameter withList
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 toList
conversion. The array toList
conversion will be avoided if possible, but it will be attempted if there's no other alternative. Note that unlike withList
to array conversions, here FreeMarker can't cope with multi-dimensional lists, that is, an array-of-arrays won't be converted to aList
-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 withchar
orCharacter
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
orWrapperTemplateModel
, a decision has to be made if to what Java type the value should be unwrapped to (e.g. toString
or toMap
). 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, usuallyObject
. NowBeansWrapper
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 removingCloneable
,Serializable
, andComparable
(in that order), that will be chosen. Only then it falls back toObject
. (b) The common most specific class of a primitive type and the corresponding boxing class was sometimesObject
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 alwaysObject
, now if they are a primitive-boxing class pair, then it's the boxing class, otherwise it'sNumber
. (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 (likeint
instead ofInteger
) as the hint. This did not make sense sincegetAdaptedObject
can only returnObject
-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 withWrapperTemplateModel
. 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. NowBeansWrapper
always passes in and expects the boxing type (likeInteger
) 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 aURLTemplateLoader
, whenTemplateLoader.getReader()
was called for the second time, and theReader
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, theTemplateCache
has forgotten to close the firstReader
, 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 internalMap
is concurrently modified on a non-safe way when getting length-1String
key that exists but maps tonull
. This operation will accidentally add aCharacter
key to the internal map, which is not a thread-safe operation. -
Bug fixed [273]:
TemplateLoader
-s that usejava.net.URLConnection
-s should setURLConnection.useCaches
tofalse
, or else it won't detect template caches on certain configurations.-
URLTemplateLoader
and its subclasses andWebApplicationTemplateLoader
now has asetURLConnectionUsesCaches(Boolean)
method. It's recommended to set this property tofalse
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. Thenull
value will leave the caching of thejava.net.URLConnection
on its default, which is usuallytrue
. -
If
incompatible_improvements
is set to 2.3.21 (or higher) and templates are loaded throughConfiguration.getTemplate
, and theTemplateLoader
in use hasURLConnectionUsesCaches
left onnull
, it will behave as if it was set tofalse
. Note that thisincompatible_improvements
trick only works if the template is loaded throughConfiguration.getTemplate
(orTemplateCache
).
-
-
Bug fixed: When changing the properties of
DefaultObjectWrapper
orBeansWrapper
that influenced class introspection results (likeexposureLevel
orexposeFields
), 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 expectedLinakeError
-s, onlyClassNotFoundException
.) -
Bug fixed:
ClassUtil.forName
, used inside FreeMarker everywhere to resolve class names to classes, if the thread context class loader isnull
, no longer tries to load with the bootstrap class loader before loading with the defining class loader of FreeMarker. -
Bug fixed [311]:
TemplateBooleanModel.TRUE
andFALSE
are now serializable -
Bug fixed: Various issues in
Version
class, such as wronghashCode
, 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.