package com.bigdata.samples; import java.io.File; import java.io.IOException; import java.util.Properties; import org.openrdf.model.Literal; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.ValueFactory; import org.openrdf.model.vocabulary.RDFS; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.RepositoryResult; import org.openrdf.sail.SailException; import com.bigdata.journal.BufferMode; import com.bigdata.journal.ITx; import com.bigdata.journal.Options; import com.bigdata.rdf.sail.BigdataSail; import com.bigdata.rdf.sail.BigdataSailRepository; import com.bigdata.rdf.sail.BigdataSailRepositoryConnection; import com.bigdata.rdf.store.BD; import com.bigdata.service.AbstractTransactionService; /** * Example of History retention usage with Sail interface classes. * * A sequence of commits updates the value of a statement * and retains the commit time of each. * * We can confirm that the correct data is associated with the relevant * commit time * * On closing and re-opening the store, the historical data remains * accessible, however, the release time is recalculated when connections * are closed. The code clarifies several of the issues involved. * * @see http://sourceforge.net/apps/mediawiki/bigdata/index.php?title=RetentionHistory * * @author Martyn Cutcher */ public class ReleaseTimes { public static void main(String[] args) throws IOException, InterruptedException, SailException, RepositoryException { /** * First create a new repository with 5000ms (5 second) retention */ BigdataSail sail = new BigdataSail(getProperties("5000")); sail.initialize(); BigdataSailRepository repo = new BigdataSailRepository(sail); final BigdataSailRepositoryConnection cxn = repo.getConnection(); cxn.setAutoCommit(false); final ValueFactory vf = sail.getValueFactory(); /* * Create some terms. */ final URI devuri = vf.createURI(BD.NAMESPACE + "developer"); final Literal mp = vf.createLiteral("mike personick"); final Literal bt = vf.createLiteral("bryan thompson"); final Literal mc = vf.createLiteral("martyn cutcher"); /* * Associate statements with recorded state */ System.out.println("Creating historical states..."); final long state1 = assertStatement(cxn, devuri, RDFS.LABEL, mp); final long state2 = assertStatement(cxn, devuri, RDFS.LABEL, bt); final long state3 = assertStatement(cxn, devuri, RDFS.LABEL, mc); System.out.println("State1: " + state1 + ", state2: " + state2 + ", state3: " + state3); // Check historical state System.out.println("Checking historical states..."); check(checkState(repo, state2, devuri, RDFS.LABEL, bt)); check(checkState(repo, state1, devuri, RDFS.LABEL, mp)); check(checkState(repo, state3, devuri, RDFS.LABEL, mc)); System.out.println("Shutting down..."); // Shutdown sail cxn.close(); sail.shutDown(); // Reopen with sufficient retention time to cover previous states sail = new BigdataSail(getProperties("5000")); sail.initialize(); repo = new BigdataSailRepository(sail); final BigdataSailRepositoryConnection cxn2 = repo.getConnection(); cxn2.setAutoCommit(false); System.out.println("Reopened"); // wait for data to "age" Thread.sleep(5000); // ... confirm earliest historical access after open check(checkState(repo, state2, devuri, RDFS.LABEL, bt)); System.out.println("History retained after re-open"); // closing the connection will reset the release time so // that subsequent accesses will fail check(!checkState(repo, state2, devuri, RDFS.LABEL, bt)); check(!checkState(repo, state1, devuri, RDFS.LABEL, mp)); System.out.println("History released after closing read-only connections"); // this is last committed state, so will succeed check(checkState(repo, state3, devuri, RDFS.LABEL, mc)); System.out.println("History of last commit point accessible"); // .. and will also succeed directly against READ_COMMITTED check(checkState(repo, ITx.READ_COMMITTED, devuri, RDFS.LABEL, mc)); // Updating the statement... assertStatement(cxn2, devuri, RDFS.LABEL, mp); System.out.println("Statement udated"); // the previous historical access now fails check(!checkState(repo, state3, devuri, RDFS.LABEL, mc)); System.out.println("History of last commit point of re-opened journal no longer accessible after update"); // ..and the READ_COMMITTED is updated check(checkState(repo, ITx.READ_COMMITTED, devuri, RDFS.LABEL, mp)); System.out.println("Committed state confirmed"); cxn2.close(); sail.shutDown(); System.out.println("DONE"); } private static void check(boolean checkState) { if (!checkState) { throw new AssertionError("Unexpected results"); } } /** * Define statement with a URI identifier, type and literal label * * First ensure that any previous valued statement is removed. * * Commit, and return the store state. */ private static long assertStatement(BigdataSailRepositoryConnection cxn, URI id, URI pred, Literal label) throws RepositoryException { cxn.remove(id, pred, null); check(!cxn.getStatements(id, pred, null, false).hasNext()); cxn.add(id, pred, label); check(cxn.getStatements(id, pred, label, false).hasNext()); // Return the time of the commit return cxn.commit2(); } /** * To check a specific state we retrieve a readOnlyConnection from the * repository for the time/state specified. * * An IllegalStateException is thrown if no valid connection can be * established. * * We then retrieve statements from the connection, checking that only * the requested statement is present. */ static boolean checkState(final BigdataSailRepository repo, final long state, final URI id, final URI pred, final Literal label ) { try { final BigdataSailRepositoryConnection cxn; try { cxn = repo.getReadOnlyConnection(state); } catch (IllegalStateException e) { return false; // invalid state! } try { RepositoryResult<Statement> results = cxn.getStatements(id, pred, null, false); // nothing found if (!results.hasNext()) { return false; } // Check that result is the one expected final String labelstr = label.toString(); final String value = results.next().getObject().toString(); if (!labelstr.equals(value)) { return false; } // ..and that no others are returned return !results.hasNext(); } finally { cxn.close(); } } catch (Exception e) { e.printStackTrace(); throw new AssertionError("Unable to load index"); } } static String filename = null; static public Properties getProperties(String releaseAge) throws IOException { Properties properties = new Properties(); // create temporary file for this application run if (filename == null) filename = File.createTempFile("BIGDATA", "jnl").getAbsolutePath(); properties.setProperty(Options.FILE, filename); properties.setProperty(Options.DELETE_ON_EXIT, "true"); // Set RWStore properties.setProperty(Options.BUFFER_MODE, BufferMode.DiskRW.toString()); // Set minimum commit history properties.setProperty(AbstractTransactionService.Options.MIN_RELEASE_AGE, releaseAge); return properties; } }