/** * The MIT License * Copyright (c) 2014 JMXTrans Team * * 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.core.config; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor.AbortPolicy; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; import javax.management.MalformedObjectNameException; import javax.xml.bind.JAXBException; import javax.xml.parsers.ParserConfigurationException; import org.jmxtrans.core.lifecycle.LifecycleAware; import org.jmxtrans.core.log.Logger; import org.jmxtrans.core.log.LoggerFactory; import org.jmxtrans.core.monitoring.MBeanRegistry; import org.jmxtrans.core.monitoring.ObjectNameFactory; import org.jmxtrans.core.monitoring.SelfNamedMBean; import org.jmxtrans.core.query.ResultNameStrategy; import org.jmxtrans.core.query.Server; import org.jmxtrans.core.scheduler.JmxTransThreadFactory; import org.jmxtrans.core.scheduler.NaiveScheduler; import org.jmxtrans.core.scheduler.QueryGenerator; import org.jmxtrans.core.scheduler.QueryProcessor; import org.jmxtrans.core.scheduler.ResultProcessor; import org.jmxtrans.utils.PropertyPlaceholderResolver; import org.jmxtrans.utils.io.Resource; import org.jmxtrans.utils.time.Clock; import org.jmxtrans.utils.time.SystemClock; import org.xml.sax.SAXException; import static java.lang.String.format; import static java.lang.management.ManagementFactory.getPlatformMBeanServer; import static java.util.Collections.singleton; import static java.util.concurrent.TimeUnit.MINUTES; @ThreadSafe public class JmxTransBuilder { private final boolean ignoreParsingErrors; @Nonnull private final Iterable<Resource> configResources; private final Logger logger = LoggerFactory.getLogger(getClass().getName()); @Nonnull private final ObjectNameFactory executorObjectNameFactory = new ObjectNameFactory("executor"); @Nonnull private final ObjectNameFactory outputObjectNameFactory = new ObjectNameFactory("outputWriter"); public JmxTransBuilder( boolean ignoreParsingErrors, @Nonnull Iterable<Resource> configResources) { this.ignoreParsingErrors = ignoreParsingErrors; this.configResources = configResources; } public NaiveScheduler build() throws ParserConfigurationException, IOException, SAXException, JAXBException, IllegalAccessException, InstantiationException, ClassNotFoundException, MalformedObjectNameException { SystemClock clock = new SystemClock(); long shutdownTimerMillis = 1000; MBeanRegistry mBeanRegistry = new MBeanRegistry(getPlatformMBeanServer()); ExecutorService queryExecutor = createExecutorService("queries", 2, 1000, 1, MINUTES, mBeanRegistry); ExecutorService resultExecutor = createExecutorService("results", 2, 1000, 1, MINUTES, mBeanRegistry); ScheduledExecutorService queryTimer = createScheduledExecutorService("queryTimer", mBeanRegistry); Configuration configuration = loadConfiguration(clock); registerMBeans(configuration, mBeanRegistry); return new NaiveScheduler( queryExecutor, resultExecutor, queryTimer, new QueryGenerator( clock, configuration.getPeriod(), configuration.getServers(), new QueryProcessor( clock, configuration.getOutputWriters(), queryExecutor, new ResultProcessor( clock, resultExecutor ), new ResultNameStrategy() ), queryTimer ), Collections.<LifecycleAware>singletonList(mBeanRegistry), shutdownTimerMillis ); } private void registerMBeans(Configuration configuration, MBeanRegistry mBeanRegistry) { for (Server server : configuration.getServers()) { registerMBeans(mBeanRegistry, server.getQueries()); registerMBeans(mBeanRegistry, configuration.getOutputWriters()); } } private void registerMBeans(MBeanRegistry mBeanRegistry, Iterable<?> objects) { for (Object object : objects) { if (!(object instanceof SelfNamedMBean)) break; SelfNamedMBean selfNamedMBean = (SelfNamedMBean)object; try { mBeanRegistry.register(selfNamedMBean); } catch (MalformedObjectNameException e) { logger.warn(format("Could not register bean [%s]", selfNamedMBean), e); } } } private Configuration loadConfiguration(Clock clock) throws JAXBException, ParserConfigurationException, SAXException, IOException, IllegalAccessException, ClassNotFoundException, InstantiationException { Iterable<ConfigParser> parsers = getConfigParsers(clock); Collection<Configuration> configurations = new ArrayList<>(); for (Resource configResource : configResources) { boolean parsed = false; for (ConfigParser parser : parsers) { if (parser.supports(configResource)) { try { Configuration configuration = parser.parseConfiguration(configResource); configurations.add(configuration); parsed = true; } catch (Exception e) { String message = "Could not parse configuration " + configResource.getPath(); if (ignoreParsingErrors) { logger.info(message); } else { throw new JmxtransConfigurationException(message, e); } } } } if (!parsed && !ignoreParsingErrors) { throw new JmxtransConfigurationException("Found no parsers supporting config file " + configResource.getPath()); } } return new ConfigurationMerger().merge(configurations); } private Iterable<ConfigParser> getConfigParsers(Clock clock) throws JAXBException, ParserConfigurationException, SAXException, IOException { ConfigParser configParser = XmlConfigParser.newInstance( new PropertyPlaceholderResolverXmlPreprocessor(new PropertyPlaceholderResolver()), clock, outputObjectNameFactory); return singleton(configParser); } @Nonnull private ScheduledExecutorService createScheduledExecutorService( @Nonnull String componentName, @Nonnull MBeanRegistry mBeanRegistry) throws MalformedObjectNameException { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new JmxTransThreadFactory(componentName), new AbortPolicy()); mBeanRegistry.register( executorObjectNameFactory.create(componentName), new ThreadPoolExecutorMetrics(executor)); return executor; } @Nonnull private ExecutorService createExecutorService( @Nonnull String componentName, int maxThreads, int maxQueueSize, int keepAliveTime, @Nonnull TimeUnit unit, @Nonnull MBeanRegistry mBeanRegistry) throws MalformedObjectNameException { ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, maxThreads, keepAliveTime, unit, new ArrayBlockingQueue<Runnable>(maxQueueSize), new JmxTransThreadFactory(componentName), new AbortPolicy()); mBeanRegistry.register( executorObjectNameFactory.create(componentName), new ThreadPoolExecutorMetrics(executor)); return executor; } }