com.mnxfst.testing.server.cfg.PTestServerConfigurationParser.java Source code

Java tutorial

Introduction

Here is the source code for com.mnxfst.testing.server.cfg.PTestServerConfigurationParser.java

Source

/*
 *  The ptest framework provides you with a performance test utility
 *  Copyright (C) 2012  Christian Kreutzfeldt <mnxfst@googlemail.com>
 *
 *  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, see <http://www.gnu.org/licenses/>.
 *
 */

package com.mnxfst.testing.server.cfg;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

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.http.message.BasicNameValuePair;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.mnxfst.testing.server.exception.ServerConfigurationFailedException;

/**
 * Parses the configuration file of a ptest server instance
 * @author mnxfst
 * @since 21.03.2012
 */
public class PTestServerConfigurationParser {

    private static final String XML_ROOT = "ptest-server";

    private static final String XPATH_HOSTNAME = "/ptest-server/hostname";
    private static final String XPATH_PORT = "/ptest-server/port";
    private static final String XPATH_SOCKET_POOL_SIZE = "/ptest-server/socketPoolSize";
    private static final String XPATH_ALL_HANDLER_SETTINGS = "/ptest-server/contextHandlers/*";
    private static final String XPATH_HANDLER_PATH = "path";
    private static final String XPATH_HANDLER_CLASS = "class";

    protected final XPathExpression xpathExpressionHostname;
    protected final XPathExpression xpathExpressionPort;
    protected final XPathExpression xpathExpressionSocketPoolSize;
    protected final XPathExpression xpathExpressionAllHandlerSettings;
    protected final XPathExpression xpathExpressionHandlerPath;
    protected final XPathExpression xpathExpressionHandlerClass;

    /**
     * Initializes the instance
     */
    public PTestServerConfigurationParser() {
        XPath xpath = XPathFactory.newInstance().newXPath();
        try {
            xpathExpressionHostname = xpath.compile(XPATH_HOSTNAME);
            xpathExpressionPort = xpath.compile(XPATH_PORT);
            xpathExpressionSocketPoolSize = xpath.compile(XPATH_SOCKET_POOL_SIZE);
            xpathExpressionAllHandlerSettings = xpath.compile(XPATH_ALL_HANDLER_SETTINGS);
            xpathExpressionHandlerClass = xpath.compile(XPATH_HANDLER_CLASS);
            xpathExpressionHandlerPath = xpath.compile(XPATH_HANDLER_PATH);
        } catch (XPathExpressionException e) {
            throw new RuntimeException(
                    "Failed to create xpath expressions from preconfigured pattern. Error: " + e.getMessage());
        }
    }

    /**
     * Parses the contents of the referenced configuration file into an object
     * @param filename
     * @return
     * @throws ServerConfigurationFailedException
     */
    public PTestServerConfiguration parseServerConfiguration(String filename)
            throws ServerConfigurationFailedException {

        if (filename == null || filename.isEmpty())
            throw new ServerConfigurationFailedException("Missing required config filename");

        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        try {
            FileInputStream fIn = new FileInputStream(filename);
            int c = 0;
            while ((c = fIn.read()) != -1)
                bOut.write(c);
        } catch (FileNotFoundException e) {
            throw new ServerConfigurationFailedException("Referenced configuration file does not exist");
        } catch (IOException e) {
            throw new ServerConfigurationFailedException(
                    "Failed to read contents from configuration file '" + filename + "'. Error: " + e.getMessage());
        }

        return parseServerConfiguration(bOut.toByteArray());
    }

    /**
     * Parses the contents of the provided configuration file contents into an object
     * @param configFileContents
     * @return
     * @throws ServerConfigurationFailedException
     */
    public PTestServerConfiguration parseServerConfiguration(byte[] configFileContents)
            throws ServerConfigurationFailedException {

        if (configFileContents == null || configFileContents.length < 1)
            throw new ServerConfigurationFailedException("Missing required config file contents");

        try {
            // parse the provided byte array into a valid document and check resul 
            Document cfgDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder()
                    .parse(new ByteArrayInputStream(configFileContents));
            if (cfgDocument == null)
                throw new ServerConfigurationFailedException(
                        "Provided contents could not be parsed into a valid document");
            Node rootNode = cfgDocument.getFirstChild();
            if (rootNode == null)
                throw new ServerConfigurationFailedException(
                        "The provided document does not have a valid root node");
            if (rootNode.getNodeName() == null || rootNode.getNodeName().isEmpty())
                throw new ServerConfigurationFailedException(
                        "The root node of the provided document does not have a valid name");
            if (!rootNode.getNodeName().equalsIgnoreCase(XML_ROOT))
                throw new ServerConfigurationFailedException(
                        "The provided document does not contain a valid configuration since it misses the required root node");

            // extract hostname, port and socket pool size from configuration
            String hostname = evaluateString(xpathExpressionHostname, cfgDocument);
            if (hostname == null || hostname.trim().isEmpty())
                throw new ServerConfigurationFailedException("Missing the required settings for 'hostname'");

            Integer port = evaluateInteger(xpathExpressionPort, cfgDocument);
            if (port == null)
                throw new ServerConfigurationFailedException("Missing the required settings for 'port'");

            Integer socketPoolSize = evaluateInteger(xpathExpressionSocketPoolSize, cfgDocument);
            if (socketPoolSize == null)
                throw new ServerConfigurationFailedException("Missing the required settings for 'socketPoolSize'");

            // extract handler configurations and validate for emptiness and null
            NodeList handlerCfgs = evaluateNodeList(xpathExpressionAllHandlerSettings, cfgDocument);
            if (handlerCfgs == null || handlerCfgs.getLength() < 1)
                throw new ServerConfigurationFailedException(
                        "No context handlers defined. Please provide at least one context");

            PTestServerConfiguration serverConfiguration = new PTestServerConfiguration(hostname, port,
                    socketPoolSize);

            // step through handler configurations and extract path and class
            for (int i = 0; i < handlerCfgs.getLength(); i++) {
                Node handlerCfgNode = handlerCfgs.item(i);
                String ctxPath = evaluateString(xpathExpressionHandlerPath, handlerCfgNode);
                String ctxClass = evaluateString(xpathExpressionHandlerClass, handlerCfgNode);

                // validate path and class - invalid values provided for any of them leads to an exception
                if (ctxPath == null || ctxPath.trim().isEmpty())
                    throw new ServerConfigurationFailedException(
                            "Invalid context path found in configuration file contents");
                if (ctxClass == null || ctxClass.trim().isEmpty())
                    throw new ServerConfigurationFailedException(
                            "Invalid handler class found in configuration file contents");

                // TODO additional configuration            
                serverConfiguration.addContextHandlerSetting(ctxPath, new BasicNameValuePair("class", ctxClass));
            }

            return serverConfiguration;
        } catch (IOException e) {
            throw new ServerConfigurationFailedException(
                    "Failed to read contents from provided configuration. Error: " + e.getMessage());
        } catch (SAXException e) {
            throw new ServerConfigurationFailedException(
                    "Failed to parse contents from provided configuration. Error: " + e.getMessage());
        } catch (ParserConfigurationException e) {
            throw new ServerConfigurationFailedException(
                    "Failed to parse contents from provided configuration. Error: " + e.getMessage());
        } catch (XPathExpressionException e) {
            throw new ServerConfigurationFailedException(
                    "Failed to evaluate xpath expression on configuration. Error: " + e.getMessage());
        }
    }

    ///////////////////////////////////////////// protected methods /////////////////////////////////////////////

    /**
     * Evaluates the given expression on the referenced document into a result object of type string
     * @param expression
     * @param document
     * @return
     * @throws XPathExpressionException
     */
    protected String evaluateString(XPathExpression expression, Object document) throws XPathExpressionException {
        if (expression == null)
            throw new XPathExpressionException("Null is not a valid expression");
        if (document == null)
            throw new XPathExpressionException("An xpath expression must not be applied to a NULL document");

        return (String) expression.evaluate(document, XPathConstants.STRING);
    }

    /**
     * Evaluates the given expression on the referenced document and returns a result object of type string
     * @param expression
     * @param document
     * @return
     * @throws XPathExpressionException
     */
    protected Integer evaluateInteger(XPathExpression expression, Object document) throws XPathExpressionException {

        String value = evaluateString(expression, document);
        if (value != null && !value.isEmpty()) {
            try {
                return Integer.parseInt(value);
            } catch (NumberFormatException e) {
                throw new XPathExpressionException(
                        "Failed to parse numerical value from evaluate string '" + value + "'");
            }
        }
        return null;
    }

    /**
     * Evaluates the given expression on the referenced document and returns a result object of type {@link NodeList} 
     * @param expression
     * @param document
     * @return
     * @throws XPathExpressionException
     */
    protected NodeList evaluateNodeList(XPathExpression expression, Object document)
            throws XPathExpressionException {
        if (expression == null)
            throw new XPathExpressionException("Null is not a valid expression");
        if (document == null)
            throw new XPathExpressionException("An xpath expression must not be applied to a NULL document");

        return (NodeList) expression.evaluate(document, XPathConstants.NODESET);
    }

}