You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by "Christoph Rueger (Jira)" <ji...@apache.org> on 2022/10/27 22:11:00 UTC

[jira] [Created] (FREEMARKER-213) Handling AutoCloseable Iterators in ?filter / ?first when the iterator is not fully consumed

Christoph Rueger created FREEMARKER-213:
-------------------------------------------

             Summary: 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


HI, 
We have expressions like:

{code:java}
${myCatalog.getCSVFileRowsIterator()?filter(row -> row.get("categoryId") == "123")?first}
{code}


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