2.3.22 - FreeMarker Manual

- Home
- Manual
- Java API

- Contribute
- Report a Bug
- Download

Manual
Previous Next

# 2.3.22

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` and `has_api`. `value?api` provides access to the API (usually, the Java API) of `value`, like `value?api.someJavaMethod()`, if the value itself supports this extra feature. 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 a `Map` 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 call `myMethod`. If, however, you write `myMap?api.myMethod()` instead, that means `myMap.myMethod()` in Java.

  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 in `myMap[key]`), because now you can write `myMap?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 is `data\-id`, as `data-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 converts `1`, `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 of `foo_en.ftl`, when the template was get as `foo.ftl`, but behind the scenes was loaded from `foo_en.ftl`).

  - "Template not found" errors are now more detailed, giving hints about accidentally using `\` instead of `/`, or backing out of the `TemplateLoader`'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` or `Map.containsKey` of a wrapped `Map` throws a `ClassCastException` or `NullPointerException`, 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 its `incompatible_improvements` set to 2.3.22 (see how here...), or more precisely, with its new `useAdaptersForContainers` setting set to `true` (which defaults to `true` when `incompatible_improvements` is set to 2.3.22): It doesn't copy `Map`-s, `List`-s, and arrays anymore when wrapping them into `TemplateModel`-s (which is the interface through with templates access all values), just wraps them into thin `TemplateModel` adapters, that will reach the original object for all operations. The wrapped values will be instances of the new `DefaultMapAdapter`, `DefaultListAdapter` and `DefaultArrayAdapter` classes, instead of the legacy (copying) `SimpleHash` and `SimpleSequence` classes. (Note that many projects use pure `BeansWrapper` instead of `DefaultObjectWrapper`, which has always used the adapter approach, albeit a different implementation of it. As the shortcomings of `DefaultObjectWrapper` are fixed now, it's always recommended over `BeansWrapper`, as `BeansWrapper` 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 a `SortedMap` (typically, a `TreeMap`) is wrapped with `DefaultObjectWrapper` and then a 1 character long string is get from it that doesn't exist. To fix the issue, if the wrapped `Map` is a `SortedMap` and it's wrapped by `DefaultObjectWrapper`, it won't try to fall back to a `Character` key after with the `String` key has got `null`. (This change should be backward compatible, because when a `SortedMap` has `Character` keys, the initial attempt with `String` key causes `ClassCastException`, thus, such `SortedMap`-s were never usable as FTL hashes.)

  - Bug fixed [368]: Only with `incompatible_improvements` set to 2.3.22 or with its new `useAdaptersForContainers` setting set to `true`: Key order and other behavioral peculiarities of "custom" `Map` types isn't lost anymore. The same stands for `List`-s too.

  - Added new setting, `forceLegacyNonListCollections`. This only matters when `useAdaptersForContainers` is `true`. Then, unless you set this to `true`, `java.util.Collection`-s that aren't `List`-s (like `Set`-s) will continue using `SimpleSequence` (i.e., the copying approach) instead of the adapter approach. The default is `false`, at least until `incompatible_improvements` 2.4.0, because `SimpleSequence` gave indexed access to these non-`List`-s, like in `mySet[2]`, which is strange but some existing templates may utilize this, even if only accidentally. With `forceLegacyNonListCollections` set to `false`, indexed access won't be possible for `Set`-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 set `forceLegacyNonListCollections` to `false`. (The adapter approach is implemented by `DefaultNonListCollectionAdapter`.)

  - Added new, experimental FTL type interface, `freemarker.template.TemplateCollectionModelEx`, which adds the `size()`, `isEmpty()`, and `boolean contains(TemplateModel)` methods to the `TemplateCollectionModel` interface. This was added because when wrapping `java.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 the `forceLegacyNonListCollections` property of `DefaultObjectWrapper` to `false` (see earlier).

  - Added new experimental interface, `freemarker.template.ObjectWrapperAndUnwrapper`. This extends `ObjectWrapper` with unwrapping functionality. This functionality has already existed for a long time in `BeansWrapper` and its subclasses, like in `DefaultObjectWrapper`, but it wasn't "factored out" into its own published interface that other `ObjectWrapper`-s could implement. This is useful for `TemplateModel` implementations that don't want to require a `BeansWrapper` (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 with `function` 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 discover `META-INF/**/*.tld`-s that are visible for the class loader but aren't in `WEB-INF/lib/*.jar`-s. For this feature to be active, you must setup the extra TLD lookup with the `MetaInfTldSources` and/or `ClasspathTlds` `FreemarkerServlet` init-params (see the Java API documentation of `FreemarkerServlet` 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` and `ClasspathTlds` can also be appended to or replaced by the values of Java system properties `org.freemarker.jsp.metaInfTldSources` and `org.freemarker.jsp.classpathTlds`, respectively. Thus one can adjust these in the Eclipse run configuration without modifying the `web.xml`. (See the Java API documentation of `FreemarkerServlet` for more.)

  - `FreemarkerServlet` now recognizes the `org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern` servlet context attribute, and adds entries to `MetaInfTldSources` (introduced above) from it.

  - Added `protected FreemarkerServlet.createTaglibFactory()` to allow fine tuning the settings of the `TaglibFactory`. It now have a few setters, like `setObjectWrapper`, `setMetaInfTldSource`, etc.

  - Added new servlet init-param, `BufferSize`. This sets the buffer size via `HTTPServletResponse.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 like `classpath:com/example/myapp/templates`. This is similar to the old `class://com/example/myapp/templates`, but it uses the Thread Context Class Loader of the thread that initializes `FreemarkerSerlvet`, and thus will work even if `freemarker.jar` is not local to the web application. `class://` has the problem that it uses the defining class loader of `FreemarkerSerlvet` itself (or of its subclass).

  - If `incompatible_improvements` is set to 2.3.22 (or higher), the `TemplatePath` 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 a `freemarker.cache.MultiTemplateLoader`.

  - Added new servlet `init-param`, `ExceptionOnMissingTemplate`. Setting this to `true` 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's `false` (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 what `createObjectWrapper()` is usually overridden for, but without unwillingly disabling the processing of the related init-params (like of `object_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 JSP `PageContext` API) and later read back with the JSP `PageContext` API (typically in a custom JSP tag), might come back as FreeMarker `TemplateModel` 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 use `DefaultObjectWrapper` or `SimpleObjectWrapper` (or a subclass of them):

    - FTL date/time/date-time values may came back as `freemarker.template.SimpleDate`-s, now they come back as `java.util.Date`-s instead.

    - FTL sequence values may came back as `SimpleSequence`-s, now they come back as `java.util.List`-s as expected. This stands assuming that the `object_wrapper` configuration setting is a subclass of `BeansWrapper` (such as `DefaultObjectWrapper`), 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 the `ObjectWrapper` implementation).

    - FTL hash values may came back as `SimpleHash`-es, now they come back as `java.util.Map`-s as expected (again, assuming that the object wrapper is a subclass of `BeansWrapper`).

    - FTL collection values may came back as `SimpleCollection`-s, now they come back as `java.util.Collection`-s as expected (again, assuming that the object wrapper is a subclass of `BeansWrapper`).

  - Bug fixed: Now `*.tld` files are searched in `WEB-INF/` and in all its subdirectories recursively. Earlier they were only searched directly under `WEB-INF/` and `WEB-INF/lib/`.

  - Bug fixed: Leading and trailing whitespace in TLD-s inside the `name` and `tag-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 over `META-INF/**/*.tld`-s coming from jar-s or classpath directories. Earlier, it was the other way around, except that `META-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 under `WEB-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 by `FreemarkerServlet`'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` and `taglib` 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, like `java ... -Dorg.freemarker.loggerLibrary=SLF4J`. This option deprecates `Logger.selectLoggerLibrary(int)` as that was inherently unreliable (because you usually can't control class initialization order very well). The system property has precedence over `Logger.selectLoggerLibrary`.

  - Generally improved implementation quality (more info printed when something fails, etc.).

  - New configuration setting: `log_template_exceptions` (`Configuration.setLogTemplateExceptions(boolean)`). This specifies if `TemplateException`-s thrown by template processing are logged by FreeMarker or not. The default is `true` for backward compatibility, but that results in logging the exception twice in properly written applications, because there the `TemplateException` 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 to `false`. (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 a `DirectiveCallPlace` object when called from a custom directive (i.e., from `TemplateDirectiveModel.execute()`). The `DirectiveCallPlace` 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). See `DirectiveCallPlace` 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 what `TemplateLoader`-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 a `TemplateLookupStrategy` 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, when `foo.ftl` is requested, a custom `TemplateLookupStrategy` could first look for `@somedomain.com/foo.ftl`, and then for `@default/foo.ftl`. See the JavaDoc of the relevant `Configuration.getTemplate(...)` overload for more details; note there the requirements regarding the `hashCode` and `equals` of the `customLookupCondition`.

  - 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 between `TemplateNameFormat.DEFAULT_2_3_0` (the default) and `DEFAULT_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 gives `MalformedTemplateNameFormatException` instead of `TempalteNotFoundException`. It allows scheme names to be terminated with `:` alone, instead of a `://` (which is also supported), like in `classpath: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 of `TemplateNameFormat.DEFAULT_2_4_0`.

  - `ClassTemplateLoader` now can be created by specifying a `ClassLoader` directly, rather than by specifying a base `Class`. That is, now there's `ClassTemplateLoader(ClassLoader, String)` constructor, and also a `Configuration.setClassLoaderForTemplateLoading(ClassLoader, String)` method.

  - Added new exception, `TemplateNotFoundException`, which is now used instead of `TemplateNotFoundException` when getting a template. As it extends `TemplateNotFoundException`, 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 has `getTemplateName()` and `getCustomLookupCondition()` methods.

  - `Template`-s now have a `getSourceName()` method, in additionally to `getName()`. 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 the `TemplateLoader`, while `getName()` 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 uses `getSourceName()`. Also, `TemplateException` now has a `getSourceName()` method.

  - `Configuration.getTemplate(...)` overloads now accept `null` for the `locale` and `encoding` 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 the `sourceName` of the template to the `suspendEnvironmentSpi` callback, rather than its `name`. You should also use the `sourceName` in `registerTemplateSpi` and such, not the `name`.

- Configuration:

  - Added `Configuration.unsetXxx` and `isXxxExplicitlySet` methods for several settings. Unsetting a setting makes it behave as if `setXxx` was never called, thus the setting will use the default value that fits the current `incompatible_improvements` value and will be adjusted as `incompatible_improvements` is changed later.

  - When configuring FreeMarker from `java.util.Properties` (or with `String`-`String` name-value pairs in general):

    - The `default` setting value is now recognized by `template_exception_handler`, `template_storage`, `template_loader` (and by the new `template_lookup_strategy` and `template_name_format`) settings, and it causes `Configuration.unsetXxx()` to be called.

    - Bug fixed: When setting `object_wrapper` to `default` (as opposed to not specifying it), it has ignored the `incompatible_improvements` and has always used `ObjectWrapper.DEFAULT_WRAPPER`. This fix only matters when `incompatible_improvements` is exactly 2.3.21, as that's when the default object wrapper was changed from `ObjectWrapper.DEFAULT_WRAPPER` to the result of `new 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 use `ObjectWrapper.DEFAULT_WRAPPER` regardless of the value of the `incompatible_improvements` setting, use the new `default_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 parent `Template` (see `Configurable.getParent()`) of the `Environment` anymore to the `Template` that's included or where `#nested` "returns" to. Thus, the parent of `Environment` will be now always the main `Template`. (The main `Template` is the `Template` whose `process` or `createProcessingEnvironment` method was called to initiate the output generation.) Note that this only matters if you have set settings directly on `Template` objects (not to be confused with setting settings in templates via `#setting`, which just modifies the `Environment`, and so isn't affected by this fix), and almost nobody does that. Also note that macro calls have never changed the `Environment` parent to the `Template` 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` and `DEBUG` `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 for `HashMap` subclasses (such as `LinkedHashMap`) and `SortedMap`-s (such as `TreeMap`), but not for more exotic `Map`-s, like Guava's `ImmutableMap`. Also, any other behavioral peculiarities of the original `Map` (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 a `Map` or `List` of a special FreeMarker specific type (that acted as an adapter for the `TemplateModel`).

- 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 original `Map`/`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 wrapped `Map`-s are `SimpleHash`-es, and wrapped `List`-s are `SimpleSequence`-s, etc., instead of them just being `TemplateHashModel`-s and `TemplateSequenceModel`-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 to `List`-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` and `SimpleSequence` 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 existing `Map` or `List` or array.

- `List`-s and `Map`-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 known `List` and `Map` implementations (`HashMap`, `ArrayList`, etc.) before accessing them from multiple threads. So this is mostly a concern with custom `List` and `Map` implementations, which aren't as mature as the standard Java classes. Note that this was always like so with pure `BeansWrapper`, 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 a `AbstractSequentialList` (like a `LinkedList`), the resulting adapter will implement `TemplateCollectionModel` for more efficient enumeration (`#list`-ing), in additionally to `TemplateSequenceModel` of course. `TemplateCollectionModel` allows FTL to traverse the list without accessing elements by index. With the legacy copying approach `TemplateCollectionModel` 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 of `SimpleCollection`. 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 with `Iterable`) aren't thread-safe anymore, to spare some synchronizations, after all, exposing the same `Iterator` 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" of `Iterator`-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.

Previous Next