All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.rdf.store.AbstractTestCase Maven / Gradle / Ivy

There is a newer version: 2.1.4
Show newest version
/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program 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; version 2 of the License.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Oct 14, 2006
 */

package com.bigdata.rdf.store;

import java.io.File;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import junit.framework.TestCase2;

import org.openrdf.model.Statement;
import org.openrdf.model.Value;
import org.openrdf.rio.RDFHandler;
import org.openrdf.rio.helpers.RDFHandlerBase;

import com.bigdata.btree.IIndex;
import com.bigdata.btree.ITupleIterator;
import com.bigdata.btree.ITupleSerializer;
import com.bigdata.btree.UnisolatedReadWriteIndex;
import com.bigdata.btree.keys.IKeyBuilder;
import com.bigdata.btree.proc.AbstractKeyArrayIndexProcedure.ResultBitBuffer;
import com.bigdata.btree.proc.BatchContains.BatchContainsConstructor;
import com.bigdata.btree.proc.IResultHandler;
import com.bigdata.journal.BufferMode;
import com.bigdata.journal.Journal;
import com.bigdata.journal.Options;
import com.bigdata.journal.TestHelper;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.IVUtility;
import com.bigdata.rdf.model.BigdataResource;
import com.bigdata.rdf.model.BigdataURI;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.rdf.model.BigdataValueSerializer;
import com.bigdata.rdf.model.StatementEnum;
import com.bigdata.rdf.rio.BasicRioLoader;
import com.bigdata.rdf.spo.ISPO;
import com.bigdata.rdf.spo.SPO;
import com.bigdata.rdf.spo.SPOComparator;
import com.bigdata.rdf.spo.SPOKeyOrder;
import com.bigdata.rdf.spo.SPOTupleSerializer;
import com.bigdata.relation.accesspath.AbstractArrayBuffer;
import com.bigdata.relation.accesspath.IBuffer;
import com.bigdata.service.AbstractClient;
import com.bigdata.service.Split;
import com.bigdata.striterator.IChunkedOrderedIterator;
import com.bigdata.striterator.IKeyOrder;
import com.bigdata.util.BytesUtil;

/**
 * 

* Abstract harness for testing under a variety of configurations. In order to * test a specific configuration, create a concrete instance of this class. The * configuration can be described using a mixture of a .properties * file of the same name as the test class and custom code. *

*

* When debugging from an IDE, it is very helpful to be able to run a single * test case. You can do this, but you MUST define the property * testClass as the name test class that has the logic required * to instantiate and configure an appropriate object manager instance for the * test. *

*/ abstract public class AbstractTestCase extends TestCase2 { /** A null {@link IV} reference (NOT a NullIV object). */ @SuppressWarnings("rawtypes") protected final IV NULL = null; // // Constructors. // public AbstractTestCase() {} public AbstractTestCase(String name) {super(name);} //************************************************************ //************************************************************ //************************************************************ /** * Invoked from {@link TestCase#setUp()} for each test in the suite. */ protected void setUp(final ProxyTestCase testCase) throws Exception { begin = System.currentTimeMillis(); if (log.isInfoEnabled()) log.info("\n\n================:BEGIN:" + testCase.getName() + ":BEGIN:===================="); // @see BLZG-1501 (remove LRUNexus) // if (LRUNexus.INSTANCE != null) { // // flush everything before/after a unit test. // LRUNexus.INSTANCE.discardAllCaches(); // } } /** * Invoked from {@link TestCase#tearDown()} for each test in the suite. */ protected void tearDown(final ProxyTestCase testCase) throws Exception { // @see BLZG-1501 (remove LRUNexus) // if (LRUNexus.INSTANCE != null) { // // flush everything before/after a unit test. // LRUNexus.INSTANCE.discardAllCaches(); // } final long elapsed = System.currentTimeMillis() - begin; if (log.isInfoEnabled()) log.info("\n================:END:" + testCase.getName() + " ("+elapsed+"ms):END:====================\n"); TestHelper.checkJournalsClosed(testCase, this); } private long begin; // // Properties // private Properties m_properties; /** *

* Returns properties read from a hierarchy of sources. The underlying * properties read from those sources are cached, but a new properties * object is returned on each invocation (to prevent side effects by the * caller). *

*

* In general, a test configuration critically relies on both the properties * returned by this method and the appropriate properties must be provided * either through the command line or in a properties file. *

* * @return A new properties object. */ public Properties getProperties() { if( m_properties == null ) { /* * Read properties from a hierarchy of sources and cache a * reference. */ m_properties = super.getProperties(); // disregard the inherited properties. // m_properties = new Properties(); // m_properties = new Properties( m_properties ); // disable statistics collection (federation) m_properties .setProperty( AbstractClient.Options.COLLECT_PLATFORM_STATISTICS, "false"); m_properties.setProperty( AbstractClient.Options.COLLECT_QUEUE_STATISTICS, "false"); m_properties.setProperty(AbstractClient.Options.HTTPD_PORT, "-1"); // disable statistics collection (journal) m_properties.setProperty( Journal.Options.COLLECT_PLATFORM_STATISTICS, "false"); m_properties.setProperty(Journal.Options.COLLECT_QUEUE_STATISTICS, "false"); m_properties .setProperty(Journal.Options.HTTPD_PORT, "-1"/* none */); m_properties.setProperty(Options.BUFFER_MODE,BufferMode.Disk.toString()); // m_properties.setProperty(Options.BUFFER_MODE,BufferMode.Transient.toString()); /* * If an explicit filename is not specified... */ if(m_properties.get(Options.FILE)==null) { /* * Use a temporary file for the test. Such files are always deleted when * the journal is closed or the VM exits. */ m_properties.setProperty(Options.CREATE_TEMP_FILE,"true"); m_properties.setProperty(Options.DELETE_ON_EXIT,"true"); } } return new Properties(m_properties); } /** * This method is invoked from methods that MUST be proxied to this class. * {@link GenericProxyTestCase} extends this class, as do the concrete * classes that drive the test suite for specific GOM integration test * configuration. Many method on this class must be proxied from * {@link GenericProxyTestCase} to the delegate. Invoking this method from * the implementations of those methods in this class provides a means of * catching omissions where the corresponding method is NOT being delegated. * Failure to delegate these methods means that you are not able to share * properties or object manager instances across tests, which means that you * can not do configuration-based testing of integrations and can also wind * up with mutually inconsistent test fixtures between the delegate and each * proxy test. */ protected void checkIfProxy() { if( this instanceof ProxyTestCase ) { throw new AssertionError(); } } //************************************************************ //************************************************************ //************************************************************ // // Test helpers. // // protected static final long N = IRawTripleStore.N; abstract protected AbstractTripleStore getStore(Properties properties); abstract protected AbstractTripleStore reopenStore(AbstractTripleStore store); public void assertEquals(SPO expected, SPO actual) { assertEquals(null,expected,actual); } public void assertEquals(String msg, SPO expected, SPO actual) { if(!expected.equals(actual)) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } fail(msg+"Expecting: "+expected+" not "+actual); } } public void assertEquals(SPO[] expected, SPO[] actual) { assertEquals(null,expected,actual); } public void assertEquals(String msg, SPO[] expected, SPO[] actual) { if( msg == null ) { msg = ""; } else { msg = msg + " : "; } if( expected == null && actual == null ) { return; } if( expected == null && actual != null ) { fail( msg+"Expected a null array." ); } if( expected != null && actual == null ) { fail( msg+"Not expecting a null array." ); } if (expected.length != actual.length) { /* * Only do message construction if we know that the assert will * fail. */ assertEquals(msg + "length differs.", expected.length, actual.length); } for( int i=0; iactual) { assertSameSPOs("", expected, actual); } static public void assertSameSPOs(String msg, ISPO[] expected, IChunkedOrderedIterator actual) { /* * clone expected[] and put into the same order as the iterator. */ expected = expected.clone(); final IKeyOrder keyOrder = actual.getKeyOrder(); if (keyOrder != null) { Arrays.sort(expected, keyOrder.getComparator()); } int i = 0; while (actual.hasNext()) { if (i >= expected.length) { // buffer up to N 'extra' values from the itr for the err msg. final Vector v = new Vector(); while (actual.hasNext() && v.size() < 10) { v.add(actual.next()); } fail(msg + ": The iterator is willing to visit more than " + expected.length + " objects. The next " + v.size() + " objects would be: " + Arrays.toString(v.toArray())); } final ISPO g = actual.next(); if (!expected[i].equals(g)) { /* * Only do message construction if we know that the assert will * fail. */ fail(msg + ": Different objects at index=" + i + ": expected=" + expected[i] + ", actual=" + g); } i++; } if (i < expected.length) { fail(msg + ": The iterator SHOULD have visited " + expected.length + " objects, but only visited " + i + " objects."); } } /** * Verify that the iterator visits the expected {@link ISPO}s in any order * without duplicates. * * @param store * Used to resolve term identifiers for messages. * @param expected * @param actual */ static public void assertSameSPOsAnyOrder(AbstractTripleStore store, ISPO[] expected, IChunkedOrderedIterator actual) { assertSameSPOsAnyOrder(store, expected, actual, false); } /** * Verify that the iterator visits the expected {@link ISPO}s in any order * without duplicates, optionally ignoring axioms. * * @param store * Used to resolve term identifiers for messages. * @param expected * @param actual (The iterator will be closed). * @param ignoreAxioms */ static public void assertSameSPOsAnyOrder(AbstractTripleStore store, ISPO[] expected, IChunkedOrderedIterator actual, boolean ignoreAxioms) { try { Map map = new TreeMap(SPOComparator.INSTANCE); for (ISPO tmp : expected) { map.put(tmp, tmp); } int i = 0; while (actual.hasNext()) { final ISPO actualSPO = actual.next(); if (ignoreAxioms && actualSPO.isAxiom()) { continue; } if (log.isInfoEnabled()) log.info("actual: " + actualSPO.toString(store)); final ISPO expectedSPO = map.remove(actualSPO); if (expectedSPO == null) { fail("Not expecting: " + actualSPO.toString(store) + " at index=" + i); } // log.info("expected: "+expectedSPO.toString(store)); StatementEnum expectedType = expectedSPO.hasStatementType() ? expectedSPO.getStatementType() : null; StatementEnum actualType = actualSPO.hasStatementType() ? actualSPO.getStatementType() : null; if (expectedType != actualType) { // defer message generation until assert fails. assertEquals("expected=" + expectedSPO + ",actual=" + actualSPO, expectedType, actualType); } i++; } if (!map.isEmpty()) { // @todo convert term identifiers before rendering. if (log.isInfoEnabled()) log.info("Iterator empty but still expecting: " + map.values()); fail("Expecting: " + map.size() + " more statements: " + map.values()); } } finally { actual.close(); } } static public void assertSameStatements(Statement[] expected, BigdataStatementIterator actual) { assertSameStatements("", expected, actual); } /** * @todo since there is no way to know the natural order for the statement * iterator we can not sort expected into the same order. therefore * this should test for the same statements in any order */ static public void assertSameStatements(final String msg, final Statement[] expected, final BigdataStatementIterator actual) { int i = 0; while (actual.hasNext()) { if (i >= expected.length) { fail(msg + ": The iterator is willing to visit more than " + expected.length + " objects."); } Statement g = actual.next(); if (!expected[i].equals(g)) { /* * Only do message construction if we know that the assert will * fail. */ fail(msg + ": Different objects at index=" + i + ": expected=" + expected[i] + ", actual=" + g); } i++; } if (i < expected.length) { fail(msg + ": The iterator SHOULD have visited " + expected.length + " objects, but only visited " + i + " objects."); } } /** * Verify that TERM2ID and ID2TERM have the same range count and that * all ID2TERM entries resolve a TERM2ID entry and that each TERM2ID * entry leads to an ID2TERM entry. * * @param store2 */ static public void assertLexiconIndicesConsistent( final AbstractTripleStore store) { final IIndex t2id = store.getLexiconRelation().getTerm2IdIndex(); final IIndex id2t = store.getLexiconRelation().getId2TermIndex(); final BigdataValueSerializer valSer = store .getValueFactory().getValueSerializer(); /* * First, scan the TERMS index and verify that each IV maps to an entry * in the ID2TERMS index which maps back to the original entry in the * TERMS index. */ { final ITupleSerializer t2idTupleSer = t2id.getIndexMetadata() .getTupleSerializer(); final IKeyBuilder keyBuilder = id2t.getIndexMetadata() .getTupleSerializer().getKeyBuilder(); final ITupleIterator itr = t2id.rangeIterator(); while (itr.hasNext()) { final IV iv = (IV) itr.next().getObject(); keyBuilder.reset(); final byte[] ivAsKey = iv.encode(keyBuilder).getKey(); // TODO inefficient point lookup. final byte[] encodedValue = id2t.lookup(ivAsKey); assertNotNull(encodedValue); // Decode the Value. final BigdataValue decodedValue = valSer .deserialize(encodedValue); // Generate key for T2ID index. final byte[] term2IdKey = t2idTupleSer .serializeKey(decodedValue); // TODO inefficient point lookup. final byte[] encodedIV = t2id.lookup(term2IdKey); if (encodedIV == null) { fail("No entry in TERMS index: v=" + decodedValue + ", iv=" + iv); } if (!BytesUtil.bytesEqual(ivAsKey, encodedIV)) { /* * The IV that we got back by round tripping back through * the TERMS2ID index does not agree with the IV we obtained * from the iterator scanning the TERMS2ID index. */ fail("IV: original=" + BytesUtil.toString(ivAsKey) + ", afterRoundTrip=" + BytesUtil.toString(encodedIV)); } } } /* * Scan the ID2TERM index, verifying that each entry maps to an entry in * the TERMS2ID index which is associated with the same IV. */ { final ITupleSerializer t2idTupleSer = t2id.getIndexMetadata() .getTupleSerializer(); final IKeyBuilder keyBuilder = id2t.getIndexMetadata() .getTupleSerializer().getKeyBuilder(); final ITupleIterator itr = id2t.rangeIterator(); while (itr.hasNext()) { final BigdataValue v = itr.next().getObject(); final IV iv = v.getIV(); assertNotNull(v.stringValue(), iv); // Generate key for T2ID index. final byte[] term2IdKey = t2idTupleSer.serializeKey(v); // TODO inefficient point lookup. final byte[] encodedIV = t2id.lookup(term2IdKey); if (encodedIV == null) { fail("No entry in TERMS index: v=" + v + ", iv=" + iv); } final IV decodedIV = IVUtility.decodeFromOffset( encodedIV, 0/* offset */); if (!iv.equals(decodedIV)) { /* * The IV that we got back by round tripping back through * the TERMS2ID index does not agree with the IV we obtained * from the iterator scanning the ID2TERMS index. */ fail("IV: original=" + iv + ", afterRoundTrip=" + decodedIV + " for value=" + v); } } } /* * Verify that the range counts on TERM2ID and ID2TERM are the same. */ { final long rc1 = t2id.rangeCount(); final long rc2 = id2t.rangeCount(); assertEquals("lexicon range counts: t2id=" + rc1 + ", id2t=" + rc2, rc1, rc2); } } /** * Validates that the same statements are found in each of the statement * indices. */ static public void assertStatementIndicesConsistent( final AbstractTripleStore db, final int maxerrors) { if (log.isInfoEnabled()) log.info("Verifying statement indices"); final AtomicInteger nerrs = new AtomicInteger(0); final int from, to; if (db.getSPOKeyArity() == 3) { from = SPOKeyOrder.FIRST_TRIPLE_INDEX; to = SPOKeyOrder.LAST_TRIPLE_INDEX; } else { from = SPOKeyOrder.FIRST_QUAD_INDEX; to = SPOKeyOrder.LAST_QUAD_INDEX; } for (int i = from; i <= to; i++) { for (int j = from; j <= to; j++) { if (i <= j) { // Just compare the upper diagonal. continue; } assertSameStatements(db, SPOKeyOrder.valueOf(i), SPOKeyOrder.valueOf(j), nerrs, maxerrors); } } assertEquals(0, nerrs.get()); } /** * Verify all statements reported by one access path are found on another * access path. *

* Note: This is basically a JOIN. If there is an expected tuple that is not * found then it is an error. Like a JOIN, we need to process a chunk at a * time for efficiency and unroll the inner loop (this is necessary for * efficiency not only when using scale-out, but also to avoid doing a whole * bunch of point tests against an {@link UnisolatedReadWriteIndex}). * Unlike a normal JOIN, the join order is fixed - we visit the expected * access path and then join against the actual access path. * * @param db * The database. * @param keyOrderExpected * Scan this statement index. * @param keyOrderActual * Verifying that each statement is also present in this index. * @param nerrs * Used to report the #of errors. */ static private void assertSameStatements(// final AbstractTripleStore db,// final SPOKeyOrder keyOrderExpected,// final SPOKeyOrder keyOrderActual,// final AtomicInteger nerrs,// final int maxerrors ) { if (log.isInfoEnabled()) log.info("Verifying " + keyOrderExpected + " against " + keyOrderActual); // the access path that is being tested. final IIndex actualIndex = db.getSPORelation().getIndex(keyOrderActual); // the serializer for the access path that is being tested. final SPOTupleSerializer tupleSer = (SPOTupleSerializer) actualIndex .getIndexMetadata().getTupleSerializer(); /* * An iterator over the access path whose statements are being treated * as ground truth for this pass over the data. */ final IChunkedOrderedIterator itre = db.getAccessPath( keyOrderExpected).iterator(); try { while (itre.hasNext()) { if (nerrs.get() > 10) throw new RuntimeException("Too many errors"); /* * This is a chunk of expected statements in the natural order * for the "actual" access path. */ final ISPO[] expectedChunk = itre.nextChunk(keyOrderActual); /* * Construct a batch contains test for those statements and * submit it to the actual index. The aggregator will verify * that each expected statement exists in the actual index and * report an error for those that were not found. */ final int fromIndex = 0; final int toIndex = expectedChunk.length; final byte[][] keys = new byte[expectedChunk.length][]; final byte[][] vals = null; for (int i = 0; i < expectedChunk.length; i++) { keys[i] = tupleSer.serializeKey(expectedChunk[i]); } final AtomicLong nfound = new AtomicLong(); final IResultHandler resultHandler = new IResultHandler() { @Override public void aggregate(final ResultBitBuffer result, final Split split) { // #of elements in this split. final int n = result.getResultCount(); // flag indicating found/not found for each tuple. final boolean[] a = result.getResult(); int delta = 0; for (int i = 0; i < n; i++) { if (a[i]) { // found. delta++; } else { /* * This happens when the statement is not in the * index AND there is no successor of the * statement in the index. */ final ISPO expectedSPO = expectedChunk[i]; log.error("Statement not found" + ": index=" + keyOrderActual + ", stmt=" + expectedSPO); nerrs.incrementAndGet(); } } nfound.addAndGet(delta); } /** * The #of statements found. */ @Override public Long getResult() { return nfound.get(); } }; actualIndex.submit(fromIndex, toIndex, keys, vals, BatchContainsConstructor.INSTANCE, resultHandler); } } finally { itre.close(); } } /** * Helper class verifies that all statements identified by a re-parse of * some RDF/XML file are present in the KB. * * @author Bryan Thompson */ static protected class StatementVerifier extends BasicRioLoader { final private AbstractTripleStore db; final private AtomicInteger nerrs; final private int maxerrors; final IBuffer buffer; /** * * @param db * The database. * @param capacity * The buffer capacity (the maximum #of statements to be * processed in a batch). * @param nerrs * Used to track and report the #of errors as a side-effect. * @param maxerrors * The maximum #of errors before the test will abort. */ public StatementVerifier(final AbstractTripleStore db, final int capacity, final AtomicInteger nerrs, final int maxerrors) { super(db.getValueFactory()); this.db = db; this.nerrs = nerrs; this.maxerrors = maxerrors; this.buffer = new AbstractArrayBuffer(capacity, Statement.class, null/* filter */) { @Override protected long flush(final int n, final Statement[] a) { verifyStatements( n , a ); return n; } }; } /** * Report an error. * * @param msg * The error message. */ private void error(final String msg) { log.error(msg); if (nerrs.incrementAndGet() > maxerrors) { throw new RuntimeException("Too many errors"); } } /** * Extended to flush the {@link #buffer}. */ protected void success() { super.success(); buffer.flush(); } public RDFHandler newRDFHandler() { return new RDFHandlerBase() { public void handleStatement(final Statement stmt) { buffer.add(stmt); } }; } private void verifyStatements(final int n, final Statement[] a) { final Map termSet = new LinkedHashMap( n); { for (int i = 0; i < n; i++) { final Statement stmt = a[i]; termSet.put(stmt.getSubject(), db.getValueFactory() .asValue(stmt.getSubject())); termSet.put(stmt.getPredicate(), db.getValueFactory() .asValue(stmt.getPredicate())); termSet.put(stmt.getObject(), db.getValueFactory() .asValue(stmt.getObject())); } final int nterms = termSet.size(); final BigdataValue[] terms = new BigdataValue[nterms]; int i = 0; for (BigdataValue term : termSet.values()) { terms[i++] = term; } db.getLexiconRelation() .addTerms(terms, nterms, true/* readOnly */); int nunknown = 0; for (BigdataValue term : terms) { if (term.getIV() == null) { error("Unknown term: " + term); nunknown++; } } if (nunknown > 0) { log.warn("" + nunknown + " out of " + nterms + " terms were not found."); } } if (log.isInfoEnabled()) log.info("There are " + termSet.size() + " distinct terms in the parsed statements."); /* * Now verify reverse lookup for those terms. */ { final Set> ivs = new LinkedHashSet>(termSet.size()); for(BigdataValue term : termSet.values()) { final IV iv = term.getIV(); if (iv == null || iv.isNullIV()) { // ignore terms that we know were not found. continue; } ivs.add(iv); } // batch resolve ids to terms. final Map, BigdataValue> reverseMap = db .getLexiconRelation().getTerms(ivs); for(BigdataValue expectedTerm : termSet.values()) { final IV iv = expectedTerm.getIV(); if (iv == null) { // ignore terms that we know were not found. continue; } final BigdataValue actualTerm = reverseMap.get(iv); if (actualTerm == null || !actualTerm.equals(expectedTerm)) { error("expectedTerm=" + expectedTerm + ", assigned termId=" + iv + ", but reverse lookup reports: " + actualTerm); } } } /* * Now verify the statements using the assigned term identifiers. * * @todo not handling the context position - it is either unbound or * a statement identifier. */ { final SPO[] b = new SPO[n]; int n2 = 0; for (int i = 0; i < n; i++) { final Statement stmt = a[i]; final BigdataResource s = (BigdataResource) db .asValue(termSet.get(stmt.getSubject())); final BigdataURI p = (BigdataURI) db.asValue(termSet .get(stmt.getPredicate())); final BigdataValue o = (BigdataValue) db.asValue(termSet .get(stmt.getObject())); boolean ok = true; if (s == null) { log.error("Subject not found: "+stmt.getSubject()); ok = false; } if(p == null) { log.error("Predicate not found: "+stmt.getPredicate()); ok = false; } if(o == null) { log.error("Object not found: "+stmt.getObject()); ok = false; } if(!ok) { log.error("Unable to resolve statement with unresolvable terms: "+stmt); } else { // Leave the StatementType blank for bulk complete. b[n2++] = new SPO(s.getIV(), p.getIV(), o .getIV() /*, StatementType */); } } final IChunkedOrderedIterator itr = db .bulkCompleteStatements(b, n2); try { int i = 0; while (itr.hasNext()) { final ISPO spo = itr.next(); if (!spo.hasStatementType()) { error("Statement not found: " + spo.toString(db)); } i++; } if (log.isInfoEnabled()) log.info("Verified " + i + " statements parsed from file."); } finally { itr.close(); } } } } /** * Recursively removes any files and subdirectories and then removes the * file (or directory) itself. * * @param f * A file or directory. */ protected void recursiveDelete(File f) { if(f.isDirectory()) { File[] children = f.listFiles(); for(int i=0; i





© 2015 - 2024 Weber Informatics LLC | Privacy Policy