Dear Jena community,

we didn't find a way (in the original Jena API 2.10) to completely detach a result set from the underlying model. It's possible to detach it from the QueryExecution by using ResultSetFactory.copyResults but the bindings still point to the TDB backed model.

By this we encountered some issues as we store results sets for a long time and the read transaction that created the results stays alive (even if we calls commit or end). Having this read transaction opened leads to problems when you have many writers after that (stack usage, huge journal files, ...).

We finally found a way to convert TDB backed result set to a completely detached ResultSetRewindable. If you find this useful, it's attached below.

Cheers
André



package com.hojoki.core.jena;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.ResultSetRewindable;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.sparql.core.ResultBinding;
import com.hp.hpl.jena.sparql.core.Var;
import com.hp.hpl.jena.sparql.engine.binding.Binding;
import com.hp.hpl.jena.sparql.engine.binding.BindingFactory;
import com.hp.hpl.jena.tdb.store.GraphTDB;

public class ResultsetUtil {

  public static boolean isTDBBacked(ResultSet resultSet) {
    Model model = resultSet.getResourceModel();
    if (model != null) {
      Graph g = model.getGraph();
      return g instanceof GraphTDB;
    }
    return false;
  }

public static ResultSetRewindable createDetachedRewindableResultSet(ResultSet resultSet) {

    if(resultSet instanceof HojokiResultSet)
      return (HojokiResultSet) resultSet;

return isTDBBacked(resultSet) ? detachAndConvertToRewindable(resultSet) : convertToRewindable(resultSet);
  }

private static ResultSetRewindable convertToRewindable(ResultSet resultSet) {
    return toHojokiResultSet(resultSet, false);
  }

private static ResultSetRewindable detachAndConvertToRewindable(ResultSet resultSet) {
    return toHojokiResultSet(resultSet, true);
  }

public static ResultSetRewindable toHojokiResultSet(ResultSet set, boolean detachBindings) {

    List<String> resultVars = set.getResultVars();

    final List<Binding> bindings = new ArrayList<Binding>();
    while (set.hasNext()) {
      Binding binding = set.nextBinding();
      bindings.add(detachBindings ? detachedBinding(binding) : binding);
    }

    return new HojokiResultSet(resultVars, null, bindings);
  }

  protected static Binding detachedBinding(Binding binding) {
    Iterator<Var> varsIt = binding.vars();
    Binding initial = BindingFactory.binding();
    while (varsIt.hasNext()) {
      Var var = varsIt.next();
      initial = BindingFactory.binding(initial, var, binding.get(var));
    }
    return initial;
  }

}

class HojokiResultSet implements ResultSetRewindable {
  private List<String> resultVars;
  private int rowNumber;
  private Model model;
  private final List<Binding> bindings;
  private Iterator<Binding> iter;

public HojokiResultSet(List<String> resultVars, Model m, List<Binding> bindings) {
    this.bindings = bindings;
    this.resultVars = resultVars;
    model = m;
    reset();
  }

  /**
   * @throws UnsupportedOperationException
   *           Always thrown.
   */

  @Override
  public void remove() throws java.lang.UnsupportedOperationException {
throw new UnsupportedOperationException(this.getClass().getName() + ".remove");
  }

  /**
   * Is there another possibility?
   */
  @Override
  public boolean hasNext() {
    return iter.hasNext();
  }

  @Override
  public Binding nextBinding() {

    Binding binding = iter.next();
    if (binding != null)
      rowNumber++;

    return binding;
  }

  /**
* Moves onto the next result possibility. The returned object is actual the
   * binding for this result.
   */
  @Override
  public QuerySolution nextSolution() {
    return new ResultBinding(model, nextBinding());
  }

  /** Moves onto the next result possibility. */

  @Override
  public QuerySolution next() {
    return nextSolution();
  }

  /**
* Return the "row number" - a count of the number of possibilities returned
   * so far. Remains valid (as the total number of possibilities) after the
   * iterator ends.
   */

  @Override
  public int getRowNumber() {
    return rowNumber;
  }

  /**
   * Get the variable names for the projection
   */

  @Override
  public List<String> getResultVars() {
    return resultVars;
  }

  public Model getModel() {
    return model;
  }

  @Override
  public Model getResourceModel() {
    return getModel();
  }

  @Override
  public void reset() {
    iter = bindings.iterator();
    rowNumber = 0;
  }

  @Override
  public int size() {
    return bindings.size();
  }

}


--
Dr. André Lanka  *  0178 / 134 44 47  *  http://dr-lanka.de

Reply via email to