/**
* PODD is an OWL ontology database used for scientific project management
*
* Copyright (C) 2009-2013 The University Of Queensland
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.podd.restlet;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.openrdf.OpenRDFException;
import org.openrdf.model.Model;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.util.GraphUtil;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.config.RepositoryConfigSchema;
import org.openrdf.repository.config.RepositoryImplConfig;
import org.openrdf.repository.config.RepositoryImplConfigBase;
import org.openrdf.repository.http.HTTPRepository;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.Rio;
import org.openrdf.rio.UnsupportedRDFormatException;
import org.openrdf.sail.memory.MemoryStore;
import org.restlet.Context;
import org.restlet.ext.freemarker.ContextTemplateLoader;
import org.restlet.security.ChallengeAuthenticator;
import org.restlet.security.Realm;
import org.restlet.security.Role;
import org.semanticweb.owlapi.model.OWLException;
import org.semanticweb.owlapi.model.OWLOntologyID;
import org.semanticweb.owlapi.model.OWLOntologyManagerFactory;
import org.semanticweb.owlapi.model.OWLOntologyManagerFactoryRegistry;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactoryRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.ansell.propertyutil.PropertyUtil;
import com.github.ansell.restletutils.FixedRedirectCookieAuthenticator;
import com.github.podd.api.PoddArtifactManager;
import com.github.podd.api.PoddOWLManager;
import com.github.podd.api.PoddSchemaManager;
import com.github.podd.api.PoddSesameManager;
import com.github.podd.api.data.DataReferenceManager;
import com.github.podd.api.data.PoddDataRepositoryManager;
import com.github.podd.api.purl.PoddPurlManager;
import com.github.podd.api.purl.PoddPurlProcessorFactory;
import com.github.podd.api.purl.PoddPurlProcessorFactoryRegistry;
import com.github.podd.exception.PoddException;
import com.github.podd.impl.PoddArtifactManagerImpl;
import com.github.podd.impl.PoddOWLManagerImpl;
import com.github.podd.impl.PoddRepositoryManagerImpl;
import com.github.podd.impl.PoddSchemaManagerImpl;
import com.github.podd.impl.PoddSesameManagerImpl;
import com.github.podd.impl.data.DataReferenceManagerImpl;
import com.github.podd.impl.data.PoddDataRepositoryManagerImpl;
import com.github.podd.impl.purl.PoddPurlManagerImpl;
import com.github.podd.impl.purl.UUIDPurlProcessorFactoryImpl;
import com.github.podd.utils.DebugUtils;
import com.github.podd.utils.InferredOWLOntologyID;
import com.github.podd.utils.PODD;
import com.github.podd.utils.PoddRoles;
import com.github.podd.utils.PoddUser;
import com.github.podd.utils.PoddUserStatus;
import com.github.podd.utils.PoddWebConstants;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
/**
* @author Peter Ansell p_ansell@yahoo.com
*
*/
public class ApplicationUtils
{
private static final Logger log = LoggerFactory.getLogger(ApplicationUtils.class);
public static ChallengeAuthenticator getNewAuthenticator(final Realm nextRealm, final Context newChildContext,
final PropertyUtil props)
{
ChallengeAuthenticator result = null;
final String authMethod =
props.get(PoddWebConstants.PROPERTY_CHALLENGE_AUTH_METHOD, PoddWebConstants.DEF_CHALLENGE_AUTH_METHOD);
if(authMethod.equalsIgnoreCase("cookie"))
{
ApplicationUtils.log.info("Using cookie authenticator");
final byte[] encryptionKey =
props.get(PoddWebConstants.PROPERTY_COOKIE_ENCRYPTION_KEY,
PoddWebConstants.DEF_COOKIE_ENCRYPTION_KEY).getBytes(StandardCharsets.UTF_8);
final FixedRedirectCookieAuthenticator auth =
new FixedRedirectCookieAuthenticator(newChildContext, nextRealm.getName(), encryptionKey);
auth.setEncryptAlgorithm(props.get(PoddWebConstants.PROPERTY_COOKIE_ENCRYPTION_ALGORITHM,
PoddWebConstants.DEF_COOKIE_ENCRYPTION_ALGORITHM));
// The submit path is the path that the form on the login page is actually submitted to
auth.setLoginPath(props.get(PoddWebConstants.PROPERTY_PATH_LOGIN_SUBMIT,
PoddWebConstants.DEF_PATH_LOGIN_SUBMIT));
auth.setLogoutPath(props.get(PoddWebConstants.PROPERTY_PATH_LOGOUT, PoddWebConstants.DEF_PATH_LOGOUT));
auth.setCookieName(props.get(PoddWebConstants.PROPERTY_COOKIE_NAME, PoddWebConstants.DEF_COOKIE_NAME));
auth.setIdentifierFormName(props.get(PoddWebConstants.PROPERTY_LOGIN_FIELD_USERNAME,
PoddWebConstants.DEF_LOGIN_FIELD_USERNAME));
auth.setSecretFormName(props.get(PoddWebConstants.PROPERTY_LOGIN_FIELD_PASSWORD,
PoddWebConstants.DEF_LOGIN_FIELD_PASSWORD));
auth.setFixedRedirectUri(props.get(PoddWebConstants.PROPERTY_PATH_REDIRECT_LOGGED_IN,
PoddWebConstants.DEF_PATH_REDIRECT_LOGGED_IN));
// Authenticator must be intercepting login and logout requests
auth.setInterceptingLogin(true);
auth.setInterceptingLogout(true);
auth.setMultiAuthenticating(false);
// These are the two independent links between the authenticator and the realm
auth.setVerifier(nextRealm.getVerifier());
auth.setEnroler(nextRealm.getEnroler());
// Authentication must be optional to allow unauthenticated resource access, to display
// the login page and for public access. We still fail if authentication fails in
// authenticated resources.
auth.setOptional(true);
result = auth;
}
else
{
ApplicationUtils.log.error("Did not recognise ChallengeAuthenticator method authMethod={}", authMethod);
throw new RuntimeException("Did not recognise ChallengeAuthenticator method authMethod=" + authMethod);
}
return result;
}
public static Repository getNewManagementRepository(final PropertyUtil props) throws RepositoryException
{
final String repositoryUrl =
props.get(PoddWebConstants.PROPERTY_MANAGEMENT_SESAME_LOCATION,
PoddWebConstants.DEFAULT_MANAGEMENT_SESAME_LOCATION);
return ApplicationUtils.getNewManagementRepositoryInternal(repositoryUrl);
}
private static Repository getNewManagementRepositoryInternal(final String repositoryUrl) throws RepositoryException
{
Repository repository;
// if we weren't able to find a repository URL in the configuration, we
// setup an in-memory store
if(repositoryUrl == null || repositoryUrl.trim().isEmpty())
{
repository = new SailRepository(new MemoryStore());
try
{
repository.initialize();
ApplicationUtils.log.info("Created an in memory store as management repository for PODD");
}
catch(final RepositoryException ex)
{
repository.shutDown();
ApplicationUtils.log.error("Could not initialise Sesame In Memory management repository");
throw new RuntimeException("Could not initialise Sesame In Memory management repository", ex);
}
}
else
{
repository = new HTTPRepository(repositoryUrl.trim());
try
{
repository.initialize();
ApplicationUtils.log.info("Using sesame http repository as management repository for PODD: {}",
repositoryUrl);
}
catch(final RepositoryException ex)
{
repository.shutDown();
ApplicationUtils.log.error("Could not initialise Sesame HTTP management repository with URL={}",
repositoryUrl);
throw new RuntimeException("Could not initialise Sesame HTTP management repository with URL="
+ repositoryUrl, ex);
}
}
RepositoryConnection testConnection = null;
try
{
testConnection = repository.getConnection();
testConnection.setNamespace("poddBase", PODD.PODD_BASE);
testConnection.setNamespace("poddScience", PODD.PODD_SCIENCE);
testConnection.setNamespace("poddPlant", PODD.PODD_PLANT);
testConnection.setNamespace("poddUser", PODD.PODD_USER);
}
finally
{
if(testConnection != null)
{
testConnection.close();
}
}
return repository;
}
public static Configuration getNewTemplateConfiguration(final Context newChildContext)
{
final Configuration result = new Configuration();
result.setDefaultEncoding("UTF-8");
result.setURLEscapingCharset("UTF-8");
// FIXME: Make this configurable
result.setTemplateLoader(new ContextTemplateLoader(newChildContext, "clap://class/templates"));
final BeansWrapper myWrapper = new BeansWrapper();
myWrapper.setSimpleMapWrapper(true);
result.setObjectWrapper(myWrapper);
return result;
}
public static void setupApplication(final PoddWebServiceApplication application, final Context applicationContext)
throws OpenRDFException, UnsupportedRDFormatException, IOException, OWLException, PoddException
{
final PropertyUtil props = application.getPropertyUtil();
ApplicationUtils.log.debug("application {}", application);
ApplicationUtils.log.debug("applicationContext {}", applicationContext);
final Repository nextManagementRepository = ApplicationUtils.getNewManagementRepository(props);
final String permanentRepositoryConfigPath =
props.get(PoddWebConstants.PROPERTY_PERMANENT_SESAME_REPOSITORY_CONFIG,
PoddWebConstants.DEFAULT_PERMANENT_SESAME_REPOSITORY_CONFIG);
final InputStream repositoryImplConfigStream =
ApplicationUtils.class.getResourceAsStream(permanentRepositoryConfigPath);
if(repositoryImplConfigStream == null)
{
ApplicationUtils.log.error("Could not find repository config");
}
final Model graph = Rio.parse(repositoryImplConfigStream, "", RDFFormat.TURTLE);
final Resource repositoryNode = GraphUtil.getUniqueSubject(graph, RepositoryConfigSchema.REPOSITORYTYPE, null);
final RepositoryImplConfig repositoryImplConfig = RepositoryImplConfigBase.create(graph, repositoryNode);
final String poddHome = props.get(PoddWebConstants.PROPERTY_PODD_HOME, "");
final Path poddHomePath = Paths.get(poddHome);
application.setPoddRepositoryManager(new PoddRepositoryManagerImpl(nextManagementRepository,
repositoryImplConfig, props.get(PoddWebConstants.PROPERTY_PERMANENT_SESAME_REPOSITORY_SERVER,
PoddWebConstants.DEFAULT_PERMANENT_SESAME_REPOSITORY_SERVER), poddHomePath, props));
// File Reference Manager
final DataReferenceManager nextDataReferenceManager = new DataReferenceManagerImpl();
// PURL manager
final PoddPurlProcessorFactoryRegistry nextPurlRegistry = new PoddPurlProcessorFactoryRegistry();
// TODO: Generalise the following so they don't have to be done here
// Could call the purl methods with the preferred prefix maybe
nextPurlRegistry.clear();
final PoddPurlProcessorFactory nextPurlProcessorFactory = new UUIDPurlProcessorFactoryImpl();
final String purlPrefix = props.get(PoddWebConstants.PROPERTY_PURL_PREFIX, null);
nextPurlProcessorFactory.setPrefix(purlPrefix);
nextPurlRegistry.add(nextPurlProcessorFactory);
final PoddPurlManager nextPurlManager = new PoddPurlManagerImpl();
nextPurlManager.setPurlProcessorRegistry(nextPurlRegistry);
final Collection<OWLOntologyManagerFactory> ontologyManagers =
OWLOntologyManagerFactoryRegistry.getInstance().get(
props.get(PoddWebConstants.PROPERTY_OWLAPI_MANAGER, PoddWebConstants.DEFAULT_OWLAPI_MANAGER));
if(ontologyManagers == null || ontologyManagers.isEmpty())
{
ApplicationUtils.log.error("OWLOntologyManagerFactory was not found");
}
final OWLReasonerFactory reasonerFactory =
OWLReasonerFactoryRegistry.getInstance().getReasonerFactory("Pellet");
if(reasonerFactory == null)
{
ApplicationUtils.log.error("OWLReasonerFactory was null");
}
final PoddOWLManager nextOWLManager =
new PoddOWLManagerImpl(ontologyManagers.iterator().next(), reasonerFactory);
// File Repository Manager
final PoddDataRepositoryManager nextDataRepositoryManager = new PoddDataRepositoryManagerImpl();
nextDataRepositoryManager.setRepositoryManager(application.getPoddRepositoryManager());
nextDataRepositoryManager.setOWLManager(nextOWLManager);
try
{
// TODO: Configure data repositories in a cleaner manner than this
final Model aliasConfiguration = application.getDataRepositoryConfig();
nextDataRepositoryManager.initialise(aliasConfiguration);
}
catch(PoddException | IOException e)
{
ApplicationUtils.log.error("Fatal Error!!! Could not initialize File Repository Manager", e);
}
application.setPoddDataRepositoryManager(nextDataRepositoryManager);
final PoddSesameManager poddSesameManager = new PoddSesameManagerImpl();
application.setPoddSchemaManager(new PoddSchemaManagerImpl());
application.getPoddSchemaManager().setOwlManager(nextOWLManager);
application.getPoddSchemaManager().setRepositoryManager(application.getPoddRepositoryManager());
application.getPoddSchemaManager().setSesameManager(poddSesameManager);
application.setPoddArtifactManager(new PoddArtifactManagerImpl());
application.getPoddArtifactManager().setRepositoryManager(application.getPoddRepositoryManager());
application.getPoddArtifactManager().setDataReferenceManager(nextDataReferenceManager);
application.getPoddArtifactManager().setDataRepositoryManager(nextDataRepositoryManager);
application.getPoddArtifactManager().setPurlManager(nextPurlManager);
application.getPoddArtifactManager().setOwlManager(nextOWLManager);
application.getPoddArtifactManager().setSchemaManager(application.getPoddSchemaManager());
application.getPoddArtifactManager().setSesameManager(poddSesameManager);
ApplicationUtils.setupSchemas(application);
final List<Role> roles = application.getRoles();
// FIXME: Why does the list need to be cleared here?
roles.clear();
roles.addAll(PoddRoles.getRoles());
final PoddSesameRealm nextRealm =
new PoddSesameRealm(nextManagementRepository, PODD.VF.createURI(props.get(
PODD.PROPERTY_USER_MANAGEMENT_GRAPH, PODD.DEFAULT_USER_MANAGEMENT_GRAPH.stringValue())));
// FIXME: Make this configurable
nextRealm.setName("PODDRealm");
// Check if there is a current admin, and only add our test admin user if there is no admin
// in the system
boolean foundCurrentAdmin = false;
for(final PoddUser nextUser : nextRealm.getUsers())
{
if(nextRealm.findRoles(nextUser).contains(PoddRoles.ADMIN.getRole()))
{
foundCurrentAdmin = true;
break;
}
}
if(!foundCurrentAdmin)
{
final URI testAdminUserHomePage = PODD.VF.createURI("http://www.example.com/testAdmin");
final String username =
props.get(PoddWebConstants.PROPERTY_INITIAL_ADMIN_USERNAME,
PoddWebConstants.DEFAULT_INITIAL_ADMIN_USERNAME);
final char[] password =
props.get(PoddWebConstants.PROPERTY_INITIAL_ADMIN_PASSWORD,
PoddWebConstants.DEFAULT_INITIAL_ADMIN_PASSWORD).toCharArray();
final PoddUser testAdminUser =
new PoddUser(username, password, "Initial Admin", "User", "initial.admin.user@example.com",
PoddUserStatus.ACTIVE, testAdminUserHomePage, "Local Organisation", "Dummy-ORCID");
nextRealm.addUser(testAdminUser);
nextRealm.map(testAdminUser, PoddRoles.ADMIN.getRole());
final Set<Role> testAdminUserRoles = nextRealm.findRoles(testAdminUser);
ApplicationUtils.log
.warn("Automatically created a new initial admin user as no current administrators were found: username={} roles={}",
username, testAdminUserRoles);
// FIXME: Should put the application in maintenance mode at this point (when that is
// supported), to require password/username change before opening up to other users
}
final ChallengeAuthenticator newAuthenticator =
ApplicationUtils.getNewAuthenticator(nextRealm, applicationContext, props);
application.setAuthenticator(newAuthenticator);
application.setRealm(nextRealm);
// Setup the Freemarker configuration
final Configuration newTemplateConfiguration = ApplicationUtils.getNewTemplateConfiguration(applicationContext);
application.setTemplateConfiguration(newTemplateConfiguration);
// Create a custom error handler using our overridden PoddStatusService together with the
// Freemarker configuration
final PoddStatusService statusService = new PoddStatusService(newTemplateConfiguration);
application.setStatusService(statusService);
}
/**
* @param application
* @param props
*
*/
public static void setupSchemas(final PoddWebServiceApplication application) throws IOException, OpenRDFException,
OWLException, PoddException
{
final PropertyUtil props = application.getPropertyUtil();
final PoddSchemaManager poddSchemaManager = application.getPoddSchemaManager();
final PoddArtifactManager poddArtifactManager = application.getPoddArtifactManager();
/*
* Since the schema ontology upload feature is not yet supported, necessary schemas are
* uploaded here at application start up.
*/
try
{
final String schemaManifest = props.get(PODD.KEY_SCHEMAS, PODD.PATH_DEFAULT_SCHEMAS);
final RDFFormat format = Rio.getParserFormatForFileName(schemaManifest, RDFFormat.RDFXML);
Model model = null;
try (final InputStream schemaManifestStream = application.getClass().getResourceAsStream(schemaManifest);)
{
if(schemaManifestStream == null)
{
throw new RuntimeException("Could not find the schema ontology manifest: " + schemaManifest);
}
model = Rio.parse(schemaManifestStream, "", format);
}
if(ApplicationUtils.log.isDebugEnabled())
{
ApplicationUtils.log.debug("Schema manifest contents");
DebugUtils.printContents(model);
}
ApplicationUtils.log.debug("About to upload schema ontologies");
// Returns an ordered list of the schema ontologies that were uploaded
final List<InferredOWLOntologyID> schemaOntologies = poddSchemaManager.uploadSchemaOntologies(model);
if(!schemaOntologies.isEmpty())
{
ApplicationUtils.log.debug("Uploaded new schema ontologies: {}", schemaOntologies);
}
else
{
ApplicationUtils.log.debug("No new schema ontologies uploaded this time");
}
// NOTE: The following is not ordered at this point in time
// TODO: Do we gain anything from ordering this collection
final Set<InferredOWLOntologyID> currentSchemaOntologies = poddSchemaManager.getCurrentSchemaOntologies();
if(!currentSchemaOntologies.isEmpty())
{
ApplicationUtils.log.debug("Existing current schema ontologies: {}", currentSchemaOntologies);
}
else
{
ApplicationUtils.log.debug("Found no existing current schema ontologies");
}
final List<InferredOWLOntologyID> updatedCurrentSchemaOntologies = new ArrayList<>();
for(final InferredOWLOntologyID nextSchemaOntology : schemaOntologies)
{
if(currentSchemaOntologies.contains(nextSchemaOntology))
{
ApplicationUtils.log.debug("Existing schema ontologies contains next schema ontologies: {}",
nextSchemaOntology);
updatedCurrentSchemaOntologies.add(nextSchemaOntology);
}
}
if(!updatedCurrentSchemaOntologies.isEmpty())
{
ApplicationUtils.log.debug("Found new versions of existing schema ontologies: {}",
updatedCurrentSchemaOntologies);
}
// TODO: Offer one-time migration based on updatedCurrentSchemaOntologies
// For now, we always attempt to update all artifacts to the current schema ontologies
// Once the upgrade process is well developed, may want to streamline application
// startup to avoid attempting to do this every time
final ConcurrentMap<InferredOWLOntologyID, Set<? extends OWLOntologyID>> currentArtifactImports =
new ConcurrentHashMap<>();
final ConcurrentMap<InferredOWLOntologyID, Set<InferredOWLOntologyID>> artifactsToUpdate =
new ConcurrentHashMap<>();
final List<InferredOWLOntologyID> unpublishedArtifacts = poddArtifactManager.listUnpublishedArtifacts();
ApplicationUtils.log.debug("Existing unpublished artifacts: \n{}", unpublishedArtifacts);
for(final InferredOWLOntologyID nextArtifact : unpublishedArtifacts)
{
if(poddArtifactManager.isPublished(nextArtifact))
{
ApplicationUtils.log.debug("Not attempting to update schema ontologies for published artifact: {}",
nextArtifact.getOntologyIRI());
// Must not update the schema imports for published artifacts
continue;
}
ApplicationUtils.log.debug("Fetching schema imports for unpublished artifact: {}",
nextArtifact.getOntologyIRI());
final Set<? extends OWLOntologyID> schemaImports = poddArtifactManager.getSchemaImports(nextArtifact);
// Cache the current artifact imports so they are easily accessible without calling
// the above method again if they need to be updated
currentArtifactImports.put(nextArtifact, schemaImports);
for(final OWLOntologyID nextSchemaImport : schemaImports)
{
boolean foundNonCurrentVersion = false;
OWLOntologyID matchingSchema = null;
for(final InferredOWLOntologyID nextUpdatedSchemaImport : currentSchemaOntologies)
{
// If the ontology IRI of the artifacts schema import was in the updated
// list, then signal it for updating
if(nextUpdatedSchemaImport.getOntologyIRI().equals(nextSchemaImport.getOntologyIRI())
&& !nextUpdatedSchemaImport.getVersionIRI().equals(nextSchemaImport.getVersionIRI()))
{
foundNonCurrentVersion = true;
}
if(nextUpdatedSchemaImport.getOntologyIRI().equals(nextSchemaImport.getOntologyIRI()))
{
matchingSchema = nextSchemaImport;
}
}
// If there is a new schema, or they import an old version of one of the
// schemas, then import all of the current schemas
// FIXME: Naive strategy to ensure that we get all of the imports that
// users expect is to add all of the current schema ontologies here
// Need to customise strategies for users here, or in the GUI to select the
// schema ontologies that they wish to use for each artifact
if(foundNonCurrentVersion || matchingSchema == null)
{
ApplicationUtils.log.info("Found out of date or missing schema version: old=<{}> new=<{}>",
nextSchemaImport, matchingSchema);
Set<InferredOWLOntologyID> set = new HashSet<>();
final Set<InferredOWLOntologyID> putIfAbsent = artifactsToUpdate.putIfAbsent(nextArtifact, set);
if(putIfAbsent != null)
{
set = putIfAbsent;
}
// FIXME: Choose these in a way which will not automatically include
// everything
set.addAll(currentSchemaOntologies);
// Do not continue this loop in this naive strategy
break;
}
}
}
ApplicationUtils.log.debug("Unpublished artifacts requiring schema updates: \n{}", artifactsToUpdate);
for(final Entry<InferredOWLOntologyID, Set<InferredOWLOntologyID>> nextEntry : artifactsToUpdate.entrySet())
{
// FIXME: Naive strategy is to fail for each import
// If/When we support linked artifacts, this logic will need to be improved to
// ensure that both parents and dependencies are not updated if any of them are not
// consistent with the new schema versions
try
{
final InferredOWLOntologyID nextArtifactToUpdate = nextEntry.getKey();
ApplicationUtils.log.debug("About to update schema imports for: {}", nextArtifactToUpdate);
poddArtifactManager.updateSchemaImports(nextArtifactToUpdate,
currentArtifactImports.get(nextArtifactToUpdate), nextEntry.getValue());
ApplicationUtils.log.debug("Completed updating schema imports for: {}", nextArtifactToUpdate);
}
catch(final Throwable e)
{
ApplicationUtils.log.error("Could not update schema imports automatically due to exception: ", e);
}
}
// Enable the following for debugging
// dumpSchemaGraph(application, nextRepository);
}
catch(IOException | OpenRDFException | OWLException | PoddException e)
{
ApplicationUtils.log.error("Fatal Error!!! Could not load schema ontologies", e);
throw e;
}
}
private ApplicationUtils()
{
}
}