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:
apiandhas_api.value?apiprovides 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 aMapinto 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.myPropertytranslates 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
?apiis only the last resort.Using
?apialso happens to offer a workaround for the lack of non-StringMapkey support in FTL's[]operator (as inmyMap[key]), because now you can writemyMap?api.get(nonStringKey).?apiis 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-idwould 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_abcand?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_lastand?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_beforeand?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.ftlinstead 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
#settingdirective 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.getorMap.containsKeyof a wrappedMapthrows aClassCastExceptionorNullPointerException, 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_improvementsset to 2.3.22 (see how here...), or more precisely, with its newuseAdaptersForContainerssetting set totrue(which defaults totruewhenincompatible_improvementsis 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 thinTemplateModeladapters, that will reach the original object for all operations. The wrapped values will be instances of the newDefaultMapAdapter,DefaultListAdapterandDefaultArrayAdapterclasses, instead of the legacy (copying)SimpleHashandSimpleSequenceclasses. (Note that many projects use pureBeansWrapperinstead ofDefaultObjectWrapper, which has always used the adapter approach, albeit a different implementation of it. As the shortcomings ofDefaultObjectWrapperare fixed now, it's always recommended overBeansWrapper, asBeansWrappergives 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]:
ClassCastExceptionwhen aSortedMap(typically, aTreeMap) is wrapped withDefaultObjectWrapperand then a 1 character long string is get from it that doesn't exist. To fix the issue, if the wrappedMapis aSortedMapand it's wrapped byDefaultObjectWrapper, it won't try to fall back to aCharacterkey after with theStringkey has gotnull. (This change should be backward compatible, because when aSortedMaphasCharacterkeys, the initial attempt withStringkey causesClassCastException, thus, suchSortedMap-s were never usable as FTL hashes.) -
Bug fixed [368]: Only with
incompatible_improvementsset to 2.3.22 or with its newuseAdaptersForContainerssetting set totrue: Key order and other behavioral peculiarities of "custom"Maptypes isn't lost anymore. The same stands forList-s too. -
Added new setting,
forceLegacyNonListCollections. This only matters whenuseAdaptersForContainersistrue. 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_improvements2.4.0, becauseSimpleSequencegave indexed access to these non-List-s, like inmySet[2], which is strange but some existing templates may utilize this, even if only accidentally. WithforceLegacyNonListCollectionsset tofalse, indexed access won't be possible forSet-s and such anymore (nor will?firstand?lastwork, but?sizewill 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 setforceLegacyNonListCollectionstofalse. (The adapter approach is implemented byDefaultNonListCollectionAdapter.) -
Added new, experimental FTL type interface,
freemarker.template.TemplateCollectionModelEx, which adds thesize(),isEmpty(), andboolean contains(TemplateModel)methods to theTemplateCollectionModelinterface. This was added because when wrappingjava.util.Collectionsthese 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?sizewhen setting theforceLegacyNonListCollectionsproperty ofDefaultObjectWrappertofalse(see earlier). -
Added new experimental interface,
freemarker.template.ObjectWrapperAndUnwrapper. This extendsObjectWrapperwith unwrapping functionality. This functionality has already existed for a long time inBeansWrapperand its subclasses, like inDefaultObjectWrapper, but it wasn't "factored out" into its own published interface that otherObjectWrapper-s could implement. This is useful forTemplateModelimplementations 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,?apiitself isn't.
-
-
FreemarkerServletimprovements:-
FreemarkerServletnow supports custom JSP EL functions (defined in TLD-s withfunctionXML 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]:
FreemarkerServletcan 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 theMetaInfTldSourcesand/orClasspathTldsFreemarkerServletinit-params (see the Java API documentation ofFreemarkerServletfor 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-INFdirectories that are visible for the class loader will be searched for TLD-s. -
MetaInfTldSourcesandClasspathTldscan also be appended to or replaced by the values of Java system propertiesorg.freemarker.jsp.metaInfTldSourcesandorg.freemarker.jsp.classpathTlds, respectively. Thus one can adjust these in the Eclipse run configuration without modifying theweb.xml. (See the Java API documentation ofFreemarkerServletfor more.) -
FreemarkerServletnow recognizes theorg.eclipse.jetty.server.webapp.ContainerIncludeJarPatternservlet 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
TemplatePathservlet 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.jaris not local to the web application.class://has the problem that it uses the defining class loader ofFreemarkerSerlvetitself (or of its subclass). -
If
incompatible_improvementsis set to 2.3.22 (or higher), theTemplatePathservlet 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 totruechanges 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_improvementsset to 2.3.22 (or higher): Some kind of values, when put into the JSP page scope (via#globalor via the JSPPageContextAPI) and later read back with the JSPPageContextAPI (typically in a custom JSP tag), might come back as FreeMarkerTemplateModelobjects 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 useDefaultObjectWrapperorSimpleObjectWrapper(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_wrapperconfiguration 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 theObjectWrapperimplementation). -
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
*.tldfiles 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
nameandtag-classelements 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.createConfigurationwon'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
xsdfiles (web-appandtaglibschemas) 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.loggerLibrarysystem 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 istruefor backward compatibility, but that results in logging the exception twice in properly written applications, because there theTemplateExceptionthrown 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.)
-
-
Environmentand custom directive related improvements:-
Added
Environment.getCurrentDirectiveCallPlace(), which returns aDirectiveCallPlaceobject when called from a custom directive (i.e., fromTemplateDirectiveModel.execute()). TheDirectiveCallPlaceobjects 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). SeeDirectiveCallPlacein the Java API documentation for more. -
Added
Environment.getMainTemplate(). Deprecated the ambiguous (and often broken: [145])Environment.getTemplate().
-
-
Template loading:
-
Added new
Configurationsetting,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 aTemplateLookupStrategyto 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.ftlis requested, a customTemplateLookupStrategycould 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 thehashCodeandequalsof thecustomLookupCondition. -
Added new
Configurationsetting,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_0has 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 givesMalformedTemplateNameFormatExceptioninstead 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. -
ClassTemplateLoadernow can be created by specifying aClassLoaderdirectly, 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 ofTemplateNotFoundExceptionwhen 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,TemplateExceptionnow has agetSourceName()method. -
Configuration.getTemplate(...)overloads now acceptnullfor thelocaleandencodingparameters, in which case they use the same defaults as the overloads where the parameter is omitted. -
Debugger SPI implementators, attention: The
DebugBreakinstruction will now send thesourceNameof the template to thesuspendEnvironmentSpicallback, rather than itsname. You should also use thesourceNameinregisterTemplateSpiand such, not thename.
-
-
Configuration:
-
Added
Configuration.unsetXxxandisXxxExplicitlySetmethods for several settings. Unsetting a setting makes it behave as ifsetXxxwas never called, thus the setting will use the default value that fits the currentincompatible_improvementsvalue and will be adjusted asincompatible_improvementsis changed later. -
When configuring FreeMarker from
java.util.Properties(or withString-Stringname-value pairs in general):-
The
defaultsetting value is now recognized bytemplate_exception_handler,template_storage,template_loader(and by the newtemplate_lookup_strategyandtemplate_name_format) settings, and it causesConfiguration.unsetXxx()to be called. -
Bug fixed: When setting
object_wrappertodefault(as opposed to not specifying it), it has ignored theincompatible_improvementsand has always usedObjectWrapper.DEFAULT_WRAPPER. This fix only matters whenincompatible_improvementsis exactly 2.3.21, as that's when the default object wrapper was changed fromObjectWrapper.DEFAULT_WRAPPERto 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_WRAPPERregardless of the value of theincompatible_improvementssetting, use the newdefault_2_3_0value.
-
-
Bug fixed: Changing the value of the
localized_lookupsetting 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_improvementsset to 2.3.22 (or higher):#includeand#nesteddoesn't change the parentTemplate(seeConfigurable.getParent()) of theEnvironmentanymore to theTemplatethat's included or where#nested"returns" to. Thus, the parent ofEnvironmentwill be now always the mainTemplate. (The mainTemplateis theTemplatewhoseprocessorcreateProcessingEnvironmentmethod was called to initiate the output generation.) Note that this only matters if you have set settings directly onTemplateobjects (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 theEnvironmentparent to theTemplatethat 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_DEBUGandDEBUGTemplateExceptionHandleroutput 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 forHashMapsubclasses (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/Listis kept when the wrapped value is passed back to a Java method from the template. With the legacy approach the Java methods have received aMaporListof 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
TemplateDirectiveModelimplementations) 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. -
SimpleHashandSimpleSequencehaven'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 existingMaporListor 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 knownListandMapimplementations (HashMap,ArrayList, etc.) before accessing them from multiple threads. So this is mostly a concern with customListandMapimplementations, 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
Listis aAbstractSequentialList(like aLinkedList), the resulting adapter will implementTemplateCollectionModelfor more efficient enumeration (#list-ing), in additionally toTemplateSequenceModelof course.TemplateCollectionModelallows FTL to traverse the list without accessing elements by index. With the legacy copying approachTemplateCollectionModelwasn't implemented as it wasn't needed for efficient enumeration there. -
Iterators (when you put them directly into the data-model) are wrapped into
DefaultIteratorAdapterinstead ofSimpleCollection. This has two consequences:-
The wrapped
Iteratoris 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 sameIteratorto 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.
-
