podd.util.WebappInitializationUtil.java Source code

Java tutorial

Introduction

Here is the source code for podd.util.WebappInitializationUtil.java

Source

/*
 * Copyright (c) 2009 - 2010. School of Information Technology and Electrical
 * Engineering, The University of Queensland.  This software is being developed
 * for the "Phenomics Ontoogy Driven Data Management Project (PODD)" project.
 * PODD is a National e-Research Architecture Taskforce (NeAT) project
 * co-funded by ANDS and ARCS.
 *
 * PODD 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PODD 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 PODD.  If not, see <http://www.gnu.org/licenses/>.
 */

package podd.util;

import info.aduna.collections.iterators.CloseableIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.io.IOUtils;
import org.hibernate.SessionFactory;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.rio.RDFFormat;
import org.semanticweb.owl.io.OWLOntologyOutputTarget;
import org.semanticweb.owl.io.RDFXMLOntologyFormat;
import org.semanticweb.owl.io.StringOutputTarget;
import org.semanticweb.owl.model.OWLAxiom;
import org.semanticweb.owl.model.OWLClass;
import org.semanticweb.owl.model.OWLClassAxiom;
import org.semanticweb.owl.model.OWLDescription;
import org.semanticweb.owl.model.OWLEntity;
import org.semanticweb.owl.model.OWLOntology;
import org.semanticweb.owl.model.OWLOntologyChangeException;
import org.semanticweb.owl.model.OWLOntologyManager;
import org.semanticweb.owl.model.OWLOntologyURIMapper;
import org.semanticweb.owl.model.OWLSubClassAxiom;
import org.springframework.core.io.AbstractResource;
import org.springframework.core.io.Resource;
import podd.client.PoddClientException;
import podd.client.owl.PoddClientOntologyRegistry;
import podd.dataaccess.AdministrationSettingsDAO;
import podd.dataaccess.DatastoreRegistryDAO;
import podd.dataaccess.PoddConceptDAO;
import podd.dataaccess.RepositoryRoleDAO;
import podd.dataaccess.UserDAO;
import podd.dataaccess.hibernate.HibernateSessionHelper;
import podd.exception.DataAccessException;
import podd.model.EntityFactory;
import podd.model.datastore.Datastore;
import podd.model.entity.PoddConcept;
import podd.model.user.AdministrationSettings;
import podd.model.user.RepositoryRole;
import podd.model.user.User;
import podd.triples.SesameRepoWrapper;
import podd.util.common.vocabulary.EnumerableRDFNamespace;
import podd.util.owl.OntologyHelper;
import podd.util.owl.OntologyRegistry;
import uk.ac.manchester.cs.owl.OWLOntologyURIMapperImpl;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import static org.semanticweb.owl.model.AxiomType.SUBCLASS;
import static podd.model.user.AdministrationSettings.SELF_REGISTRATION_ENABLED;
import static podd.model.user.AdministrationSettings.USER_STATUS_ACTIVE;
import static podd.model.user.RepositoryRole.PROJECT_CREATOR;
import static podd.model.user.RepositoryRole.PUBLIC;
import static podd.model.user.RepositoryRole.REPOSITORY_ADMINISTRATOR;
import static podd.model.user.RepositoryRole.REPOSITORY_USER;
import static podd.model.user.UserStatus.ACTIVE;
import static podd.util.common.Constants.PODD_URI;
import static podd.util.common.vocabulary.PoddModelNamespace.PODD_MODEL;
import static podd.util.common.vocabulary.PoddNamespaceHelper.PODD_NAMESPACE_HELPER;

/**
 * @author Yuan-Fang Li
 * @version $Id$
 */

public class WebappInitializationUtil {
    private static final int TWENTY_MILLISECONDS = 20;
    private static final String USERNAME_KEY = "userName";

    private final Logger LOGGER = LoggerFactory.getLogger(getClass());

    private User adminUser;
    private Resource repoAdminUserResource;
    private Resource datastoreResource;

    private AbstractResource ontologyDirectory;
    private String ontologyNames;
    private boolean toReset;
    private boolean toIngest;
    private UserDAO userDao;
    private DatastoreRegistryDAO datastoreRegistryDao;
    private RepositoryRoleDAO rrDao;
    private AdministrationSettingsDAO adminSettingsDao;
    private PoddConceptDAO conceptDao;
    private OntologyHelper ontologyHelper;
    private EntityFactory entityFactory;
    private SesameRepoWrapper repoWrapper;

    private OntologyRegistry ontologyRegistry;
    private List<File> ontologyFiles;
    private SessionFactory sessionFactory;

    public void setToReset(boolean toRest) {
        this.toReset = toRest;
    }

    public void setOntologyDirectory(AbstractResource ontologyDirectory) {
        this.ontologyDirectory = ontologyDirectory;
    }

    public void setOntologyNames(String ontologies) {
        this.ontologyNames = ontologies;
    }

    public void setRepoAdminUserResource(Resource repoAdminUserResource) {
        this.repoAdminUserResource = repoAdminUserResource;
    }

    public void setDatastoreResource(Resource datastoreResource) {
        this.datastoreResource = datastoreResource;
    }

    public void setUserDao(UserDAO userDao) {
        this.userDao = userDao;
    }

    public void setDatastoreRegistryDao(DatastoreRegistryDAO datastoreRegistryDao) {
        this.datastoreRegistryDao = datastoreRegistryDao;
    }

    public void setConceptDao(PoddConceptDAO conceptDao) {
        this.conceptDao = conceptDao;
    }

    public void setRrDao(RepositoryRoleDAO rrDao) {
        this.rrDao = rrDao;
    }

    public void setAdminSettingsDao(AdministrationSettingsDAO adminSettingsDao) {
        this.adminSettingsDao = adminSettingsDao;
    }

    public void setOntologyHelper(OntologyHelper ontologyHelper) {
        this.ontologyHelper = ontologyHelper;
    }

    public void setEntityFactory(EntityFactory entityFactory) {
        this.entityFactory = entityFactory;
    }

    public void setOntologyRegistry(OntologyRegistry ontologyRegistry) {
        this.ontologyRegistry = ontologyRegistry;
    }

    public void setToIngest(boolean toIngest) {
        this.toIngest = toIngest;
    }

    public void setRepoWrapper(SesameRepoWrapper repoWrapper) {
        this.repoWrapper = repoWrapper;
    }

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void doInitialize() throws Exception {
        HibernateSessionHelper.openSession(sessionFactory);
        try {
            if (toReset) {
                LOGGER.info("Initializing PODD repository.");
                setupDefaultRepoRoles();
                setupDefaultAdministrationSettings();
                adminUser = createRepoAdminUser(repoAdminUserResource);
                createDefaultDatastore(datastoreResource);
                processAndAddOWLClasses();
                initializePoddClientOntologyRegistry();
                addOntologiesToTripleStore();
            }
        } finally {
            HibernateSessionHelper.closeSession(sessionFactory);
        }
    }

    private void addOntologiesToTripleStore() throws Exception {
        RDFXMLOntologyFormat format = new RDFXMLOntologyFormat();
        final RepositoryConnection conn = repoWrapper.getConnection();
        try {
            for (OWLOntology ont : ontologyRegistry.getOntologies()) {
                OWLOntologyOutputTarget target = new StringOutputTarget();
                ontologyRegistry.getOntologyManager().saveOntology(ont, format, target);
                final InputStream in = IOUtils.toInputStream(target.toString());
                try {
                    conn.add(in, ont.getURI().toString(), RDFFormat.RDFXML);
                    LOGGER.info("Added ontology to triple store: " + ont.getURI().toString());
                } finally {
                    IOUtils.closeQuietly(in);
                }
            }
            conn.commit();
        } finally {
            conn.close();
        }
    }

    private void processAndAddOWLClasses() throws DataAccessException {
        ontologyFiles = new LinkedList<File>();
        for (String filename : ontologyNames.split(",")) {
            try {
                File file = new File(ontologyDirectory.getFile().getAbsolutePath() + File.separator + filename);
                ontologyFiles.add(file);
            } catch (IOException e) {
                throw new DataAccessException("Error loading ontology from directory: "
                        + ontologyDirectory.getFilename() + " : " + filename, e);
            }
        }
        ingestPoddConceptsFromOntologies();
    }

    /**
     * Initialize the PODD Client ontology registry
     * @throws PoddClientException
     */
    private void initializePoddClientOntologyRegistry() throws PoddClientException {
        List<URI> ontologyURIs = new ArrayList<URI>();
        for (File file : ontologyFiles) {
            ontologyURIs.add(file.toURI());
        }

        PoddClientOntologyRegistry.getInstance().setModels(ontologyURIs);
    }

    public User getRepoAdmin() {
        return adminUser;
    }

    private User createRepoAdminUser(Resource resource) {
        try {
            File propertyFile = resource.getFile();
            final FileReader fileReader = new FileReader(propertyFile);
            Properties properties = new Properties();
            properties.load(fileReader);

            // try to load the user and if it doesn't exist create a new one
            String username = properties.get(USERNAME_KEY).toString();
            User admin = userDao.loadByUserName(username);
            if (null == admin) {
                admin = entityFactory.createUser(null);
            }
            // add properties to the user
            for (Object key : properties.keySet()) {
                final String value = properties.get(key).toString();
                String methodName = getSetterMethodName((String) key);
                final Method method = User.class.getDeclaredMethod(methodName, String.class);
                method.invoke(admin, value);
            }
            admin.setStatus(ACTIVE);
            admin.setRepositoryRole(rrDao.getRepositoryRole(REPOSITORY_ADMINISTRATOR.getName()));
            LOGGER.info("Creating repository administrator user: " + admin.getUserName());
            userDao.saveOrUpdate(admin);
            return admin;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void createDefaultDatastore(Resource resource) {
        if (null == resource)
            return;
        try {
            File propertyFile = resource.getFile();
            final FileReader fileReader = new FileReader(propertyFile);
            Properties properties = new Properties();
            properties.load(fileReader);

            // try to load the datastore and if it doesn't exist create a new one
            String ip = properties.get("ip").toString();
            int port = Integer.parseInt(properties.get("port").toString());
            String login = properties.get("login").toString();
            String defaultDirectory = properties.get("defaultDirectory").toString();
            Datastore datastore = datastoreRegistryDao.loadByIP(ip);
            if (null == datastore) {
                datastore = new Datastore(ip, port, login, defaultDirectory);
            } else {
                datastore.setPort(port);
                datastore.setLoginId(login);
                datastore.setDefaultDirectory(defaultDirectory);
            }
            datastore.setAccessMethod(properties.get("accessMethod").toString());
            LOGGER.info("Creating default datastore: " + datastore.getLoginId() + "@" + datastore.getIp() + ":"
                    + datastore.getPort());
            datastoreRegistryDao.saveOrUpdate(datastore);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private String getSetterMethodName(String fieldName) {
        String methodName = "set";
        String capitalFieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
        return methodName + capitalFieldName;
    }

    private void ingestPoddConceptsFromOntologies() {
        Map<OWLClass, Set<OWLAxiom>> conceptClasses = new HashMap<OWLClass, Set<OWLAxiom>>();
        try {
            PODD_NAMESPACE_HELPER.addNamespace(PODD_MODEL.uri, PODD_MODEL);
            OWLOntologyManager manager = ontologyRegistry.getOntologyManager();
            final OWLClass poddClass = manager.getOWLDataFactory().getOWLClass(PODD_MODEL.PODD_CONCEPT.getURI());
            final OWLOntologyURIMapperImpl mapper = new OWLOntologyURIMapperImpl();
            manager.addURIMapper(mapper);
            for (File file : ontologyFiles) {
                processOneOntology(file, conceptClasses, poddClass, mapper);
            }
            if (toIngest) {
                LOGGER.info("Ingesting a total of " + conceptClasses.size() + " concepts.");
                int i = 0;
                for (OWLClass cls : conceptClasses.keySet()) {
                    createPoddConcept(cls, conceptClasses.get(cls), ++i);
                }
            }
        } catch (Exception e) {
            LOGGER.error("Error ingesting concepts", e);
            throw new RuntimeException(e);
        }
    }

    private void processOneOntology(File file, Map<OWLClass, Set<OWLAxiom>> map, OWLClass poddClass,
            OWLOntologyURIMapperImpl mapper) throws Exception {
        OWLOntologyManager manager = ontologyRegistry.getOntologyManager();
        LOGGER.debug("Loading ontology file: " + file.toURI().toString());
        final OWLOntology ontology = manager.loadOntologyFromPhysicalURI(file.toURI());
        mapper.addMapping(ontology.getURI(), file.toURI());
        ontologyRegistry.addModel(ontology);
        enrichNamespace(ontology);
        final Set<OWLSubClassAxiom> owlSubClassAxiomSet = ontology.getAxioms(SUBCLASS);
        for (OWLSubClassAxiom axiom : owlSubClassAxiomSet) {
            final OWLDescription owlClass = axiom.getSubClass();
            if (!owlClass.isAnonymous() && ontologyRegistry.isSubClassOf(owlClass.asOWLClass(), poddClass)) {
                addToMap(owlClass.asOWLClass(), ontology, map);
            }
        }
    }

    private void enrichNamespace(OWLOntology ontology) throws URISyntaxException {
        String namespace = ontology.getURI().toString();
        if (!namespace.endsWith("#")) {
            namespace = namespace + "#";
        }
        if (null == PODD_NAMESPACE_HELPER.getNamespace(namespace)) {
            PODD_NAMESPACE_HELPER.addNewNamespace(namespace);
        }
        addOrUpdateEntities(ontology, PODD_NAMESPACE_HELPER.getNamespace(namespace));
    }

    private void addOrUpdateEntities(OWLOntology ontology, EnumerableRDFNamespace namespace) {
        final Set<OWLEntity> entities = ontology.getReferencedEntities();
        for (OWLEntity entity : entities) {
            if (entity.getURI().toString().startsWith(namespace.uri)) {
                namespace.addElement(entity.getURI());
            }
        }
    }

    /**
     * This method is not used locally, so should be deleted.
     * @return
     */
    @Deprecated
    private OWLOntologyURIMapper createOntologyMapper() {
        final OWLOntologyURIMapperImpl mapper = new OWLOntologyURIMapperImpl();
        for (File file : ontologyFiles) {
            final String ontName = file.getName().substring(0, file.getName().indexOf('.'));
            mapper.addMapping(URI.create(PODD_URI + ontName), file.toURI());
        }
        return mapper;
    }

    private void addToMap(OWLClass owlClass, OWLOntology ontology, Map<OWLClass, Set<OWLAxiom>> map) {
        final Set<OWLClassAxiom> classAxiomSet = ontology.getAxioms(owlClass);
        Set<OWLAxiom> set = map.get(owlClass);
        if (null == set) {
            set = new HashSet<OWLAxiom>();
        }
        set.addAll(classAxiomSet);
        map.put(owlClass, set);

        set.addAll(ontology.getEntityAnnotationAxioms(owlClass));
    }

    private void createPoddConcept(OWLClass owlClass, Set<OWLAxiom> axiomSet, int idx) throws Exception {
        OWLOntologyManager manager = ontologyRegistry.getOntologyManager();
        final String conceptName = owlClass.toString();
        // try to load the concept and if it doesn't exist create a new one
        PoddConcept concept = getFirstConcept(conceptDao.loadByLocalName(conceptName));
        if (null != concept) {
            final OWLOntology ontology = concept.getCurrentDefinition();
            boolean isChanged = ontologyChanged(axiomSet, manager, concept, ontology, owlClass);
            if (isChanged) {
                conceptDao.update(concept);
            }
        } else {
            concept = createConcept(axiomSet, conceptName, manager, owlClass);
        }
        if (null != concept) {
            LOGGER.info("Ingested concept " + idx + ": " + concept.getConceptName());
        }
    }

    private PoddConcept createConcept(Set<OWLAxiom> axiomSet, String conceptName, OWLOntologyManager manager,
            OWLClass owlClass) throws Exception {
        PoddConcept concept = entityFactory.createPoddConcept(adminUser);
        concept.setConceptName(conceptName);
        concept.setLabel("Predefined concept: " + conceptName);
        // FIXME: HACK: This URI needs to be created using customisable patterns, ideally, it should be natively known by the author
        URI baseUri = URI.create(PODD_MODEL.uri.replace('#', '/') + conceptName + "_Ontology");
        final OWLOntology ontology = ontologyHelper.getTimestampedOntology(baseUri);
        boolean isChanged = ontologyChanged(axiomSet, manager, concept, ontology, owlClass);
        if (isChanged) {
            conceptDao.save(concept);
        }
        return concept;
    }

    private boolean ontologyChanged(Set<OWLAxiom> axiomSet, OWLOntologyManager manager, PoddConcept concept,
            OWLOntology ontology, OWLClass owlClass) throws OWLOntologyChangeException, DataAccessException {
        try {
            final boolean sameOntology = testSameOntology(ontology, owlClass, axiomSet);
            if (!sameOntology) {
                ParameterUtil.wait(TWENTY_MILLISECONDS);
                manager.addAxioms(ontology, axiomSet);
                concept.setCurrentDefinition(ontology);
                return true;
            } else {
                return false;
            }
        } finally {
            manager.removeOntology(ontology.getURI());
        }
    }

    private boolean testSameOntology(OWLOntology curOntology, OWLClass concept, Set<OWLAxiom> axiomSet) {
        final Set<OWLAxiom> oldAxiomSet = new HashSet<OWLAxiom>();
        oldAxiomSet.addAll(curOntology.getAxioms(concept));
        oldAxiomSet.addAll(curOntology.getEntityAnnotationAxioms(concept));
        final int oldCount = oldAxiomSet.size();
        final int newCount = axiomSet.size();
        return oldCount == newCount && oldAxiomSet.containsAll(axiomSet);
    }

    private void setupDefaultRepoRoles() {
        try {
            saveOrUpdateRole(PUBLIC);
            saveOrUpdateRole(REPOSITORY_USER);
            saveOrUpdateRole(PROJECT_CREATOR);
            saveOrUpdateRole(REPOSITORY_ADMINISTRATOR);
        } catch (DataAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private void saveOrUpdateRole(RepositoryRole role) throws DataAccessException {
        RepositoryRole repositoryRole = rrDao.getRepositoryRole(role.getName());
        if (null == repositoryRole) {
            repositoryRole = role;
        } else {
            repositoryRole.setDescription(role.getDescription());
        }
        rrDao.saveOrUpdate(repositoryRole);
    }

    private void setupDefaultAdministrationSettings() throws DataAccessException {
        saveOrUpdateAdminSetting(SELF_REGISTRATION_ENABLED);
        saveOrUpdateAdminSetting(USER_STATUS_ACTIVE);
    }

    private void saveOrUpdateAdminSetting(AdministrationSettings setting) throws DataAccessException {
        AdministrationSettings adminSetting = adminSettingsDao.getAdministrationSetting(setting.getName());
        if (null == adminSetting) {
            adminSetting = setting;
        } else {
            adminSetting.setValue(setting.getValue());
        }
        adminSettingsDao.saveOrUpdate(adminSetting);
    }

    private <T> T getFirstConcept(CloseableIterator<T> iterator) {
        try {
            if (iterator.hasNext()) {
                return iterator.next();
            } else {
                return null;
            }
        } finally {
            iterator.close();
        }
    }
}