This is an automated email from the ASF dual-hosted git repository. andy pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/jena.git
commit aecfdc3b34afcfb50634d4565588c43bc2df521b Author: Andy Seaborne <[email protected]> AuthorDate: Tue Mar 5 22:37:48 2024 +0000 GH-2309: Close internal InputStream --- .../org/apache/jena/query/ResultSetCloseable.java | 25 +++---- .../org/apache/jena/query/ResultSetFactory.java | 68 +++++++---------- .../jena/riot/resultset/ResultSetOnClose.java | 86 ++++++++++++++++++++++ .../org/apache/jena/riot/rowset/RowSetOnClose.java | 65 ++++++++++++++++ .../org/apache/jena/riot/rowset/RowSetWrapper.java | 62 ++++++++++++++++ .../sparql/engine/ResultSetCheckCondition.java | 6 +- 6 files changed, 255 insertions(+), 57 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/query/ResultSetCloseable.java b/jena-arq/src/main/java/org/apache/jena/query/ResultSetCloseable.java index f21e67838b..23fcfa0f2a 100644 --- a/jena-arq/src/main/java/org/apache/jena/query/ResultSetCloseable.java +++ b/jena-arq/src/main/java/org/apache/jena/query/ResultSetCloseable.java @@ -18,16 +18,15 @@ package org.apache.jena.query; -import java.io.Closeable ; +import org.apache.jena.riot.resultset.ResultSetOnClose; -import org.apache.jena.sparql.resultset.ResultSetWrapper ; - -/** A {@link ResultSet} that closes the associated {@link QueryExecution} +/** + * A {@link ResultSet} that closes the associated {@link QueryExecution} * via {@link AutoCloseable}. - */ -public class ResultSetCloseable extends ResultSetWrapper implements AutoCloseable, Closeable { + */ +public class ResultSetCloseable extends ResultSetOnClose implements AutoCloseable { - /** Return a closable resultset for a {@link QueryExecution}. + /** Return a closable result set for a {@link QueryExecution}. * The {@link QueryExecution} must be for a {@code SELECT} query. * @param queryExecution {@code QueryExecution} must be for a {@code SELECT} query. * @return ResultSetCloseable @@ -37,16 +36,10 @@ public class ResultSetCloseable extends ResultSetWrapper implements AutoCloseabl throw new IllegalArgumentException("Not an execution for a SELECT query"); return new ResultSetCloseable(queryExecution.execSelect(), queryExecution) ; } - - private QueryExecution qexec ; + /** @deprecated The constructor will become private. Use {@link #closeableResultSet}. */ + @Deprecated public ResultSetCloseable(ResultSet rs, QueryExecution qexec) { - super(rs) ; - this.qexec = qexec ; - } - - @Override - public void close() { - qexec.close() ; + super(rs, ()->qexec.close()) ; } } diff --git a/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java b/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java index e9b9b1ee27..d0138779de 100644 --- a/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java +++ b/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java @@ -32,22 +32,20 @@ import org.apache.jena.riot.RDFDataMgr; import org.apache.jena.riot.ReadAnything; import org.apache.jena.riot.ResultSetMgr ; import org.apache.jena.riot.resultset.ResultSetLang; +import org.apache.jena.riot.resultset.ResultSetOnClose; import org.apache.jena.sparql.core.Var; import org.apache.jena.sparql.engine.QueryIterator ; import org.apache.jena.sparql.engine.ResultSetStream ; import org.apache.jena.sparql.exec.RowSet; import org.apache.jena.sparql.graph.GraphFactory ; import org.apache.jena.sparql.resultset.* ; -import org.apache.jena.sparql.sse.Item ; -import org.apache.jena.sparql.sse.SSE ; -import org.apache.jena.sparql.sse.builders.BuilderTable ; /** ResultSetFactory - make result sets from places other than a query. */ public class ResultSetFactory { // See also ResultSetMgr - which post-dates this code. // Ideally, read operations here should call ResultSetMgr. - // The exception is XML from a string and the arcachic RDF to ResultSet forms. + // The exception is XML from a string and the archaic RDF to ResultSet forms. /** * Load a result set from file or URL into a result set (memory backed). @@ -69,8 +67,18 @@ public class ResultSetFactory { public static ResultSet load(String filenameOrURI, ResultsFormat format) { if (format == null) format = ResultsFormat.guessSyntax(filenameOrURI); - InputStream in = IO.openFile(filenameOrURI); - return load(in, format) ; + // Result sets are iterators. + // The input stream isn't finished with when the result set is created. + // Close input stream when the result set is finished with + // (exhausted or closed). + InputStream in = IO.openFile(filenameOrURI) ; + ResultSet rs = load(in, format) ; + Runnable onClose = ()-> { + try { in.close(); } + catch (IOException ex) { throw IOX.exception(ex); } + }; + ResultSet rs2 = new ResultSetOnClose(rs, onClose); + return rs; } /** @@ -159,7 +167,7 @@ public class ResultSetFactory { /** * Load a result set (or any other model) from file or URL. Does not have to - * be a result set (e.g. CONSTRUCT results) but it does interpret the + * be a result set (e.g. CONSTRUCt results) but it does interpret the * ResultSetFormat possibilities. * * @param model @@ -167,7 +175,10 @@ public class ResultSetFactory { * @param filenameOrURI * @param format * @return Model + * + * @deprecated This functions will become be internal. */ + @Deprecated public static Model loadAsModel(Model model, String filenameOrURI, ResultsFormat format) { if (model == null) model = GraphFactory.makeDefaultModel(); @@ -236,25 +247,6 @@ public class ResultSetFactory { return ResultSetMgr.read(in, ResultSetLang.RS_JSON); } - /** - * Read from an input stream which is the format of the SPARQL result set - * format in SSE. - * - * @param in - * InputStream - * @return ResultSet - */ - public static ResultSet fromSSE(InputStream in) { - Item item = SSE.parse(in); - Log.warn(ResultSet.class, "Reading SSE result set not full implemented"); - // See SPARQLResult. Have a level of ResultSetFactory that does - // "get SPARQLResult". - // Or just boolean/result set because those are both srx. etc. - - BuilderTable.build(item); - return null; - } - /** * Turns an RDF model, with properties and classes from the result set * vocabulary, into a SPARQL result set. The result set formed is a copy in @@ -263,7 +255,7 @@ public class ResultSetFactory { * @param model * @return ResultSet */ - static public ResultSet makeResults(Model model) { + public static ResultSet makeResults(Model model) { return new RDFInput(model); } @@ -275,7 +267,7 @@ public class ResultSetFactory { * @param model * @return ResultSetRewindable */ - static public ResultSetRewindable makeRewindable(Model model) { + public static ResultSetRewindable makeRewindable(Model model) { return new RDFInput(model); } @@ -287,7 +279,7 @@ public class ResultSetFactory { * @param resultSet * @return ResultSetRewindable */ - static public ResultSetRewindable makeRewindable(ResultSet resultSet) { + public static ResultSetRewindable makeRewindable(ResultSet resultSet) { if ( resultSet instanceof ResultSetRewindable ) { ResultSetRewindable rsw = (ResultSetRewindable)resultSet; rsw.reset(); @@ -303,11 +295,10 @@ public class ResultSetFactory { * @param rowSet * @return ResultSetRewindable */ - static public ResultSetRewindable makeRewindable(RowSet rowSet) { + public static ResultSetRewindable makeRewindable(RowSet rowSet) { return makeRewindable(ResultSet.adapt(rowSet)); } - /** * Turns an existing result set into one with peeking capabilities * <p> @@ -325,16 +316,14 @@ public class ResultSetFactory { * Result set to wrap * @return Peekable results */ - static public ResultSetPeekable makePeekable(ResultSet resultSet) { + public static ResultSetPeekable makePeekable(ResultSet resultSet) { return new ResultSetPeeking(resultSet); } - /** - * Return a closable resultset for a {@link QueryExecution}. The - * {@link QueryExecution} must be for a {@code SELECT} query. + /** Return a closable resultset for a {@link QueryExecution}. + * The {@link QueryExecution} must be for a {@code SELECT} query. * <p> * Example: - * * <pre> * QueryExecution qExec = QueryExecutionFactory.create(...); * try (ResultSetCloseable rs = ResultSetFactory.closableResultSet(qExec) ) { @@ -342,8 +331,7 @@ public class ResultSetFactory { * } * </pre> * - * @param queryExecution {@code QueryExecution} must be for a {@code SELECT} - * query. + * @param queryExecution {@code QueryExecution} must be for a {@code SELECT} query. * @return ResultSetCloseable */ public static ResultSetCloseable closeableResultSet(QueryExecution queryExecution) { @@ -358,7 +346,7 @@ public class ResultSetFactory { * @param results * @return ResultSet */ - static public ResultSetRewindable copyResults(ResultSet results) { + public static ResultSetRewindable copyResults(ResultSet results) { return new ResultSetMem(results); } @@ -370,7 +358,7 @@ public class ResultSetFactory { * List of variables, by name, for the result set * @return ResultSet */ - static public ResultSet create(QueryIterator queryIterator, List<String> vars) { + public static ResultSet create(QueryIterator queryIterator, List<String> vars) { return ResultSetStream.create(Var.varList(vars), queryIterator); } } diff --git a/jena-arq/src/main/java/org/apache/jena/riot/resultset/ResultSetOnClose.java b/jena-arq/src/main/java/org/apache/jena/riot/resultset/ResultSetOnClose.java new file mode 100644 index 0000000000..a05a67418d --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/riot/resultset/ResultSetOnClose.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.riot.resultset; + +import java.util.NoSuchElementException; +import java.util.function.Consumer; + +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.resultset.ResultSetWrapper; + +/** + * Perform an action on a result set when iteration finishes + * or {@code close()} is called. + */ +public class ResultSetOnClose extends ResultSetWrapper { + private final Runnable onClose; + private boolean isClosed = false; + + public ResultSetOnClose(ResultSet rs, Runnable onClose) { + super(rs); + this.onClose = onClose; + } + + private void onClose() { + if ( isClosed ) + return; + isClosed = true; + onClose.run(); + } + + @Override + public boolean hasNext() { + boolean b = super.hasNext(); + if ( !b ) + onClose(); + return b; + } + + @Override + public QuerySolution next() { + try { return super.next(); } + catch (NoSuchElementException ex) { onClose(); throw ex; } + } + + @Override + public void forEachRemaining(Consumer<? super QuerySolution> action) { + super.forEachRemaining(action); + onClose(); + } + + @Override + public QuerySolution nextSolution() { + try { return super.nextSolution(); } + catch (NoSuchElementException ex) { onClose(); throw ex; } + } + + @Override + public Binding nextBinding() { + try { return super.nextBinding(); } + catch (NoSuchElementException ex) { onClose(); throw ex; } + } + + @Override + public void close() { + super.close(); + onClose(); + } +} \ No newline at end of file diff --git a/jena-arq/src/main/java/org/apache/jena/riot/rowset/RowSetOnClose.java b/jena-arq/src/main/java/org/apache/jena/riot/rowset/RowSetOnClose.java new file mode 100644 index 0000000000..df04dbb655 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/riot/rowset/RowSetOnClose.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.riot.rowset; + +import java.util.NoSuchElementException; + +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.exec.RowSet; + +/** + * Perform an action on a row set when iteration finishes + * or {@code close()} is called. + */ +public class RowSetOnClose extends RowSetWrapper { + private final Runnable onClose; + private boolean isClosed = false; + + public RowSetOnClose(RowSet rs, Runnable onClose) { + super(rs); + this.onClose = onClose; + } + + private void onClose() { + if ( isClosed ) + return; + isClosed = true; + onClose.run(); + } + + @Override + public boolean hasNext() { + boolean b = super.hasNext(); + if ( !b ) + onClose(); + return b; + } + + @Override + public Binding next() { + try { return super.next(); } + catch (NoSuchElementException ex) { onClose(); throw ex; } + } + + @Override + public void close() { + super.close(); + onClose(); + } +} \ No newline at end of file diff --git a/jena-arq/src/main/java/org/apache/jena/riot/rowset/RowSetWrapper.java b/jena-arq/src/main/java/org/apache/jena/riot/rowset/RowSetWrapper.java new file mode 100644 index 0000000000..955c3ac202 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/riot/rowset/RowSetWrapper.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.riot.rowset; + +import java.util.List; + +import org.apache.jena.sparql.core.Var; +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.exec.RowSet; + +public class RowSetWrapper implements RowSet { + + private final RowSet other; + public RowSet get() { + return other; + } + + public RowSetWrapper(RowSet other) { + this.other = other; + } + + @Override + public boolean hasNext() { + return other.hasNext(); + } + + @Override + public Binding next() { + return other.next(); + } + + @Override + public List<Var> getResultVars() { + return other.getResultVars(); + } + + @Override + public long getRowNumber() { + return other.getRowNumber(); + } + + @Override + public void close() { + other.close(); + } +} diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetCheckCondition.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetCheckCondition.java index a2794924d2..44d20aad8e 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetCheckCondition.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/ResultSetCheckCondition.java @@ -30,18 +30,22 @@ import org.apache.jena.sparql.engine.binding.Binding ; /** ResultSet wrapper that check whether some condition is true * (e.g. the QueryExecution has not been closed). + * @deprecated To be removed. */ +@Deprecated public class ResultSetCheckCondition implements ResultSet { interface Condition { boolean check() ; } private final ResultSet other ; private final Condition condition ; + @Deprecated public ResultSetCheckCondition(ResultSet other, QueryExecution qExec) { this(other, checkQExec(qExec) ) ; } - public ResultSetCheckCondition(ResultSet other, Condition condition) { + @Deprecated + public ResultSetCheckCondition(ResultSet other, Condition condition) { this.other = other ; this.condition = condition ; }
