ServiceQueryEvaluationStep.java
/*******************************************************************************
* Copyright (c) 2021 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.query.algebra.evaluation.impl.evaluationsteps;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.SingletonIteration;
import org.eclipse.rdf4j.query.Binding;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.algebra.Service;
import org.eclipse.rdf4j.query.algebra.Var;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedService;
import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolver;
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
import org.eclipse.rdf4j.query.impl.MapBindingSet;
public final class ServiceQueryEvaluationStep implements QueryEvaluationStep {
private final Service service;
private final Var serviceRef;
private final FederatedServiceResolver serviceResolver;
public ServiceQueryEvaluationStep(Service service, Var serviceRef, FederatedServiceResolver serviceResolver) {
this.service = service;
this.serviceRef = serviceRef;
this.serviceResolver = serviceResolver;
}
@Override
public CloseableIteration<BindingSet> evaluate(BindingSet bindings) {
String serviceUri;
if (serviceRef.hasValue()) {
serviceUri = serviceRef.getValue().stringValue();
} else {
if (bindings != null && bindings.getValue(serviceRef.getName()) != null) {
serviceUri = bindings.getBinding(serviceRef.getName()).getValue().stringValue();
} else {
throw new QueryEvaluationException("SERVICE variables must be bound at evaluation time.");
}
}
try {
FederatedService fs = serviceResolver.getService(serviceUri);
// create a copy of the free variables, and remove those for which
// bindings are available (we can set them as constraints!)
Set<String> freeVars = new HashSet<>(service.getServiceVars());
freeVars.removeAll(bindings.getBindingNames());
// Get bindings from values pre-bound into variables.
MapBindingSet allBindings = new MapBindingSet();
for (Binding binding : bindings) {
allBindings.setBinding(binding.getName(), binding.getValue());
}
Set<Var> boundVars = getBoundVariables(service);
for (Var boundVar : boundVars) {
freeVars.remove(boundVar.getName());
allBindings.setBinding(boundVar.getName(), boundVar.getValue());
}
bindings = allBindings;
String baseUri = service.getBaseURI();
// special case: no free variables => perform ASK query
if (freeVars.isEmpty()) {
boolean exists = fs.ask(service, bindings, baseUri);
// check if triples are available (with inserted bindings)
if (exists) {
return new SingletonIteration<>(bindings);
} else {
return EMPTY_ITERATION;
}
}
// otherwise: perform a SELECT query
return fs.select(service, freeVars, bindings,
baseUri);
} catch (RuntimeException e) {
// suppress exceptions if silent
if (service.isSilent()) {
return new SingletonIteration<>(bindings);
} else {
throw e;
}
}
}
private Set<Var> getBoundVariables(Service service) {
BoundVarVisitor visitor = new BoundVarVisitor();
visitor.meet(service);
return visitor.boundVars;
}
private static class BoundVarVisitor extends AbstractSimpleQueryModelVisitor<RuntimeException> {
final Set<Var> boundVars = new HashSet<>();
private BoundVarVisitor() {
super(true);
}
@Override
public void meet(Var var) {
if (var.hasValue()) {
boundVars.add(var);
}
}
}
}