All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.rdf4j.federated.evaluation.TripleSourceBase Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
/*******************************************************************************
 * 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