[ 
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)

Reply via email to