Date of release: 2015-03-01
Note that since 2.3.22 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.22 "incompatible improvements" (it's always clearly indicated), because they could, with very small chance, break existing applications. For actively maintained applications it's probably better to allow them. See how to set "incomplatible improvements" here.
Changes on the FTL side
-
New built-ins:
api
andhas_api
.value?api
provides access to the API (usually, the Java API) ofvalue
, likevalue?api.someJavaMethod()
orvalue?api.someBeanProperty
), if the value itself supports exposing its API. This meant to be used rarely, when you need to call a Java method of an object, but the by-design simplistic view of the value that FreeMarker exposes to the templates hides that, and there's no equivalent built-in either. For example, when you put aMap
into the data-model (and you are using the default object wrapper),myMap.myMethod()
in a template basically translates to((Method) myMap.get("myMethod")).invoke(...)
in Java, thus you can't callmyMethod
. If, however, you writemyMap?api.myMethod()
instead, that meansmyMap.myMethod()
in Java. Similarly,myMap?api.myProperty
translates tomyMap.getMyProperty()
in Java, instead of tomyMap.get("myProperty")
.If you can, rely on the capabilities of the FTL types and the related built-ins as far as possible. Using
?api
is only the last resort.Using
?api
also happens to offer a workaround for the lack of non-String
Map
key support in FTL's[]
operator (as inmyMap[key]
), because now you can writemyMap?api.get(nonStringKey)
.?api
is not enabled by default and isn't available for all values. See more here... -
Identifiers (like
someVariable
) can now contain minus (-
), dot (.
), and colon (:
) at any position, but those characters must be escaped with a preceding backslash (\
), or else they would be interpreted as operators. For example, to read the variable whose name is "data-id", the correct expression isdata\-id
, asdata-id
would be interpreted as "data minus id". This also works for named macro parameters, which is useful when you want to accept arbitrary HTML attributes in a catch-all parameter, like in<@box class="someCssClass" data\-id=product.id />
. (When you enumerate the catch-all parameter names inside the macro, the key string you get is"data-id"
without\
of course.) -
Added
?lower_abc
and?upper_abc
. This converts1
,2
,3
, etc., to the string"a"
,"b"
,"c"
, etc. (or for"A"
,"B"
,"C"
, etc.). When reaching"z"
, it continues like"aa"
,"ab"
, etc. This is the same logic that you can see in column labels in spreadsheet applications (like Excel or Calc). More details... -
Added
?keep_before_last
and?keep_after_last
. Example:"foo.bar.txt"?keep_before_last(".")
returns"foo.bar"
,"foo.bar.txt"?keep_after_last(".")
returns"txt"
. (These work like?keep_before
and?keep_after
, but those look for the first occurrence of the separator.) -
Added many missing UNICODE letters and digits to the set of legal identifier characters, like Korean letters (bug fixed: [129])
-
Error message quality improvements:
-
Several improvements when calling custom JSP tags; see them in its own section later.
-
Bug fixed: When localized lookup or template acquisition has kicked in, error messages have still quoted the name used for requesting the template, rather that the actual template source name (like
foo.ftl
instead offoo_en.ftl
, when the template was get asfoo.ftl
, but behind the scenes was loaded fromfoo_en.ftl
). -
"Template not found" errors are now more detailed, giving hints about accidentally using
\
instead of/
, or backing out of theTemplateLoader
's root directory. -
The
#setting
directive gives more helpful error message when the setting name is not recognized, and lists the allowed setting names or a correction suggestion. -
When a bad special variable name (
.name
) is encountered, the list of available names is shown in the error message. -
When
Map.get
orMap.containsKey
of a wrappedMap
throws aClassCastException
orNullPointerException
, the error will point to the causing FTL expression (with some explanation), rather than bubbling up as low level runtime error.
-
Changes on the Java side
-
Object wrapping improvements:
-
DefaultObjectWrapper
, only with itsincompatible_improvements
set to 2.3.22 (see how here...), or more precisely, with its newuseAdaptersForContainers
setting set totrue
(which defaults totrue
whenincompatible_improvements
is set to 2.3.22): It doesn't copyMap
-s,List
-s, and arrays anymore when wrapping them intoTemplateModel
-s (which is the interface through with templates access all values), just wraps them into thinTemplateModel
adapters, that will reach the original object for all operations. The wrapped values will be instances of the newDefaultMapAdapter
,DefaultListAdapter
andDefaultArrayAdapter
classes, instead of the legacy (copying)SimpleHash
andSimpleSequence
classes. (Note that many projects use pureBeansWrapper
instead ofDefaultObjectWrapper
, which has always used the adapter approach, albeit a different implementation of it. As the shortcomings ofDefaultObjectWrapper
are fixed now, it's always recommended overBeansWrapper
, asBeansWrapper
gives quite confusing multi-typed values and is substantially slower.)While keeping backward compatibility as much as possible was an important factor in this change, this is a quite deep change, so you may want to review the consequences and reasons here... (But again, this change is not active by default, so merely updating FreeMarker wont risk the stability of existing applications)
-
Added
TemplateMethodModelEx BeansWrapper.wrap(Object object, Method method)
for wrapping methods without wrapping their parent object and without going through overloaded method selection on invocation time. -
Bug fixed [372]:
ClassCastException
when aSortedMap
(typically, aTreeMap
) is wrapped withDefaultObjectWrapper
and then a 1 character long string is get from it that doesn't exist. To fix the issue, if the wrappedMap
is aSortedMap
and it's wrapped byDefaultObjectWrapper
, it won't try to fall back to aCharacter
key after with theString
key has gotnull
. (This change should be backward compatible, because when aSortedMap
hasCharacter
keys, the initial attempt withString
key causesClassCastException
, thus, suchSortedMap
-s were never usable as FTL hashes.) -
Bug fixed [368]: Only with
incompatible_improvements
set to 2.3.22 or with its newuseAdaptersForContainers
setting set totrue
: Key order and other behavioral peculiarities of "custom"Map
types isn't lost anymore. The same stands forList
-s too. -
Added new setting,
forceLegacyNonListCollections
. This only matters whenuseAdaptersForContainers
istrue
. Then, unless you set this totrue
,java.util.Collection
-s that aren'tList
-s (likeSet
-s) will continue usingSimpleSequence
(i.e., the copying approach) instead of the adapter approach. The default isfalse
, at least untilincompatible_improvements
2.4.0, becauseSimpleSequence
gave indexed access to these non-List
-s, like inmySet[2]
, which is strange but some existing templates may utilize this, even if only accidentally. WithforceLegacyNonListCollections
set tofalse
, indexed access won't be possible forSet
-s and such anymore (nor will?first
and?last
work, but?size
will still do), so you may want to retest old templates. On the other hand, you get the advantages of the adapter approach. Hence, in new projects it's highly recommended to setforceLegacyNonListCollections
tofalse
. (The adapter approach is implemented byDefaultNonListCollectionAdapter
.) -
Added new, experimental FTL type interface,
freemarker.template.TemplateCollectionModelEx
, which adds thesize()
,isEmpty()
, andboolean contains(TemplateModel)
methods to theTemplateCollectionModel
interface. This was added because when wrappingjava.util.Collections
these extra capabilities area available anyway, but FTL couldn't tap on them till now. While the exact interface details are marked as experimental, the feature itself is already utilized for?size
when setting theforceLegacyNonListCollections
property ofDefaultObjectWrapper
tofalse
(see earlier). -
Added new experimental interface,
freemarker.template.ObjectWrapperAndUnwrapper
. This extendsObjectWrapper
with unwrapping functionality. This functionality has already existed for a long time inBeansWrapper
and its subclasses, like inDefaultObjectWrapper
, but it wasn't "factored out" into its own published interface that otherObjectWrapper
-s could implement. This is useful forTemplateModel
implementations that don't want to require aBeansWrapper
(or its subclass), only the availability of the unwrapping functionality. -
Added new experimental interfaces to implement
?api
(see it in the FTL section):TemplateModelWithAPISupport
,ObjectAPIWrapper
,RichObjectWrapper
. Note that while the interfaces are experimental,?api
itself isn't.
-
-
FreemarkerServlet
improvements:-
FreemarkerServlet
now supports custom JSP EL functions (defined in TLD-s withfunction
XML elements). Earlier it has ignored them. The custom EL function can be called like a Java method, for example:<#assign u=JspTaglibs["/WEB-INF/utils.tld"]> ... ${u.truncate(title, 25)}
. -
Bug fixed: Error message was unhelpful when there was a type mismatch between the actual and the expected type of a custom tag parameter. This was a very frequent problem of users who call JSP taglibs from FTL (the typical "java.lang.IllegalArgumentException: argument type mismatch", without any FTL context). Now it's a proper error with explanation, solution tip, and FTL error position/quotation.
-
RFE resolved [113] [114]:
FreemarkerServlet
can now discoverMETA-INF/**/*.tld
-s that are visible for the class loader but aren't inWEB-INF/lib/*.jar
-s. For this feature to be active, you must setup the extra TLD lookup with theMetaInfTldSources
and/orClasspathTlds
FreemarkerServlet
init-params (see the Java API documentation ofFreemarkerServlet
for the description of these). For example, if you run your application from Eclipse with an embedded Servlet container, and thus the tag library jar-s aren't on the standard locations but are in the classpath like any other dependencies, now you can just write:<init-param> <param-name>MetaInfTldSources</param-name> <param-value>classpath</param-value> </init-param>
and then all the
META-INF
directories that are visible for the class loader will be searched for TLD-s. -
MetaInfTldSources
andClasspathTlds
can also be appended to or replaced by the values of Java system propertiesorg.freemarker.jsp.metaInfTldSources
andorg.freemarker.jsp.classpathTlds
, respectively. Thus one can adjust these in the Eclipse run configuration without modifying theweb.xml
. (See the Java API documentation ofFreemarkerServlet
for more.) -
FreemarkerServlet
now recognizes theorg.eclipse.jetty.server.webapp.ContainerIncludeJarPattern
servlet context attribute, and adds entries toMetaInfTldSources
(introduced above) from it. -
Added
protected FreemarkerServlet.createTaglibFactory()
to allow fine tuning the settings of theTaglibFactory
. It now have a few setters, likesetObjectWrapper
,setMetaInfTldSource
, etc. -
Added new servlet init-param,
BufferSize
. This sets the buffer size viaHTTPServletResponse.setBufferSize()
if the response state still allows that, ignores it otherwise. -
The
TemplatePath
servlet init-param now supports a new kind of path, that looks likeclasspath:com/example/myapp/templates
. This is similar to the oldclass://com/example/myapp/templates
, but it uses the Thread Context Class Loader of the thread that initializesFreemarkerSerlvet
, and thus will work even iffreemarker.jar
is not local to the web application.class://
has the problem that it uses the defining class loader ofFreemarkerSerlvet
itself (or of its subclass). -
If
incompatible_improvements
is set to 2.3.22 (or higher), theTemplatePath
servlet init-param supports specifying multiple comma separated paths inside[...]
, like<param-value>[ WEB-INF/templates, classpath:com/example/myapp/templates ]</param-value>
. This internally creates afreemarker.cache.MultiTemplateLoader
. -
Added new servlet
init-param
,ExceptionOnMissingTemplate
. Setting this totrue
changes the behavior on template-not-found errors to similar to what you experience with other kind of template exceptions (a HTTP 500 "Internal Server error" response on most setups). When it'sfalse
(the legacy behavior), you only get a HTTP 404 "Not found". While that's also how JSP views work, this turns out to be a problem, because some frameworks give 404 to the visitor too if the MVC view gives 404. But to get to the point where you forward to the MVC View, the visitor had to visit a valid URL, only that page misses its View, so its broken on the server side, so it should be a 500. -
Added new overridable method:
FreemarkerServlet.createDefaultObjectWrapper()
. This can be used for whatcreateObjectWrapper()
is usually overridden for, but without unwillingly disabling the processing of the related init-params (like ofobject_wrapper
). -
Improved (or fixed) error logging: Now logs will always get into FreeMarker's own log, not only into the servlet container log. Also, earlier template-not-found and template parsing error details logs were sometimes lost, depending on the servlet container.
-
Bug fixed, only active with
incompatible_improvements
set to 2.3.22 (or higher): Some kind of values, when put into the JSP page scope (via#global
or via the JSPPageContext
API) and later read back with the JSPPageContext
API (typically in a custom JSP tag), might come back as FreeMarkerTemplateModel
objects instead of as objects with a standard Java type. Other Servlet scopes aren't affected. It's highly unlikely that something expects the presence of this bug. The affected values are of the FTL types listed below, and to trigger the bug, they either had to be created directly in the template (like as an FTL literal or with?date
/time
/datetime
), or you had to useDefaultObjectWrapper
orSimpleObjectWrapper
(or a subclass of them):-
FTL date/time/date-time values may came back as
freemarker.template.SimpleDate
-s, now they come back asjava.util.Date
-s instead. -
FTL sequence values may came back as
SimpleSequence
-s, now they come back asjava.util.List
-s as expected. This stands assuming that theobject_wrapper
configuration setting is a subclass ofBeansWrapper
(such asDefaultObjectWrapper
), but that's practically always the case in applications that use FreeMarker's JSP extension (otherwise it can still work, but it depends on the quality and capabilities of theObjectWrapper
implementation). -
FTL hash values may came back as
SimpleHash
-es, now they come back asjava.util.Map
-s as expected (again, assuming that the object wrapper is a subclass ofBeansWrapper
). -
FTL collection values may came back as
SimpleCollection
-s, now they come back asjava.util.Collection
-s as expected (again, assuming that the object wrapper is a subclass ofBeansWrapper
).
-
-
Bug fixed: Now
*.tld
files are searched inWEB-INF/
and in all its subdirectories recursively. Earlier they were only searched directly underWEB-INF/
andWEB-INF/lib/
. -
Bug fixed: Leading and trailing whitespace in TLD-s inside the
name
andtag-class
elements is now removed. -
Unwanted behavior fixed: In case multiple TLD-s map to the same tag library URI, now
WEB-INF/**/*.tld
-s has priority overMETA-INF/**/*.tld
-s coming from jar-s or classpath directories. Earlier, it was the other way around, except thatMETA-INF/lib/*.tld
-s could still take precedence randomly. While the JSP specification (2.2) explicitly states that the order is not defined and shouldn't be relied upon, it's just logical that if someone puts a TLD directly underWEB-INF
, he meant that to be used in that particular web application, rather than the TLD-s coming from the dependency jars which are often shared by multiple web applications. -
Bug fixed: Defaults set in an overridden
FreemarkerServlet.createConfiguration
won't be accidentally overwritten byFreemarkerServlet
's factory defaults anymore. This was a problem with theses settings only:template_exception_handler
,log_template_exceptions
,object_wrapper
,template_loader
. -
Bug fixed: If you had multiple
FreemarkerServlet
-s with different configuration settings in the same servlet context, that could lead to malfunction. (Normally, you only have one, just like there's only one servlet that processes*.jsp
.) -
Removed all the
xsd
files (web-app
andtaglib
schemas) from the FreeMarker artifact and from the XML entity resolver, as they were unused during XML parsing. -
Generally improved implementation quality (maintainability, error messages, performance bug fixes, test coverage) and better API documentation.
-
-
Logging facility improvements:
-
Just like earlier, when auto-selecting the logger library (the default behavior), FreeMarker choses Log4j if it's available. But now, if that turns out to be
log4j-over-slf4j
, FreeMarker will use SLF4J directly instead. (This fixes the issue where the logged location points to FreeMarker's log adapter class instead of the real call place.) -
FreeMarker now recognizes the
org.freemarker.loggerLibrary
system property, which specifies which logger to use, likejava ... -Dorg.freemarker.loggerLibrary=SLF4J
. This option deprecatesLogger.selectLoggerLibrary(int)
as that was inherently unreliable (because you usually can't control class initialization order very well). The system property has precedence overLogger.selectLoggerLibrary
. -
Generally improved implementation quality (more info printed when something fails, etc.).
-
New configuration setting:
log_template_exceptions
(Configuration.setLogTemplateExceptions(boolean)
). This specifies ifTemplateException
-s thrown by template processing are logged by FreeMarker or not. The default istrue
for backward compatibility, but that results in logging the exception twice in properly written applications, because there theTemplateException
thrown by the public FreeMarker API is also logged by the caller (even if only as the cause exception of a higher level exception). Hence, in modern applications it should be set tofalse
. (Note that this setting has no effect on the logging of exceptions caught by#attempt
/#recover
; those are always logged.)
-
-
Environment
and custom directive related improvements:-
Added
Environment.getCurrentDirectiveCallPlace()
, which returns aDirectiveCallPlace
object when called from a custom directive (i.e., fromTemplateDirectiveModel.execute()
). TheDirectiveCallPlace
objects lets you associate an arbitrary object to the directive invocation inside the template, which can be used for call-place-bound caching (like the minification of non-dynamic nested content). SeeDirectiveCallPlace
in the Java API documentation for more. -
Added
Environment.getMainTemplate()
. Deprecated the ambiguous (and often broken: [145])Environment.getTemplate()
.
-
-
Template loading:
-
Added new
Configuration
setting,template_lookup_strategy
(Configuration.setTemplateLookupStrategy(TemplateLookupStrategy)
). This allows customizing whatTemplateLoader
-level names will be tried when a template is requested. With this you can, for example, define a custom localized lookup sequence instead of the default (which looks like:foo_de_LU_MAC.ftl, foo_de_LU.ftl, foo_de.ftl,
foo.ftl
). -
Added new
Configuration.getTemplate(...)
parameter,Object customLookupCondition
. This parameter can be used by custom aTemplateLookupStrategy
to deduce the actual template name(s) from the requested name (similarly to as the default lookup strategy does that based on the locale). For example, on a multi-domain Web site, one may want to define some templates that are specialized to a domain, and thus use the domain name as the custom lookup condition. Then, whenfoo.ftl
is requested, a customTemplateLookupStrategy
could first look for@somedomain.com/foo.ftl
, and then for@default/foo.ftl
. See the JavaDoc of the relevantConfiguration.getTemplate(...)
overload for more details; note there the requirements regarding thehashCode
andequals
of thecustomLookupCondition
. -
Added new
Configuration
setting,template_name_format
(Configuration.setTemplateNameFormat(TemplateNameFormat)
). This allows specifying the naming rules used by FreeMarker. For now, custom implementations aren't allowed, and you can only chose betweenTemplateNameFormat.DEFAULT_2_3_0
(the default) andDEFAULT_2_4_0
(recommended, at least for new projects).DEFAULT_2_4_0
has several advantages, but isn't fully backward compatible (though most applications won't be affected). For typical mistakes like using backslash instead of slash, or backing out of the root, it givesMalformedTemplateNameFormatException
instead ofTempalteNotFoundException
. It allows scheme names to be terminated with:
alone, instead of a://
(which is also supported), like inclasspath:foo/bar.ftl
. It fixes numerous legacy glitches (bugs), mostly related to the interpretation of..
after special steps like.
or*
. See the full list of differences in the Java API documentation ofTemplateNameFormat.DEFAULT_2_4_0
. -
ClassTemplateLoader
now can be created by specifying aClassLoader
directly, rather than by specifying a baseClass
. That is, now there'sClassTemplateLoader(ClassLoader, String)
constructor, and also aConfiguration.setClassLoaderForTemplateLoading(ClassLoader, String)
method. -
Added new exception,
TemplateNotFoundException
, which is now used instead ofTemplateNotFoundException
when getting a template. As it extendsTemplateNotFoundException
, this change is backward compatible. The main goal was to counter the common misunderstanding that template paths are real file paths. However, the new exception also has the benefit that it can give additional FreeMarker-specific information about the error, like right now it hasgetTemplateName()
andgetCustomLookupCondition()
methods. -
Template
-s now have agetSourceName()
method, in additionally togetName()
. These two return the same as far as no localized lookup or acquisition (*
in the name) or other lookup strategy was actively involved. But when it was,getSourceName()
gives the name with which the template was actually loaded from theTemplateLoader
, whilegetName()
returns (and had always returned) the name with which the template was requested (in canonicalized form).getName()
is used for everything (like for relative inclusion resolution), except for location information in error messages, which now usesgetSourceName()
. Also,TemplateException
now has agetSourceName()
method. -
Configuration.getTemplate(...)
overloads now acceptnull
for thelocale
andencoding
parameters, in which case they use the same defaults as the overloads where the parameter is omitted. -
Debugger SPI implementators, attention: The
DebugBreak
instruction will now send thesourceName
of the template to thesuspendEnvironmentSpi
callback, rather than itsname
. You should also use thesourceName
inregisterTemplateSpi
and such, not thename
.
-
-
Configuration:
-
Added
Configuration.unsetXxx
andisXxxExplicitlySet
methods for several settings. Unsetting a setting makes it behave as ifsetXxx
was never called, thus the setting will use the default value that fits the currentincompatible_improvements
value and will be adjusted asincompatible_improvements
is changed later. -
When configuring FreeMarker from
java.util.Properties
(or withString
-String
name-value pairs in general):-
The
default
setting value is now recognized bytemplate_exception_handler
,template_storage
,template_loader
(and by the newtemplate_lookup_strategy
andtemplate_name_format
) settings, and it causesConfiguration.unsetXxx()
to be called. -
Bug fixed: When setting
object_wrapper
todefault
(as opposed to not specifying it), it has ignored theincompatible_improvements
and has always usedObjectWrapper.DEFAULT_WRAPPER
. This fix only matters whenincompatible_improvements
is exactly 2.3.21, as that's when the default object wrapper was changed fromObjectWrapper.DEFAULT_WRAPPER
to the result ofnew DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_21).build()
, which is a bit different singleton, as it has read-only configuration settings and bug fixed overloaded method selection rules. To useObjectWrapper.DEFAULT_WRAPPER
regardless of the value of theincompatible_improvements
setting, use the newdefault_2_3_0
value.
-
-
Bug fixed: Changing the value of the
localized_lookup
setting now empties the template cache, so that old lookup results won't be reused. (This of course only matters if you change this setting under an already running service, which is very unlikely.)
-
-
Miscellaneous:
-
Bug fixed [145], active only with
incompatible_improvements
set to 2.3.22 (or higher):#include
and#nested
doesn't change the parentTemplate
(seeConfigurable.getParent()
) of theEnvironment
anymore to theTemplate
that's included or where#nested
"returns" to. Thus, the parent ofEnvironment
will be now always the mainTemplate
. (The mainTemplate
is theTemplate
whoseprocess
orcreateProcessingEnvironment
method was called to initiate the output generation.) Note that this only matters if you have set settings directly onTemplate
objects (not to be confused with setting settings in templates via#setting
, which just modifies theEnvironment
, and so isn't affected by this fix), and almost nobody does that. Also note that macro calls have never changed theEnvironment
parent to theTemplate
that contains the macro definition, so there's no change there now. -
Bug fixed [419]: FreeMarker doesn't fail anymore when it has no permission to read Java system properties, like when used in unsigned applets. It just logs some warnings.
-
HTML_DEBUG
andDEBUG
TemplateExceptionHandler
output now contains a warning like "HTML_DEBUG mode; use RETHROW in production!", due to frequent misuse. -
Some fixes and improvements in template canonical form output, and as a consequence of that, in FTL stack trace instruction displaying.
-
Marked some historically public but otherwise internal API-s as deprecated, so that the disclaimer is more apparent in IDE-s.
-
Notes
The
consequences and reasons of introducing adapter approach for
container types in DefaultObjectWrapper
when its
incompatibleImprovements is set to 2.3.22:
-
With the new approach (the adapter approach), the key order of
Map
-s is never lost. The copying approach could only keep that forHashMap
subclasses (such asLinkedHashMap
) andSortedMap
-s (such asTreeMap
), but not for more exoticMap
-s, like Guava'sImmutableMap
. Also, any other behavioral peculiarities of the originalMap
(e.g., case insensitive key lookup) is kept now. -
The exact type and identity of the
Map
/List
is kept when the wrapped value is passed back to a Java method from the template. With the legacy approach the Java methods have received aMap
orList
of a special FreeMarker specific type (that acted as an adapter for theTemplateModel
). -
Performance characteristics change, mostly for the better, but it depends on the application. If the template reads the same(!) entry from the data model roughly once or twice (or not at all), which is typical, them the adapter approach gives better results, otherwise the legacy copying approach is faster (as it can reuse the wrapped entry from the previous read), though this slowdown certainly not a concern for most applications. The performance of the new adapter approach is more predictable, because it has no initial "spike" to set up the container copy (especially painful for huge collections), instead the performance is linearly proportional to the number of data model reads (and not to the number of collection entries).
-
If the
Map
/List
/array is changed after it was wrapped, the change will now become visible in the data-model. With the copying approach, the wrapped value was a shallow-snapshot of the originalMap
/List
/array. While it's unlikely that someone has deliberately utilized this, it's a risk factor when switching to adapters. -
It's theoretically possible that some code (mostly
TemplateDirectiveModel
implementations) mistakenly assumed that wrappedMap
-s areSimpleHash
-es, and wrappedList
-s areSimpleSequence
-s, etc., instead of them just beingTemplateHashModel
-s andTemplateSequenceModel
-s. Such code was always wrong, but now it will indeed break, so it's a risk factor. -
As now the exact type of the wrapped original object is used for overloaded method selection, the choice can be different (and similar to what it would be with pure
BeansWrapper
). It's difficult to find cases where this matters. A change is most probable around arrays, as with the copying approach they were unwrapped toList
-s, not to the original array. As the overloaded method mechanism can convert between arrays and lists (in both directions), it's usually not a problem. But, it doesn't do conversion between different array types when the overloaded method has various types on the parameter position of the array, so that's a risk factor. -
SimpleHash
andSimpleSequence
haven't become deprecated. They are still used for hashes and sequences created in FTL, and are recommended for values that are built specifically to be used from templates, rather than wrapping an already existingMap
orList
or array. -
List
-s andMap
-s that are exposed to templates in multiple threads are now under greater stress regarding their correct operation under multi-threaded read-only access. This is because the adapters won't copy their contents into well knownList
andMap
implementations (HashMap
,ArrayList
, etc.) before accessing them from multiple threads. So this is mostly a concern with customList
andMap
implementations, which aren't as mature as the standard Java classes. Note that this was always like so with pureBeansWrapper
, which is used by a lot of projects/frameworks (like by Struts) for a long time, so it's not an uncharted territory. -
When the wrapped
List
is aAbstractSequentialList
(like aLinkedList
), the resulting adapter will implementTemplateCollectionModel
for more efficient enumeration (#list
-ing), in additionally toTemplateSequenceModel
of course.TemplateCollectionModel
allows FTL to traverse the list without accessing elements by index. With the legacy copying approachTemplateCollectionModel
wasn't implemented as it wasn't needed for efficient enumeration there. -
Iterators (when you put them directly into the data-model) are wrapped into
DefaultIteratorAdapter
instead ofSimpleCollection
. This has two consequences:-
The wrapped
Iterator
is now unwrapped properly to the original Java object when it's passed to Java method from the template. -
Wrapped
Iterator
-s (not to be confused withIterable
) aren't thread-safe anymore, to spare some synchronizations, after all, exposing the sameIterator
to multiple parallel template executions doesn't make much sense. This shouldn't be a migration concern, as even earlier, only one of those template executions could succeed (the "content" ofIterator
-s wasn't copied, so the one who first accessed it become the exclusive owner). The change is just that earlier it was guaranteed that the other threads will fail (that was the thread-safe about it), while now there are no such guarantees.
-