All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.wasdev.wlp.maven.plugins.ServerConfigDocument Maven / Gradle / Ivy

/**
 * (C) Copyright IBM Corporation 2017.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.wasdev.wlp.maven.plugins;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.maven.plugin.logging.Log;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class ServerConfigDocument {

    private static ServerConfigDocument instance;

    private static Log log;

    private static DocumentBuilder docBuilder;

    private static File configDirectory;
    private static File serverXMLFile;

    private static Set locations;
    private static Properties props;

    private static final XPathExpression XPATH_SERVER_APPLICATION;
    private static final XPathExpression XPATH_SERVER_WEB_APPLICATION;
    private static final XPathExpression XPATH_SERVER_ENTERPRISE_APPLICATION;
    private static final XPathExpression XPATH_SERVER_INCLUDE;
    private static final XPathExpression XPATH_SERVER_VARIABLE;

    static {
        try {
            XPath xPath = XPathFactory.newInstance().newXPath();
            XPATH_SERVER_APPLICATION = xPath.compile("/server/application");
            XPATH_SERVER_WEB_APPLICATION = xPath.compile("/server/webApplication");
            XPATH_SERVER_ENTERPRISE_APPLICATION = xPath.compile("/server/enterpriseApplication");
            XPATH_SERVER_INCLUDE = xPath.compile("/server/include");
            XPATH_SERVER_VARIABLE = xPath.compile("/server/variable");
        } catch (XPathExpressionException ex) {
            // These XPath expressions should all compile statically.
            // Compilation failures mean the expressions are not syntactically
            // correct
            throw new RuntimeException(ex);
        }
    }

    public Set getLocations() {
        return locations;
    }

    public static Properties getProperties() {
        return props;
    }

    private static File getServerXML() {
        return serverXMLFile;
    }

    public ServerConfigDocument(Log log, File serverXML, File configDir, File bootstrapFile,
            Map bootstrapProp, File serverEnvFile) {
        initializeAppsLocation(log, serverXML, configDir, bootstrapFile, bootstrapProp, serverEnvFile);
    }

    private static DocumentBuilder getDocumentBuilder() {
        if (docBuilder == null) {
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            docBuilderFactory.setIgnoringComments(true);
            docBuilderFactory.setCoalescing(true);
            docBuilderFactory.setIgnoringElementContentWhitespace(true);
            docBuilderFactory.setValidating(false);
            try {
                docBuilder = docBuilderFactory.newDocumentBuilder();
            } catch (ParserConfigurationException e) {
                // fail catastrophically if we can't create a document builder
                throw new RuntimeException(e);
            }
        }
        return docBuilder;
    }

    public static ServerConfigDocument getInstance(Log log, File serverXML, File configDir, File bootstrapFile,
            Map bootstrapProp, File serverEnvFile) throws IOException {
        // Initialize if instance is not created yet, or source server xml file
        // location has been changed.
        if (instance == null || !serverXML.getCanonicalPath().equals(getServerXML().getCanonicalPath())) {
            instance = new ServerConfigDocument(log, serverXML, configDir, bootstrapFile, bootstrapProp, serverEnvFile);
        }
        return instance;
    }

    private static void initializeAppsLocation(Log log, File serverXML, File configDir, File bootstrapFile,
            Map bootstrapProp, File serverEnvFile) {
        try {
            ServerConfigDocument.log = log;
            serverXMLFile = serverXML;
            configDirectory = configDir;

            locations = new HashSet();
            props = new Properties();

            Document doc = parseDocument(new FileInputStream(serverXMLFile));

            // Server variable precedence in ascending order if defined in
            // multiple locations.
            //
            // 1. variables from 'server.env'
            // 2. variables from 'bootstrap.properties'
            // 3. variables defined in  files
            // 4. variables from configDropins/defaults/
            // 5. variables defined in server.xml
            // e.g. 
            // 6. variables from configDropins/overrides/

            Properties fProps;
            // get variables from server.env
            File cfgDirFile = getFileFromConfigDirectory("server.env");

            if (cfgDirFile != null) {
                fProps = parseProperties(new FileInputStream(cfgDirFile));
                props.putAll(fProps);
            } else if (serverEnvFile.exists()) {
                fProps = parseProperties(new FileInputStream(serverEnvFile));
                props.putAll(fProps);
            }

            cfgDirFile = getFileFromConfigDirectory("bootstrap.properties");

            if (cfgDirFile != null) {
                fProps = parseProperties(new FileInputStream(cfgDirFile));
                props.putAll(fProps);
            } else if (bootstrapProp != null && !bootstrapProp.isEmpty()) {
                 while (bootstrapProp.values().remove(null))
                     ;
                props.putAll(bootstrapProp);
            } else if (bootstrapFile.exists()) {
                fProps = parseProperties(new FileInputStream(bootstrapFile));
                props.putAll(fProps);
            }

            parseIncludeVariables(doc);
            parseConfigDropinsDirVariables("defaults");
            parseVariables(doc);
            parseConfigDropinsDirVariables("overrides");

            parseApplication(doc, XPATH_SERVER_APPLICATION);
            parseApplication(doc, XPATH_SERVER_WEB_APPLICATION);
            parseApplication(doc, XPATH_SERVER_ENTERPRISE_APPLICATION);
            parseInclude(doc);
            parseConfigDropinsDir();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void parseApplication(Document doc, XPathExpression expression) throws XPathExpressionException {

        NodeList nodeList = (NodeList) expression.evaluate(doc, XPathConstants.NODESET);

        for (int i = 0; i < nodeList.getLength(); i++) {
            String nodeValue = nodeList.item(i).getAttributes().getNamedItem("location").getNodeValue();

            // add unique values only
            if (!nodeValue.isEmpty()) {
                String resolved = getResolvedVariable(nodeValue);
                if (!locations.contains(resolved)) {
                    locations.add(resolved);
                }
            }
        }
    }

    private static void parseInclude(Document doc) throws XPathExpressionException, IOException, SAXException {
        // parse include document in source server xml
        NodeList nodeList = (NodeList) XPATH_SERVER_INCLUDE.evaluate(doc, XPathConstants.NODESET);

        for (int i = 0; i < nodeList.getLength(); i++) {
            String nodeValue = nodeList.item(i).getAttributes().getNamedItem("location").getNodeValue();

            if (!nodeValue.isEmpty()) {
                Document docIncl = getIncludeDoc(nodeValue);

                if (docIncl != null) {
                    parseApplication(docIncl, XPATH_SERVER_APPLICATION);
                    parseApplication(docIncl, XPATH_SERVER_WEB_APPLICATION);
                    parseApplication(docIncl, XPATH_SERVER_ENTERPRISE_APPLICATION);
                    // handle nested include elements
                    parseInclude(docIncl);
                }
            }
        }
    }

    private static void parseConfigDropinsDir() throws XPathExpressionException, IOException, SAXException {
        File configDropins = getConfigDropinsDir();

        if (configDropins != null && configDropins.exists()) {
            File overrides = new File(configDropins, "overrides");
            if (overrides.exists()) {
                parseDropinsFiles(overrides.listFiles());
            }

            File defaults = new File(configDropins, "defaults");
            if (defaults.exists()) {
                parseDropinsFiles(defaults.listFiles());
            }
        }
    }

    private static void parseDropinsFiles(File[] files) throws XPathExpressionException, IOException, SAXException {
        for (int i = 0; i < files.length; i++) {
            if (files[i].isFile()) {
                parseDropinsFile(files[i]);
            }
        }
    }

    private static Document parseDropinsXMLFile(File file) throws FileNotFoundException, IOException {
        try (FileInputStream is = new FileInputStream(file)) {
            return parseDocument(is);
        } catch (SAXException ex) {
            // If the file was not valid XML, assume it was some other non XML
            // file in dropins.
            log.info("Skipping parsing " + file.getAbsolutePath() + " because it was not recognized as XML.");
            return null;
        }
    }

    private static void parseDropinsFile(File file) throws IOException, XPathExpressionException, SAXException {
        // get input XML Document
        Document doc = parseDropinsXMLFile(file);
        if (doc != null) {
            parseApplication(doc, XPATH_SERVER_APPLICATION);
            parseApplication(doc, XPATH_SERVER_WEB_APPLICATION);
            parseApplication(doc, XPATH_SERVER_ENTERPRISE_APPLICATION);
            parseInclude(doc);
        }
    }

    private static Document getIncludeDoc(String loc) throws IOException, SAXException {

        Document doc = null;
        File locFile = null;

        if (loc.startsWith("http:") || loc.startsWith("https:")) {
            if (isValidURL(loc)) {
                URL url = new URL(loc);
                URLConnection connection = url.openConnection();
                doc = parseDocument(connection.getInputStream());
            }
        } else if (loc.startsWith("file:")) {
            if (isValidURL(loc)) {
                locFile = new File(loc);
                if (locFile.exists()) {
                    InputStream inputStream = new FileInputStream(locFile.getCanonicalPath());
                    doc = parseDocument(inputStream);
                }
            }
        } else if (loc.startsWith("ftp:")) {
            // TODO handle ftp protocol
        } else {
            locFile = new File(loc);

            // check if absolute file
            if (locFile.isAbsolute()) {
                InputStream inputStream = new FileInputStream(locFile.getCanonicalPath());
                doc = parseDocument(inputStream);
            } else {
                // check configDirectory first if exists
                if (configDirectory != null && configDirectory.exists()) {
                    locFile = new File(configDirectory, loc);
                }

                if (locFile == null || !locFile.exists()) {
                    locFile = new File(getServerXML().getParentFile(), loc);
                }

                if (locFile != null && locFile.exists()) {
                    InputStream inputStream = new FileInputStream(locFile.getCanonicalPath());
                    doc = parseDocument(inputStream);
                }
            }
        }
        return doc;
    }

    private static Document parseDocument(InputStream in) throws SAXException, IOException {
        try (InputStream ins = in) { // ins will be auto-closed
            return getDocumentBuilder().parse(ins);
        }
    }

    private static Properties parseProperties(InputStream ins) throws Exception {
        Properties props = null;
        try {
            props = new Properties();
            props.load(ins);
        } catch (Exception e) {
            throw e;
        } finally {
            if (ins != null) {
                ins.close();
            }
        }
        return props;
    }

    private static boolean isValidURL(String url) {
        try {
            URL testURL = new URL(url);
            testURL.toURI();
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    private static String getResolvedVariable(String nodeValue) {
        final String VARIABLE_NAME_PATTERN = "\\$\\{(.*?)\\}";

        String resolved = nodeValue;
        Pattern varNamePattern = Pattern.compile(VARIABLE_NAME_PATTERN);
        Matcher varNameMatcher = varNamePattern.matcher(nodeValue);

        while (varNameMatcher.find()) {
            String variable = getProperties().getProperty(varNameMatcher.group(1));

            if (variable != null && !variable.isEmpty()) {
                resolved = resolved.replaceAll("\\$\\{" + varNameMatcher.group(1) + "\\}", variable);
            }
        }
        return resolved;
    }

    private static void parseVariables(Document doc) throws XPathExpressionException {
        // parse input document
        NodeList nodeList = (NodeList) XPATH_SERVER_VARIABLE.evaluate(doc, XPathConstants.NODESET);

        for (int i = 0; i < nodeList.getLength(); i++) {
            String varName = nodeList.item(i).getAttributes().getNamedItem("name").getNodeValue();
            String varValue = nodeList.item(i).getAttributes().getNamedItem("value").getNodeValue();

            // add unique values only
            if (!varName.isEmpty() && !varValue.isEmpty()) {
                props.put(varName, varValue);
            }
        }
    }

    private static void parseIncludeVariables(Document doc) throws XPathExpressionException, IOException, SAXException {
        // parse include document in source server xml
        NodeList nodeList = (NodeList) XPATH_SERVER_INCLUDE.evaluate(doc, XPathConstants.NODESET);

        for (int i = 0; i < nodeList.getLength(); i++) {
            String nodeValue = nodeList.item(i).getAttributes().getNamedItem("location").getNodeValue();

            if (!nodeValue.isEmpty()) {
                Document docIncl = getIncludeDoc(nodeValue);

                if (docIncl != null) {
                    parseVariables(docIncl);
                    // handle nested include elements
                    parseIncludeVariables(docIncl);
                }
            }
        }
    }

    private static File getConfigDropinsDir() {
        File configDropins = null;
        if (configDropins == null || !configDropins.exists()) {
            configDropins = new File(getServerXML().getParent(), "configDropins");
        }
        return configDropins;
    }

    private static void parseConfigDropinsDirVariables(String inDir)
            throws XPathExpressionException, SAXException, IOException {
        File configDropins = getConfigDropinsDir();

        if (configDropins != null && configDropins.exists()) {
            File dir = new File(configDropins, inDir);

            if (dir.exists()) {
                File[] cfgFiles = dir.listFiles();

                for (int i = 0; i < cfgFiles.length; i++) {
                    if (cfgFiles[i].isFile()) {
                        parseDropinsFilesVariables(cfgFiles[i]);
                    }
                }
            }
        }
    }

    private static void parseDropinsFilesVariables(File file)
            throws SAXException, IOException, XPathExpressionException {
        // get input XML Document
        Document doc = parseDropinsXMLFile(file);
        if (doc != null) {
            parseVariables(doc);
            parseIncludeVariables(doc);
        }
    }

    /*
     * Get the file from configDrectory if it exists; otherwise return def only
     * if it exists, or null if not
     */
    private static File getFileFromConfigDirectory(String file, File def) {
        File f = new File(configDirectory, file);
        if (configDirectory != null && f.exists()) {
            return f;
        }
        if (def != null && def.exists()) {
            return def;
        }
        return null;
    }

    private static File getFileFromConfigDirectory(String file) {
        return getFileFromConfigDirectory(file, null);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy