/* * NodeTypeInitializer.java * * Created on May 6, 2010, 6:00:57 PM * * Description: Initializes node types from an XML file. * * Copyright (C) May 6, 2010, Stephen L. Reed. * * This program 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. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.texai.ahcsSupport; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import net.jcip.annotations.NotThreadSafe; import net.sf.ehcache.CacheManager; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.openrdf.model.URI; import org.openrdf.model.impl.URIImpl; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.texai.ahcsSupport.domainEntity.NodeType; import org.texai.ahcsSupport.domainEntity.RoleType; import org.texai.kb.CacheInitializer; import org.texai.kb.persistence.DistributedRepositoryManager; import org.texai.kb.persistence.RDFEntityManager; import org.texai.kb.persistence.RDFEntityPersister; import org.texai.util.StringUtils; import org.texai.util.TexaiException; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** Initializes node types from an XML file. * * @author reed */ @NotThreadSafe public class NodeTypeInitializer { /** the log4j logger */ private static final Logger LOGGER = Logger.getLogger(NodeTypeInitializer.class); /** the indicator whether debug logging is enabled */ private static final boolean IS_DEBUG_LOGGING_ENABLED = LOGGER.isDebugEnabled(); /** the RDF entity manager */ private RDFEntityManager rdfEntityManager; /** the id assigned by the persistence framework */ private URI id; /** the node type ids */ private final List<URI> ids = new ArrayList<>(); /** the node type name */ private String typeName; /** the names of inherited node types */ private Set<String> inheritedNodeTypeNames = new HashSet<>(); /** the names of role types */ private Set<String> roleTypeNames = new HashSet<>(); /** the node's mission description in English */ private String missionDescription; /** the inherited node type dictionary, id --> set of names of inherited node types */ private final Map<URI, Set<String>> inheritedNodeTypeDictionary = new HashMap<>(); /** the node access */ private NodeAccess nodeAccess; /** Constructs a new NodeTypeInitializer instance. */ public NodeTypeInitializer() { } /** Initializes the application. * * @param rdfEntityManager the RDF entity manager * rule unit test specifications */ public void initialize(final RDFEntityManager rdfEntityManager) { //Preconditions assert rdfEntityManager != null : "rdfEntityManager must not be null"; this.rdfEntityManager = rdfEntityManager; nodeAccess = new NodeAccess(rdfEntityManager); } /** Reads the file and persists the node types after all have been parsed. * * @param nodeTypesPath node types file path */ public void process(final String nodeTypesPath) { //Preconditions assert nodeTypesPath != null : "nodeTypesPath must not be null"; assert !nodeTypesPath.isEmpty() : "nodeTypesPath must not be an empty string"; resetState(); final BufferedInputStream bufferedInputStream; try { final File nodeTypesFile = new File(nodeTypesPath); LOGGER.info("parsing the node types file: " + nodeTypesFile.toString()); bufferedInputStream = new BufferedInputStream(new FileInputStream(nodeTypesFile)); } catch (final FileNotFoundException ex) { throw new TexaiException(ex); } try { final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); final SAXParser saxParser = saxParserFactory.newSAXParser(); final SAXHandler myHandler = new SAXHandler(); saxParser.parse(bufferedInputStream, myHandler); } catch (final ParserConfigurationException | SAXException | IOException ex) { LOGGER.fatal(StringUtils.getStackTraceAsString(ex)); throw new TexaiException(ex); } resolveInheritedNodeTypeNames(); displayLoadedNodeTypes(); try { final RepositoryConnection repositoryConnection = DistributedRepositoryManager.getInstance().getRepositoryConnectionForRepositoryName("NodeRoleTypes"); LOGGER.info("repository size: " + repositoryConnection.size()); repositoryConnection.close(); } catch (final RepositoryException ex) { throw new TexaiException(ex); } } /** Provides a SAX parsing handler. */ final class SAXHandler extends DefaultHandler { /** the string builder */ private final StringBuilder stringBuilder = new StringBuilder(); /** Constructs a new SAXHandler instance. */ public SAXHandler() { } /** Receives notification of the start of an element. * * @param uri the element tag * @param localName the local name * @param qName the qualified name * @param attributes the attributes */ @Override public void startElement( final String uri, final String localName, final String qName, final Attributes attributes) { //Preconditions assert qName != null : "qName must not be null"; assert !qName.isEmpty() : "qName must not be empty"; if (IS_DEBUG_LOGGING_ENABLED) { LOGGER.debug("startElement qName: " + qName); } stringBuilder.setLength(0); } /** Receive notification of character data inside an element. * * @param characters the characters * @param start the start position in the character array * @param length the length of the character string */ @Override public void characters(final char[] characters, final int start, final int length) { //LOGGER.debug("characters, start: " + start + ", length: " + length); final int end = start + length; for (int i = start; i < end; i++) { stringBuilder.append(characters[i]); } } /** Receives notification of the end of an element. * * @param uri the element tag * @param localName the local name * @param qName the qualified name */ @Override public void endElement( final String uri, final String localName, final String qName) { //Preconditions assert qName != null : "qName must not be null"; assert !qName.isEmpty() : "qName must not be empty"; assert nodeAccess != null : "nodeAccess must not be null"; if (IS_DEBUG_LOGGING_ENABLED) { LOGGER.debug("endElement qName: " + qName); LOGGER.debug("stringBuilder: " + stringBuilder.toString()); } if (qName.equals("id") && stringBuilder.length() > 0) { id = new URIImpl(stringBuilder.toString()); } else if (qName.equals("name")) { typeName = stringBuilder.toString(); } else if (qName.equals("inherited-node-type-name") && stringBuilder.length() > 0) { inheritedNodeTypeNames.add(stringBuilder.toString()); } else if (qName.equals("role-type-name") && stringBuilder.length() > 0) { roleTypeNames.add(stringBuilder.toString()); } else if (qName.equals("mission")) { missionDescription = stringBuilder.toString(); } else if (qName.equals("node-type")) { NodeType nodeType = null; final List<NodeType> existingNodeTypes = nodeAccess.findNodeTypes(typeName); if (existingNodeTypes.size() == 1) { nodeType = existingNodeTypes.get(0); } if (existingNodeTypes.size() > 1) { // corrupt store, remove the multiple existing node type objects having the current node type name for (final NodeType existingNodeType : existingNodeTypes) { LOGGER.warn("removing unexpected multiple existing node types for " + typeName); rdfEntityManager.remove(existingNodeType); } } if (nodeType == null) { nodeType = new NodeType(); } else { nodeType.clearInheritedNodeTypes(); nodeType.clearRoleTypes(); } if (missionDescription != null && !missionDescription.isEmpty()) { nodeType.setMissionDescription(missionDescription); } if (typeName != null && !typeName.isEmpty()) { nodeType.setTypeName(typeName); } for (final String roleTypeName : roleTypeNames) { final RoleType roleType = nodeAccess.findRoleType(roleTypeName); if (roleType == null) { throw new TexaiException("role type not found: " + roleTypeName); } else { nodeType.addRoleType(roleType); } } if (IS_DEBUG_LOGGING_ENABLED) { LOGGER.debug("\n" + nodeType.toXML()); } if (id != null) { rdfEntityManager.setIdFor(nodeType, id); } rdfEntityManager.persist(nodeType); LOGGER.info("persisted: " + nodeType.toString() + " id: " + nodeType.getId()); assert isLoadedCorrectly(nodeType); ids.add(nodeType.getId()); if (!inheritedNodeTypeNames.isEmpty()) { inheritedNodeTypeDictionary.put(nodeType.getId(), Collections.unmodifiableSet(inheritedNodeTypeNames)); } resetState(); } stringBuilder.setLength(0); } } /** Resets the parsing state for the next role type definition. */ private void resetState() { id = null; typeName = null; inheritedNodeTypeNames = new HashSet<>(); roleTypeNames = new HashSet<>(); missionDescription = null; } /** Verifies that the given node type has been persisted correctly. * * @param nodeType the given node type * @return true if no assertion errors occur. */ private boolean isLoadedCorrectly(final NodeType nodeType) { //Preconditions assert nodeAccess != null : "nodeAccess must not be null"; final List<NodeType> existingNodeTypes = nodeAccess.findNodeTypes(nodeType.getTypeName()); assert existingNodeTypes.size() == 1; if (IS_DEBUG_LOGGING_ENABLED) { LOGGER.debug("persisted\n" + nodeType.toXML()); LOGGER.debug("loaded\n" + existingNodeTypes.get(0).toXML()); } return true; } /** Resolves inherited node type names. */ private void resolveInheritedNodeTypeNames() { //Preconditions assert nodeAccess != null : "nodeAccess must not be null"; // resolve the inherited node type names, which might have been forward references for (final Entry<URI, Set<String>> entry : inheritedNodeTypeDictionary.entrySet()) { final URI id1 = entry.getKey(); final NodeType loadedNodeType = rdfEntityManager.find(NodeType.class, id1); assert loadedNodeType != null; final Set<String> inheritedNodeTypeNames1 = entry.getValue(); assert !inheritedNodeTypeNames1.isEmpty(); for (final String inheritedNodeTypeName : inheritedNodeTypeNames1) { final NodeType inheritedNodeType = nodeAccess.findNodeType(inheritedNodeTypeName); if (inheritedNodeType == null) { throw new TexaiException("inherited node type not found: " + inheritedNodeTypeName); } loadedNodeType.addInheritedNodeType(inheritedNodeType); } rdfEntityManager.persist(loadedNodeType); } } /** Displays the loaded role types. */ private void displayLoadedNodeTypes() { for (final URI id1 : ids) { final NodeType loadedNodeType = rdfEntityManager.find(NodeType.class, id1); LOGGER.info("\n" + loadedNodeType.toXML(2)); } } /** Finalizes this application. */ public void finalization() { LOGGER.info("NodeTypeInitializer completed"); } /** Executes this application. * * @param args the command line arguments (not used) */ public static void main(final String[] args) { CacheInitializer.initializeCaches(); DistributedRepositoryManager.clearNamedRepository("NodeRoleTypes"); Logger.getLogger(RDFEntityPersister.class).setLevel(Level.WARN); // initialize the role types final RoleTypeInitializer roleTypeInitializer = new RoleTypeInitializer(); final RDFEntityManager rdfEntityManager = new RDFEntityManager(); roleTypeInitializer.initialize(rdfEntityManager); roleTypeInitializer.process("data/role-types.xml"); roleTypeInitializer.finalization(); // initialize the node types final NodeTypeInitializer nodeTypeInitializer = new NodeTypeInitializer(); nodeTypeInitializer.initialize(rdfEntityManager); nodeTypeInitializer.process("data/node-types.xml"); nodeTypeInitializer.finalization(); rdfEntityManager.close(); DistributedRepositoryManager.shutDown(); CacheManager.getInstance().shutdown(); } }