org.openanzo.datasource.nodecentric.internal.NodeCentricDatasource.java Source code

Java tutorial

Introduction

Here is the source code for org.openanzo.datasource.nodecentric.internal.NodeCentricDatasource.java

Source

/*******************************************************************************
 * Copyright (c) 2007 Cambridge Semantics Incorporated.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * File:        $Source$
 * Created by:  Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
 * Created on:  Oct 27, 2007
 * Revision:   $Id$
 *
 * Contributors:
 *     Cambridge Semantics Incorporated - initial API and implementation
 *******************************************************************************/
package org.openanzo.datasource.nodecentric.internal;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URL;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.collections15.MultiMap;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.openanzo.cache.ICache;
import org.openanzo.cache.ICacheListener;
import org.openanzo.cache.ICacheProvider;
import org.openanzo.datasource.DatasourceDictionary;
import org.openanzo.datasource.IAuthorizationService;
import org.openanzo.datasource.IDatasource;
import org.openanzo.datasource.IModelService;
import org.openanzo.datasource.IQueryService;
import org.openanzo.datasource.IReplicationService;
import org.openanzo.datasource.IResetService;
import org.openanzo.datasource.IServerQuadStoreProvider;
import org.openanzo.datasource.IUpdateService;
import org.openanzo.datasource.nodecentric.query.GraphSet;
import org.openanzo.datasource.nodecentric.sql.Backup;
import org.openanzo.datasource.nodecentric.sql.GlitterRdbWrapper;
import org.openanzo.datasource.nodecentric.sql.LastTransactionTime;
import org.openanzo.datasource.nodecentric.sql.NamedGraphRdbWrapper;
import org.openanzo.datasource.nodecentric.sql.ServerRdbWrapper;
import org.openanzo.datasource.nodecentric.sql.Backup.SelectFullStatementsNRResult;
import org.openanzo.datasource.nodecentric.sql.Backup.SelectFullStatementsResult;
import org.openanzo.datasource.nodecentric.sql.Backup.SelectNamedGraphsRevisionedResult;
import org.openanzo.datasource.nodecentric.sql.Backup.SelectStatementsRevisionedResult;
import org.openanzo.datasource.nodecentric.sql.GlitterRdbWrapper.SelectQueryDatasetsResult;
import org.openanzo.datasource.services.BaseDatasource;
import org.openanzo.datasource.services.CachedAuthorizationService;
import org.openanzo.datasource.update.NamedGraphType;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.AnzoRuntimeException;
import org.openanzo.exceptions.CompoundAnzoException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.jdbc.container.CoreDBConfiguration;
import org.openanzo.jdbc.layout.CompositeNodeLayout;
import org.openanzo.jdbc.utils.ClosableIterator;
import org.openanzo.jdbc.utils.PreparedStatementProvider;
import org.openanzo.jdbc.utils.RdbException;
import org.openanzo.ontologies.openanzo.Dataset;
import org.openanzo.ontologies.openanzo.NamedGraph;
import org.openanzo.ontologies.system.Datasource;
import org.openanzo.ontologies.system.DatasourceCapability;
import org.openanzo.ontologies.system.SystemFactory;
import org.openanzo.osgi.IServiceTrackerListener;
import org.openanzo.osgi.OsgiServiceTracker;
import org.openanzo.rdf.BlankNode;
import org.openanzo.rdf.Constants;
import org.openanzo.rdf.IDataset;
import org.openanzo.rdf.INamedGraph;
import org.openanzo.rdf.Literal;
import org.openanzo.rdf.PlainLiteral;
import org.openanzo.rdf.Resource;
import org.openanzo.rdf.Statement;
import org.openanzo.rdf.TypedLiteral;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.Value;
import org.openanzo.rdf.Constants.GRAPHS;
import org.openanzo.rdf.datatype.TypeMaps;
import org.openanzo.rdf.utils.Collections;
import org.openanzo.rdf.utils.ReadWriteUtils;
import org.openanzo.rdf.vocabulary.XMLSchema;
import org.openanzo.services.AnzoPrincipal;
import org.openanzo.services.DynamicServiceStats;
import org.openanzo.services.IAuthorizationEventListener;
import org.openanzo.services.IOperationContext;
import org.openanzo.services.IStatisticsProvider;
import org.openanzo.services.IUpdateResultListener;
import org.openanzo.services.impl.BaseOperationContext;
import org.openanzo.services.serialization.BackupRevision;
import org.openanzo.services.serialization.IBackupHandler;
import org.openanzo.services.serialization.XMLBackupReader;
import org.openanzo.services.serialization.XMLGraphBackupWriter;
import org.openanzo.services.serialization.XMLWritingUtils;
import org.osgi.framework.BundleContext;
import org.osgi.service.event.EventAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Back-end implementation that talks to a NodeCentric RDB store.
 * 
 * @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
 * 
 */
public class NodeCentricDatasource extends BaseDatasource implements IDatasource, IStatisticsProvider {

    protected static final Logger log = LoggerFactory.getLogger(NodeCentricDatasource.class);

    /** Max number of operations per transactions */
    public static final int MAX_OPERATION_SIZE = 32000;

    private static final long SCHEMA_VERSION = 12;

    private static final int PS_CACHE_SIZE = 100;

    static final String serverUpper = "SERVER";

    static final String serverLower = "server";

    private static final String table = "TABLE";

    private static final String seq = "SEQUENCE";

    private static final String view = "VIEW";

    static final String STATEMENTS = "STATEMENTS";

    static final String STATEMENTS_NR = "STATEMENTS_NR";

    /** Max number of JDBC connections to open in pool */
    private int MAX_QUERY_CONNECTIONS = 8;

    /** Max number of JDBC connections to open in pool */
    private int MAX_WRITE_CONNECTIONS = 4;

    /** Existence of this table determines if database has been initialized */
    private static final String INITIAL_DATABASE_TABLE = "STATEMENTS";

    /** Connection Configuration */
    private final CoreDBConfiguration configuration;

    /** Query Pool */
    public final GenericObjectPool queryPool;

    /** Write Pool */
    public final GenericObjectPool writePool;

    private final IAuthorizationService authorizationService;

    private final NodeCentricResetService resetService;

    private final NodeCentricIndexService indexService;

    private final NodeCentricIndexUpdateHandler indexHandler;

    private final NodeCentricModelService modelService;

    private final NodeCentricUpdateService updateService;

    private final NodeCentricReplicationService replicationService;

    private final NodeCentricQueryService queryService;

    private final WeakHashMap<Connection, ReentrantLock> connectionLocks = new WeakHashMap<Connection, ReentrantLock>();

    protected final PreparedStatementProvider statementProvider;

    private final CompositeNodeLayout nodeLayout;

    private static final String INITIALIZE_CONNECTION = "initializeConnection";

    private final NodeCentricDatasourceStatistics stats = new NodeCentricDatasourceStatistics();

    private final ReentrantLock serverLock = new ReentrantLock();

    private final ReentrantLock graphSetLock = new ReentrantLock();

    private final Condition graphSetReady = graphSetLock.newCondition();

    private final Collection<Long> purgedSets = new ArrayList<Long>();

    private final Thread purgeSetThread;

    /** Shared reset lock used during testing */
    private final ReentrantReadWriteLock resetLock = new ReentrantReadWriteLock();

    private final Set<IAuthorizationEventListener> aclEventListeners;

    private final OsgiServiceTracker<IUpdateResultListener> updateListenerTracker;

    private final String instanceId;

    private final EventAdmin eventAdmin;

    private final ICacheProvider cacheProvider;

    private boolean initialized = false;

    private final ICache<GraphSet, GraphSet> graphSets;

    private Dictionary<? extends Object, ? extends Object> configProperties;

    /**
     * Create a new NodeCentricDatasource.
     * 
     * @param bundleContext
     * @param configProperties
     * @param cacheProvider
     * @param aclEventListeners
     * @param eventAdmin
     * @throws AnzoException
     */
    public NodeCentricDatasource(BundleContext bundleContext,
            Dictionary<? extends Object, ? extends Object> configProperties, ICacheProvider cacheProvider,
            Set<IAuthorizationEventListener> aclEventListeners, EventAdmin eventAdmin) throws AnzoException {
        super(configProperties);
        this.configProperties = configProperties;
        //Runtime runtime = Runtime.getRuntime();
        //int nrOfProcessors = runtime.availableProcessors();
        //this.MAX_QUERY_CONNECTIONS = Math.max(2, nrOfProcessors - 1);
        // this.MAX_WRITE_CONNECTIONS = Math.max(2, nrOfProcessors - 1);
        if (DatasourceDictionary.getMaxWriteConnections(configProperties) != null) {
            this.MAX_WRITE_CONNECTIONS = DatasourceDictionary.getMaxWriteConnections(configProperties);
        }
        if (DatasourceDictionary.getMaxQueryConnections(configProperties) != null) {
            this.MAX_QUERY_CONNECTIONS = DatasourceDictionary.getMaxQueryConnections(configProperties);
        }
        this.aclEventListeners = aclEventListeners;
        this.cacheProvider = cacheProvider;
        this.eventAdmin = eventAdmin;
        this.graphSets = cacheProvider.openCache("GraphSetCache", 20, false);
        this.graphSets.registerListener(new ICacheListener<GraphSet, GraphSet>() {
            public void elementRemoved(GraphSet key, GraphSet value) {
                graphSetLock.lock();
                try {
                    purgedSets.add(key.getSetId());
                    graphSetReady.signal();
                } finally {
                    graphSetLock.unlock();
                }
            }
        });

        instanceId = (String) configProperties.get(org.osgi.framework.Constants.SERVICE_PID);
        configuration = CoreDBConfiguration.createConfiguration(configProperties);
        statementProvider = new PreparedStatementProvider();
        URL psURL = bundleContext.getBundle().getResource(configuration.getSqlFilename());
        if (psURL != null) {
            try {
                InputStream stream = psURL.openStream();
                statementProvider.loadSQLFile(stream);
            } catch (IOException ioe) {
                throw new AnzoException(ExceptionConstants.RDB.FAILED_INITIALIZING_POOL, ioe);
            }
        }
        nodeLayout = new CompositeNodeLayout(statementProvider, configuration.getSupportsSequences(), null,
                configuration.getContainerName(), configuration.getMaxLongObjectLength(),
                configuration.getOptimizationString(), true, configuration.getSupportsIdentity(),
                configuration.getSessionPrefix(),
                this.cacheProvider.<Long, URI>openCache(instanceId + "UriValue", 20000, true),
                this.cacheProvider.<URI, Long>openCache(instanceId + "UriIdValue", 20000, true),
                this.cacheProvider.<Long, BlankNode>openCache(instanceId + "BlankValue", 20000, true),
                this.cacheProvider.<BlankNode, Long>openCache(instanceId + "BlankIdValue", 20000, true),
                this.cacheProvider.<Long, PlainLiteral>openCache(instanceId + "PlainLiteralValue", 20000, true),
                this.cacheProvider.<PlainLiteral, Long>openCache(instanceId + "PlainLiteralIdValue", 20000, true),
                this.cacheProvider.<Long, TypedLiteral>openCache(instanceId + "TypedLiteralValue", 20000, true),
                this.cacheProvider.<TypedLiteral, Long>openCache(instanceId + "TypedLiteralIdValue", 20000, true),
                this.cacheProvider.<Long, String>openCache(instanceId + "LanguageValue", 20000, true),
                this.cacheProvider.<String, Long>openCache(instanceId + "LanguageIdValue", 20000, true),
                this.cacheProvider.<Long, String>openCache(instanceId + "DatatypeValue", 20000, true),
                this.cacheProvider.<String, Long>openCache(instanceId + "DatatypeIdValue", 20000, true));

        this.isPrimary = DatasourceDictionary.getIsPrimary(configProperties);

        resetService = new NodeCentricResetService(resetEnabled, this);
        updateService = new NodeCentricUpdateService(this);
        modelService = new NodeCentricModelService(this, (enableCaching) ? this.cacheProvider : null);
        indexService = new NodeCentricIndexService(this);
        indexHandler = new NodeCentricIndexUpdateHandler(this);
        queryService = new NodeCentricQueryService(this, (enableCaching) ? this.cacheProvider : null);
        replicationService = new NodeCentricReplicationService(this, (enableCaching) ? this.cacheProvider : null);
        authorizationService = new CachedAuthorizationService(new NodeCentricAuthorizationService(this),
                (enableCaching) ? this.cacheProvider : null);

        queryPool = initializeConnectionFactory(false, MAX_QUERY_CONNECTIONS);
        writePool = initializeConnectionFactory(true, MAX_WRITE_CONNECTIONS);

        updateService.addDatasourceUpdateResultListener(indexHandler);
        if (replicationService.getCacheUpdateListener() != null)
            updateService.addDatasourceUpdateResultListener(replicationService.getCacheUpdateListener());
        if (queryService.getCacheUpdateListener() != null)
            updateService.addDatasourceUpdateResultListener(queryService.getCacheUpdateListener());
        if (modelService.getCacheUpdateListener() != null)
            updateService.addDatasourceUpdateResultListener(modelService.getCacheUpdateListener());
        resetService.start();
        updateService.start();
        queryService.start();
        replicationService.start();
        authorizationService.start();
        indexHandler.start();
        indexService.start();
        modelService.start();
        try {
            Connection connection = (Connection) writePool.borrowObject();
            writePool.returnObject(connection);
        } catch (AnzoException ae) {
            throw ae;
        } catch (Exception e) {
            throw new AnzoException(ExceptionConstants.RDB.FAILED_INITIALIZING_POOL, e);
        }
        updateListenerTracker = new OsgiServiceTracker<IUpdateResultListener>(
                new IServiceTrackerListener<IUpdateResultListener>() {

                    public void unregisterService(IUpdateResultListener service) {
                        updateService.removeGlobalUpdateResultListener(service);
                    }

                    public void registerService(IUpdateResultListener service) {
                        updateService.addGlobalUpdateResultListener(service);
                    }

                    public Class<IUpdateResultListener> getComponentType() {
                        return IUpdateResultListener.class;
                    }

                }, bundleContext);
        updateListenerTracker.open();
        setupCapabilities();
        // state = ServiceLifecycleState.STARTED;
        setupStats(bundleContext);
        indexHandler.rebuildIndex(indexHandler.rebuildIndex);
        purgeSetThread = new Thread("PurgeQueryGraphSets") {
            @Override
            public void run() {
                while (!interrupted()) {
                    try {
                        graphSetLock.lockInterruptibly();
                        try {
                            if (purgedSets.size() > 0) {
                                try {
                                    Connection connection = getWriteConnection();
                                    begin(connection, true, true);
                                    GlitterRdbWrapper.BatchPurgeQueryDataset ps = new GlitterRdbWrapper.BatchPurgeQueryDataset(
                                            connection, statementProvider);
                                    try {
                                        for (Long id : purgedSets) {
                                            ps.addEntry(id);
                                        }
                                        purgedSets.clear();
                                        ps.executeStatement();
                                        commit(connection, true, true);
                                        ps.close();
                                    } finally {
                                        returnWriteConnection(connection);
                                    }
                                } catch (AnzoException ae) {
                                    log.error(LogUtils.RDB_MARKER, "Error purging graph datasets", ae);
                                }
                            }
                            graphSetReady.await();
                        } finally {
                            graphSetLock.unlock();
                        }
                    } catch (InterruptedException ie) {
                        return;
                    }
                }
            }
        };
        purgeSetThread.setDaemon(true);
        purgeSetThread.start();
    }

    public boolean isSelfDescribing() {
        return true;
    }

    public DynamicServiceStats getStatistics() {
        return stats;
    }

    public String getName() {
        String name = "";
        try {
            name = "Datasource=NodeCentricDatasource_" + URLEncoder.encode(getInstanceURI().toString(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("Should never happen since UTF-8 must be supported by all JVMs on all platforms.", e);
        }
        return name;
    }

    public String getDescription() {
        return "Node Centric Datasource:" + getInstanceURI().toString();
    }

    public IModelService getModelService() {
        return modelService;
    }

    public IQueryService getQueryService() {
        return queryService;
    }

    public IReplicationService getReplicationService() {
        return replicationService;
    }

    public IResetService getResetService() {
        return resetService;
    }

    public IUpdateService getUpdateService() {
        return updateService;
    }

    public IServerQuadStoreProvider getServerQuadStoreProvider() {
        return updateService;
    }

    public IAuthorizationService getAuthorizationService() {
        return authorizationService;
    }

    @Override
    public void setupCapabilities() {
        super.setupCapabilities();
    }

    /**
     * @param bundleStopping
     *            true if the bundle stopping explicitly, and not due to overall osgi system shutdown
     * @throws AnzoException
     *             {@link ExceptionConstants.OSGI#ERROR_STOPPING_SERVICE} if there was a problem stopping this service
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_CLOSING_POOL} if there was a problem closing the database connection pool
     */
    public void close(boolean bundleStopping) throws AnzoException {
        if (!bundleStopping) {
            cleanupStats();
        }
        ArrayList<AnzoException> exceptions = new ArrayList<AnzoException>();
        if (queryPool != null) {
            try {
                queryPool.close();
            } catch (Exception e) {
                exceptions.add(new AnzoException(ExceptionConstants.RDB.FAILED_CLOSING_POOL, e));
            }
        }
        if (writePool != null) {
            try {
                writePool.close();
            } catch (Exception e) {
                exceptions.add(new AnzoException(ExceptionConstants.RDB.FAILED_CLOSING_POOL, e));
            }
        }
        if (exceptions.size() > 0) {
            throw new CompoundAnzoException(exceptions, ExceptionConstants.OSGI.ERROR_STOPPING_SERVICE);
        }
        if (purgeSetThread != null) {
            purgeSetThread.interrupt();
        }
    }

    @Override
    public void postReset() {
        super.postReset();
        setupCapabilities();
    }

    void resetDatasource() throws AnzoException {
        getNodeLayout().clearCache();
        modelService.reset();
        updateService.reset();
        indexService.reset();
        authorizationService.reset();
        replicationService.reset();
        queryService.reset();
        indexHandler.reset();
        graphSets.clear();
        purgedSets.clear();
        capabilities = null;
    }

    /**
     * Backup data to a file
     * 
     * @param fileName
     * @throws AnzoException
     */
    public void backupData(String fileName) throws AnzoException {
        Connection connection = getWriteConnection();
        try {
            lockTable(connection, true);
            ClosableIterator<Long> uuids = Backup.selectDistinctRevisionedUUIDs(statementProvider, connection);
            Collection<Long> uuidIds = new ArrayList<Long>();
            Collections.addAll(uuids, uuidIds);
            uuids = Backup.selectDistinctNonRevisionedUUIDs(statementProvider, connection);
            Collections.addAll(uuids, uuidIds);
            //Calendar cal = Calendar.getInstance();
            //directoryName + "/datasourceBackup_" + cal.get(Calendar.YEAR) + cal.get(Calendar.MONTH) + cal.get(Calendar.DAY_OF_MONTH) + cal.get(Calendar.HOUR_OF_DAY) + cal.get(Calendar.MINUTE) + ".xml"
            exportGraphs(uuidIds, fileName, connection);
        } finally {
            unlockTable(connection, true);
            returnWriteConnection(connection);
        }
    }

    /**
     * Export a set of set of graphs
     * 
     * @param graphsToExport
     *            set of graphs to export
     * @param fileName
     *            filename to which exports are written
     * @throws AnzoException
     */
    public void exportGraphs(Collection<URI> graphsToExport, String fileName) throws AnzoException {
        final NodeCentricOperationContext connectionContext = getWriteContext(
                new BaseOperationContext("EXPORT", BaseOperationContext.generateOperationId(),
                        new AnzoPrincipal("sysadmin", null, null, true, false)));
        try {
            lockTable(connectionContext.getConnection(), true);
            Collection<Long> uuidIds = new ArrayList<Long>();
            for (URI graph : graphsToExport) {
                Long graphId = connectionContext.getNodeLayout().fetchId(graph, connectionContext.getConnection());
                if (graphId != null) {
                    ClosableIterator<Long> uuids = Backup.selectRevisionedGraphUUIDs(
                            connectionContext.getStatementProvider(), connectionContext.getConnection(), graphId);
                    Collections.addAll(uuids, uuidIds);
                    uuids = Backup.selectNonRevisionedGraphUUIDs(connectionContext.getStatementProvider(),
                            connectionContext.getConnection(), graphId);
                    Collections.addAll(uuids, uuidIds);
                }
            }
            exportGraphs(uuidIds, fileName, connectionContext.getConnection());
        } finally {
            unlockTable(connectionContext.getConnection(), true);
            returnWriteContext(connectionContext);
        }
    }

    @SuppressWarnings("null")
    private void exportGraphs(Collection<Long> uuids, String fileName, Connection connection) throws AnzoException {
        try {
            Writer writer = new OutputStreamWriter(new FileOutputStream(fileName), Constants.byteEncoding);
            XMLGraphBackupWriter.writeBackupsStart(writer);
            for (Long uuidId : uuids) {
                ClosableIterator<SelectNamedGraphsRevisionedResult> results = Backup
                        .selectNamedGraphsRevisioned(statementProvider, connection, uuidId);
                URI ngURI = null;
                URI metaURI = null;
                URI uuid = (URI) nodeLayout.fetchValue(uuidId, connection);
                Long id = null;
                Long metaId = null;
                if (results.hasNext()) {
                    SelectNamedGraphsRevisionedResult result = results.next();
                    ngURI = (URI) nodeLayout.fetchValue(result.getId(), connection);
                    id = result.getId();
                    metaId = result.getMetaId();
                    metaURI = (URI) nodeLayout.fetchValue(result.getMetaId(), connection);
                    URI lastMode = (URI) nodeLayout.fetchValue(result.getLastModifiedBy(), connection);
                    long revision = result.getRevision();
                    long hstart = result.getHstart();
                    long hend = result.getHend();
                    XMLGraphBackupWriter.writeNamedGraphBackupStart(writer, ngURI, metaURI, uuid, true);
                    XMLGraphBackupWriter.writeRevisionsStart(writer);
                    XMLGraphBackupWriter.writeRevisionInfo(writer, revision, hstart, hend, lastMode);
                }
                while (results.hasNext()) {
                    SelectNamedGraphsRevisionedResult result = results.next();
                    URI lastMode = (URI) nodeLayout.fetchValue(result.getLastModifiedBy(), connection);
                    long revision = result.getRevision();
                    long hstart = result.getHstart();
                    long hend = result.getHend();
                    XMLGraphBackupWriter.writeRevisionInfo(writer, revision, hstart, hend, lastMode);
                }
                XMLGraphBackupWriter.writeRevisionsEnd(writer);
                XMLGraphBackupWriter.writeStatementsStart(writer, ngURI.toString());
                ClosableIterator<SelectStatementsRevisionedResult> statements = Backup
                        .selectStatementsRevisioned(statementProvider, connection, uuidId, id);
                while (statements.hasNext()) {
                    SelectStatementsRevisionedResult result = statements.next();
                    Resource subj = (Resource) nodeLayout.fetchValue(result.getSubject(), connection);
                    URI pred = (URI) nodeLayout.fetchValue(result.getPredicate(), connection);
                    Value obj = nodeLayout.fetchValue(result.getObject(), connection);
                    long end = result.getEnd();
                    XMLWritingUtils.handleStatement(writer, subj, pred, obj, ngURI, result.getStart(),
                            (end > 0) ? end : null);

                }
                XMLGraphBackupWriter.writeStatementsEnd(writer);
                XMLGraphBackupWriter.writeStatementsStart(writer, metaURI.toString());
                ClosableIterator<SelectStatementsRevisionedResult> metaStatements = Backup
                        .selectStatementsRevisioned(statementProvider, connection, uuidId, metaId);
                while (metaStatements.hasNext()) {
                    SelectStatementsRevisionedResult result = metaStatements.next();
                    Resource subj = (Resource) nodeLayout.fetchValue(result.getSubject(), connection);
                    URI pred = (URI) nodeLayout.fetchValue(result.getPredicate(), connection);
                    Value obj = nodeLayout.fetchValue(result.getObject(), connection);
                    long end = result.getEnd();
                    XMLWritingUtils.handleStatement(writer, subj, pred, obj, metaURI, result.getStart(),
                            (end > 0) ? end : null);
                }
                XMLGraphBackupWriter.writeStatementsEnd(writer);
                XMLGraphBackupWriter.writeNamedGraphBackupEnd(writer);
                writer.flush();
            }
            XMLGraphBackupWriter.writeBackupsEnd(writer);
            writer.flush();
            writer.close();
        } catch (IOException ioe) {
            log.error(LogUtils.RDB_MARKER, "Error exporting graphs", ioe);
            throw new AnzoException(ExceptionConstants.IO.WRITE_ERROR, ioe);
        }
    }

    /**
     * Reset the server from a set of backup data
     * 
     * @param fileName
     *            filename for backup data
     * @throws AnzoException
     */
    public void restoreData(String fileName) throws AnzoException {
        resetService.restoreData(fileName);
    }

    /**
     * Initialize a jdbc connection pool
     * 
     * @param type
     *            either rw or query pool
     * @param maxActive
     *            maximum number of connections in pool
     * @param configuration
     *            configuration properties used for creating database connections
     * @return connection pool
     * @throws AnzoException
     *             {@link ExceptionConstants#RDB.DRIVER_NAME} if there was a problem loading class for database driver
     */
    private GenericObjectPool initializeConnectionFactory(boolean write, int maxActive) throws AnzoException {
        // Will use in jndi
        // DataSource ds = (DataSource) ctx.lookup(RepositoryProperties.getDatabaseJndiName(properties));
        try {
            Class.forName(configuration.getDriverClassName());
        } catch (ClassNotFoundException e1) {
            throw new AnzoException(ExceptionConstants.RDB.DRIVER_NAME, e1, configuration.getDriverClassName());
        }
        Properties props = new Properties();
        props.put("user", configuration.getUser());
        props.put("password", configuration.getPassword());
        props.put("SetBigStringTryClob", "true");
        ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(configuration.getJdbcUrl(), props);
        GenericObjectPool connectionPool = new GenericObjectPool();
        connectionPool.setMaxActive(maxActive);
        connectionPool.setMaxIdle(Math.max(1, maxActive / 2));
        connectionPool.setMinIdle(0);
        connectionPool.setMinEvictableIdleTimeMillis(1000 * 60 * 10);
        connectionPool.setTimeBetweenEvictionRunsMillis(1000 * 60 * 10);
        connectionPool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
        connectionPool.setMaxWait(GenericKeyedObjectPool.DEFAULT_MAX_WAIT);
        connectionPool.setTestOnBorrow(true);
        GenericKeyedObjectPoolFactory statementPool = new GenericKeyedObjectPoolFactory(null, PS_CACHE_SIZE,
                GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK, GenericKeyedObjectPool.DEFAULT_MAX_WAIT, PS_CACHE_SIZE,
                PS_CACHE_SIZE, GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
                GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
                GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
                GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
                GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
                GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE);
        PoolableConnectionFactory pcf = new PoolableConnectionFactory(connectionFactory, connectionPool,
                statementPool, configuration.getValidationQuery(), false, true) {
            @Override
            public synchronized Object makeObject() throws Exception {
                Connection connection = (Connection) super.makeObject();
                initializeConnection(connection);
                return connection;
            }
        };

        if (configuration.getSupportsIsolation() && write)
            pcf.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
        else if (configuration.getSupportsIsolation() && !write)
            pcf.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
        return connectionPool;
    }

    private long getCurrentVersion(Connection connection) throws AnzoException {
        Long currentVersion = ServerRdbWrapper.getServerVersion(getStatementProvider(), connection);
        if (currentVersion == null) {
            return -1;
        } else {
            return currentVersion;
        }
    }

    private void verifyVersion(Connection connection, long currentVersion) throws AnzoException {
        if (currentVersion < SCHEMA_VERSION) {
            for (long i = currentVersion; i < SCHEMA_VERSION; i++) {
                try {
                    String name = (i + "To" + (i + 1));
                    begin(connection, true, true);
                    log.info(LogUtils.RDB_MARKER, "Updating from db schema {} to {}", i, (i + 1));
                    if (getStatementProvider().getSqlString(name) != null) {
                        getStatementProvider().runSQLGroup(name, configuration.getInitParams(), connection);
                    }
                    if (i == 9) {
                        nineToTen(connection);
                    }
                    commit(connection, true, true);

                    String nameMore = (i + "To" + (i + 1) + "more");
                    if (getStatementProvider().getSqlString(nameMore) != null) {
                        getStatementProvider().runSQLGroupCommitIndividual(nameMore, configuration.getInitParams(),
                                connection);
                    }
                    begin(connection, true, true);
                    ServerRdbWrapper.setServerVersion(getStatementProvider(), connection, i + 1);
                    commit(connection, true, true);
                    log.info(LogUtils.RDB_MARKER, "Updated from db schema {} to {}", i, (i + 1));
                } catch (SQLException sqle) {
                    log.info(LogUtils.RDB_MARKER, "Failed updating from db schema " + i + " to " + (i + 1), sqle);
                    abort(connection, true, true);
                }
            }
        }
    }

    private void nineToTen(Connection connection) throws AnzoException {
        Long id = nodeLayout.fetchId(NamedGraph.modifiedProperty, connection);
        if (id != null) {

            Backup.BatchReplaceStatement bps = new Backup.BatchReplaceStatement(connection, statementProvider);
            int needsDoing = 0;

            for (SelectFullStatementsResult result : Backup.selectFullStatements(statementProvider, connection,
                    id)) {
                Value object = nodeLayout.fetchValue(result.getObject(), connection);
                if (object instanceof TypedLiteral) {
                    URI datatype = ((TypedLiteral) object).getDatatypeURI();
                    if (datatype.equals(XMLSchema.LONG)) {
                        Long lmt = (Long) ((TypedLiteral) object).getNativeValue();
                        XMLGregorianCalendar cal = TypeMaps.getXMLCaledar(lmt);
                        Literal newDateTime = Constants.valueFactory.createTypedLiteral(cal);
                        Long newOid = nodeLayout.store(newDateTime, connection, 0);
                        String oldStmtId = result.getId();
                        String newStmtId = MessageFormat.format(ID_STRING, Long.toString(result.getNamedGraphId()),
                                Long.toString(result.getSubject()), id.toString(), Long.toString(newOid));
                        bps.addEntry(newStmtId, newOid, oldStmtId);
                        needsDoing++;
                    }
                }
            }
            if (needsDoing > 0) {
                log.info(LogUtils.RDB_MARKER,
                        "Updated old modified long timestamps to dateTime timestamps:" + needsDoing);
                bps.executeStatement();
                bps.close();
            }
            Backup.BatchReplaceStatementNR bpns = new Backup.BatchReplaceStatementNR(connection, statementProvider);
            needsDoing = 0;
            for (SelectFullStatementsNRResult result : Backup.selectFullStatementsNR(statementProvider, connection,
                    id)) {
                Value object = nodeLayout.fetchValue(result.getObject(), connection);
                if (object instanceof TypedLiteral) {
                    URI datatype = ((TypedLiteral) object).getDatatypeURI();
                    if (datatype.equals(XMLSchema.LONG)) {
                        Long lmt = (Long) ((TypedLiteral) object).getNativeValue();
                        XMLGregorianCalendar cal = TypeMaps.getXMLCaledar(lmt);
                        Literal newDateTime = Constants.valueFactory.createTypedLiteral(cal);
                        Long newOid = nodeLayout.store(newDateTime, connection, 0);
                        String oldStmtId = result.getId();
                        String newStmtId = MessageFormat.format(ID_STRING, Long.toString(result.getNamedGraphId()),
                                Long.toString(result.getSubject()), id.toString(), Long.toString(newOid));
                        bpns.addEntry(newStmtId, newOid, oldStmtId);
                        needsDoing++;
                    }
                }
            }
            if (needsDoing > 0) {
                log.info(LogUtils.RDB_MARKER,
                        "Updated old modified long timestamps to dateTime timestamps:" + needsDoing);
                bpns.executeStatement();
                bpns.close();
            }
        }
    }

    /**
     * Start the JDBC connection, initializing tables if they don't already exist
     * 
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_INITIALZE_TEMPTABLES} if there was a problem running the createTemporaryTables sql group
     */
    void initializeConnection(Connection connection) throws AnzoException {
        if (!initialized) {
            boolean inited = isInitialized(connection, INITIAL_DATABASE_TABLE);
            if (!inited) {
                NodeCentricOperationContext context = new NodeCentricOperationContext(INITIALIZE_CONNECTION,
                        BaseOperationContext.generateOperationId(), null, connection, this);
                resetService.resetDatabase(context, true, (MultiMap<URI, Statement>) null);
                if (indexHandler != null) {
                    indexHandler.reset();
                }
            } else {
                long currentVersion = getCurrentVersion(connection);
                verifyVersion(connection, currentVersion);
            }
        }
        if (configuration.getForceTempCreation() || !hasTempTable(connection, "STMTS_TMP")) {
            try {
                begin(connection, false, true);
                statementProvider.runSQLGroup("createTemporaryTables", configuration.getInitParams(), connection);
                commit(connection, false, true);
            } catch (SQLException sqle) {
                abort(connection, false, true);
                if (!configuration.getForceTempCreation()) {
                    log.error(LogUtils.RDB_MARKER, "Error creating temporary tables", sqle);
                    throw new AnzoException(ExceptionConstants.RDB.FAILED_INITIALZE_TEMPTABLES, sqle);
                }
            }
        }
        if (!initialized) {
            begin(connection, true, true);
            for (Long transactionId : LastTransactionTime.selectUncommitedTransactions(statementProvider,
                    connection, getInstanceId())) {
                log.error(LogUtils.RDB_MARKER,
                        "Transaction [{}] uncommited from previous instance of server, cleaning it up now.",
                        transactionId);
                nodeLayout.abortReferencedIds(connection, transactionId);
                NodeCentricServerQuadStore.abortTransaction(connection, statementProvider, configuration, true,
                        transactionId, false);
                NodeCentricServerQuadStore.abortTransaction(connection, statementProvider, configuration, false,
                        transactionId, false);
                NamedGraphRdbWrapper.purgelockedNamedGraph(statementProvider, connection, transactionId);
            }
            LastTransactionTime.purgeTransactions(statementProvider, connection, getInstanceId());
            //GlitterRdbWrapper.purgeQueryDatasets(statementProvider, connection, getInstanceId());
            long lastId = -1;
            GraphSet lastSet = null;
            for (SelectQueryDatasetsResult result : GlitterRdbWrapper.selectQueryDatasets(statementProvider,
                    connection)) {
                long id = result.getDatasetId();
                long gid = result.getGraphId();
                if (lastId != id) {
                    if (lastSet != null) {
                        graphSets.put(lastSet, lastSet);
                    }
                    lastSet = new GraphSet(id);
                }
                lastId = id;
                URI graphUri = (URI) nodeLayout.fetchValue(gid, connection);
                if (lastSet != null) {
                    lastSet.add(graphUri);
                }
            }
            for (GraphSet gs : graphSets.keySet()) {
                gs.setRevisionedCount(GlitterRdbWrapper.countValidRevisionedGraphsInSet(statementProvider,
                        connection, gs.getSetId()));
                gs.setNonRevisionedCount((gs.getRevisionedCount() == gs.size()) ? 0
                        : GlitterRdbWrapper.countValidNonRevisionedGraphsInSet(statementProvider, connection,
                                gs.getSetId()));
            }
            initialized = true;
            commit(connection, true, true);
        }
    }

    /**
     * Determine if the database is initialized, or is it already being initialized by another connection. First check if a specified table exists. If it does,
     * check the initialized status.
     * 
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_WAITING_DB_INIT} if there was a problem waiting for the initialization status
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_GET_INIT_STATUS} if there was a problem querying for the initialization status
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_SETTING_ISOLATION} if there was a problem changing the transaction isolation level
     */
    private boolean isInitialized(Connection connection, String tablename) throws AnzoException {
        int isolation = Connection.TRANSACTION_REPEATABLE_READ;
        if (configuration.getSupportsIsolation()) {
            try {
                isolation = connection.getTransactionIsolation();
                connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
            } catch (SQLException e) {
                log.error(LogUtils.RDB_MARKER, "Error setting tranaction isolation", e);
                throw new AnzoException(ExceptionConstants.RDB.FAILED_SETTING_ISOLATION, e);
            }
        }
        try {
            ResultSet rs = null;
            //Need to check all the tables, and not just the server table.  If we only check the server table, we could overwrite tables containing data.
            try {
                try {
                    rs = connection.getMetaData().getTables(null, null,
                            configuration.getUsesUppercase() ? serverUpper : serverLower, new String[] { table });
                    if (!rs.next()) {
                        return !checkIfTablesExists(connection, true);
                    }
                    String tbl = rs.getString(3);
                    if (!tbl.equalsIgnoreCase(serverUpper)) {
                        return !checkIfTablesExists(connection, true);
                    }
                } finally {
                    if (rs != null) {
                        rs.close();
                    }
                }
            } catch (SQLException e) {
                log.error(LogUtils.RDB_MARKER, "error checking tables", e);
                throw new AnzoException(ExceptionConstants.RDB.FAILED_GETTING_TABLE_STATUS, e, serverUpper);
            }
            try {
                long initialized = 2;
                int retries = 0;
                //While the server is initializing in another connection, wait. Wait a total of 20 seconds, and then fail.
                while (initialized > 1) {
                    if (retries++ < 30) {
                        Long init = ServerRdbWrapper.getInitialized(statementProvider, connection);
                        if (init == null) {
                            return true;
                        }
                        initialized = init.longValue();
                        if (initialized > 1) {
                            if ((System.currentTimeMillis() - initialized) > 60000) {
                                log.error(LogUtils.RDB_MARKER,
                                        "A previous connections was intializing the database, but its been over a minute, so assume a fauilure");
                                ServerRdbWrapper.setInitializingFailed(statementProvider, connection);
                                return false;
                            }
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException ie) {
                            }
                        }
                    } else {
                        throw new AnzoException(ExceptionConstants.RDB.FAILED_WAITING_DB_INIT);
                    }
                }
                checkIfTablesExists(connection, false);
                return true;
            } catch (RdbException e) {
                log.error(LogUtils.RDB_MARKER, "Error checking initialization state", e);
                throw new AnzoException(ExceptionConstants.RDB.FAILED_GET_INIT_STATUS, e);
            }
        } finally {
            try {
                if (configuration.getSupportsIsolation()) {
                    connection.setTransactionIsolation(isolation);
                }
            } catch (SQLException e) {
                log.error(LogUtils.RDB_MARKER, "Error setting transaction isolation level", e);
                throw new AnzoException(ExceptionConstants.RDB.FAILED_SETTING_ISOLATION, e);
            }
        }
    }

    private boolean checkIfTablesExists(Connection connection, boolean none) throws AnzoException {
        ResultSet rs = null;
        try {
            long currentVersion = none ? SCHEMA_VERSION : getCurrentVersion(connection);

            boolean tables = true;
            boolean sequences = false;
            boolean views = false;
            try {
                rs = connection.getMetaData().getTableTypes();
                while (rs.next() && (!tables || !sequences || !views)) {
                    String type = rs.getString(1);
                    if (type.toUpperCase().equals(table)) {
                        tables = true;
                    } else if (type.toUpperCase().equals(seq)) {
                        sequences = true;
                    } else if (type.toUpperCase().equals(view)) {
                        views = true;
                    }
                }
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }
            if (tables) {
                try {
                    rs = connection.getMetaData().getTables(null, null, null, new String[] { table });

                    HashSet<String> requiredTables = new HashSet<String>();
                    requiredTables.add(serverUpper);
                    java.util.Collections.addAll(requiredTables, resetService.getRequiredTables());
                    java.util.Collections.addAll(requiredTables, resetService.getNodeCentricTables());
                    while (rs.next()) {
                        String tbl = rs.getString(3);
                        if (requiredTables.remove(tbl.toUpperCase()) && none) {
                            throw new AnzoException(ExceptionConstants.RDB.INCOMPLETE_DATABASE);
                        }
                        if (tbl.toUpperCase().equals("ANZO_U")) {
                            ResultSet metadata = connection.getMetaData().getColumns(null, null, tbl, null);
                            while (metadata.next()) {
                                String name = metadata.getString(4);
                                if (name.toUpperCase().equals("VALUE")) {
                                    int size = metadata.getInt(7);
                                    configuration.setMaxLongObjectLength(size);
                                    nodeLayout.setMaxLength(size);
                                    break;
                                }
                            }
                        }
                    }
                    if (!none && requiredTables.size() > 0) {
                        throw new AnzoException(ExceptionConstants.RDB.FAILED_GETTING_TABLE_STATUS,
                                Arrays.toString(requiredTables.toArray()));
                    }
                } finally {
                    if (rs != null) {
                        rs.close();
                    }
                }
            }
            if (sequences) {
                String seqs[][] = resetService.getRequiredSequences();
                for (int i = 0; i < currentVersion; i++) {
                    String vseq[] = seqs[i];
                    if (vseq != null && vseq.length > 0) {
                        try {
                            rs = connection.getMetaData().getTables(null, null, null, new String[] { seq });
                            HashSet<String> requiredSeq = new HashSet<String>();
                            java.util.Collections.addAll(requiredSeq, vseq);
                            while (rs.next()) {
                                String tbl = rs.getString(3);
                                if (requiredSeq.remove(tbl.toUpperCase()) && none) {
                                    throw new AnzoException(ExceptionConstants.RDB.INCOMPLETE_DATABASE);
                                }
                            }
                            if (!none && requiredSeq.size() > 0) {
                                throw new AnzoException(ExceptionConstants.RDB.FAILED_GETTING_TABLE_STATUS,
                                        Arrays.toString(requiredSeq.toArray()));
                            }
                        } finally {
                            if (rs != null) {
                                rs.close();
                            }
                        }
                    }
                }
            }
            if (views) {
                try {
                    rs = connection.getMetaData().getTables(null, null, null, new String[] { view });

                    HashSet<String> required = new HashSet<String>();
                    if (currentVersion < 12) {
                        required.add("ALL_STMTS_VIEW");
                    } else {
                        java.util.Collections.addAll(required, resetService.getRequiredViews());
                    }
                    while (rs.next()) {
                        String tbl = rs.getString(3);
                        if (required.remove(tbl.toUpperCase()) && none) {
                            throw new AnzoException(ExceptionConstants.RDB.INCOMPLETE_DATABASE);
                        }
                    }
                    if (!none && required.size() > 0) {
                        throw new AnzoException(ExceptionConstants.RDB.FAILED_GETTING_TABLE_STATUS,
                                Arrays.toString(required.toArray()));
                    }
                } finally {
                    if (rs != null) {
                        rs.close();
                    }
                }
            }
        } catch (SQLException e) {
            log.error(LogUtils.RDB_MARKER, "Error checking if statements exist", e);
            throw new AnzoException(ExceptionConstants.RDB.FAILED_INITIALZE_DB, e);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    log.debug(LogUtils.RDB_MARKER, "Error closing result set", e);

                }
            }
        }
        return true;
    }

    /**
     * Determine if the temptable with the given name is available
     * 
     * @param connection
     *            connection to the underlying database
     * @param tablename
     *            name of temporary table to lookup
     * @return true if temporary table exists
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_GETTING_TABLE_STATUS} if there was a problem querying for information about the temp table
     * 
     */
    private boolean hasTempTable(Connection connection, String tablename) throws AnzoException {
        try {
            ResultSet rs = null;
            try {
                rs = connection.getMetaData().getTables(null, null, configuration.getSessionPrefix()
                        + (configuration.getUsesUppercase() ? tablename.toUpperCase() : tablename.toLowerCase()),
                        null);
                if (!rs.next()) {
                    return false;
                }
                String tbl = rs.getString(3);
                if (!tbl.equalsIgnoreCase(tablename)) {
                    return false;
                }
                return true;
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }
        } catch (SQLException e) {
            log.error(LogUtils.RDB_MARKER, "Error checking for temporat table status", e);
            throw new AnzoException(ExceptionConstants.RDB.FAILED_GETTING_TABLE_STATUS, e, tablename);
        }
    }

    /**
     * Get a {@link Connection} from the write pool
     * 
     * @return {@link Connection} from the write pool
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_GETTING_CONNECTION} if there was a problem getting a connection from the write pool
     * 
     */
    private Connection getWriteConnection() throws AnzoException {
        long start = 0;
        if (stats.isEnabled()) {
            start = System.currentTimeMillis();
            stats.getGetWriteConnectionUse().increment();
        }
        try {
            return (Connection) writePool.borrowObject();
        } catch (AnzoException ae) {
            throw ae;
        } catch (Exception exception) {
            throw new AnzoException(ExceptionConstants.RDB.FAILED_GETTING_CONNECTION, exception);
        } finally {
            if (stats.isEnabled()) {
                stats.getGetWriteConnectionDuration().addTime((System.currentTimeMillis() - start));
            }
        }
    }

    // private Hashtable<Connection, Thread> connectionThreads = new Hashtable<Connection, Thread>();

    /**
     * Get a {@link Connection} from the query pool
     * 
     * @return {@link Connection} from the query pool
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_GETTING_CONNECTION} if there was a problem getting a connection from the query pool
     * 
     */
    private Connection getQueryConnection() throws AnzoException {
        long start = 0;
        if (stats.isEnabled()) {
            start = System.currentTimeMillis();
            stats.getGetQueryConnectionUse().increment();
        }
        try {

            Connection connection = (Connection) queryPool.borrowObject();

            return connection;
        } catch (AnzoException ae) {
            throw ae;
        } catch (Exception exception) {
            throw new AnzoException(ExceptionConstants.RDB.FAILED_GETTING_CONNECTION, exception);
        } finally {
            if (stats.isEnabled()) {
                stats.getGetQueryConnectionDuration().addTime((System.currentTimeMillis() - start));
            }
        }
    }

    /**
     * Return a {@link Connection} to the write pool
     * 
     * @param connection
     *            {@link Connection} to return
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_GETTING_CONNECTION} if there was a problem returning a connection to the write pool
     * 
     */
    private void returnWriteConnection(Connection connection) throws AnzoException {
        try {
            if (!connection.isClosed()) {
                if (!connection.getAutoCommit()) {
                    abort(connection, false, true);
                }
                writePool.returnObject(connection);
            } else {
                writePool.invalidateObject(connection);
            }

        } catch (AnzoException ae) {
            throw ae;
        } catch (Exception exception) {
            throw new AnzoException(ExceptionConstants.RDB.FAILED_RETURNING_CONNECTION, exception);
        }
    }

    /**
     * Return a {@link Connection} to the query pool
     * 
     * @param connection
     *            {@link Connection} to return
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_GETTING_CONNECTION} if there was a problem returning a connection to the query pool
     * 
     */
    private void returnQueryConnection(Connection connection) throws AnzoException {
        try {
            if (!connection.isClosed()) {
                queryPool.returnObject(connection);
            } else {
                queryPool.invalidateObject(connection);
            }
        } catch (AnzoException ae) {
            throw ae;
        } catch (Exception exception) {
            throw new AnzoException(ExceptionConstants.RDB.FAILED_RETURNING_CONNECTION, exception);
        }
    }

    /**
     * Wrap the underlying IOperationContext with a NodeCentricOperationContext, that contains a connection from the query pool
     * 
     * @param context
     *            underlying context for the operation
     * @return a NodeCentricOperationContext, that contains a connection from the query pool
     * @throws AnzoException
     */
    public NodeCentricOperationContext getQueryContext(IOperationContext context) throws AnzoException {
        return new NodeCentricOperationContext(context, getQueryConnection(), this);
    }

    /**
     * Wrap the underlying IOperationContext with a NodeCentricOperationContext, that contains a connection from the write pool
     * 
     * @param context
     *            underlying context for the operation
     * @return a NodeCentricOperationContext, that contains a connection from the write pool
     * @throws AnzoException
     */
    public NodeCentricOperationContext getWriteContext(IOperationContext context) throws AnzoException {
        return new NodeCentricOperationContext(context, getWriteConnection(), this);
    }

    /**
     * Return the connection within this context to the query pool, and return the wrapped context
     * 
     * @param context
     *            context containing a query connection
     * @return the wrapped context
     * @throws AnzoException
     */
    public IOperationContext returnQueryContext(NodeCentricOperationContext context) throws AnzoException {
        returnQueryConnection(context.getConnection());
        context.setConnection(null);
        return context.getRootContext();
    }

    /**
     * Return the connection within this context to the write pool, and return the wrapped context
     * 
     * @param context
     *            context containing a query connection
     * @return the wrapped context
     * @throws AnzoException
     */
    public IOperationContext returnWriteContext(NodeCentricOperationContext context) throws AnzoException {
        if (context.getConnection() != null) {
            returnWriteConnection(context.getConnection());
            context.setConnection(null);
        }
        return context.getRootContext();
    }

    public void rebindWriteContext(NodeCentricOperationContext context) throws AnzoException {
        if (context.getConnection() != null) {
            returnWriteConnection(context.getConnection());
        }
        context.setConnection(getWriteConnection());
    }

    public void rebindQueryContext(NodeCentricOperationContext context) throws AnzoException {
        if (context.getConnection() != null) {
            returnQueryConnection(context.getConnection());
        }
        context.setConnection(getQueryConnection());
    }

    /**
     * Get the RepositoryConnectionConfiguration object for the back-end
     * 
     * @return the RepositoryConnectionConfiguration object for the back-end
     */
    public CoreDBConfiguration getConfiguration() {
        return configuration;
    }

    /**
     * Lock the the database SERVER table
     * 
     * @param connection
     *            {@link Connection} to underlying database
     * @param needsWrite
     *            this connection is going write to the database
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_LOCK_TABLES} if there was a problem locking the database table
     * 
     */
    private void lockTable(Connection connection, boolean needsWrite) throws AnzoException {
        if (configuration.getSupportsTableLocks() && needsWrite) {
            try {
                ServerRdbWrapper.lockTable(statementProvider, connection, serverUpper,
                        getConfiguration().getTableLocksExtras());
            } catch (RdbException e) {
                throw new AnzoException(ExceptionConstants.RDB.FAILED_LOCK_TABLES, e, serverUpper);
            }
        } else if (needsWrite) {
            serverLock.lock();
        }
    }

    /**
     * Unlock the the database SERVER table
     * 
     * @param connection
     *            {@link Connection} to underlying database
     * @param needsWrite
     *            this connection is going write from the database
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_UNLOCK_TABLES} if there was a problem unlocking the database table
     * 
     */
    private void unlockTable(Connection connection, boolean needsWrite) throws AnzoException {
        if (configuration.getSupportsTableUnLocks() && needsWrite) {
            try {
                ServerRdbWrapper.unlockTable(statementProvider, connection, serverUpper);
            } catch (RdbException e) {
                throw new AnzoException(ExceptionConstants.RDB.FAILED_UNLOCK_TABLES, e, serverUpper);
            }
        } else if (!configuration.getSupportsTableLocks() && needsWrite) {
            serverLock.unlock();
        }
    }

    /**
     * Determine if this connection is within a transaction
     * 
     * @param connection
     *            connection of which to determine transaction status
     * @return true if connection is within transaction
     */
    public boolean isInTransaction(Connection connection) {
        ReentrantLock lock = connectionLocks.get(connection);
        if (lock != null) {
            return lock.isLocked();
        }
        return false;
    }

    /**
     * Begin database transaction
     * 
     * Note:Database already in transaction
     * 
     * @param connection
     *            {@link Connection} to underlying database
     * @param needsWrite
     *            if true, tables will be locked if needed
     * @param needsTransaction
     *            TODO
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#ALREADY_IN_RDB_TRANSACTION} if this connection is already with a transaction
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_START_RDB_TRANSACTION} if there was a problem setting autoCommit to false
     */
    public void begin(Connection connection, boolean needsWrite, boolean needsTransaction) throws AnzoException {
        long start = 0;
        if (stats.isEnabled()) {
            start = System.currentTimeMillis();
            stats.getBeginUse().increment();
        }
        try {
            ReentrantLock lock = connectionLocks.get(connection);
            if (lock == null) {
                lock = new ReentrantLock();
                connectionLocks.put(connection, lock);
            }
            if (lock.isLocked()) {
                throw new AnzoException(ExceptionConstants.RDB.ALREADY_IN_RDB_TRANSACTION);
            }
            lock.lock();
            if (lock.getHoldCount() == 1 && needsTransaction) {
                try {
                    connection.setAutoCommit(false);
                } catch (SQLException e) {
                    lock.unlock();
                    log.error(LogUtils.RDB_MARKER, "Error starting jdbc transaction", e);
                    throw new AnzoRuntimeException(ExceptionConstants.RDB.FAILED_START_RDB_TRANSACTION, e);
                }
                try {
                    lockTable(connection, needsWrite);
                } catch (AnzoException e) {
                    try {
                        connection.setAutoCommit(false);
                    } catch (SQLException sqle) {
                        log.error(LogUtils.RDB_MARKER, "Error aborting jdbc transaction", sqle);
                    }
                    lock.unlock();
                    throw e;
                }

            }
        } finally {
            if (stats.isEnabled()) {
                stats.getBeginDuration().addTime((System.currentTimeMillis() - start));
            }
        }
    }

    /**
     * Abort database transaction
     * 
     * Note:Database already in transaction
     * 
     * @param connection
     *            {@link Connection} to underlying database
     * @param needsWrite
     *            if true, tables will be locked if needed
     * @param needsTransaction
     *            TODO
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#DIDNT_START_RDB_TRANSACTION} if this thread didn't start the transaction
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_ROLLBACK_RDB_TRANSACTION} if there was a problem rolling back the connection
     */
    protected void abort(Connection connection, boolean needsWrite, boolean needsTransaction) throws AnzoException {
        long start = 0;
        if (stats.isEnabled()) {
            start = System.currentTimeMillis();
            stats.getAbortUse().increment();
        }
        try {
            ReentrantLock lock = connectionLocks.get(connection);
            if (lock == null) {
                lock = new ReentrantLock();
                connectionLocks.put(connection, lock);
            }
            if (lock.isLocked()) {
                if (lock.isHeldByCurrentThread()) {
                    try {
                        if (needsTransaction) {
                            ArrayList<AnzoException> exceptions = null;
                            try {
                                if (!connection.isClosed()) {
                                    try {
                                        connection.rollback();
                                        connection.setAutoCommit(true);
                                    } catch (SQLException e) {
                                        log.error(LogUtils.RDB_MARKER, "Error rolling back transaction", e);
                                        exceptions = new ArrayList<AnzoException>();
                                        exceptions.add(new AnzoException(
                                                ExceptionConstants.RDB.FAILED_ROLLBACK_RDB_TRANSACTION, e));
                                    }
                                    try {
                                        unlockTable(connection, needsWrite);
                                    } catch (AnzoException ae) {
                                        log.error(LogUtils.RDB_MARKER, "Error unlocking table", ae);
                                        if (exceptions == null) {
                                            exceptions = new ArrayList<AnzoException>();
                                        }
                                        exceptions.add(ae);
                                    }
                                }
                            } catch (SQLException e) {
                                log.error(LogUtils.RDB_MARKER, "Error rollingback jdbc transaction", e);
                                exceptions = new ArrayList<AnzoException>();
                                exceptions.add(new AnzoException(
                                        ExceptionConstants.RDB.FAILED_ROLLBACK_RDB_TRANSACTION, e));
                            }

                            if (exceptions != null && exceptions.size() > 0) {
                                throw new CompoundAnzoException(exceptions,
                                        ExceptionConstants.RDB.FAILED_ROLLBACK_RDB_TRANSACTION);
                            }
                        }
                    } finally {
                        lock.unlock();
                        nodeLayout.clearUncommittedCache();
                    }
                } else {
                    throw new AnzoException(ExceptionConstants.RDB.DIDNT_START_RDB_TRANSACTION);
                }
            }
        } finally {
            if (stats.isEnabled()) {
                stats.getAbortDuration().addTime((System.currentTimeMillis() - start));
            }
        }
    }

    /**
     * Commit database transaction
     * 
     * Note:Database already in transaction
     * 
     * @param connection
     *            {@link Connection} to underlying database
     * @param needsWrite
     *            if true, tables will be locked if needed
     * @param needsTransaction
     *            TODO
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#NOT_IN_RDB_TRANSACTION} if connection isn't in a transaction
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#DIDNT_START_RDB_TRANSACTION} if this thread didn't start the transaction
     * @throws AnzoException
     *             {@link ExceptionConstants.RDB#FAILED_COMMIT_RDB_TRANSACTION} if there was a problem committing the connection
     */
    public void commit(Connection connection, boolean needsWrite, boolean needsTransaction) throws AnzoException {
        long start = 0;
        if (stats.isEnabled()) {
            start = System.currentTimeMillis();
            stats.getCommitUse().increment();
        }
        try {
            ReentrantLock lock = connectionLocks.get(connection);
            if (lock == null) {
                lock = new ReentrantLock();
                connectionLocks.put(connection, lock);
            }
            if (lock.isLocked()) {
                if (lock.isHeldByCurrentThread()) {
                    try {
                        if (needsTransaction) {
                            ArrayList<AnzoException> exceptions = null;
                            try {
                                connection.commit();
                                connection.setAutoCommit(true);
                            } catch (SQLException e) {
                                log.error(LogUtils.RDB_MARKER, "Error commmiting jdbc transaction", e);
                                exceptions = new ArrayList<AnzoException>();
                                exceptions.add(
                                        new AnzoException(ExceptionConstants.RDB.FAILED_COMMIT_RDB_TRANSACTION, e));
                            }
                            try {
                                unlockTable(connection, needsWrite);
                            } catch (AnzoException ae) {
                                log.error(LogUtils.RDB_MARKER, "Error unlocking tables", ae);
                                if (exceptions == null) {
                                    exceptions = new ArrayList<AnzoException>();
                                }
                                exceptions.add(ae);
                            }
                            if (exceptions != null && exceptions.size() > 0) {
                                throw new CompoundAnzoException(exceptions,
                                        ExceptionConstants.RDB.FAILED_COMMIT_RDB_TRANSACTION);
                            }
                        }
                    } finally {
                        lock.unlock();
                        nodeLayout.clearUncommittedCache();
                    }
                } else {
                    throw new AnzoException(ExceptionConstants.RDB.DIDNT_START_RDB_TRANSACTION);
                }
            } else {
                throw new AnzoException(ExceptionConstants.RDB.NOT_IN_RDB_TRANSACTION);
            }
        } finally {
            if (stats.isEnabled()) {
                stats.getCommitDuration().addTime((System.currentTimeMillis() - start));
            }
        }
    }

    /**
     * Get the shared PreparedStatementProvider for the back-end
     * 
     * @return the shared PreparedStatementProvider for the back-end
     */
    public PreparedStatementProvider getStatementProvider() {
        return statementProvider;
    }

    /**
     * Get the shared CompositeNodeLayout for the back-end
     * 
     * @return the shared CompositeNodeLayout for the back-end
     */
    public CompositeNodeLayout getNodeLayout() {
        return nodeLayout;
    }

    /**
     * Get the IndexService for the back-end
     * 
     * @return the IndexService for the back-end
     */
    public NodeCentricIndexService getIndexService() {
        return indexService;
    }

    /**
     * Determine if the given named graph is stored, and if so, what type of storage
     * 
     * @param context
     *            Connection context for this operation
     * @param namedGraphUri
     *            URI of namedGraph to lookup
     * @return NamedGraphType for the given namedGraph if it is stored within this backend
     * @throws AnzoException
     */
    protected NamedGraphType containsNamedGraph(NodeCentricOperationContext context, URI namedGraphUri,
            boolean metadata) throws AnzoException {
        Long ngId = context.getNodeLayout().fetchId(namedGraphUri, context.getConnection());
        if (ngId == null) {
            return null;
        }
        Long result = metadata
                ? NamedGraphRdbWrapper.containsMetadataGraphRevisioned(context.getStatementProvider(),
                        context.getConnection(), ngId)
                : NamedGraphRdbWrapper.containsNamedGraphRevisioned(context.getStatementProvider(),
                        context.getConnection(), ngId);
        if (result != null) {
            return NamedGraphType.REVISIONED;
        }
        result = metadata
                ? NamedGraphRdbWrapper.containsMetadataGraphNonRevisioned(context.getStatementProvider(),
                        context.getConnection(), ngId)
                : NamedGraphRdbWrapper.containsNamedGraphNonRevisioned(context.getStatementProvider(),
                        context.getConnection(), ngId);
        if (result != null) {
            return NamedGraphType.NON_REVISIONED_PERSISTED;
        }
        return null;

    }

    /**
     * @return the initParams
     */
    public String[] getInitParams() {
        return configuration.getInitParams();
    }

    /**
     * Runstats on the underlying database
     * 
     * @throws AnzoException
     */
    public void runstats() throws AnzoException {
        Connection connection = getWriteConnection();
        try {
            String[][] tableSets = { NodeCentricResetService.liveTables, resetService.getNodeCentricTables() };
            for (String[] tables : tableSets) {
                for (String table : tables) {
                    try {
                        ServerRdbWrapper.stats(statementProvider, connection, table);
                    } catch (RdbException sqle) {
                        log.error(LogUtils.RDB_MARKER, "SQL Error deleting from table", sqle);
                    }
                }
            }
        } finally {
            returnWriteConnection(connection);
        }
    }

    @Override
    public ReentrantReadWriteLock getLockProvider() {
        return resetLock;
    }

    /**
     * @param datasourceInstanceURI
     *            the datasourceInstanceURI to set
     */
    public void setDatasourceInstanceURI(URI datasourceInstanceURI) {
    }

    /**
     * @return the dictionary
     */
    public Dictionary<? extends Object, ? extends Object> getConfigurationParameters() {
        return configProperties;
    }

    /**
     * @return the aclEventListeners
     */
    public Set<IAuthorizationEventListener> getAclEventListeners() {
        return aclEventListeners;
    }

    /**
     * @return the graphSets
     */
    public ICache<GraphSet, GraphSet> getGraphSets() {
        return graphSets;
    }

    /**
     * @return the eventAdmin
     */
    public EventAdmin getEventAdmin() {
        return eventAdmin;
    }

    private static final String ID_STRING = "{0}:{1}:{2}:{3}";

    /**
     * Import backup/migration data into datasource, without doing a reset
     * 
     * @param replace
     *            If true, replace a graph if it already exists, otherwise ignore data from backup
     * @param fileName
     *            Filename containing backup data
     * @throws AnzoException
     */
    public void importBackupData(final boolean replace, String fileName) throws AnzoException {
        final NodeCentricOperationContext connectionContext = getWriteContext(
                new BaseOperationContext("MIGRATE", BaseOperationContext.generateOperationId(),
                        new AnzoPrincipal("sysadmin", null, null, true, false)));
        try {
            lockTable(connectionContext.getConnection(), true);

            try {
                Reader fileReader = ReadWriteUtils.createSmartFileReader(fileName);
                XMLBackupReader reader = new XMLBackupReader(fileReader);
                try {
                    reader.read(new IBackupHandler() {
                        URI ngURI;

                        URI mdURI;

                        Long graphId;

                        Long metaId;

                        Long uuidId;

                        Long graphsDatasetId;

                        Long metadataDatasetId;

                        Long namedGraphPropertyId;

                        Long graphsUUID;

                        Long metadataUUID;

                        long timestamp = 0;

                        public void start() throws AnzoException {
                            begin(connectionContext.getConnection(), true, true);
                            getIndexHandler().indexer.preIndex();
                            graphsDatasetId = nodeLayout.store(GRAPHS.GRAPHS_DATASET,
                                    connectionContext.getConnection(), 1);
                            metadataDatasetId = nodeLayout.store(GRAPHS.METADATA_GRAPHS_DATASET,
                                    connectionContext.getConnection(), 1);
                            namedGraphPropertyId = nodeLayout.store(Dataset.namedGraphProperty,
                                    connectionContext.getConnection(), 1);
                            graphsUUID = nodeLayout.fetchId(
                                    getModelService().getUUIDforUri(connectionContext, GRAPHS.GRAPHS_DATASET),
                                    connectionContext.getConnection());
                            metadataUUID = nodeLayout.fetchId(getModelService().getUUIDforUri(connectionContext,
                                    GRAPHS.METADATA_GRAPHS_DATASET), connectionContext.getConnection());
                        }

                        public void handleStatement(boolean metadata, boolean revisioned, Statement statement,
                                Long start, Long end) throws AnzoException {
                            Long s = nodeLayout.store(statement.getSubject(), connectionContext.getConnection(), 1);
                            Long p = nodeLayout.store(statement.getPredicate(), connectionContext.getConnection(),
                                    1);
                            Long o = nodeLayout.store(statement.getObject(), connectionContext.getConnection(), 1);
                            String stmtId = MessageFormat.format(ID_STRING,
                                    (metadata) ? metaId.toString() : graphId.toString(), s.toString(), p.toString(),
                                    o.toString());
                            if (revisioned) {
                                Backup.restoreStatement(statementProvider, connectionContext.getConnection(),
                                        stmtId, metadata ? 1 : 0, uuidId, metadata ? metaId : graphId, s, p, o,
                                        start, end);
                            } else {
                                Backup.restoreStatementNR(statementProvider, connectionContext.getConnection(),
                                        stmtId, metadata ? 1 : 0, metadata ? metaId : graphId, s, p, o);
                            }
                            if ((end == null || end.longValue() > 0) && statement.getObject() instanceof Literal) {
                                StatementWrapper sw = new StatementWrapper(metadata ? mdURI : ngURI,
                                        metadata ? metaId : graphId, statement.getSubject(), s,
                                        statement.getPredicate(), p, statement.getObject(), o, timestamp);
                                getIndexHandler().indexer.index(sw);
                            }
                        }

                        public boolean handleNamedGraph(boolean revisioned, URI namedGraphUri, URI metadataURI,
                                URI uuid, Collection<BackupRevision> revisions) throws AnzoException {
                            NamedGraphType type = containsNamedGraph(connectionContext, namedGraphUri, false);
                            if (type == null || replace) {
                                ngURI = namedGraphUri;
                                mdURI = metadataURI;
                                graphId = nodeLayout.store(namedGraphUri, connectionContext.getConnection(), 1);
                                metaId = nodeLayout.store(metadataURI, connectionContext.getConnection(), 1);
                                uuidId = nodeLayout.store(uuid, connectionContext.getConnection(), 1);
                                if (type != null) {
                                    if (revisioned) {
                                        Backup.purgeNamedGraphRevisioned(statementProvider,
                                                connectionContext.getConnection(), graphId);
                                        Backup.purgeNamedGraphStatementsRevisioned(statementProvider,
                                                connectionContext.getConnection(), graphId, metaId);
                                    } else {
                                        Backup.purgeNamedGraphNonRevisioned(statementProvider,
                                                connectionContext.getConnection(), graphId);
                                        Backup.purgeNamedGraphStatementsNonRevisioned(statementProvider,
                                                connectionContext.getConnection(), graphId, metaId);
                                    }
                                    getIndexHandler().indexer.purgeNamedGraph(graphId);
                                    getIndexHandler().indexer.purgeNamedGraph(metaId);
                                }
                                for (BackupRevision backup : revisions) {
                                    Long lastMod = nodeLayout.store(backup.lastModifiedBy,
                                            connectionContext.getConnection(), 1);
                                    if (revisioned) {
                                        Backup.restoreNamedGraph(statementProvider,
                                                connectionContext.getConnection(), backup.start, backup.end,
                                                graphId, metaId, uuidId, backup.revision, lastMod);
                                    } else {
                                        Backup.restoreNamedGraphNR(statementProvider,
                                                connectionContext.getConnection(), backup.start, graphId, metaId,
                                                uuidId, backup.revision, lastMod);
                                    }
                                    if (backup.start != null
                                            && (backup.end == null || backup.end.longValue() == 0)) {
                                        timestamp = backup.start;
                                    }
                                }
                                if (type == null) {
                                    if (!namedGraphUri.equals(GRAPHS.GRAPHS_DATASET)
                                            && !namedGraphUri.equals(GRAPHS.METADATA_GRAPHS_DATASET)) {
                                        String stmtId = MessageFormat.format(ID_STRING, graphsDatasetId,
                                                graphsDatasetId, namedGraphPropertyId, graphId);
                                        Backup.restoreStatement(statementProvider,
                                                connectionContext.getConnection(), stmtId, 0, graphsUUID,
                                                graphsDatasetId, graphsDatasetId, namedGraphPropertyId, graphId, 0,
                                                null);
                                        stmtId = MessageFormat.format(ID_STRING, metadataDatasetId,
                                                metadataDatasetId, namedGraphPropertyId, metaId);
                                        Backup.restoreStatement(statementProvider,
                                                connectionContext.getConnection(), stmtId, 0, metadataUUID,
                                                metadataDatasetId, metadataDatasetId, namedGraphPropertyId, metaId,
                                                0, null);
                                    }
                                }
                                return true;
                            } else {
                                return false;
                            }
                        }

                        public void end() throws AnzoException {
                            commit(connectionContext.getConnection(), true, true);
                            nodeLayout.commitReferencedIds(connectionContext.getConnection(), 1);
                            getIndexHandler().indexer.postIndex();
                        }
                    });
                } catch (AnzoException ae) {
                    abort(connectionContext.getConnection(), true, true);
                } finally {
                    if (fileReader != null) {
                        fileReader.close();
                    }
                }

            } catch (Exception e) {
                log.error(LogUtils.RDB_MARKER, "Error importing backup data", e);
            }

        } finally {
            unlockTable(connectionContext.getConnection(), true);
            returnWriteContext(connectionContext);
        }
    }

    /**
     * @return the indexHandler
     */
    protected NodeCentricIndexUpdateHandler getIndexHandler() {
        return indexHandler;
    }

    /**
     * @return the instanceId
     */
    public String getInstanceId() {
        return instanceId;
    }

    @Override
    public void executeCommand(String command, IDataset request, IDataset response) throws AnzoException {
        if (command.equals("runstats")) {
            runstats();
        } else {
            super.executeCommand(command, request, response);
        }
    }

    /**
     * Get the capabilities of the datasource
     */
    @Override
    public INamedGraph getCapabilities() {
        INamedGraph graph = super.getCapabilities();
        URI uri = getInstanceURI();
        Datasource datasource = SystemFactory.getDatasource(uri, graph);
        datasource.addDatasourceCapability(DatasourceCapability.DatasourceRead);
        datasource.addDatasourceCapability(DatasourceCapability.DatasourceQuery);
        datasource.addDatasourceCapability(DatasourceCapability.DatasourceUpdate);
        datasource.addDatasourceCapability(DatasourceCapability.DatasourceEvents);
        return graph;
    }
}