InMemorySesameEngine.java

/**
 * *****************************************************************************
 * Copyright 2013 SEMOSS.ORG
 *
 * This file is part of SEMOSS.
 *
 * SEMOSS is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * SEMOSS is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * SEMOSS. If not, see <http://www.gnu.org/licenses/>.
 * ****************************************************************************
 */
package com.ostrichemulators.semtool.rdf.engine.impl;

import com.ostrichemulators.semtool.model.vocabulary.SEMTOOL;
import com.ostrichemulators.semtool.user.LocalUserImpl;
import com.ostrichemulators.semtool.user.Security;
import com.ostrichemulators.semtool.util.Constants;
import java.util.Properties;
import org.apache.log4j.Logger;

import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;

import com.ostrichemulators.semtool.util.UriBuilder;
import info.aduna.iteration.Iterations;
import java.io.File;
import java.util.List;
import org.openrdf.model.Model;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.impl.TreeModel;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.sail.Sail;
import org.openrdf.sail.inferencer.fc.ForwardChainingRDFSInferencer;
import org.openrdf.sail.memory.MemoryStore;

/**
 * Holds the database in memory, and uses the Sesame API to facilitate querying
 * of RDF data sources.
 */
public class InMemorySesameEngine extends AbstractSesameEngine {

	public static final String SYNC_DELAY = "sync-delay";
	public static final String MEMSTORE_DIR = "memory-store-dir";
	public static final String INFER = "infer";

	private static final Logger log = Logger.getLogger( InMemorySesameEngine.class );
	private RepositoryConnection rc = null;
	private boolean iControlMyRc = false;

	protected InMemorySesameEngine() {
	}

	public static InMemorySesameEngine open() {
		return open( new Properties() );
	}

	public static InMemorySesameEngine open( Properties props ) {
		InMemorySesameEngine eng = new InMemorySesameEngine();
		try {
			eng.openDB( props );
		}
		catch ( Exception e ) {
			log.error( e );
		}
		return eng;
	}

	public static InMemorySesameEngine open( boolean infer ) {
		Properties props = new Properties();
		props.setProperty( INFER, Boolean.toString( infer ) );
		return open( props );
	}

	public static Properties generateProperties( File file ) {

		Properties props = new Properties();
		props.setProperty( MEMSTORE_DIR, ( "memorystore.data".equals( file.getName() )
				? file.getAbsoluteFile().getParent()
				: file.getPath() ) );
		props.setProperty( Constants.ENGINE_IMPL,
				InMemorySesameEngine.class.getCanonicalName() );
		return props;
	}

	@Override
	protected final void createRc( Properties p ) {
		if ( null != rc ) {
			// we've already have our rc created, so there's nothing to do here
			return;
		}

		Security.getSecurity().associateUser( this, new LocalUserImpl() );

		MemoryStore memstore = ( p.containsKey( MEMSTORE_DIR )
				? new MemoryStore( new File( p.getProperty( MEMSTORE_DIR ) ) )
				: new MemoryStore() );

		if ( p.containsKey( SYNC_DELAY ) ) {
			memstore.setSyncDelay( Long.parseLong( p.getProperty( SYNC_DELAY ) ) );
		}

		Sail sail = ( p.containsKey( INFER )
				? new ForwardChainingRDFSInferencer( memstore )
				: memstore );

		Repository repo = new SailRepository( sail );

		try {
			repo.initialize();
			rc = repo.getConnection();
		}
		catch ( Exception e ) {
			try {
				repo.shutDown();
			}
			catch ( Exception ex ) {
				log.error( ex, ex );
			}
		}

		setRepositoryConnection( rc, true );
	}

	/**
	 * Method setRepositoryConnection. Sets the repository connection.
	 *
	 * @param rc RepositoryConnection. The repository connection that this is
	 * being set to.
	 */
	private void setRepositoryConnection( RepositoryConnection rc,
			boolean takeControl ) {

		this.rc = rc;
		iControlMyRc = takeControl;

		try {

			URI baseuri = null;
			// if the baseuri isn't already set, then query the kb for void:Dataset
			RepositoryResult<Statement> rr
					= rc.getStatements( null, RDF.TYPE, SEMTOOL.Database, false );
			List<Statement> stmts = Iterations.asList( rr );
			for ( Statement s : stmts ) {
				baseuri = URI.class.cast( s.getSubject() );
				break;
			}

			if ( null == baseuri ) {
				// no base uri in the DB, so make a new one
				baseuri = getNewBaseUri();
				//rc.begin();
				rc.add( baseuri, RDF.TYPE, SEMTOOL.Database );
				//rc.add(  baseuri, SEMTOOL.ReificationModel, SEMTOOL.SEMTOOL_Reification );
				//rc.commit();
			}

			setBaseUri( baseuri );
		}
		catch ( RepositoryException re ) {
			log.warn( re, re );
		}
	}

	public void setBuilders( UriBuilder data, UriBuilder schema ) {
		this.setDataBuilder( data );
		this.setSchemaBuilder( schema );
	}

	/**
	 * Method getRepositoryConnection. Gets the repository connection.
	 *
	 * @return RepositoryConnection - the connection to the repository.
	 */
	@Override
	public RepositoryConnection getRawConnection() {
		return this.rc;
	}

	/**
	 * Creates a model from all the statements in this engine
	 * @return
	 * @throws RepositoryException
	 */
	public Model toModel() throws RepositoryException {
		TreeModel model = new TreeModel();

		RepositoryResult<Statement> stmts
				= rc.getStatements( null, null, null, false );
		while ( stmts.hasNext() ) {
			model.add( stmts.next() );
		}

		return model;
	}

	/**
	 * Closes the data base associated with the engine. This will prevent further
	 * changes from being made in the data store and safely ends the active
	 * transactions and closes the engine.
	 */
	@Override
	public void closeDB() {
		if ( iControlMyRc ) {
			try {
				rc.close();
			}
			catch ( Exception e ) {
				log.error( e, e );
			}

			Repository repo = rc.getRepository();
			try {
				repo.shutDown();
			}
			catch ( Exception e ) {
				log.error( e, e );
			}

			rc = null;
		}
		super.closeDB();
	}
}