com.git.original.common.config.XMLFileConfigDocument.java Source code

Java tutorial

Introduction

Here is the source code for com.git.original.common.config.XMLFileConfigDocument.java

Source

/**
 * @(#)XMLFileConfigDocument.java, 2013-2-24.
 * 
 * Copyright 2013 Netease, Inc. All rights reserved.
 * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.git.original.common.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * XML?
 * <p>
 * ?: XML????Element, ??Attribute
 * 
 * 
 * @author linaoxiang
 */
public class XMLFileConfigDocument extends BaseConfigDocument {

    /**  */
    private static final Logger LOG = LoggerFactory.getLogger(XMLFileConfigDocument.class);

    /**
     * ?
     */
    protected String configFilePath = null;

    /**
     * ???
     * <p>
     * ??????
     */
    private String configFileDigest = StringUtils.EMPTY;

    /**
     * ???
     */
    private String dbVersion = null;

    /**
     * ???(?, ???)
     */
    private boolean ignoreDb = false;

    /**
     * ??????,?()
     */
    private Long scanMillis = null;

    /**
     * 
     * 
     * @param confPath
     *            ??, ?HMAIL_HOME
     */
    public XMLFileConfigDocument(String confPath) {
        super();
        this.configFilePath = confPath;
    }

    /**
     * ?
     * 
     * @param rootNode
     *            ?
     */
    XMLFileConfigDocument(ConfigNode rootNode) {
        super(rootNode);
    }

    @Override
    protected ConfigNode doLoad() throws Exception {
        return doLoad(this.configFilePath);
    }

    protected ConfigNode doLoad(String configFilePath) throws Exception {
        InputStream in = null;
        try {
            File confFile = new File(configFilePath);
            if (!confFile.isAbsolute()) {
                confFile = new File(this.getHmailHome(), configFilePath);
            }
            if (!confFile.exists()) {
                throw new FileNotFoundException("path:" + confFile);
            }

            String currentDigest = confFile.lastModified() + ";" + confFile.length();
            if (currentDigest.equals(this.configFileDigest)) {
                // ??, ?
                return null;
            }

            in = new FileInputStream(confFile);

            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
            NodeBuilder nb = new NodeBuilder();
            parser.parse(in, nb);
            ConfigNode result = nb.getRootNode();

            if (result != null) {
                try {
                    this.dbVersion = result.getAttribute(CONF_DOC_ATTRIBUTE_DB_VERSION);
                } catch (Exception ex) {
                    this.dbVersion = null;
                }

                try {
                    String str = result.getAttribute(CONF_DOC_ATTRIBUTE_IGNORE_DB);
                    if (StringUtils.isEmpty(str)) {
                        this.ignoreDb = false;
                    } else {
                        this.ignoreDb = Boolean.parseBoolean(str);
                    }
                } catch (Exception ex) {
                    this.ignoreDb = false;
                }

                try {
                    String tmp = result.getAttribute(CONF_DOC_ATTRIBUTE_SCAN_PERIOD);
                    if (StringUtils.isEmpty(tmp)) {
                        this.scanMillis = null;
                    } else {
                        this.scanMillis = ConfigNode.textToNumericMillis(tmp.trim());
                    }
                } catch (Exception ex) {
                    this.scanMillis = null;
                }
            }

            LOG.debug("Load config from local dir success, file:{}", configFilePath);
            return result;
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // ignore exception
                }
            }
        }
    }

    @Override
    public String toString() {
        return "[FileConfig]" + configFilePath;
    }

    /**
     * XML??
     * 
     * @param elem
     *            XML
     * @return ?
     */
    static ConfigNode convertElement(Element elem) {
        if (elem == null) {
            return null;
        }

        ConfigNode cn = new ConfigNode(elem.getTagName(), null);

        NamedNodeMap attrNodeMap = elem.getAttributes();
        if (attrNodeMap != null) {
            for (int i = 0; i < attrNodeMap.getLength(); i++) {
                Node node = attrNodeMap.item(i);
                cn.addAttribute(node.getNodeName(), node.getNodeValue());
            }
        }

        NodeList nodeList = elem.getChildNodes();
        if (nodeList != null && nodeList.getLength() > 0) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);

                switch (node.getNodeType()) {
                case Node.ATTRIBUTE_NODE:
                    cn.addAttribute(node.getNodeName(), node.getNodeValue());
                    break;
                case Node.ELEMENT_NODE:
                    ConfigNode child = convertElement((Element) node);
                    cn.addChild(child);
                    break;
                case Node.TEXT_NODE:
                    cn.value = node.getNodeValue();
                    break;
                default:
                    continue;
                }
            }
        }

        return cn;
    }

    /**
     * ??XML
     * 
     * @param cn
     *            ?
     * @return XML
     */
    @SuppressWarnings("unchecked")
    static Element convertConfigNode(Document doc, ConfigNode cn) {
        Element elem = doc.createElement(cn.getName());

        Map<String, String> attrMap = cn.attributes();
        if (attrMap != null) {
            for (Entry<String, String> entry : attrMap.entrySet()) {
                elem.setAttribute(entry.getKey(), entry.getValue());
            }
        }

        if (cn.hasChildren()) {
            for (Object child : cn.getAllChildren()) {
                if (child instanceof List) {
                    List<ConfigNode> cnList = (List<ConfigNode>) child;
                    for (ConfigNode node : cnList) {
                        elem.appendChild(convertConfigNode(doc, node));
                    }
                } else {
                    elem.appendChild(convertConfigNode(doc, (ConfigNode) child));
                }
            }

        } else if (cn.value != null) {
            elem.setTextContent(cn.value.toString());
        }

        return elem;
    }

    @Override
    public String getDbVersion() {
        return this.dbVersion;
    }

    @Override
    public boolean ignoreDb() {
        return this.ignoreDb;
    }

    @Override
    public Long getScanMillis() {
        return this.scanMillis;
    }

    /** {@link ConfigNode} */
    private static class NodeBuilder extends DefaultHandler {
        /** node */
        private ConfigNode rootNode = null;

        /** ?node */
        private ConfigNode currentNode = null;

        /** node(?) */
        private Deque<ConfigNode> cnDeque = new ArrayDeque<ConfigNode>();

        /** ? */
        private StringBuilder tmpValue = new StringBuilder();

        /**
         * @return the rootNode
         */
        public ConfigNode getRootNode() {
            return rootNode;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.xml.sax.helpers.DefaultHandler#startDocument()
         */
        @Override
        public void startDocument() throws SAXException {
            cnDeque.clear();
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.xml.sax.helpers.DefaultHandler#endDocument()
         */
        @Override
        public void endDocument() throws SAXException {
            if (!cnDeque.isEmpty()) {
                throw new SAXException("cnDeque is not empty");
            }
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
         * java.lang.String, java.lang.String, org.xml.sax.Attributes)
         */
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes)
                throws SAXException {
            this.currentNode = new ConfigNode(qName, null);

            if (attributes != null && attributes.getLength() > 0) {
                for (int i = 0; i < attributes.getLength(); i++) {
                    currentNode.addAttribute(attributes.getQName(i), attributes.getValue(i));
                }
            }

            this.cnDeque.push(currentNode);
            this.tmpValue.setLength(0);
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
         * java.lang.String, java.lang.String)
         */
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (this.currentNode != this.cnDeque.pop()) {
                // ?currentNode?
                throw new SAXException("current node is not the first one of the cnDeque.");
            }

            if (tmpValue.length() > 0) {
                this.currentNode.value = tmpValue.toString();
            }

            if (!this.cnDeque.isEmpty()) {
                this.cnDeque.peek().addChild(currentNode);
            } else {
                this.rootNode = this.currentNode;
            }
            this.currentNode = this.cnDeque.peek();
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
         */
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (!this.cnDeque.isEmpty()) {
                tmpValue.append(ch, start, length);
            }
        }

    }

}