package com.ontotext.kim.util.datastore; import java.io.*; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Properties; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.log4j.Logger; import org.openrdf.repository.Repository; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.sail.SailRepository; import com.ontotext.kim.client.query.KIMQueryException; import com.ontotext.kim.client.semanticrepository.QueryResultListener; import com.ontotext.kim.semanticrepository.TimedListener; import com.ontotext.kim.semanticrepository.UnmanagedRepositoryFactory; /** * @author mnozchev * */ public class PrivateRepositoryFeed implements QueryResultListener.Feed { private static final String SETTINGS_HASH_PROPERTY = "settingsHash"; private static final String SNAPSHOT_PROPERTIES_FILENAME = "snapshot.properties"; private final URL configFile; private final String query; private final int settingsHash; private final Logger log = Logger.getLogger(PrivateRepositoryFeed.class); private final File dictionaryPath; public PrivateRepositoryFeed(URL url, String query, int settingsHash) { this.configFile = url; this.query = query; this.settingsHash = settingsHash; dictionaryPath = new File(configFile.getFile()).getParentFile().getAbsoluteFile(); if (!verifyHash(dictionaryPath, settingsHash)) { boolean deleteSuccesful = new File(dictionaryPath, "kim.trusted.entities.cache").delete(); if (deleteSuccesful) { log.info("Cache is going to be refreshed due to a configuration change."); } else { log.warn("Cache needed to be refreshed due to a configuration change, but the system denied deleting it."); } } } public void feedTo(QueryResultListener listener) throws KIMQueryException { UnmanagedRepositoryFactory factory = new UnmanagedRepositoryFactory(); TimedListener timedListener = new TimedListener(true, listener, -1); FileUtils.deleteQuietly(new File("owlim-storage")); Reader configReader = null; try { configReader = getConfigReader(); Repository rep = factory.createRepository(configReader); rep.initialize(); log.info("Initialized Sesame repository: " + (rep instanceof SailRepository ? ((SailRepository)rep).getSail().toString() : rep.toString())); try { RepositoryConnection conn = rep.getConnection(); QueryResultListener.Feed dataFeed = new RepositoryFeed(conn, null, query); dataFeed.feedTo(timedListener); saveSettings(timedListener.getTimeTakenMS(), timedListener.getTuplesCnt()); } finally { rep.shutDown(); } } catch (Exception e) { throw new KIMQueryException(e); } finally { IOUtils.closeQuietly(configReader); } } private Reader getConfigReader() throws IOException { String configTemplate = FileUtils.readFileToString(new File(configFile.getFile())); configTemplate = configTemplate.replace("%relpath%", dictionaryPath.getAbsolutePath().replace('\\', '/')); return new StringReader(configTemplate); } private void saveSettings(Long timeTakenMS, int labelsCount) { Properties props = new Properties(); props.put("snapshotDate", new SimpleDateFormat().format(new Date())); props.put("pluginVersion", getPackageVersion()); props.put("labelsCount", String.valueOf(labelsCount)); props.put("snapshotTimeTakenInSeconds", String.valueOf(timeTakenMS == null ? "" : timeTakenMS / 1000)); props.put(SETTINGS_HASH_PROPERTY, String.valueOf(settingsHash)); OutputStream settingsWriter = null; try { File settingsFile = new File(dictionaryPath, SNAPSHOT_PROPERTIES_FILENAME); settingsWriter = new FileOutputStream(settingsFile); props.store(settingsWriter, "Metadata about the last taken snapshot"); } catch (IOException e) { log.warn("Could not save snapshot metadata: " + e.getMessage()); } finally { IOUtils.closeQuietly(settingsWriter); } } private String getPackageVersion() { if (this.getClass().getPackage() == null) return "n/a"; String res = this.getClass().getPackage().getImplementationVersion(); return res == null ? "n/a" : res; } private boolean verifyHash(File dictionaryPath, int settingsHash) { log.info("Looking for changes in configuration ..."); Properties props = new Properties(); File propsFile = new File(dictionaryPath, PrivateRepositoryFeed.SNAPSHOT_PROPERTIES_FILENAME); propsFile = propsFile.getAbsoluteFile(); if (!propsFile.isFile()) { log.debug("Could not find " + propsFile); return true; } InputStream inStream = null; try { inStream = new FileInputStream(propsFile); props.load(inStream); String oldHash = (String) props.get(PrivateRepositoryFeed.SETTINGS_HASH_PROPERTY); if (oldHash == null || oldHash.trim().length() == 0) { log.info("Snaphot metadata present, but configuration checksum is missing."); return true; } return settingsHash == NumberUtils.toInt(oldHash.trim(), settingsHash); } catch (IOException e) { log.info("Could not read " + propsFile, e); // We fail the hash check only if we successfully read the old hash and found it // to be different. return true; } finally { IOUtils.closeQuietly(inStream); } } }