AbstractEngine.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.SEMONTO;
import java.io.IOException;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.openrdf.repository.RepositoryException;
import com.ostrichemulators.semtool.rdf.engine.api.IEngine;
import com.ostrichemulators.semtool.util.Constants;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import com.ostrichemulators.semtool.rdf.engine.api.InsightManager;
import com.ostrichemulators.semtool.rdf.engine.api.UpdateExecutor;
import com.ostrichemulators.semtool.rdf.engine.util.EngineManagementException;
import com.ostrichemulators.semtool.user.Security;
import com.ostrichemulators.semtool.user.User;
import com.ostrichemulators.semtool.util.UriBuilder;
import com.ostrichemulators.semtool.util.Utility;
import java.util.Collection;
import org.apache.log4j.Level;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.impl.URIImpl;
/**
* An Abstract Engine that sets up the base constructs needed to create an
* engine.
*/
public abstract class AbstractEngine implements IEngine {
public static final String REMOTE_KEY = "repo-is-remote";
private static final Logger log = Logger.getLogger( AbstractEngine.class );
private static final Logger provenance = Logger.getLogger( "provenance" );
private String engineName = null;
private Properties prop = new Properties();
private InsightManager insightEngine;
private UriBuilder schemabuilder;
private UriBuilder databuilder;
private URI baseuri;
public AbstractEngine() {
}
/**
* Opens the database. This function calls (in this order)
* <ol>
* <li>{@link #startLoading(java.util.Properties) },
* <li>{@link #setUris(java.lang.String, java.lang.String) },
* <li>{@link #finishLoading(java.util.Properties) }
* </ol>
*
* @param initprops
*/
@Override
public void openDB( Properties initprops ) throws RepositoryException {
prop = Utility.copyProperties( initprops );
if ( log.isDebugEnabled() ) {
StringBuilder sb = new StringBuilder( "db properties:" );
final String lf = System.getProperty( "line.separator" );
for ( String key : initprops.stringPropertyNames() ) {
sb.append( lf ).append( key ).append( "=>" ).append( initprops.getProperty( key ) );
}
log.debug( sb.toString() );
}
startLoading( prop );
String baseuristr = prop.getProperty( Constants.BASEURI_KEY, "" );
String owlstarter = prop.getProperty( Constants.SEMOSS_URI, null );
if ( null == owlstarter ) {
log.warn( "no schema URI set...using " + SEMONTO.NAMESPACE );
owlstarter = SEMONTO.NAMESPACE;
}
baseuri = new URIImpl( setUris( baseuristr, owlstarter ).stringValue() );
finishLoading( prop );
}
/**
* Initiates the loading process with the given properties. If overridden,
* subclasses should be sure to call their superclass's version of this
* function in addition to whatever other processing they do.
*
* @param props
* @throws RepositoryException
*/
protected void startLoading( Properties props ) throws RepositoryException {
insightEngine = createInsightManager();
}
protected abstract InsightManager createInsightManager() throws RepositoryException;
protected void finishLoading( Properties props ) throws RepositoryException {
}
@Override
public String getProperty( String key ) {
return prop.getProperty( key );
}
@Override
public final void setSchemaBuilder( UriBuilder ns ) {
schemabuilder = ns;
}
@Override
public UriBuilder getSchemaBuilder() {
return schemabuilder;
}
@Override
public UriBuilder getDataBuilder() {
return databuilder;
}
@Override
public void setDataBuilder( UriBuilder b ) {
databuilder = b;
}
@Override
public org.openrdf.model.URI getBaseUri() {
return baseuri;
}
protected void setBaseUri( URI base ) {
baseuri = base;
}
/**
* An extension point for subclasses to set their base uris during the load
* process. This function should set the {@link #baseuri},
* {@link #databuilder}, {@link #schemabuilder}
*
* @param data the data builder uri property value from the properties file
* (possibly empty)
* @param schema the schema builder uri (never empty)
* @return this database's unique id. this will include some sort of UUID
* @throws org.openrdf.repository.RepositoryException
*/
protected abstract URI setUris( String data, String schema ) throws RepositoryException;
/**
* Update the "last modified" date of the dataset. This operation should fail
* silently if necessary
*/
protected abstract void updateLastModifiedDate();
@Override
public boolean isConnected() {
return false;
}
/**
* Sets the name of the engine. This may be a lot of times the same as the
* Repository Name
*
* @param engineName - Name of the engine that this is being set to
*/
@Override
public void setEngineName( String engineName ) {
this.engineName = engineName;
}
/**
* Gets the engine name for this engine
*
* @return Name of the engine it is being set to
*/
@Override
public String getEngineName() {
return engineName;
}
/**
* Commits the database.
*/
@Override
public void commit() {
}
@Override
public InsightManager getInsightManager() {
return insightEngine;
}
@Override
public void updateInsights( InsightManager im ) throws EngineManagementException {
log.warn( "updateInsights not yet implemented. This call does nothing." );
}
@Override
public void setInsightManager( InsightManager ie ) {
this.insightEngine = ie;
}
protected static File searchFor( String filename, File... dirs ) {
if ( null != filename ) {
File orig = new File( filename );
if ( orig.isAbsolute() && orig.exists() ) {
return orig;
}
final String basefilename = orig.getName();
final List<String> dirparts
= new ArrayList<>( Arrays.asList( filename.split( Pattern.quote(
File.separator ) ) ) );
// we're going to use the filename anyway, so we can remove it from the list
if ( !dirparts.isEmpty() ) {
dirparts.remove( dirparts.size() - 1 );
}
// special case: filename is db/XXX, we can skip the db part
if ( !dirparts.isEmpty() && "db".equals( dirparts.get( 0 ) ) ) {
dirparts.remove( 0 );
}
List<File> searchpath = new ArrayList<>();
for ( File dir : dirs ) {
searchpath.add( new File( dir, basefilename ) );
StringBuilder sb = new StringBuilder();
for ( String dirpart : dirparts ) {
sb.append( File.separator ).append( dirpart );
File check = new File( dir + sb.toString(), basefilename );
searchpath.add( check );
}
}
searchpath.add( orig );
Set<String> checked = new HashSet<>(); // don't check the same place twice
for ( File loc : searchpath ) {
if ( !checked.contains( loc.getAbsolutePath() ) ) {
try {
checked.add( loc.getAbsolutePath() );
log.debug( "looking for " + filename + " as " + loc.
getAbsolutePath() );
if ( loc.exists() ) {
log.debug( "using " + loc.getCanonicalPath() );
return loc.getCanonicalFile();
}
}
catch ( IOException ioe ) {
log.error( "could not access file: " + loc.getAbsolutePath(), ioe );
}
}
}
}
return null;
}
@Override
public void setProperty( String key, String val ) {
if ( null == val || val.isEmpty() ) {
prop.remove( key );
}
else {
prop.setProperty( key, val );
}
}
@Override
public Properties getProperties() {
Properties p = new Properties();
p.putAll( prop );
return p;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append( null == engineName ? "unnamed" : engineName );
sb.append( " (base:" );
sb.append( null == databuilder ? "not set" : getBaseUri() ).append( ")" );
return sb.toString();
}
public abstract boolean supportsSparqlBindings();
@Override
public void calculateInferences() throws RepositoryException {
// nothing to do
}
public static final URI getNewBaseUri() {
URI baseuri = UriBuilder.getBuilder( "http://os-em.com/semtool/database/" ).uniqueUri();
return baseuri;
}
protected void logProvenance( UpdateExecutor ue ) {
if ( provenance.isEnabledFor( Level.INFO ) ) {
User user = Security.getSecurity().getAssociatedUser( this );
provenance.info( user.getUsername() + ": " + ue.bindAndGetSparql() );
}
}
protected void logProvenance( Collection<Statement> stmts ) {
if ( provenance.isEnabledFor( Level.INFO ) ) {
User user = Security.getSecurity().getAssociatedUser( this );
for ( Statement stmt : stmts ) {
provenance.info( user.getUsername() + ": " + stmt );
}
}
}
@Override
public boolean isRemote() {
return Boolean.parseBoolean( prop.getProperty( REMOTE_KEY,
Boolean.FALSE.toString() ) );
}
}