/* * SesameManager.java * * Copyright (c) 1998-2007, The University of Sheffield. * * This file is part of GATE (see http://gate.ac.uk/), and is free * software, licenced under the GNU Library General Public License, * Version 2, June 1991 (in the distribution as file licence.html, * and also available at http://gate.ac.uk/gate/licence.html). * * Johann Petrak, 2009-07-26 * * $Id: SesameManager.java 11598 2009-10-13 13:44:17Z johann_p $ */ package gate.creole.ontology.impl.sesame; import gate.creole.ontology.OConstants; import java.io.File; import java.net.URISyntaxException; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.io.StringReader; import java.io.InputStream; import java.io.Reader; import java.util.HashSet; import java.util.Set; import org.apache.log4j.Logger; import org.openrdf.model.Graph; import org.openrdf.model.ValueFactory; import org.openrdf.model.impl.GraphImpl; import org.openrdf.model.util.GraphUtil; import org.openrdf.model.util.GraphUtilException; import org.openrdf.repository.sail.SailRepository; import org.openrdf.sail.memory.MemoryStore; import org.openrdf.repository.manager.LocalRepositoryManager; import org.openrdf.repository.manager.RemoteRepositoryManager; import org.openrdf.repository.manager.RepositoryManager; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFParser; import org.openrdf.rio.Rio; import org.openrdf.rio.helpers.StatementCollector; import org.openrdf.model.Resource; import org.openrdf.model.vocabulary.RDF; import org.openrdf.repository.DelegatingRepository; import org.openrdf.repository.Repository; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.config.DelegatingRepositoryImplConfig; import org.openrdf.repository.config.RepositoryConfig; import org.openrdf.repository.config.RepositoryConfigException; import org.openrdf.repository.config.RepositoryConfigSchema; import org.openrdf.repository.config.RepositoryConfigUtil; import org.openrdf.repository.config.RepositoryFactory; import org.openrdf.repository.config.RepositoryImplConfig; import org.openrdf.repository.config.RepositoryRegistry; import org.openrdf.sail.memory.model.MemValueFactory; import gate.creole.ontology.OntologyTupleQuery; /** * A class to encapsulate management of a Sesame2 repository and * make handling different repository types easier. * This class exposes as few Sesame-internal classes as possible. * All errors are handled by throwing its own runtime exception * SesameManagerException. * <p> * In order to create repositories the following protocol * of calling methods must be observed: * * * @author Johann Petrak */ public class SesameManager { private RepositoryConnection mRepositoryConnection; private Repository mRepository; private RepositoryManager mRepositoryManager; private String mRepositoryLocation; private String mRepositoryName; private static final Pattern TOKEN_PATTERN = Pattern.compile("\\{%[\\p{Print}&&[^\\}]]+%\\}"); private boolean debug = true; private class SesameManagerException extends RuntimeException { public SesameManagerException() {} public SesameManagerException(String message) { super(message); } public SesameManagerException(String message, Throwable cause) { super(message, cause); } public SesameManagerException(Throwable e) { super(e); } /** * Overridden so we can print the enclosed exception's stacktrace too. */ public void printStackTrace() { printStackTrace(System.err); } /** * Overridden so we can print the enclosed exception's stacktrace too. */ public void printStackTrace(java.io.PrintStream s) { s.flush(); super.printStackTrace(s); Throwable cause = getCause(); if (cause != null) { s.print("Caused by:\n"); cause.printStackTrace(s); } } /** * Overridden so we can print the enclosed exception's stacktrace too. */ public void printStackTrace(java.io.PrintWriter s) { s.flush(); super.printStackTrace(s); Throwable cause = getCause(); if (cause != null) { s.print("Caused by:\n"); cause.printStackTrace(s); } } } private Logger logger; public SesameManager() { logger = Logger.getLogger(this.getClass().getName()); } boolean isManagedRepository = false; /** * Connect to a managed repository located at the given location * and connect to the repository with the given name. * The repository connection is assumed to be remote if it starts with * http:// or https://, otherwise the location is assumed to be a local * directory name. * * @param repositoryLocation * @param repositoryName */ public void connectToRepository(String repositoryLocation, String repositoryName) { // connect to location and get the manager closeRepository(); connectToLocation(repositoryLocation); openRepository(repositoryName); } public void connectToRepository(java.net.URL repositoryLocation, String repositoryName) { // connect to location and get the manager closeRepository(); connectToLocation(repositoryLocation); openRepository(repositoryName); } /** * Connect to a managed repository location. * The repository connection is assumed to be remote if it starts with * http:// or https://, otherwise the location is assumed to be a local * directory name. * * @param repositoryLocation */ public void connectToLocation(String repositoryLocation) { // if the location starts with http:// it will be assumed that this // is a remote location, otherwise it will be regarded as a directory // name. logger.debug("Calling SesameManager.connectToLocation with String: "+repositoryLocation); if(repositoryLocation.startsWith("http://") || repositoryLocation.startsWith("https://")) { connectToRemoteLocation(repositoryLocation); } else { connectToLocalLocation(repositoryLocation,true); } } public void connectToLocation(java.net.URL repositoryLocation) { // if the location starts with http:// it will be assumed that this // is a remote location, otherwise it will be regarded as a directory // name. logger.debug("Calling SesameManager.connectToLocation with URL: "+repositoryLocation); logger.debug("Protocol is: "+repositoryLocation.getProtocol()); if(repositoryLocation.getProtocol().startsWith("http")) { connectToRemoteLocation(repositoryLocation.toString()); } else { connectToLocalLocation(repositoryLocation,true); } } /** * Connect to a remote managed repository location. * * @param url */ public void connectToRemoteLocation(String url) { isManagedRepository = true; setManager(new RemoteRepositoryManager(url), url); } /** * Connect to a local repository location at the given directory. * If mustexist is true, it is an error if the directory is not found. * * @param dirname * @param mustexist */ public void connectToLocalLocation(String dirname, boolean mustexist) { isManagedRepository = true; //dirname = dirname.replaceAll("/$", ""); File dir = new File(dirname); if (!dir.exists()) { throw new SesameManagerException("Specified path does not exist: " + dir.getAbsolutePath()); } if (!dir.isDirectory()) { throw new SesameManagerException("Specified path is not a directory: " + dir.getAbsolutePath()); } setManager(new LocalRepositoryManager(dir), dir.toString()); } public void connectToLocalLocation(java.net.URL dirname, boolean mustexist) { isManagedRepository = true; logger.debug("Called connectToLocalLocation "+dirname+"/"+mustexist); File dir; try { dir = new File(dirname.toURI()); } catch (URISyntaxException ex) { throw new SesameManagerException("Specified URL is invalid: "+dirname,ex); } if (!dir.exists()) { throw new SesameManagerException("Specified path does not exist: " + dir.getAbsolutePath()); } if (!dir.isDirectory()) { throw new SesameManagerException("Specified path is not a directory: " + dir.getAbsolutePath()); } setManager(new LocalRepositoryManager(dir), dir.toString()); } /** * Disconnect from a local or remote repository manager. * */ public void disconnect() { closeRepository(); if (mRepositoryManager != null) { logger.debug("Shutting down the repository manager"); mRepositoryManager.shutDown(); logger.debug("manager is shut down"); mRepositoryManager = null; mRepositoryLocation = null; logger.debug("manager and location set to null"); } } private void setManager(RepositoryManager manager, String location) { logger.debug("setManager called"); try { disconnect(); manager.initialize(); mRepositoryManager = manager; mRepositoryLocation = location; } catch (RepositoryException e) { throw new SesameManagerException("Error initializing manager: "+e); } } /** * Open a repository with the given name at the remote or local location * previously connected to. * An error is raised if no local or remote location was set prior to * calling this method. * * @param name */ public void openRepository(String name) { logger.debug("Called openRespository with ID "+name); if(mRepositoryManager != null) { try { mRepository = mRepositoryManager.getRepository(name); } catch (Exception e) { throw new SesameManagerException("Could not get repository "+name+" error is "+e); } if(mRepository == null) { throw new SesameManagerException("Getting repository failed - no repository of this name found: "+name); } try { mRepositoryConnection = mRepository.getConnection(); logger.debug("repository connection set"); } catch (Exception e) { throw new SesameManagerException("Could not get connection "+name+" error is "+e); } } else { throw new SesameManagerException("Not connected to a repository location for openRepository "+name); } } /** * Close the currently opened repository. This works for managed and * unmanaged repositories. */ public void closeRepository() { if (mRepositoryConnection != null) { try { logger.debug("Commiting the connection"); //mRepositoryConnection.commit(); logger.debug("Closing the connection"); mRepositoryConnection.close(); logger.debug("Connection closed"); // the following is NOT needed as the manager shutDown method // shuts down all repositories // mRepository.shutDown(); logger.debug("Repository shut down"); } catch (RepositoryException e) { logger.debug("!!!!! Error: ",e); // TODO: do not throw exception, might still need to disconnect // manager! // throw new SesameManagerException("Could not close Repository: "+e); } mRepositoryConnection = null; mRepository = null; mRepositoryName = null; logger.debug("connection, repository and name set to null"); } } // create repository from a template, no substitution of variables // also opens the newly created repository /** * Create a new managed repository at the current remote or local location * using the configuration information passed on as a string. * * @param config */ public void createRepository(String config) { logger.debug("createRepository called"); if(mRepositoryManager == null) { throw new SesameManagerException("No connect prior to createRepository"); } Repository systemRepo = mRepositoryManager.getSystemRepository(); ValueFactory vf = systemRepo.getValueFactory(); Graph graph = new GraphImpl(vf); RDFParser rdfParser = Rio.createParser(RDFFormat.TURTLE, vf); rdfParser.setRDFHandler(new StatementCollector(graph)); try { rdfParser.parse(new StringReader(config), RepositoryConfigSchema.NAMESPACE); } catch (Exception e) { throw new SesameManagerException("Error parsing the config string: "+e); } try { Resource repositoryNode = GraphUtil.getUniqueSubject(graph, RDF.TYPE,RepositoryConfigSchema.REPOSITORY); RepositoryConfig repConfig = RepositoryConfig.create(graph, repositoryNode); repConfig.validate(); if (RepositoryConfigUtil.hasRepositoryConfig(systemRepo, repConfig.getID())) { throw new SesameManagerException("Repository already exists with ID "+repConfig.getID()); } else { RepositoryConfigUtil.updateRepositoryConfigs(systemRepo, repConfig); mRepository = mRepositoryManager.getRepository(repConfig.getID()); // Sesame complains about the repository already being initialized // for native but not for OWLIM here ... can we always not initialize // here???? try { mRepository.initialize(); } catch (IllegalStateException ex) { // we get this if the SAIL has already been initialized, just // ignore and be happy that we can be sure that indeed it has } openRepository(repConfig.getID()); } } catch (Exception e) { throw new SesameManagerException("Error creating repository "+e); } } /** * Create an unmanaged repository with files stored in the directory * given from the configuration passed as a string. * * @param configstring * @return * @throws java.lang.Exception */ public void createUnmanagedRepository(File repositoryDirFile, String configstring) { isManagedRepository = false; logger.debug("SesameManager: creating unmanaged repo, dir is "+repositoryDirFile.getAbsolutePath()); ValueFactory vf = new MemValueFactory(); Graph graph = parseRdf(configstring, vf, RDFFormat.TURTLE); Resource repositoryNode; try { repositoryNode = GraphUtil.getUniqueSubject(graph, RDF.TYPE, RepositoryConfigSchema.REPOSITORY); } catch (GraphUtilException ex) { throw new SesameManagerException("Could not get subject of config RDF",ex); } RepositoryConfig repConfig; try { repConfig = RepositoryConfig.create(graph, repositoryNode); } catch (RepositoryConfigException ex) { throw new SesameManagerException("Could not create repository from RDF graph",ex); } try { repConfig.validate(); } catch (RepositoryConfigException ex) { throw new SesameManagerException("Could not validate repository",ex); } RepositoryImplConfig rpc = repConfig.getRepositoryImplConfig(); Repository repo = createRepositoryStack(rpc); repo.setDataDir(repositoryDirFile); try { repo.initialize(); } catch (RepositoryException ex) { throw new SesameManagerException("Could not initialize repository",ex); } try { mRepositoryConnection = repo.getConnection(); logger.debug("Repo dir is "+repo.getDataDir().getAbsolutePath()); logger.debug("Repo is writable "+repo.isWritable()); } catch (RepositoryException ex) { throw new SesameManagerException("Could not get connection for unmanaged repository",ex); } } private Graph parseRdf(String config, ValueFactory vf, RDFFormat lang) { Graph graph = new GraphImpl(vf); RDFParser rdfParser = Rio.createParser(lang, vf); rdfParser.setRDFHandler(new StatementCollector(graph)); try { rdfParser.parse(new StringReader(config), RepositoryConfigSchema.NAMESPACE); } catch (Exception e) { throw new SesameManagerException("Could not parse rdf: " + e); } return graph; } private RepositoryConfig getConfig(String config) { Repository myRepository = new SailRepository(new MemoryStore()); RepositoryConfig repConfig; try { myRepository.initialize(); } catch (RepositoryException e) { throw new SesameManagerException("Error initializing memory store: "+e); } ValueFactory vf = myRepository.getValueFactory(); Graph graph = new GraphImpl(vf); RDFParser rdfParser = Rio.createParser(RDFFormat.TURTLE, vf); rdfParser.setRDFHandler(new StatementCollector(graph)); try { rdfParser.parse(new StringReader(config), RepositoryConfigSchema.NAMESPACE); Resource repositoryNode = GraphUtil.getUniqueSubject(graph, RDF.TYPE,RepositoryConfigSchema.REPOSITORY); repConfig = RepositoryConfig.create(graph, repositoryNode); repConfig.validate(); } catch (Exception e) { throw new SesameManagerException("Error parsing the config string "+e); } return repConfig; } private Repository createRepositoryStack(RepositoryImplConfig config) { RepositoryFactory factory = RepositoryRegistry.getInstance().get(config.getType()); if (factory == null) { throw new SesameManagerException("Unsupported repository type: " + config.getType()); } Repository repository; try { repository = factory.getRepository(config); } catch (RepositoryConfigException ex) { throw new SesameManagerException("Could not get repository from factory",ex); } if (config instanceof DelegatingRepositoryImplConfig) { RepositoryImplConfig delegateConfig = ((DelegatingRepositoryImplConfig)config).getDelegate(); Repository delegate = createRepositoryStack(delegateConfig); try { ((DelegatingRepository)repository).setDelegate(delegate); } catch (ClassCastException e) { throw new SesameManagerException( "Delegate specified for repository that is not a DelegatingRepository: " + delegate.getClass()); } } return repository; } /** * Substitute variables in a configuration template string. * * @param configtemplate * @param variables * @return */ public static String substituteConfigTemplate(String configtemplate, Map<String,String> variables) { // replace all variables in the template then do the actual createRepository StringBuffer result = new StringBuffer(configtemplate.length()*2); Matcher matcher = TOKEN_PATTERN.matcher(configtemplate); while (matcher.find()) { String group = matcher.group(); // get the variable name and default String[] tokensArray = group.substring(2, group.length() - 2).split("\\|"); String var = tokensArray[0].trim(); String value = variables.get(var); if(value == null) { // try to get the default if(tokensArray.length > 1) { value = tokensArray[1].trim(); } else { value = ""; } } matcher.appendReplacement(result, value); } matcher.appendTail(result); return result.toString(); } /** * Delete the managed repository with that name. * * @param name */ public void deleteRepository(String name) { if(mRepositoryManager != null) { closeRepository(); try { boolean done = mRepositoryManager.removeRepositoryConfig(name); } catch (RepositoryException e) { throw new SesameManagerException("Could not delete repository "+name+": "+e); } catch (RepositoryConfigException e) { throw new SesameManagerException("Could not delete repository "+name+": "+e); } } else { throw new SesameManagerException("Must be connected to a location"); } } /** * Clear the current repository and remove all data from it. * */ public void clearRepository() { try { mRepositoryConnection.clear(); } catch (RepositoryException e) { throw new SesameManagerException("Could not clear repository: "+e); } } /** * Load data into the current repository from a file. * * @param from * @param baseURI * @param format */ public void importIntoRepository(File from, String baseURI, String format) { if(mRepositoryConnection != null) { RDFFormat sesameFormat = RDFFormat.valueOf(format); try { mRepositoryConnection.add(from,baseURI,sesameFormat); } catch(Exception e) { throw new SesameManagerException("Could not import: "+e); } } else { throw new SesameManagerException("Cannot import, no connection"); } } /** * Load data into the current repository from a stream. * * @param from * @param baseURI * @param format */ public void importIntoRepository(InputStream from, String baseURI, String format) { if(mRepositoryConnection != null) { RDFFormat sesameFormat = RDFFormat.valueOf(format); try { mRepositoryConnection.add(from,baseURI,sesameFormat); } catch(Exception e) { throw new SesameManagerException("Could not import: "+e); } } else { throw new SesameManagerException("Cannot import, no connection"); } } /** * Load data into the current repository from a reader * * @param from * @param baseURI * @param format */ public void importIntoRepository(Reader from, String baseURI, String format) { if(mRepositoryConnection != null) { RDFFormat sesameFormat = RDFFormat.valueOf(format); try { mRepositoryConnection.add(from,baseURI,sesameFormat); } catch(Exception e) { throw new SesameManagerException("Could not import: "+e); } } else { throw new SesameManagerException("Cannot import, no connection"); } } /** * Create a query object for the current repository. * * @param query * @return */ public OntologyTupleQuery createQuery(String query) { if(mRepositoryConnection != null) { return new UtilTupleQueryIterator( mRepositoryConnection, query, OConstants.QueryLanguage.SPARQL); } else { throw new SesameManagerException("Cannot create a query, no connection"); } } public Set<String> getRepositories() { if(mRepositoryManager == null) { return new HashSet<String>(); } try { return mRepositoryManager.getRepositoryIDs(); } catch (RepositoryException ex) { throw new SesameManagerException("Could not get repository IDs: ",ex); } } /** * Obtain the repository connection object. * * @return */ public RepositoryConnection getRepositoryConnection() { return mRepositoryConnection; } }