package org.radargun.config; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.radargun.Properties; import org.radargun.ShutDownHook; import org.radargun.logging.Log; import org.radargun.logging.LogFactory; import org.radargun.reporting.ReporterHelper; import org.radargun.stages.ScenarioCleanupStage; import org.radargun.stages.ScenarioDestroyStage; import org.radargun.stages.ScenarioInitStage; import org.radargun.stages.control.RepeatBeginStage; import org.radargun.stages.control.RepeatContinueStage; import org.radargun.stages.control.RepeatEndStage; import org.radargun.utils.Utils; import org.w3c.dom.*; /** * @author Mircea Markus <Mircea.Markus@jboss.com> */ public class DomConfigParser extends ConfigParser implements ConfigSchema { private static Log log = LogFactory.getLog(DomConfigParser.class); private static final String ATTR_XMLNS = "xmlns"; public MasterConfig parseConfig(String config) throws Exception { Document document; try { document = createDocumentBuilder().parse(config); } catch (Exception e) { throw new IllegalStateException(e); } Element root = document.getDocumentElement(); assertName(ELEMENT_BENCHMARK, root); NodeList childNodes = root.getChildNodes(); int index = 0; index = nextElement(childNodes, index); MasterConfig masterConfig; if (ELEMENT_MASTER.equals(childNodes.item(index).getLocalName())) { masterConfig = parseMaster((Element) childNodes.item(index)); index = nextElement(childNodes, index + 1); } else { masterConfig = new MasterConfig(0, null); } Map<String, byte[]> configurations = new HashMap<>(); Utils.loadConfigFile(config, configurations); masterConfig.setMasterConfigBytes(configurations.get(config)); parseClusters(masterConfig, (Element) childNodes.item(index)); index = nextElement(childNodes, index + 1); parseConfigurations(masterConfig, (Element) childNodes.item(index)); index = nextElement(childNodes, index + 1); Scenario scenario = new Scenario(); Map<String, Definition> initProperties = Collections.EMPTY_MAP; if (ELEMENT_INIT.equals(childNodes.item(index).getLocalName())) { initProperties = parseProperties(((Element) childNodes.item(index)), true); index = nextElement(childNodes, index + 1); } scenario.addStage(ScenarioInitStage.class, initProperties, null); Element scenarioElement; if (((Element) childNodes.item(index)).hasAttribute(ATTR_URL)) { String url = ((Element) childNodes.item(index)).getAttribute(ATTR_URL); scenarioElement = loadScenario(url); Utils.loadConfigFile(url, configurations); masterConfig.setScenarioBytes(configurations.get(url)); } else { scenarioElement = (Element) childNodes.item(index); } parseScenario(scenario, scenarioElement); masterConfig.setScenario(scenario); index = nextElement(childNodes, index + 1); Map<String, Definition> destroyProperties = Collections.EMPTY_MAP; Node elementDestroy = childNodes.item(index); if (elementDestroy != null && ELEMENT_DESTROY.equals(childNodes.item(index).getLocalName())) { destroyProperties = parseProperties((Element) childNodes.item(index), true); index = nextElement(childNodes, index + 1); } scenario.addStage(ScenarioDestroyStage.class, destroyProperties, null); Map<String, Definition> cleanupProperties = Collections.EMPTY_MAP; Node elementCleanup = childNodes.item(index); if (elementCleanup != null && ELEMENT_CLEANUP.equals(childNodes.item(index).getLocalName())) { cleanupProperties = parseProperties((Element) childNodes.item(index), true); index = nextElement(childNodes, index + 1); } scenario.addStage(ScenarioCleanupStage.class, cleanupProperties, null); Element reportElement = (Element) childNodes.item(index); if (reportElement != null && ELEMENT_REPORTS.equals(reportElement.getLocalName())) { parseReporting(masterConfig, reportElement); } return masterConfig; } protected DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); return documentBuilderFactory.newDocumentBuilder(); } /** * Parses scenario, which is loaded from file or URL * * @param scenarioUri path or url of the scenario file, or benchmark file which contains scenario * @return parsed scenario element * @throws ParserConfigurationException if DocumentBuilder cannot be created */ private Element loadScenario(String scenarioUri) throws ParserConfigurationException { Document document; try { document = createDocumentBuilder().parse(scenarioUri); } catch (Exception e) { throw new IllegalStateException("Could not parse imported scenario: " + scenarioUri, e); } Element root = document.getDocumentElement(); if (ScenarioSchemaGenerator.NAMESPACE.equals(root.getNamespaceURI()) && ELEMENT_SCENARIO.equals(root.getLocalName())) { return root; } else if (BenchmarkSchemaGenerator.NAMESPACE.equals(root.getNamespaceURI()) && ELEMENT_BENCHMARK.equals(root.getLocalName())) { NodeList scenarios = document.getElementsByTagNameNS("*", ELEMENT_SCENARIO); if (scenarios.getLength() <= 0) { throw new IllegalArgumentException("Did not found any scenarios in " + scenarioUri); } else if (scenarios.getLength() > 1) { throw new IllegalArgumentException("Found multiple scenarios in " + scenarioUri); } else { return (Element) scenarios.item(0); } } else { throw new IllegalStateException(String.format("Unexpected root element in %s: namespace=%s, local-name=%s", scenarioUri, root.getNamespaceURI(), root.getLocalName())); } } private int nextElement(NodeList nodeList, int start) { for (int i = start; i < nodeList.getLength(); ++i) { if (nodeList.item(i) instanceof Element) { return i; } } return -1; } private void assertName(String name, Element element) { if (!name.equals(element.getLocalName())) { throw new IllegalArgumentException("Found '" + element.getLocalName() + "', expected '" + name + "'"); } } private String getAttribute(Element element, String attribute) { String value = Evaluator.parseString(element.getAttribute(attribute)); if (value == null || value.isEmpty()) { throw new IllegalArgumentException("Element '" + attribute + "' must be defined."); } return value; } private String getAttribute(Element element, String attribute, String def) { String value = Evaluator.parseString(element.getAttribute(attribute)); if (value == null || value.isEmpty()) { return def; } return value; } private MasterConfig parseMaster(Element masterElement) { assertName(ELEMENT_MASTER, masterElement); String bindAddress = getAttribute(masterElement, ATTR_BIND_ADDRESS, "localhost"); String portString = getAttribute(masterElement, ATTR_PORT, "-1"); return new MasterConfig(Integer.parseInt(portString), bindAddress); } private void parseClusters(MasterConfig masterConfig, Element clustersElement) { if (!ELEMENT_CLUSTERS.equals(clustersElement.getLocalName())) { throw unexpected(clustersElement.getLocalName(), new String[] {ELEMENT_CLUSTERS}); } if (masterConfig.getPort() == 0 || masterConfig.getHost() == null) { throw new IllegalArgumentException("Master not configured for distributed scenario!"); } String clusterSizeBackup = System.getProperty(Properties.PROPERTY_CLUSTER_SIZE); NodeList clusters = clustersElement.getChildNodes(); for (int i = 0; i < clusters.getLength(); ++i) { if (!(clusters.item(i) instanceof Element)) continue; Element childElement = (Element) clusters.item(i); if (ELEMENT_CLUSTER.equals(childElement.getLocalName())) { int size = Integer.parseInt(getAttribute(childElement, ATTR_SIZE, "0")); System.setProperty(Properties.PROPERTY_CLUSTER_SIZE, String.valueOf(size)); addCluster(masterConfig, childElement, size); } else if (ELEMENT_SCALE.equals(childElement.getLocalName())) { int initSize = Integer.parseInt(getAttribute(childElement, ATTR_FROM)); int maxSize = Integer.parseInt(getAttribute(childElement, ATTR_TO)); int increment = Integer.parseInt(getAttribute(childElement, ATTR_INC, "1")); if (increment <= 0) throw new IllegalArgumentException("Increment must be > 0!"); NodeList scaledElements = childElement.getChildNodes(); for (int size = initSize; size <= maxSize; size += increment) { System.setProperty(Properties.PROPERTY_CLUSTER_SIZE, String.valueOf(size)); for (int j = 0; j < scaledElements.getLength(); ++j) { if (!(scaledElements.item(j) instanceof Element)) continue; Element clusterElement = (Element) scaledElements.item(j); assertName(ELEMENT_CLUSTER, clusterElement); addCluster(masterConfig, clusterElement, size); } } } else { throw unexpected(childElement.getLocalName(), new String[] {ELEMENT_CLUSTER, ELEMENT_SCALE}); } System.setProperty(Properties.PROPERTY_CLUSTER_SIZE, ""); } if (clusterSizeBackup != null) System.setProperty(Properties.PROPERTY_CLUSTER_SIZE, clusterSizeBackup); } private void addCluster(MasterConfig masterConfig, Element clusterElement, int size) { Cluster cluster = new Cluster(); NodeList groups = clusterElement.getChildNodes(); for (int j = 0; j < groups.getLength(); ++j) { if (!(groups.item(j) instanceof Element)) continue; Element groupElement = (Element) groups.item(j); assertName(ELEMENT_GROUP, groupElement); String name = getAttribute(groupElement, ATTR_NAME); String groupSize = getAttribute(groupElement, ATTR_SIZE); cluster.addGroup(name, Integer.parseInt(groupSize)); } if (size > 0) { if (cluster.getSize() <= 0) { cluster.setSize(size); } else if (cluster.getSize() != size) { throw new IllegalArgumentException("Total size for cluster is not the one specified as size! " + cluster); } } masterConfig.addCluster(cluster); } private void parseConfigurations(MasterConfig masterConfig, Element configsElement) { assertName(ELEMENT_CONFIGURATIONS, configsElement); NodeList configs = configsElement.getChildNodes(); for (int i = 0; i < configs.getLength(); ++i) { if (!(configs.item(i) instanceof Element)) continue; Element configElement = (Element) configs.item(i); if (ELEMENT_CONFIG.equals(configElement.getLocalName())) { String configName = getAttribute(configElement, ATTR_NAME); Configuration config = new Configuration(configName); NodeList setups = configElement.getChildNodes(); for (int j = 0; j < setups.getLength(); ++j) { if (!(setups.item(j) instanceof Element)) continue; Element setupElement = (Element) setups.item(j); assertName(ELEMENT_SETUP, setupElement); String plugin = getAttribute(setupElement, ATTR_PLUGIN); String group = getAttribute(setupElement, ATTR_GROUP, Cluster.DEFAULT_GROUP); String base = getAttribute(setupElement, ATTR_BASE, null); Map<String, Definition> propertyDefinitions = Collections.EMPTY_MAP; Map<String, Definition> vmArgs = Collections.EMPTY_MAP; Map<String, Definition> envs = Collections.EMPTY_MAP; String service = Configuration.DEFAULT_SERVICE; NodeList properties = setupElement.getChildNodes(); for (int k = 0; k < properties.getLength(); ++k) { if (!(properties.item(k) instanceof Element)) continue; Element setupChildElement = (Element) properties.item(k); if (ELEMENT_VM_ARGS.equals(setupChildElement.getLocalName())) { vmArgs = parseProperties(setupChildElement, true); } else if (ELEMENT_ENVIRONMENT.equals(setupChildElement.getLocalName())) { envs = parseEnvs(setupChildElement); } else if (setupChildElement.hasAttribute(ATTR_XMLNS)) { service = setupChildElement.getLocalName(); propertyDefinitions = parseProperties(setupChildElement, true); } else { throw notExternal(setupChildElement); } } config.addSetup(base, group, plugin, service, propertyDefinitions, vmArgs, envs); } masterConfig.addConfig(config); } else if (ELEMENT_TEMPLATE.equals(configElement.getLocalName())) { String name = getAttribute(configElement, ATTR_NAME); String base = getAttribute(configElement, ATTR_BASE, null); Map<String, Definition> propertyDefinitions = Collections.EMPTY_MAP; Map<String, Definition> vmArgs = Collections.EMPTY_MAP; Map<String, Definition> envs = Collections.EMPTY_MAP; NodeList properties = configElement.getChildNodes(); for (int k = 0; k < properties.getLength(); ++k) { if (!(properties.item(k) instanceof Element)) continue; Element setupChildElement = (Element) properties.item(k); if (ELEMENT_VM_ARGS.equals(setupChildElement.getLocalName())) { vmArgs = parseProperties(setupChildElement, true); } else if (ELEMENT_ENVIRONMENT.equals(setupChildElement.getLocalName())) { envs = parseEnvs(setupChildElement); } else if (setupChildElement.hasAttribute(ATTR_XMLNS)) { propertyDefinitions = parseProperties(setupChildElement, true); } else { throw notExternal(setupChildElement); } } masterConfig.addTemplate(name, base, propertyDefinitions, vmArgs, envs); } else { throw unexpected(configElement.getLocalName(), new String[] { ELEMENT_CONFIG, ELEMENT_TEMPLATE }); } } } private IllegalArgumentException notExternal(Element element) { return new IllegalArgumentException("Element " + element.getLocalName() + " does not refer to any external schema."); } private void parseScenario(Scenario scenario, Element scenarioElement) { assertName(ELEMENT_SCENARIO, scenarioElement); NodeList childNodes = scenarioElement.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node child = childNodes.item(i); if (child instanceof Element) { addScenarioItem(scenario, (Element) child); } } } private Map<String, Definition> parseProperties(Element element, boolean subElements) { NamedNodeMap attributes = element.getAttributes(); Map<String, Definition> properties = new HashMap<String, Definition>(); for (int attrIndex = 0; attrIndex < attributes.getLength(); attrIndex++) { Attr attr = (Attr) attributes.item(attrIndex); if (ATTR_XMLNS.equals(attr.getName())) continue; properties.put(attr.getName(), new SimpleDefinition(attr.getValue(), SimpleDefinition.Source.ATTRIBUTE)); } if (!subElements) { return properties; } NodeList children = element.getChildNodes(); for (int childIndex = 0; childIndex < children.getLength(); ++childIndex) { Node n = children.item(childIndex); if (n instanceof Element) { Element childElement = (Element) n; properties.put(childElement.getLocalName(), toDefinition(childElement)); } else if (n instanceof Text) { String text = ((Text) n).getWholeText().trim(); if (!text.isEmpty()) { throw new IllegalArgumentException("Non parsed text: " + text); } } else if (n instanceof Comment) { continue; } else { throw new IllegalArgumentException("Unexpected content: " + n); } } return properties; } private Map<String, Definition> parseEnvs(Element element) { Map<String, Definition> properties = new HashMap<String, Definition>(); NodeList children = element.getChildNodes(); for (int childIndex = 0; childIndex < children.getLength(); ++childIndex) { Node n = children.item(childIndex); if (n instanceof Element) { Element childElement = (Element) n; if (ELEMENT_VAR.equals(childElement.getLocalName())) { String name = childElement.getAttribute(ATTR_NAME); if (name.trim().isEmpty()) { throw new IllegalArgumentException("Environment variable must have name defined!"); } properties.put(name, new SimpleDefinition(childElement.getAttribute(ATTR_VALUE), SimpleDefinition.Source.ATTRIBUTE)); } else { throw new IllegalArgumentException("Unexpected element " + childElement); } } else if (n instanceof Text) { String text = ((Text) n).getWholeText().trim(); if (!text.isEmpty()) { throw new IllegalArgumentException("Non parsed text: " + text); } } else if (n instanceof Comment) { continue; } else { throw new IllegalArgumentException("Unexpected content: " + n); } } return properties; } private Definition toDefinition(Element element) { NamedNodeMap attributes = element.getAttributes(); NodeList children = element.getChildNodes(); if (attributes.getLength() == 0 && children.getLength() == 1 && children.item(0) instanceof Text) { return new SimpleDefinition(((Text) children.item(0)).getWholeText(), SimpleDefinition.Source.TEXT); } ComplexDefinition definition = new ComplexDefinition(); for (int i = 0; i < attributes.getLength(); ++i) { Attr attr = (Attr) attributes.item(i); if (ATTR_XMLNS.equals(attr.getName())) { definition.setNamespace(attr.getValue()); continue; } definition.add(attr.getName(), new SimpleDefinition(attr.getValue(), SimpleDefinition.Source.ATTRIBUTE)); } for (int i = 0; i < children.getLength(); ++i) { Node n = children.item(i); if (n instanceof Element) { definition.add(n.getLocalName(), toDefinition((Element) n)); } else if (n instanceof Text) { String text = ((Text) n).getWholeText().trim(); if (!text.isEmpty()) { definition.add("", new SimpleDefinition(text, SimpleDefinition.Source.TEXT)); } } else if (n instanceof Comment) { continue; } else { throw new IllegalArgumentException("Unexpected content: " + n); } } return definition; } private void parseReporting(MasterConfig masterConfig, Element reportsElement) { assertName(ELEMENT_REPORTS, reportsElement); NodeList childNodes = reportsElement.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { if (!(childNodes.item(i) instanceof Element)) continue; Element reporterElement = (Element) childNodes.item(i); assertName(ELEMENT_REPORTER, reporterElement); String type = getAttribute(reporterElement, ATTR_TYPE); ReporterConfiguration reporter = new ReporterConfiguration(type); NodeList reportElements = reporterElement.getChildNodes(); Map<String, Definition> commonProperties = new HashMap<>(); Set<String> reporterNames = ReporterHelper.getReporterNames(); String[] expectedReporters = reporterNames.toArray(new String[reporterNames.size()]); String[] expectedElements = reporterNames.toArray(new String[reporterNames.size() + 1]); expectedElements[expectedElements.length - 1] = ELEMENT_REPORT; for (int j = 0; j < reportElements.getLength(); ++j) { if (!(reportElements.item(j) instanceof Element)) continue; Element element = (Element) reportElements.item(j); if (ELEMENT_REPORT.equals(element.getLocalName())) { ReporterConfiguration.Report report = reporter.addReport(); NodeList reportChildren = element.getChildNodes(); for (int k = 0; k < reportChildren.getLength(); ++k) { if (!(reportChildren.item(k) instanceof Element)) continue; Element childElement = (Element) reportChildren.item(k); for (Map.Entry<String, Definition> property : parseReportProperties(type, childElement, expectedReporters).entrySet()) { report.addProperty(property.getKey(), property.getValue()); } } } else { for (Map.Entry<String, Definition> property : parseReportProperties(type, element, expectedElements).entrySet()) { commonProperties.put(property.getKey(), property.getValue()); } } } if (reporter.getReports().isEmpty()) { reporter.addReport(); // default one } for (ReporterConfiguration.Report report : reporter.getReports()) { for (Map.Entry<String, Definition> property : commonProperties.entrySet()) { if (report.isPropertyDefined(property.getKey())) continue; report.addProperty(property.getKey(), property.getValue()); } } masterConfig.addReporter(reporter); } } private Map<String, Definition> parseReportProperties(String type, Element element, String[] expectedElements) { if (!element.hasAttribute(ATTR_XMLNS)) { throw notExternal(element); } else if (!type.equals(element.getLocalName())) { throw new IllegalArgumentException("Expecting reporter type " + type + " but " + element.getLocalName() + " was used."); } else if (ReporterHelper.isRegistered(element.getLocalName())) { return parseProperties(element, true); } else { throw unexpected(element.getLocalName(), expectedElements); } } private IllegalArgumentException unexpected(String found, String[] expected) { StringBuilder sb = new StringBuilder("Found '").append(found).append("', expected one of "); for (int i = 0; i < expected.length; ++i) { sb.append('\'').append(expected[i]).append('\''); if (i != expected.length - 1) sb.append(", "); } return new IllegalArgumentException(sb.toString()); } private void addScenarioItem(Scenario scenario, Element element) { if (element.getLocalName().equalsIgnoreCase(ELEMENT_REPEAT)) { wrapStages(scenario, element, new Class[] {RepeatBeginStage.class}, new Class[] {RepeatContinueStage.class, RepeatEndStage.class}); } else { scenario.addStage(StageHelper.getStageClassByDashedName(element.getNamespaceURI(), element.getLocalName()), parseProperties(element, true), null); } } private void wrapStages(Scenario scenario, Element element, Class<? extends org.radargun.Stage>[] stagesBefore, Class<? extends org.radargun.Stage>[] stagesAfter) { String labelName = getAttribute(element, ATTR_NAME, ""); Map<String, Definition> properties = parseProperties(element, false); for (Class<? extends org.radargun.Stage> stage : stagesBefore) { scenario.addStage(stage, properties, labelName); } NodeList childNodes = element.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node child = childNodes.item(i); if (child instanceof Element) { addScenarioItem(scenario, (Element) child); } } for (Class<? extends org.radargun.Stage> stage : stagesAfter) { scenario.addStage(stage, properties, labelName); } } /** * Parses * * @param args */ public static void main(String[] args) { if (args.length < 2) { System.err.println("Usage: DomConfigParser config-file method"); ShutDownHook.exit(1); } MasterConfig config = null; try { config = getConfigParser().parseConfig(args[0]); } catch (Exception e) { System.err.printf("Failed to load config '%s'%n", args[0]); ShutDownHook.exit(1); } Method method = null; try { method = config.getClass().getMethod(args[1]); } catch (NoSuchMethodException e) { System.err.printf("No method '%s.%s()'%n", config.getClass().getName(), args[1]); ShutDownHook.exit(1); } try { Object result = method.invoke(config); System.out.println(String.valueOf(result)); } catch (Exception e) { System.err.println("Failed to invoke method " + method); e.printStackTrace(System.err); ShutDownHook.exit(1); } } }