org.apache.marmotta.kiwi.reasoner.test.persistence.JustificationPersistenceTest.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.marmotta.kiwi.reasoner.test.persistence.JustificationPersistenceTest.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 org.apache.marmotta.kiwi.reasoner.test.persistence;

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.not;
import info.aduna.iteration.CloseableIteration;

import java.sql.BatchUpdateException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.marmotta.kiwi.config.KiWiConfiguration;
import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
import org.apache.marmotta.kiwi.persistence.KiWiPersistence;
import org.apache.marmotta.kiwi.reasoner.model.program.Justification;
import org.apache.marmotta.kiwi.reasoner.model.program.Program;
import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParserBase;
import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection;
import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningPersistence;
import org.apache.marmotta.kiwi.sail.KiWiStore;
import org.apache.marmotta.kiwi.sail.KiWiValueFactory;
import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.sail.SailRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This test verifies the persistence functionality of the reasoning component regarding storing, loading and deleting
 * reasoning programs.
 *
 * @see org.apache.marmotta.kiwi.persistence.KiWiConnection
 * @see org.apache.marmotta.kiwi.persistence.KiWiPersistence
 * @author Sebastian Schaffert (sschaffert@apache.org)
 */
@RunWith(KiWiDatabaseRunner.class)
public class JustificationPersistenceTest {

    private KiWiPersistence persistence;
    private KiWiReasoningPersistence rpersistence;

    private Repository repository;
    private final KiWiConfiguration config;

    public JustificationPersistenceTest(KiWiConfiguration config) {
        this.config = config;
    }

    @Before
    public void initDatabase() throws Exception {
        KiWiStore store = new KiWiStore(config);

        repository = new SailRepository(store);
        repository.initialize();

        persistence = store.getPersistence();

        rpersistence = new KiWiReasoningPersistence(persistence, repository.getValueFactory());
        rpersistence.initDatabase();

    }

    @After
    public void dropDatabase() throws Exception {
        rpersistence.dropDatabase();
        persistence.dropDatabase();
        repository.shutDown();
    }

    final Logger logger = LoggerFactory.getLogger(JustificationPersistenceTest.class);

    /**
     * Test 1: create some triples through a repository connection (some inferred, some base), load a program, and
     * store justifications for the inferred triples based on rules and base triples. Test the different listing
     * functions.
     *
     */
    @Test
    public void testStoreJustifications() throws Exception {
        KiWiValueFactory v = (KiWiValueFactory) repository.getValueFactory();

        URI ctxb = v.createURI("http://localhost/context/default");
        URI ctxi = v.createURI("http://localhost/context/inferred");

        URI s1 = v.createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
        URI s2 = v.createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
        URI s3 = v.createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
        URI p1 = v.createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
        URI p2 = v.createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
        URI o1 = v.createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
        URI o2 = v.createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
        URI o3 = v.createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));

        // first, load a sample program (it does not really matter what it actually contains, since we are not really
        // running the reasoner)
        KWRLProgramParserBase parser = new KWRLProgramParser(v,
                this.getClass().getResourceAsStream("test-001.kwrl"));
        Program p = parser.parseProgram();
        p.setName("test-001");

        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            // should not throw an exception and the program should have a database ID afterwards
            connection.storeProgram(p);
            connection.commit();
        } finally {
            connection.close();
        }

        // then get a connection to the repository and create a number of triples, some inferred and some base
        RepositoryConnection con = repository.getConnection();
        try {
            con.add(s1, p1, o1);
            con.add(s2, p1, o2);
            con.add(s3, p1, o3);

            con.add(s1, p2, o1, ctxi);
            con.add(s2, p2, o2, ctxi);
            con.add(s3, p2, o3, ctxi);

            con.commit();
        } finally {
            con.close();
        }

        connection = rpersistence.getConnection();
        try {
            // retrieve the persisted triples and put them into two sets to build justifications
            List<Statement> baseTriples = asList(
                    connection.listTriples(null, null, null, v.convert(ctxb), false, true));
            List<Statement> infTriples = asList(
                    connection.listTriples(null, null, null, v.convert(ctxi), true, true));

            Assert.assertEquals("number of base triples was not 3", 3, baseTriples.size());
            Assert.assertEquals("number of inferred triples was not 3", 3, infTriples.size());

            // we manually update the "inferred" flag for all inferred triples, since this is not possible through the
            // repository API
            PreparedStatement updateInferred = connection.getJDBCConnection()
                    .prepareStatement("UPDATE triples SET inferred = true WHERE id = ?");
            for (Statement stmt : infTriples) {
                KiWiTriple triple = (KiWiTriple) stmt;
                updateInferred.setLong(1, triple.getId());
                updateInferred.addBatch();
            }
            updateInferred.executeBatch();
            updateInferred.close();

            // now we create some justifications for the inferred triples and store them
            Set<Justification> justifications = new HashSet<Justification>();
            Justification j1 = new Justification();
            j1.getSupportingRules().add(p.getRules().get(0));
            j1.getSupportingRules().add(p.getRules().get(1));
            j1.getSupportingTriples().add((KiWiTriple) baseTriples.get(0));
            j1.getSupportingTriples().add((KiWiTriple) baseTriples.get(1));
            j1.setTriple((KiWiTriple) infTriples.get(0));
            justifications.add(j1);

            Justification j2 = new Justification();
            j2.getSupportingRules().add(p.getRules().get(1));
            j2.getSupportingTriples().add((KiWiTriple) baseTriples.get(1));
            j2.getSupportingTriples().add((KiWiTriple) baseTriples.get(2));
            j2.setTriple((KiWiTriple) infTriples.get(1));
            justifications.add(j2);

            connection.storeJustifications(justifications);
            connection.commit();

            // we should now have two justifications in the database
            PreparedStatement listJustifications = connection.getJDBCConnection()
                    .prepareStatement("SELECT count(*) AS count FROM reasoner_justifications");
            ResultSet resultListJustifications = listJustifications.executeQuery();

            Assert.assertTrue(resultListJustifications.next());
            Assert.assertEquals(2, resultListJustifications.getInt("count"));
            resultListJustifications.close();
            connection.commit();

            PreparedStatement listSupportingTriples = connection.getJDBCConnection()
                    .prepareStatement("SELECT count(*) AS count FROM reasoner_just_supp_triples");
            ResultSet resultListSupportingTriples = listSupportingTriples.executeQuery();

            Assert.assertTrue(resultListSupportingTriples.next());
            Assert.assertEquals(4, resultListSupportingTriples.getInt("count"));
            resultListSupportingTriples.close();
            connection.commit();

            PreparedStatement listSupportingRules = connection.getJDBCConnection()
                    .prepareStatement("SELECT count(*) AS count FROM reasoner_just_supp_rules");
            ResultSet resultListSupportingRules = listSupportingRules.executeQuery();

            Assert.assertTrue(resultListSupportingRules.next());
            Assert.assertEquals(3, resultListSupportingRules.getInt("count"));
            resultListSupportingRules.close();
            connection.commit();

            // *** check listing justifications by base triple (supporting triple)

            // there should now be two justifications based on triple baseTriples.get(1))
            List<Justification> supported1 = asList(
                    connection.listJustificationsBySupporting((KiWiTriple) baseTriples.get(1)));
            Assert.assertEquals("number of justifications is wrong", 2, supported1.size());
            Assert.assertThat("justifications differ", supported1, hasItems(j1, j2));

            // only j1 should be supported by triple baseTriples.get(0))
            List<Justification> supported2 = asList(
                    connection.listJustificationsBySupporting((KiWiTriple) baseTriples.get(0)));
            Assert.assertEquals("number of justifications is wrong", 1, supported2.size());
            Assert.assertThat("justifications differ", supported2, allOf(hasItem(j1), not(hasItem(j2))));

            // only j2 should be supported by triple baseTriples.get(2))
            List<Justification> supported3 = asList(
                    connection.listJustificationsBySupporting((KiWiTriple) baseTriples.get(2)));
            Assert.assertEquals("number of justifications is wrong", 1, supported3.size());
            Assert.assertThat("justifications differ", supported3, allOf(hasItem(j2), not(hasItem(j1))));

            // *** check listing justificatoins by supporting rule

            // there should now be two justifications based on triple p.getRules().get(1)
            List<Justification> supported4 = asList(connection.listJustificationsBySupporting(p.getRules().get(1)));
            Assert.assertEquals("number of justifications is wrong", 2, supported4.size());
            Assert.assertThat("justifications differ", supported4, hasItems(j1, j2));

            // only j1 should be supported by triple p.getRules().get(0)
            List<Justification> supported5 = asList(connection.listJustificationsBySupporting(p.getRules().get(0)));
            Assert.assertEquals("number of justifications is wrong", 1, supported5.size());
            Assert.assertThat("justifications differ", supported5, allOf(hasItem(j1), not(hasItem(j2))));

            // *** check listing justifications by supported (inferred) triple

            // there should now be one justification supporting infTriples.get(0)
            List<Justification> supported6 = asList(
                    connection.listJustificationsForTriple((KiWiTriple) infTriples.get(0)));
            Assert.assertEquals("number of justifications is wrong", 1, supported6.size());
            Assert.assertThat("justifications differ", supported6, allOf(hasItem(j1), not(hasItem(j2))));

            // there should now be one justification supporting infTriples.get(1)
            List<Justification> supported7 = asList(
                    connection.listJustificationsForTriple((KiWiTriple) infTriples.get(1)));
            Assert.assertEquals("number of justifications is wrong", 1, supported7.size());
            Assert.assertThat("justifications differ", supported7, allOf(hasItem(j2), not(hasItem(j1))));

            // there should now be no justification supporting infTriples.get(2)
            List<Justification> supported8 = asList(
                    connection.listJustificationsForTriple((KiWiTriple) infTriples.get(2)));
            Assert.assertEquals("number of justifications is wrong", 0, supported8.size());

            // *** check listing unsupported triples
            List<KiWiTriple> unsupported = asList(connection.listUnsupportedTriples());
            Assert.assertEquals("number of unsupported triples is wrong", 1, unsupported.size());
            Assert.assertThat("unsupported triples differ", unsupported, hasItem((KiWiTriple) infTriples.get(2)));

            // now we delete justification 2; as a consequence,
            // - there should be only once justification left
            // - there should be two unsupported triples
            connection.deleteJustifications(Collections.singleton(j2));

            // we should now have one justifications in the database
            resultListJustifications = listJustifications.executeQuery();

            Assert.assertTrue(resultListJustifications.next());
            Assert.assertEquals(1, resultListJustifications.getInt("count"));
            resultListJustifications.close();
            connection.commit();

            resultListSupportingTriples = listSupportingTriples.executeQuery();

            Assert.assertTrue(resultListSupportingTriples.next());
            Assert.assertEquals(2, resultListSupportingTriples.getInt("count"));
            resultListSupportingTriples.close();
            connection.commit();

            resultListSupportingRules = listSupportingRules.executeQuery();

            Assert.assertTrue(resultListSupportingRules.next());
            Assert.assertEquals(2, resultListSupportingRules.getInt("count"));
            resultListSupportingRules.close();
            connection.commit();

            List<KiWiTriple> unsupported2 = asList(connection.listUnsupportedTriples());
            Assert.assertEquals("number of unsupported triples is wrong", 2, unsupported2.size());
            Assert.assertThat("unsupported triples differ", unsupported2, hasItem((KiWiTriple) infTriples.get(1)));

        } catch (BatchUpdateException ex) {
            if (ex.getNextException() != null) {
                ex.printStackTrace();
                throw ex.getNextException();
            } else {
                throw ex;
            }
        } finally {
            connection.close();
        }

    }

    /**
     * Workaround for https://openrdf.atlassian.net/browse/SES-1702 in Sesame 2.7.0-beta1
     * @param <E>
     * @return
     */
    public static <E, X extends Exception> List<E> asList(CloseableIteration<E, X> result) throws Exception {
        ArrayList<E> collection = new ArrayList<E>();
        try {
            while (result.hasNext()) {
                collection.add(result.next());
            }

            return collection;
        } finally {
            result.close();
        }
    }

}