org.eclipse.rdf4j.federated.evaluation.TripleSourceBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rdf4j-tools-federation Show documentation
Show all versions of rdf4j-tools-federation Show documentation
A federation engine for virtually integrating SPARQL endpoints
/*******************************************************************************
* Copyright (c) 2019 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.federated.evaluation;
import java.util.function.Supplier;
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.EmptyIteration;
import org.eclipse.rdf4j.federated.FederationContext;
import org.eclipse.rdf4j.federated.algebra.ExclusiveTupleExpr;
import org.eclipse.rdf4j.federated.algebra.FilterValueExpr;
import org.eclipse.rdf4j.federated.endpoint.Endpoint;
import org.eclipse.rdf4j.federated.evaluation.iterator.CloseDependentConnectionIteration;
import org.eclipse.rdf4j.federated.evaluation.iterator.ConsumingIteration;
import org.eclipse.rdf4j.federated.evaluation.iterator.FilteringInsertBindingsIteration;
import org.eclipse.rdf4j.federated.evaluation.iterator.FilteringIteration;
import org.eclipse.rdf4j.federated.evaluation.iterator.GraphToBindingSetConversionIteration;
import org.eclipse.rdf4j.federated.evaluation.iterator.InsertBindingsIteration;
import org.eclipse.rdf4j.federated.evaluation.iterator.SingleBindingSetIteration;
import org.eclipse.rdf4j.federated.exception.ExceptionUtil;
import org.eclipse.rdf4j.federated.monitoring.Monitoring;
import org.eclipse.rdf4j.federated.structures.QueryInfo;
import org.eclipse.rdf4j.federated.structures.QueryType;
import org.eclipse.rdf4j.federated.util.FedXUtil;
import org.eclipse.rdf4j.federated.util.QueryStringUtil;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.query.Binding;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.BooleanQuery;
import org.eclipse.rdf4j.query.GraphQuery;
import org.eclipse.rdf4j.query.GraphQueryResult;
import org.eclipse.rdf4j.query.MalformedQueryException;
import org.eclipse.rdf4j.query.Operation;
import org.eclipse.rdf4j.query.Query;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.QueryLanguage;
import org.eclipse.rdf4j.query.QueryResult;
import org.eclipse.rdf4j.query.TupleQuery;
import org.eclipse.rdf4j.query.TupleQueryResult;
import org.eclipse.rdf4j.query.impl.EmptyBindingSet;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.RepositoryException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class TripleSourceBase implements TripleSource {
private static final Logger log = LoggerFactory.getLogger(TripleSourceBase.class);
protected final FederationContext federationContext;
protected final Monitoring monitoringService;
protected final Endpoint endpoint;
public TripleSourceBase(FederationContext federationContext, Endpoint endpoint) {
this.federationContext = federationContext;
this.monitoringService = federationContext.getMonitoringService();
this.endpoint = endpoint;
}
@Override
public CloseableIteration getStatements(
String preparedQuery, BindingSet queryBindings, QueryType queryType, QueryInfo queryInfo)
throws RepositoryException, MalformedQueryException,
QueryEvaluationException {
return withConnection((conn, resultHolder) -> {
QueryResult> evaluate = null;
final String baseURI = queryInfo.getBaseURI();
try {
switch (queryType) {
case SELECT:
monitorRemoteRequest();
TupleQuery tQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, preparedQuery, baseURI);
applyBindings(tQuery, queryBindings);
applyMaxExecutionTimeUpperBound(tQuery);
configureInference(tQuery, queryInfo);
tQuery.setDataset(queryInfo.getDataset());
if (queryInfo.getResultHandler().isPresent()) {
// pass through result to configured handler, and return an empty iteration as marker result
tQuery.evaluate(queryInfo.getResultHandler().get());
resultHolder.set(new EmptyIteration<>());
} else {
evaluate = tQuery.evaluate();
resultHolder.set(((TupleQueryResult) evaluate));
}
return;
case CONSTRUCT:
case DESCRIBE:
monitorRemoteRequest();
GraphQuery gQuery = conn.prepareGraphQuery(QueryLanguage.SPARQL, preparedQuery, baseURI);
gQuery.setDataset(queryInfo.getDataset());
applyBindings(gQuery, queryBindings);
applyMaxExecutionTimeUpperBound(gQuery);
configureInference(gQuery, queryInfo);
evaluate = gQuery.evaluate();
resultHolder.set(new GraphToBindingSetConversionIteration(((GraphQueryResult) evaluate)));
return;
case ASK:
monitorRemoteRequest();
boolean hasResults;
try (conn) {
BooleanQuery bQuery = conn.prepareBooleanQuery(QueryLanguage.SPARQL, preparedQuery, baseURI);
bQuery.setDataset(queryInfo.getDataset());
applyBindings(bQuery, queryBindings);
applyMaxExecutionTimeUpperBound(bQuery);
configureInference(bQuery, queryInfo);
hasResults = bQuery.evaluate();
}
resultHolder.set(booleanToBindingSetIteration(hasResults));
return;
default:
throw new UnsupportedOperationException("Operation not supported for query type " + queryType);
}
} catch (Throwable t) {
if (evaluate != null) {
evaluate.close();
}
throw t;
}
});
}
private void applyBindings(Operation operation, BindingSet queryBindings) {
if (queryBindings == null) {
return;
}
for (Binding b : queryBindings) {
operation.setBinding(b.getName(), b.getValue());
}
}
@Override
public CloseableIteration getStatements(
String preparedQuery, BindingSet bindings, FilterValueExpr filterExpr, QueryInfo queryInfo)
throws RepositoryException, MalformedQueryException,
QueryEvaluationException {
return withConnection((conn, resultHolder) -> {
TupleQuery query = conn.prepareTupleQuery(QueryLanguage.SPARQL, preparedQuery, null);
applyMaxExecutionTimeUpperBound(query);
configureInference(query, queryInfo);
// evaluate the query
monitorRemoteRequest();
CloseableIteration res = null;
try {
res = query.evaluate();
resultHolder.set(res);
// apply filter and/or insert original bindings
if (filterExpr != null) {
if (!bindings.isEmpty()) {
res = new FilteringInsertBindingsIteration(filterExpr, bindings, res,
queryInfo.getStrategy());
} else {
res = new FilteringIteration(filterExpr, res, queryInfo.getStrategy());
}
if (!res.hasNext()) {
res.close();
conn.close();
resultHolder.set(new EmptyIteration<>());
return;
}
} else if (!bindings.isEmpty()) {
res = new InsertBindingsIteration(res, bindings);
}
res = new ConsumingIteration(res, federationContext.getConfig().getConsumingIterationMax());
resultHolder.set(res);
} catch (Throwable t) {
if (res != null) {
res.close();
}
throw t;
}
});
}
@Override
public boolean hasStatements(Resource subj,
IRI pred, Value obj, QueryInfo queryInfo, Resource... contexts) throws RepositoryException {
try (RepositoryConnection conn = endpoint.getConnection()) {
return conn.hasStatement(subj, pred, obj, queryInfo.getIncludeInferred(), contexts);
}
}
@Override
public boolean hasStatements(ExclusiveTupleExpr group, BindingSet bindings)
throws RepositoryException, MalformedQueryException,
QueryEvaluationException {
monitorRemoteRequest();
String preparedAskQuery = QueryStringUtil.askQueryString(group, bindings, group.getQueryInfo().getDataset());
try (RepositoryConnection conn = endpoint.getConnection()) {
BooleanQuery query = conn.prepareBooleanQuery(QueryLanguage.SPARQL, preparedAskQuery);
query.setDataset(group.getQueryInfo().getDataset());
configureInference(query, group.getQueryInfo());
applyMaxExecutionTimeUpperBound(query);
return query.evaluate();
}
}
protected void monitorRemoteRequest() {
monitoringService.monitorRemoteRequest(endpoint);
}
private CloseableIteration booleanToBindingSetIteration(boolean hasResult) {
if (hasResult) {
return new SingleBindingSetIteration(EmptyBindingSet.getInstance());
}
return new EmptyIteration<>();
}
/**
* Set includeInferred depending on {@link QueryInfo#getIncludeInferred()}
*
* @param query
* @param queryInfo
*/
protected void configureInference(Query query, QueryInfo queryInfo) {
try {
query.setIncludeInferred(queryInfo.getIncludeInferred());
} catch (Exception e) {
log.debug("Failed to set include inferred: " + e.getMessage());
log.trace("Details:", e);
}
}
/**
* Apply an upper bound of the maximum execution time using
* {@link FedXUtil#applyMaxQueryExecutionTime(Operation, FederationContext)}.
*
* @param operation the operation
*/
protected void applyMaxExecutionTimeUpperBound(Operation operation) {
FedXUtil.applyMaxQueryExecutionTime(operation, federationContext);
}
private CloseableIteration closeConn(RepositoryConnection dependentConn,
CloseableIteration inner) {
return new CloseDependentConnectionIteration<>(inner, dependentConn);
}
/**
* Convenience method to perform an operation on a {@link RepositoryConnection}. This method takes care for closing
* resources as well error handling. The resulting iteration has to be supplied to the {@link ResultHolder}.
*
* @param operation the {@link ConnectionOperation}
* @return the resulting iteration
*/
protected CloseableIteration withConnection(ConnectionOperation operation) {
ResultHolder resultHolder = new ResultHolder<>();
RepositoryConnection conn = null;
CloseableIteration res = null;
try {
conn = endpoint.getConnection();
operation.perform(conn, resultHolder);
res = resultHolder.get();
// do not wrap Empty and Pass-through Iterations
if (res instanceof EmptyIteration) {
conn.close();
return res;
}
return closeConn(conn, res);
} catch (Throwable t) {
try {
// handle all other exception case
var iteration = resultHolder.get();
if (iteration != null) {
iteration.close();
}
} finally {
if (conn != null) {
conn.close();
}
}
throw ExceptionUtil.traceExceptionSource(endpoint, t, "");
}
}
/**
* Interface defining the operation to be perform on the connection
*
*
* Typical pattern
*
*
*
* CloseableIteration<BindingSet, QueryEvaluationException> res = withConnection((conn, resultHolder) -> {
* // do something with conn
* resultHolder.set(...)
* });
*
*
*
* @param
* @author Andreas Schwarte
* @see TripleSourceBase#withConnection(ConnectionOperation)
*/
protected interface ConnectionOperation {
void perform(RepositoryConnection conn, ResultHolder resultHolder);
}
/**
* Holder for a result iteration to be used with {@link TripleSourceBase#withConnection(ConnectionOperation)}. Note
* that the result holder should also be set with temporary results to properly allow error handling.
*
* @param
* @author Andreas Schwarte
*/
protected static class ResultHolder implements Supplier> {
protected CloseableIteration result;
public void set(CloseableIteration result) {
this.result = result;
}
@Override
public CloseableIteration get() {
return result;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy