[
https://issues.apache.org/jira/browse/FREEMARKER-213?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17627211#comment-17627211
]
Dániel Dékány commented on FREEMARKER-213:
------------------------------------------
At a quick look, I think {{?filter}} doesn't come here, because its the thing
that consumes the result of that is who knows when it won't need more elements.
Rather, {{{}#list{}}}, {{{}?first{}}}, {{?join, etc.}} needs to close the
iterator. Unless I'm missing something here.
We can't directly use the {{AutoCloseable}} interface implemented underlying
{{Iterator}} object, as we have to rely on the {{TemplateModelIterator}}
abstraction on all places (except inside {{ObjectWrapper}} code of course). The
wrapped object is might not be an {{Iterator}} at all. But we can say that if
the {{TemplateModelIterator}} implements {{AutoCloseable}}, then at said
places, like when exiting `#list` etc. we must to call {{close()}} on that. And
then, we had to ensure that all {{TemplateModelIterator}} implementations
provided by us implement that interface, and delegate to the internal Java
object where possible.
BTW, same will be problem will pop up when we wrap {{Stream}}-s, as for some of
those closing is also important. Currently we don't support wrapping them
though, and that's another thing that's really missing.
> Handling AutoCloseable Iterators in ?filter / ?first when the iterator is not
> fully consumed
> ---------------------------------------------------------------------------------------------
>
> Key: FREEMARKER-213
> URL: https://issues.apache.org/jira/browse/FREEMARKER-213
> Project: Apache Freemarker
> Issue Type: Improvement
> Components: engine
> Affects Versions: 2.3.31
> Reporter: Christoph Rueger
> Priority: Minor
>
> HI,
> We have expressions like:
> {code:java}
> ${myCatalog.getCSVFileRowsIterator()?filter(row -> row.get("categoryId") ==
> "123")?first}
> {code}
> Under the hood this becomes an IteratorModel().
> The problem is that:
> - the underlying iterator is working on a resource (file, stream)
> - which needs to be *closed when finished*.
> - but ?filter and ?first can abandon and leave the iterator somewhere in the
> middle once it has identified a match
> - this caused Resource Leaks in some cases.
> I debugged down to the following code in _freemarker.core.new
> TemplateModelIterator() {...}.ensurePrefetchDone())_
> {code:java}
> private void ensurePrefetchDone() throws TemplateModelException {
> if (prefetchDone) {
> return;
> }
> boolean conclusionReached = false;
> do {
> if (lhoIterator.hasNext()) {
> TemplateModel element =
> lhoIterator.next();
> boolean elementMatched;
> try {
> elementMatched =
> elementMatches(element, elementTransformer, env);
> } catch (TemplateException e) {
> throw new
> _TemplateModelException(e, env, "Failed to transform element");
> }
> if (elementMatched) {
> prefetchedElement = element;
> conclusionReached = true;
> }
> } else {
> prefetchedEndOfIterator = true;
> prefetchedElement = null;
> conclusionReached = true;
> }
> } while (!conclusionReached);
> prefetchDone = true;
> }
> {code}
> It looks like at this place we have the "iterator" and also know when we stop
> iterating, when finding a match.
> *Question*
> Would it be possible to check if the underlying Iterator implements
> _java.lang.AutoCloseable_ and if yes, call _.close()_ on it, when a match is
> found?
> I am not sure if that is the right thing to do here.
> We have built workarounds so that AutoCloseable objects in the datamodel are
> closed after rendering is finished.
> But it would be better to close it as soon as possible, when the iterator is
> stopped to be consumed. This would help in scenarios where the template does
> that multiple times, e.g.:
> {code:java}
> ${myCustomObject.getCSVRowsIterator()?filter(row -> row.get("categoryId") ==
> "123")?first}
> ${myCustomObject.getCSVRowsIterator()?filter(row -> row.get("categoryId") ==
> "234")?first}
> {code}
> Ideas would be welcome, where this kind of "closing" should happen.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)