org.jmxtrans.agent.JmxTransConfigurationXmlLoader Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2013 the original author or authors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package org.jmxtrans.agent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.regex.Pattern;
import org.jmxtrans.agent.properties.NoPropertiesSourcePropertiesLoader;
import org.jmxtrans.agent.properties.PropertiesLoader;
import org.jmxtrans.agent.util.Preconditions2;
import org.jmxtrans.agent.util.PropertyPlaceholderResolver;
import org.jmxtrans.agent.util.io.IoRuntimeException;
import org.jmxtrans.agent.util.io.IoUtils;
import org.jmxtrans.agent.util.io.Resource;
import org.jmxtrans.agent.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.annotation.Nonnull;
/**
* XML configuration parser.
*
* @author Cyrille Le Clerc
*/
public class JmxTransConfigurationXmlLoader implements JmxTransConfigurationLoader {
private static final String COLLECT_INTERVAL_NAME = "collectIntervalInSeconds";
private static final Pattern ATTRIBUTE_SPLIT_PATTERN = Pattern.compile("\\s*,\\s*");
private Logger logger = Logger.getLogger(getClass().getName());
private final PropertiesLoader propertiesLoader;
@Nonnull
private final Resource configurationResource;
private ExpressionLanguageEngine expressionLanguageEngine;
public JmxTransConfigurationXmlLoader(@Nonnull Resource configurationResource, @Nonnull PropertiesLoader propertiesLoader,
@Nonnull ExpressionLanguageEngine expressionLanguageEngine) {
this.configurationResource = Preconditions2.checkNotNull(configurationResource, "configurationResource can not be null");
this.propertiesLoader = Preconditions2.checkNotNull(propertiesLoader, "propertiesLoader can not be null");
this.expressionLanguageEngine = Preconditions2.checkNotNull(expressionLanguageEngine, "expressionLanguageEngine can not be null");
}
/**
* Creates a JmxTransExporterBuilder with a PropertyLoader that does not use an
* external properties source.
*/
JmxTransConfigurationXmlLoader(@Nonnull Resource configurationResource, PropertiesLoader propertiesLoader) {
this(configurationResource, propertiesLoader, new ExpressionLanguageEngineImpl());
}
/**
* Creates a JmxTransExporterBuilder with a PropertyLoader that does not use an
* external properties source and uses the default ExpressionLanguageEngine.
*/
JmxTransConfigurationXmlLoader(@Nonnull Resource configurationResource) {
this(configurationResource, new NoPropertiesSourcePropertiesLoader(), new ExpressionLanguageEngineImpl());
}
@Override
public JmxTransExporterConfiguration loadConfiguration() {
return build(IoUtils.getFileAsDocument(configurationResource));
}
@Override
public long lastModified() {
try {
return configurationResource.lastModified();
} catch (IoRuntimeException e) {
return 0L;
}
}
protected JmxTransExporterConfiguration build(Document document) {
Element rootElement = document.getDocumentElement();
Map loadedProperties = loadPropertiesOrEmptyOnException();
PropertyPlaceholderResolver resolver = new PropertyPlaceholderResolver(loadedProperties);
JmxTransExporterConfiguration jmxTransExporterConfiguration = new JmxTransExporterConfiguration(document);
Integer collectInterval = getIntegerElementValueOrNullIfNotSet(rootElement, COLLECT_INTERVAL_NAME, resolver);
if (collectInterval != null) {
jmxTransExporterConfiguration.withCollectInterval(collectInterval, TimeUnit.SECONDS);
}
Integer reloadConfigInterval = getIntegerElementValueOrNullIfNotSet(rootElement,
"reloadConfigurationCheckIntervalInSeconds", resolver);
if (reloadConfigInterval != null) {
jmxTransExporterConfiguration.withConfigReloadInterval(reloadConfigInterval);
}
buildResultNameStrategy(rootElement, jmxTransExporterConfiguration, resolver);
buildInvocations(rootElement, jmxTransExporterConfiguration);
buildQueries(rootElement, jmxTransExporterConfiguration);
buildDiscoveryQueries(rootElement, jmxTransExporterConfiguration);
buildOutputWriters(rootElement, jmxTransExporterConfiguration, resolver);
return jmxTransExporterConfiguration;
}
@Nonnull
private Map loadPropertiesOrEmptyOnException() {
try {
return propertiesLoader.loadProperties();
} catch (Exception e) {
logger.log(Level.WARNING, "Error when loading properties from loader " + propertiesLoader + ", this source will be ignored", e);
return new HashMap<>();
}
}
public JmxTransExporterConfiguration build(JmxTransConfigurationLoader configurationDocumentLoader)
throws Exception {
JmxTransExporterConfiguration configuration = configurationDocumentLoader.loadConfiguration();
return build(configuration.getDocument());
}
private Integer getIntegerElementValueOrNullIfNotSet(Element rootElement, String elementName, PropertyPlaceholderResolver placeholderResolver) {
NodeList nodeList = rootElement.getElementsByTagName(elementName);
switch (nodeList.getLength()) {
case 0:
return null;
case 1:
Element element = (Element) nodeList.item(0);
String stringValue = placeholderResolver.resolveString(element.getTextContent());
try {
return Integer.parseInt(stringValue);
} catch (NumberFormatException e) {
throw new IllegalStateException("Invalid <" + elementName + "> value '" + stringValue + "', integer expected", e);
}
default:
logger.warning("More than 1 <" + elementName + "> element found (" + nodeList.getLength() + "), use latest");
Element lastElement = (Element) nodeList.item(nodeList.getLength() - 1);
String lastStringValue = placeholderResolver.resolveString(lastElement.getTextContent());
try {
return Integer.parseInt(lastStringValue);
} catch (NumberFormatException e) {
throw new IllegalStateException("Invalid <" + elementName + "> value '" + lastStringValue + "', integer expected", e);
}
}
}
private void buildQueries(Element rootElement, JmxTransExporterConfiguration configuration) {
NodeList queries = rootElement.getElementsByTagName("query");
for (int i = 0; i < queries.getLength(); i++) {
Element queryElement = (Element) queries.item(i);
String objectName = queryElement.getAttribute("objectName");
List attributes = getAttributes(queryElement, objectName);
String key = queryElement.hasAttribute("key") ? queryElement.getAttribute("key") : null;
String resultAlias = queryElement.getAttribute("resultAlias");
String type = queryElement.getAttribute("type");
Integer position;
try {
position = queryElement.hasAttribute("position") ? Integer.parseInt(queryElement.getAttribute("position")) : null;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid 'position' attribute for query objectName=" + objectName +
", attributes=" + attributes + ", resultAlias=" + resultAlias);
}
Integer collectInterval = intAttributeOrNull(queryElement, COLLECT_INTERVAL_NAME);
configuration.withQuery(objectName, attributes, key, position, type, resultAlias, collectInterval);
}
}
private void buildDiscoveryQueries(Element rootElement, JmxTransExporterConfiguration configuration) {
NodeList queries = rootElement.getElementsByTagName("discoveryQuery");
for (int i = 0; i < queries.getLength(); i++) {
Element queryElement = (Element) queries.item(i);
String objectName = queryElement.getAttribute("objectName");
List attributes = getAttributes(queryElement, objectName);
String key = queryElement.hasAttribute("key") ? queryElement.getAttribute("key") : null;
String resultAlias = queryElement.getAttribute("resultAlias");
String type = queryElement.getAttribute("type");
Integer position;
try {
position = queryElement.hasAttribute("position") ? Integer.parseInt(queryElement.getAttribute("position")) : null;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid 'position' attribute for query objectName=" + objectName +
", attributes=" + attributes + ", resultAlias=" + resultAlias);
}
Integer collectInterval = intAttributeOrNull(queryElement, COLLECT_INTERVAL_NAME);
configuration.withDiscoveryQuery(objectName, attributes, key, position, type, resultAlias, collectInterval);
}
}
private List getAttributes(Element queryElement, String objectName) {
String attribute = queryElement.getAttribute("attribute");
String attributes = queryElement.getAttribute("attributes");
validateOnlyAttributeOrAttributesSpecified(attribute, attributes, objectName);
if (attribute.isEmpty() && attributes.isEmpty()) {
return Collections.emptyList();
}
if (!attribute.isEmpty()) {
return Collections.singletonList(attribute);
} else {
String[] splitAttributes = ATTRIBUTE_SPLIT_PATTERN.split(attributes);
return Arrays.asList(splitAttributes);
}
}
private void validateOnlyAttributeOrAttributesSpecified(String attribute, String attributes, String objectName) {
if (!attribute.isEmpty() && !attributes.isEmpty()) {
throw new IllegalArgumentException("Only one of 'attribute' and 'attributes' is supported for a query - not both - objectName: " + objectName);
}
}
private void buildInvocations(Element rootElement, JmxTransExporterConfiguration configuration) {
NodeList invocations = rootElement.getElementsByTagName("invocation");
for (int i = 0; i < invocations.getLength(); i++) {
Element invocationElement = (Element) invocations.item(i);
String objectName = invocationElement.getAttribute("objectName");
String operation = invocationElement.getAttribute("operation");
String resultAlias = invocationElement.getAttribute("resultAlias");
Integer collectInterval = intAttributeOrNull(invocationElement, COLLECT_INTERVAL_NAME);
configuration.withInvocation(objectName, operation, resultAlias, collectInterval);
}
}
private Integer intAttributeOrNull(Element element, String attributeName) {
String value = element.getAttribute(attributeName);
if (value.isEmpty()) {
return null;
}
try {
return Integer.valueOf(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Attribute '" + attributeName + "' must be an integer", e);
}
}
private void buildResultNameStrategy(Element rootElement, JmxTransExporterConfiguration configuration, PropertyPlaceholderResolver placeholderResolver) {
NodeList resultNameStrategyNodeList = rootElement.getElementsByTagName("resultNameStrategy");
ResultNameStrategy resultNameStrategy;
switch (resultNameStrategyNodeList.getLength()) {
case 0:
// nothing to do, use default value
resultNameStrategy = new ResultNameStrategyImpl(expressionLanguageEngine);
break;
case 1:
Element resultNameStrategyElement = (Element) resultNameStrategyNodeList.item(0);
String outputWriterClass = resultNameStrategyElement.getAttribute("class");
if (outputWriterClass.isEmpty())
throw new IllegalArgumentException(" element must contain a 'class' attribute");
try {
resultNameStrategy = (ResultNameStrategy) Class.forName(outputWriterClass).newInstance();
Map settings = new HashMap<>();
NodeList settingsNodeList = resultNameStrategyElement.getElementsByTagName("*");
for (int j = 0; j < settingsNodeList.getLength(); j++) {
Element settingElement = (Element) settingsNodeList.item(j);
settings.put(settingElement.getNodeName(), placeholderResolver.resolveString(settingElement.getTextContent()));
}
resultNameStrategy.postConstruct(settings);
} catch (Exception e) {
throw new IllegalArgumentException("Exception instantiating " + outputWriterClass, e);
}
break;
default:
throw new IllegalStateException("More than 1 element found (" + resultNameStrategyNodeList.getLength() + ")");
}
configuration.resultNameStrategy = resultNameStrategy;
}
private void buildOutputWriters(Element rootElement, JmxTransExporterConfiguration configuration, PropertyPlaceholderResolver placeholderResolver) {
NodeList outputWriterNodeList = rootElement.getElementsByTagName("outputWriter");
List outputWriters = new ArrayList<>();
for (int i = 0; i < outputWriterNodeList.getLength(); i++) {
Element outputWriterElement = (Element) outputWriterNodeList.item(i);
String outputWriterClass = outputWriterElement.getAttribute("class");
if (outputWriterClass.isEmpty()) {
throw new IllegalArgumentException(" element must contain a 'class' attribute");
}
OutputWriter outputWriter;
try {
outputWriter = (OutputWriter) Class.forName(outputWriterClass).newInstance();
Map settings = new HashMap<>();
NodeList settingsNodeList = outputWriterElement.getElementsByTagName("*");
for (int j = 0; j < settingsNodeList.getLength(); j++) {
Element settingElement = (Element) settingsNodeList.item(j);
String settingText = settingElement.getTextContent();
String settingWithPlaceholdersResolved = placeholderResolver.resolveString(settingText);
String settingWithFunctionsApplied = expressionLanguageEngine.resolveExpression(settingWithPlaceholdersResolved);
settings.put(settingElement.getNodeName(), settingWithFunctionsApplied);
}
outputWriter = new OutputWriterCircuitBreakerDecorator(outputWriter);
outputWriter.postConstruct(settings);
outputWriters.add(outputWriter);
} catch (Exception e) {
throw new IllegalArgumentException("Exception instantiating " + outputWriterClass, e);
}
}
switch (outputWriters.size()) {
case 0:
logger.warning("No outputwriter defined.");
break;
case 1:
configuration.withOutputWriter(outputWriters.get(0));
break;
default:
configuration.withOutputWriter(new OutputWritersChain(outputWriters));
}
}
@Override
public String toString() {
return "JmxTransConfigurationXmlLoader{" +
"configurationResource='" + configurationResource + '\'' +
'}';
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy