/* * Copyright (c) 2013-2015 Josef Hardi <josef.hardi@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.obidea.semantika.materializer; import java.io.File; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Set; import org.slf4j.Logger; import com.obidea.semantika.app.ApplicationManager; import com.obidea.semantika.database.IDatabase; import com.obidea.semantika.database.sql.base.ISqlExpression; import com.obidea.semantika.database.sql.base.SqlSelectItem; import com.obidea.semantika.database.sql.deparser.SqlDeparser; import com.obidea.semantika.exception.SemantikaRuntimeException; import com.obidea.semantika.expression.base.ITerm; import com.obidea.semantika.expression.base.IUriReference; import com.obidea.semantika.io.FileDocumentTarget; import com.obidea.semantika.io.IDocumentTarget; import com.obidea.semantika.mapping.IUriTemplate; import com.obidea.semantika.mapping.base.IMapping; import com.obidea.semantika.mapping.base.TripleAtom; import com.obidea.semantika.mapping.base.sql.SqlColumn; import com.obidea.semantika.mapping.base.sql.SqlIsNotNull; import com.obidea.semantika.mapping.base.sql.SqlQuery; import com.obidea.semantika.mapping.base.sql.SqlSelectQuery; import com.obidea.semantika.mapping.base.sql.SqlUriConcat; import com.obidea.semantika.mapping.base.sql.SqlUriValue; import com.obidea.semantika.queryanswer.processor.TermToSqlConverter; import com.obidea.semantika.util.LogUtils; public class RdfMaterializerEngine implements IMaterializerEngine { private ApplicationManager mAppManager; private Connection mConnection; private IRdfMaterializer mMaterializer = new NTriplesMaterializer(); // by default private TermToSqlConverter mConverter = new TermToSqlConverter(); private static final Logger LOG = LogUtils.createLogger("semantika.materializer"); //$NON-NLS-1$ public RdfMaterializerEngine(final ApplicationManager appManager) { mAppManager = appManager; } public IDatabase getTargetDatabase() { return mAppManager.getTargetDatabase(); } @Override public void start() throws MaterializerEngineException { try { LOG.debug("Starting materializer engine."); //$NON-NLS-1$ mConnection = mAppManager.getConnectionProvider().getConnection(); } catch (SQLException e) { throw new MaterializerEngineException(e); } } @Override public void stop() throws MaterializerEngineException { try { LOG.debug("Stopping materializer engine."); //$NON-NLS-1$ mAppManager.getConnectionProvider().closeConnection(mConnection); } catch (SQLException e) { throw new MaterializerEngineException(e); } } @Override public boolean isStarted() { try { if (mConnection != null && !mConnection.isClosed()) { return true; } return false; } catch (SQLException e) { LOG.error("Connection error", e); return false; } } public RdfMaterializerEngine useNTriples() { LOG.debug("Using NTriples format."); //$NON-NLS-1$ setMateriliazer(new NTriplesMaterializer()); return this; } public RdfMaterializerEngine useTurtle() { LOG.debug("Using Turtle format."); //$NON-NLS-1$ setMateriliazer(new TurtleMaterializer()); return this; } public RdfMaterializerEngine useRdfXml() { LOG.debug("Using RDF/XML format."); //$NON-NLS-1$ setMateriliazer(new RdfXmlMaterializer()); return this; } public RdfMaterializerEngine useRdfJson() { LOG.debug("Using JSON-LD format."); //$NON-NLS-1$ setMateriliazer(new RdfJsonMaterializer()); return this; } public void setMateriliazer(IRdfMaterializer materializer) { mMaterializer = materializer; } @Override public void materialize(File file) throws MaterializationException { materialize(file, new DefaultProgressMonitor()); } @Override public void materialize(File file, IProgressMonitor progressMonitor) throws MaterializationException { if (file == null) { throw new MaterializationException("Output file cannot be empty"); //$NON-NLS-1$ } removeFileIfExists(file); materialize(new FileDocumentTarget(file), progressMonitor); } public void materialize(IDocumentTarget output, IProgressMonitor progressMonitor) throws MaterializationException { try { checkConnection(); int returnSize = 0; int mappingSize = mAppManager.getKnowledgeBase().getMappingSet().size(); SqlDeparser deparser = new SqlDeparser(mAppManager.getTargetDatabase().getDialect()); LOG.info("Materialization in progress."); //$NON-NLS-1$ progressMonitor.start(mappingSize); for (IMapping mapping : mAppManager.getKnowledgeBase().getMappingSet().getAll()) { ResultSet resultSet = null; progressMonitor.advanced(returnSize); try { SqlQuery query = prepareQueryForMaterialization(mapping); TriplesProjection projection = new TriplesProjection(query); String sql = deparser.deparse(query); Statement stmt = createSqlStatement(); resultSet = stmt.executeQuery(sql); returnSize = mMaterializer.materializeTuples(resultSet, projection, output); } catch (SQLException e) { throw new MaterializationException(e); } finally { if (resultSet != null && !resultSet.isClosed()) { resultSet.close(); } } } progressMonitor.finish(); } catch (SQLException e) { throw new MaterializationException(e); } } protected void checkConnection() throws SQLException { if (mConnection == null) { throw new SemantikaRuntimeException( "Connection is null, call start() method first to start the engine."); //$NON-NLS-1$ } if (mConnection.isClosed()) { throw new SemantikaRuntimeException( "Connection is closed, call start() method first to start the engine."); //$NON-NLS-1$ } } protected Statement createSqlStatement() throws SQLException { Statement stmt = mConnection.createStatement(); adjustFetchSize(stmt); return stmt; } protected void adjustFetchSize(Statement stmt) throws SQLException { final String databaseName = mAppManager.getTargetDatabase().getDatabaseProduct(); if (databaseName.equals("MySQL")) { //$NON-NLS-1$ stmt.setFetchSize(Integer.MIN_VALUE); // allow data streaming thus avoid heap memory error. } else { stmt.setFetchSize(100); } } /* * Private utility methods */ private SqlQuery prepareQueryForMaterialization(final IMapping mapping) { SqlQuery query = initQuery(mapping.getSourceQuery()); insertProjection(query, mapping.getTargetAtom()); insertSelection(query, mapping.getSourceQuery().getFromExpression()); insertFilters(query, mapping.getSourceQuery().getWhereExpression()); insertNotNullFilters(query, mapping.getTargetAtom()); return query; } private SqlQuery initQuery(SqlQuery sourceQuery) { return new SqlSelectQuery(sourceQuery.isDistinct()); } private void insertProjection(SqlQuery query, TripleAtom head) { setSubject(query, TripleAtom.getSubject(head)); setPredicate(query, TripleAtom.getPredicate(head)); setObject(query, TripleAtom.getObject(head)); } private void setSubject(SqlQuery query, ITerm term) { if (term instanceof IUriTemplate) { SqlUriConcat uriConcatExpression = mConverter.toSqlUriConcat((IUriTemplate) term); SqlSelectItem selectItem = new SqlSelectItem(uriConcatExpression); selectItem.setAliasName("subject"); //$NON-NLS-1$ query.addSelectItem(selectItem); } else if (term instanceof IUriReference) { SqlUriValue valueExpression = mConverter.toSqlUriValue((IUriReference) term); SqlSelectItem selectItem = new SqlSelectItem(valueExpression); selectItem.setAliasName("subject"); //$NON-NLS-1$ query.addSelectItem(selectItem); } else { throw new SemantikaRuntimeException("Other type: " + term.getClass()); } } private void setPredicate(SqlQuery query, ITerm term) { if (term instanceof IUriReference) { SqlUriValue valueExpression = mConverter.toSqlUriValue((IUriReference) term); SqlSelectItem selectItem = new SqlSelectItem(valueExpression); selectItem.setAliasName("predicate"); //$NON-NLS-1$ query.addSelectItem(selectItem); } else { throw new SemantikaRuntimeException("Other type: " + term.getClass()); } } private void setObject(SqlQuery query, ITerm term) { if (term instanceof SqlColumn) { SqlSelectItem selectItem = new SqlSelectItem((SqlColumn) term); selectItem.setAliasName("object"); //$NON-NLS-1$ query.addSelectItem(selectItem); } else if (term instanceof IUriTemplate) { SqlUriConcat uriConcatExpression = mConverter.toSqlUriConcat((IUriTemplate) term); SqlSelectItem selectItem = new SqlSelectItem(uriConcatExpression); selectItem.setAliasName("object"); //$NON-NLS-1$ query.addSelectItem(selectItem); } else if (term instanceof IUriReference) { SqlUriValue valueExpression = mConverter.toSqlUriValue((IUriReference) term); SqlSelectItem selectItem = new SqlSelectItem(valueExpression); selectItem.setAliasName("object"); //$NON-NLS-1$ query.addSelectItem(selectItem); } else { throw new SemantikaRuntimeException("Other type: " + term.getClass()); } } private void insertSelection(SqlQuery query, ISqlExpression fromExpression) { query.setFromExpression(fromExpression); } private void insertFilters(SqlQuery query, Set<ISqlExpression> whereExpressions) { for (ISqlExpression filter : whereExpressions) { query.addWhereExpression(filter); } } private void insertNotNullFilters(SqlQuery query, TripleAtom head) { ITerm subjectTerm = TripleAtom.getSubject(head); setNotNullFilter(query, subjectTerm); ITerm objectTerm = TripleAtom.getObject(head); setNotNullFilter(query, objectTerm); } private void setNotNullFilter(SqlQuery query, ITerm term) { if (term instanceof SqlColumn) { ISqlExpression filter = new SqlIsNotNull((SqlColumn) term); query.addWhereExpression(filter); } else if (term instanceof IUriTemplate) { IUriTemplate uriTemplate = (IUriTemplate) term; for (ITerm parameter : uriTemplate.getParameters()) { setNotNullFilter(query, parameter); } } } private static void removeFileIfExists(File file) { if (file.exists()) { file.delete(); } } }