Package org.openrdf.repository.http

Source Code of org.openrdf.repository.http.HTTPRepositoryConnection

/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 2006-2009.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.repository.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

import org.openrdf.cursor.CollectionCursor;
import org.openrdf.cursor.EmptyCursor;
import org.openrdf.http.client.BooleanQueryClient;
import org.openrdf.http.client.ConnectionClient;
import org.openrdf.http.client.GraphQueryClient;
import org.openrdf.http.client.QueryClient;
import org.openrdf.http.client.RepositoryClient;
import org.openrdf.http.client.StatementClient;
import org.openrdf.http.client.TupleQueryClient;
import org.openrdf.http.protocol.transaction.operations.AddStatementOperation;
import org.openrdf.http.protocol.transaction.operations.ClearNamespacesOperation;
import org.openrdf.http.protocol.transaction.operations.ClearOperation;
import org.openrdf.http.protocol.transaction.operations.RemoveNamespaceOperation;
import org.openrdf.http.protocol.transaction.operations.RemoveStatementsOperation;
import org.openrdf.http.protocol.transaction.operations.SetNamespaceOperation;
import org.openrdf.http.protocol.transaction.operations.TransactionOperation;
import org.openrdf.model.BNode;
import org.openrdf.model.LiteralFactory;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.URIFactory;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.query.BindingSet;
import org.openrdf.query.BooleanQuery;
import org.openrdf.query.GraphQuery;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.Query;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.repository.base.RepositoryConnectionBase;
import org.openrdf.repository.http.exceptions.IllegalStatementException;
import org.openrdf.repository.http.exceptions.RepositoryReadOnlyException;
import org.openrdf.repository.http.helpers.GraphQueryResultCursor;
import org.openrdf.repository.http.helpers.HTTPBNodeFactory;
import org.openrdf.repository.util.ModelNamespaceResult;
import org.openrdf.result.ContextResult;
import org.openrdf.result.GraphResult;
import org.openrdf.result.ModelResult;
import org.openrdf.result.NamespaceResult;
import org.openrdf.result.TupleResult;
import org.openrdf.result.impl.ContextResultImpl;
import org.openrdf.result.impl.ModelResultImpl;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandler;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.store.Isolation;
import org.openrdf.store.StoreException;

/**
* RepositoryConnection that communicates with a server using the HTTP protocol.
* Methods in this class may throw the specific StoreException subclasses
* UnautorizedException and NotAllowedException, the semantics of which are
* defined by the HTTP protocol.
*
* @see org.openrdf.http.protocol.UnauthorizedException
* @see org.openrdf.http.protocol.NotAllowedException
* @author Arjohn Kampman
* @author Herko ter Horst
* @author James Leigh
*/
class HTTPRepositoryConnection extends RepositoryConnectionBase {

  private static final int MAX_TRAN_QUEUE = 1024;

  /*
   * Note: the following debugEnabled method are private so that they can be
   * removed when open connections no longer block other connections and they
   * can be closed silently (just like in JDBC).
   */
  private static boolean debugEnabled() {
    try {
      return System.getProperty("org.openrdf.repository.debug") != null;
    }
    catch (SecurityException e) {
      // Thrown when not allowed to read system properties, for example
      // when running in applets
      return false;
    }
  }

  /*-----------*
   * Variables *
   *-----------*/

  private final HTTPRepository repository;

  private final ConnectionClient client;

  private final ValueFactory vf;

  private final List<TransactionOperation> txn = new ArrayList<TransactionOperation>(MAX_TRAN_QUEUE / 2);

  /*
   * Stores a stack trace that indicates where this connection as created if
   * debugging is enabled.
   */
  private final Throwable creatorTrace;

  private volatile boolean isOpen = true;

  private volatile boolean autoCommit = true;

  /** If connection cannot use shared repository cache. */
  private volatile boolean modified = false;

  private volatile Isolation isolation;

  private volatile boolean readOnly = false;

  /*--------------*
   * Constructors *
   *--------------*/

  public HTTPRepositoryConnection(HTTPRepository repository, ConnectionClient client) {
    super(repository);
    this.repository = repository;
    this.client = client;

    URIFactory uf = repository.getURIFactory();
    LiteralFactory lf = repository.getLiteralFactory();
    HTTPBNodeFactory bf = new HTTPBNodeFactory(client.bnodes());
    this.vf = new ValueFactoryImpl(bf, uf, lf);

    this.creatorTrace = debugEnabled() ? new Throwable() : null;
  }

  /*---------*
   * Methods *
   *---------*/

  public ValueFactory getValueFactory() {
    return vf;
  }

  /**
   * Verifies that the connection is open, throws a {@link StoreException} if
   * it isn't.
   */
  protected void verifyIsOpen()
    throws StoreException
  {
    if (!isOpen()) {
      throw new StoreException("Connection has been closed");
    }
  }

  /**
   * Verifies that the connection is not in read-only mode, throws a
   * {@link StoreException} if it is.
   */
  protected void verifyNotReadOnly()
    throws StoreException
  {
    if (isReadOnly()) {
      throw new StoreException("Connection is in read-only mode");
    }
  }

  /**
   * Verifies that the connection has an active transaction, throws a
   * {@link StoreException} if it hasn't.
   */
  protected void verifyTxnActive()
    throws StoreException
  {
    if (isAutoCommit()) {
      throw new StoreException("Connection does not have an active transaction");
    }
  }

  /**
   * Verifies that the connection does not have an active transaction, throws a
   * {@link StoreException} if the connection is it has.
   */
  protected void verifyNotTxnActive(String msg)
    throws StoreException
  {
    if (!isAutoCommit()) {
      throw new StoreException(msg);
    }
  }

  public boolean isOpen()
    throws StoreException
  {
    return isOpen;
  }

  public void close()
    throws StoreException
  {
    if (isOpen()) {
      flush();
      isOpen = false;
      if (modified) {
        logger.warn("Rolling back transaction due to connection close", new Throwable());
        rollback();
      }
      client.close();
    }
  }

  @Override
  protected void finalize()
    throws Throwable
  {
    try {
      if (isOpen()) {
        if (creatorTrace != null) {
          logger.warn("Closing connection due to garbage collection, connection was created in:",
              creatorTrace);
        }
        close();
      }
    }
    finally {
      super.finalize();
    }
  }

  public Isolation getTransactionIsolation()
    throws StoreException
  {
    // TODO read from remote store
    if (isolation == null) {
      return repository.getMetaData().getDefaultIsolation();
    }
    return isolation;
  }

  public void setTransactionIsolation(Isolation isolation)
    throws StoreException
  {
    verifyNotTxnActive("transaction isolation level cannot be changed during a transaction");
    this.isolation = isolation;
  }

  public boolean isReadOnly() {
    return readOnly;
  }

  public void setReadOnly(boolean readOnly) {
    this.readOnly = readOnly;
  }

  public boolean isAutoCommit()
    throws StoreException
  {
    return autoCommit;
  }

  public void begin()
    throws StoreException
  {
    verifyIsOpen();
    verifyNotTxnActive("Connection already has an active transaction");

    client.begin();
    autoCommit = false;
  }

  public void commit()
    throws StoreException
  {
    verifyIsOpen();
    verifyTxnActive();

    flush();
    client.commit();
    repository.modified();
    modified = false;
    autoCommit = true;
  }

  public void rollback()
    throws StoreException
  {
    verifyIsOpen();
    verifyTxnActive();

    synchronized (txn) {
      txn.clear();
    }
    client.rollback();
    modified = false;
    autoCommit = true;
  }

  public Query prepareQuery(QueryLanguage ql, String qry, String baseURI)
    throws StoreException, MalformedQueryException
  {
    verifyIsOpen();

    flush();
    QueryClient query = client.queries().postQuery(ql, qry, baseURI);
    if (query instanceof GraphQueryClient) {
      return new HTTPGraphQuery(qry, (GraphQueryClient)query);
    }
    if (query instanceof BooleanQueryClient) {
      return new HTTPBooleanQuery(qry, (BooleanQueryClient)query);
    }
    if (query instanceof TupleQueryClient) {
      return new HTTPTupleQuery(qry, (TupleQueryClient)query);
    }
    throw new StoreException("Unsupported query type: " + query);
  }

  public TupleQuery prepareTupleQuery(QueryLanguage ql, String qry, String baseURI)
    throws StoreException, MalformedQueryException
  {
    verifyIsOpen();

    flush();
    TupleQueryClient query = client.queries().postTupleQuery(ql, qry, baseURI);
    return new HTTPTupleQuery(qry, query);
  }

  public GraphQuery prepareGraphQuery(QueryLanguage ql, String qry, String baseURI)
    throws StoreException, MalformedQueryException
  {
    verifyIsOpen();

    flush();
    GraphQueryClient query = client.queries().postGraphQuery(ql, qry, baseURI);
    return new HTTPGraphQuery(qry, query);
  }

  public BooleanQuery prepareBooleanQuery(QueryLanguage ql, String qry, String baseURI)
    throws StoreException, MalformedQueryException
  {
    verifyIsOpen();

    flush();
    BooleanQueryClient query = client.queries().postBooleanQuery(ql, qry, baseURI);
    return new HTTPBooleanQuery(qry, query);
  }

  public ContextResult getContextIDs()
    throws StoreException
  {
    verifyIsOpen();

    flush();
    List<Resource> contextList = new ArrayList<Resource>();

    TupleResult contextIDs = client.contexts().list();
    try {
      while (contextIDs.hasNext()) {
        BindingSet bindingSet = contextIDs.next();
        Value context = bindingSet.getValue("contextID");

        if (context instanceof Resource) {
          contextList.add((Resource)context);
        }
      }
    }
    finally {
      contextIDs.close();
    }

    return new ContextResultImpl(new CollectionCursor<Resource>(contextList));
  }

  public ModelResult match(Resource subj, URI pred, Value obj, boolean inf, Resource... ctx)
    throws StoreException
  {
    verifyIsOpen();

    if (noMatch(subj, pred, obj, inf, ctx)) {
      return new ModelNamespaceResult(this, new EmptyCursor<Statement>());
    }

    flush();
    StatementClient statements = client.statements();
    GraphResult result = statements.get(subj, pred, obj, inf, ctx);
    return new ModelResultImpl(new GraphQueryResultCursor(result));
  }

  public <H extends RDFHandler> H exportMatch(Resource subj, URI pred, Value obj, boolean includeInferred,
      H handler, Resource... contexts)
    throws RDFHandlerException, StoreException
  {
    verifyIsOpen();

    flush();
    client.statements().get(subj, pred, obj, includeInferred, handler, contexts);
    return handler;
  }

  public long sizeMatch(Resource subj, URI pred, Value obj, boolean includeInferred, Resource... contexts)
    throws StoreException
  {
    verifyIsOpen();

    if (cachable(subj, pred, obj, contexts)) {
      return repository.size(subj, pred, obj, includeInferred, contexts);
    }
    flush();
    return client.size().get(subj, pred, obj, includeInferred, contexts);
  }

  @Override
  public boolean hasMatch(Resource subj, URI pred, Value obj, boolean includeInferred, Resource... contexts)
    throws StoreException
  {
    verifyIsOpen();

    if (cachable(subj, pred, obj, contexts)) {
      return repository.hasStatement(subj, pred, obj, includeInferred, contexts);
    }
    flush();
    StatementClient statements = client.statements();
    statements.setLimit(1);
    GraphResult result = statements.get(subj, pred, obj, includeInferred, contexts);
    try {
      return result.hasNext();
    }
    finally {
      result.close();
    }
  }

  @Override
  protected void addInputStreamOrReader(Object inputStreamOrReader, String baseURI, RDFFormat dataFormat,
      Resource... contexts)
    throws IOException, RDFParseException, StoreException
  {
    if (repository.isReadOnly()) {
      throw new RepositoryReadOnlyException();
    }
    // Send bytes directly to the server
    flush();
    StatementClient httpClient = client.statements();
    if (inputStreamOrReader instanceof InputStream) {
      httpClient.upload(((InputStream)inputStreamOrReader), baseURI, dataFormat, false, contexts);
    }
    else if (inputStreamOrReader instanceof Reader) {
      httpClient.upload(((Reader)inputStreamOrReader), baseURI, dataFormat, false, contexts);
    }
    else {
      throw new IllegalArgumentException("inputStreamOrReader must be an InputStream or a Reader, is a: "
          + inputStreamOrReader.getClass());
    }
    if (isAutoCommit()) {
      repository.modified();
    }
    else {
      modified = true;
    }
  }

  public void add(Resource subject, URI predicate, Value object, Resource... contexts)
    throws StoreException
  {
    verifyIsOpen();
    verifyNotReadOnly();

    if (repository.isReadOnly()) {
      throw new RepositoryReadOnlyException();
    }
    if (repository.isIllegal(subject, predicate, object, contexts)) {
      throw new IllegalStatementException();
    }
    add(new AddStatementOperation(subject, predicate, object, contexts));
  }

  public void removeMatch(Resource subject, URI predicate, Value object, Resource... contexts)
    throws StoreException
  {
    verifyIsOpen();
    verifyNotReadOnly();

    if (repository.isReadOnly()) {
      throw new RepositoryReadOnlyException();
    }
    if (!noMatch(subject, predicate, object, true, contexts)) {
      add(new RemoveStatementsOperation(subject, predicate, object, contexts));
    }
  }

  @Override
  public void clear(Resource... contexts)
    throws StoreException
  {
    verifyIsOpen();
    verifyNotReadOnly();

    if (repository.isReadOnly()) {
      throw new RepositoryReadOnlyException();
    }
    add(new ClearOperation(contexts));
  }

  public void removeNamespace(String prefix)
    throws StoreException
  {
    verifyIsOpen();
    verifyNotReadOnly();

    if (repository.isReadOnly()) {
      throw new RepositoryReadOnlyException();
    }
    add(new RemoveNamespaceOperation(prefix));
  }

  public void clearNamespaces()
    throws StoreException
  {
    verifyIsOpen();
    verifyNotReadOnly();

    if (repository.isReadOnly()) {
      throw new RepositoryReadOnlyException();
    }
    add(new ClearNamespacesOperation());
  }

  public void setNamespace(String prefix, String name)
    throws StoreException
  {
    verifyIsOpen();
    verifyNotReadOnly();

    if (repository.isReadOnly()) {
      throw new RepositoryReadOnlyException();
    }
    add(new SetNamespaceOperation(prefix, name));
  }

  public NamespaceResult getNamespaces()
    throws StoreException
  {
    verifyIsOpen();

    if (!modified && isAutoCommit()) {
      return repository.getNamespaces();
    }
    flush();
    return client.namespaces().list();
  }

  public String getNamespace(String prefix)
    throws StoreException
  {
    verifyIsOpen();

    if (!modified && isAutoCommit()) {
      return repository.getNamespace(prefix);
    }
    flush();
    return client.namespaces().get(prefix);
  }

  @Override
  public String toString() {
    return repository.toString() + " Connection";
  }

  protected RepositoryClient getClient()
    throws StoreException
  {
    flush();
    return client;
  }

  /**
   * If this connection has not modified any statements and the pattern does
   * not contains connection specific BNodes.
   */
  private boolean cachable(Resource subj, URI pred, Value obj, Resource... contexts)
    throws StoreException
  {
    if (modified || !isAutoCommit()) {
      return false;
    }
    if (subj instanceof BNode) {
      return false;
    }
    if (obj instanceof BNode) {
      return false;
    }
    if (contexts == null) {
      return true;
    }
    for (Resource ctx : contexts) {
      if (ctx instanceof BNode) {
        return false;
      }
    }
    return true;
  }

  /**
   * Will never connect to the remote server.
   *
   * @return if it is known that this pattern (or super set) has no matches.
   */
  private boolean noMatch(Resource subj, URI pred, Value obj, boolean includeInferred, Resource... contexts)
    throws StoreException
  {
    if (cachable(subj, pred, obj, contexts)) {
      return repository.noMatch(subj, pred, obj, includeInferred, contexts);
    }
    return false;
  }

  private void add(TransactionOperation operation)
    throws StoreException
  {
    modified = true;
    synchronized (txn) {
      txn.add(operation);
      if (isAutoCommit() || txn.size() >= MAX_TRAN_QUEUE) {
        flush();
      }
    }
  }

  private void flush()
    throws StoreException
  {
    synchronized (txn) {
      if (!txn.isEmpty()) {
        client.statements().post(txn);
        txn.clear();
        if (isAutoCommit()) {
          repository.modified();
          modified = false;
        }
      }
    }
  }
}
TOP

Related Classes of org.openrdf.repository.http.HTTPRepositoryConnection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.