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)

Reply via email to