[ 
https://issues.apache.org/jira/browse/FREEMARKER-213?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17744859#comment-17744859
 ] 

Simon commented on FREEMARKER-213:
----------------------------------

In terms of trying to find another hack that Christoph could try, if 
getCSVRowsIterator adds some state, then it can remember the last iterator 
returned and close it when asked for a fresh one.

Otherwise some custom stuff where getCSVRowsIterator does not return an 
Iterator:
{code:java}
${myCustomObject.getCSVRowsIterator().withFilterProperty("categoryId").firstMatch("123")}{code}
Maybe implemented as:
{code:java}
class FakeIterator<I extends Iterator<Row> & AutoCloseable> {
    private final I iterator;
    private final Function<Row,?> mapper;

    public FakeIterator(I it) {
        this(it, Function.identity());
    }
    FakeIterator(I it, Function<Row,?> mapper) {
        this.iterator = it;
        this.mapper = mapper;
    }

    public FakeIterator withFilterProperty(String property) {
        Function<Row,?> mapper = row -> row.get(property);
        return new FakeIterator(iterator, mapper);
    }

    public Row firstMatch(String term) {
        try (iterator) {
            while (iterator.hasNext()) {
                Row row = iterator.next();
                if (mapper.apply(row).equals(term)) {
                    return row;
                }
            }
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}{code}

> 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