package org.aksw.jena_sparql_api.retry.core;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Supplier;
import org.aksw.jena_sparql_api.core.QueryExecutionDecorator;
import org.apache.jena.graph.Triple;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import com.google.common.util.concurrent.ListenableFuture;
import com.nurkiewicz.asyncretry.AsyncRetryExecutor;
import com.nurkiewicz.asyncretry.RetryExecutor;
import com.nurkiewicz.asyncretry.backoff.Backoff;
import com.nurkiewicz.asyncretry.policy.AbortRetryException;
import com.nurkiewicz.asyncretry.policy.RetryPolicy;
public class QueryExecutionRetry
extends QueryExecutionDecorator
{
protected Supplier<QueryExecution> supplier;
protected int retryCount;
protected long retryDelayInMs;
protected RetryPolicy retryPolicy;
protected Backoff backoff;
protected boolean fixedDelay;
protected ScheduledExecutorService scheduler;
protected boolean aborted = false;
public QueryExecutionRetry(Supplier<QueryExecution> supplier, int retryCount, long retryDelayInMs, ScheduledExecutorService scheduler) {
super(null);
this.supplier = supplier;
this.retryCount = retryCount;
this.retryDelayInMs = retryDelayInMs;
this.scheduler = scheduler;
}
public QueryExecutionRetry(Supplier<QueryExecution> supplier, RetryPolicy retryPolicy, Backoff backoff, boolean fixedDelay, ScheduledExecutorService scheduler) {
super(null);
this.supplier = supplier;
this.retryPolicy = retryPolicy;
this.backoff = backoff;
this.fixedDelay = fixedDelay;
this.scheduler = scheduler;
}
public <T> T doTry(Callable<T> callable) {
Callable<T> wrapper = () -> {
if(decoratee == null) {
decoratee = supplier.get();
}
try {
return callable.call();
} catch(Exception e) {
if(aborted) {
throw new AbortRetryException();
}
decoratee = null;
throw new RuntimeException(e);
}
};
//ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
RetryExecutor executor = new AsyncRetryExecutor(scheduler, retryPolicy, backoff, fixedDelay);
ListenableFuture<T> future = executor.getWithRetry(wrapper);
// scheduler.shutdown();
try {
T result = future.get();
return result;
} catch (Exception e) {
throw new RuntimeException("Query Execution failed, even with retries.", e);
} finally {
scheduler.shutdown();
}
// CallableRetry<T> retry = new CallableRetry<T>(callable, retryCount, retryDelayInMs);
// try {
// T result = retry.call();
// return result;
// } catch(Exception e) {
// throw new RuntimeException(e);
// }
}
// TODO Maybe add synchronization so that we ensure that we do not obtain a new qe from the supplier
// immediately after having called abort
@Override
public void abort() {
aborted = true;
if(decoratee != null) {
decoratee.abort();
}
}
@Override
public boolean execAsk() {
return doTry(() -> super.execAsk());
}
@Override
public ResultSet execSelect() {
return doTry(() -> super.execSelect());
}
@Override
public Model execConstruct() {
return doTry(() -> super.execConstruct());
}
@Override
public Model execConstruct(final Model model) {
return doTry(() -> super.execConstruct(model));
}
@Override
public Iterator<Triple> execConstructTriples() {
return doTry(() -> super.execConstructTriples());
}
@Override
public Model execDescribe() {
return doTry(() -> super.execDescribe());
}
@Override
public Model execDescribe(final Model model) {
return doTry(() -> super.execDescribe(model));
}
@Override
public Iterator<Triple> execDescribeTriples() {
return doTry(() -> super.execDescribeTriples());
}
}