Package org.jmxtrans.embedded

Source Code of org.jmxtrans.embedded.Query

/*
* 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.embedded;

import org.jmxtrans.embedded.output.OutputWriter;
import org.jmxtrans.embedded.util.Preconditions;
import org.jmxtrans.embedded.util.concurrent.DiscardingBlockingQueue;
import org.jmxtrans.embedded.util.jmx.JmxUtils2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.management.*;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
* Describe a JMX query on which metrics are collected and hold the query and export business logic
* ({@link #collectMetrics()} and {@link #exportCollectedMetrics()}).
*
* @author <a href="mailto:cleclerc@xebia.fr">Cyrille Le Clerc</a>
* @author Jon Stevens
*/
public class Query implements QueryMBean {

    private static final AtomicInteger queryIdSequence = new AtomicInteger();

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * Parent.
     */
    private EmbeddedJmxTrans embeddedJmxTrans;

    /**
     * Mainly used for monitoring.
     */
    private String id = "query-" + queryIdSequence.getAndIncrement();

    /**
     * ObjectName of the Query MBean(s) to monitor, can contain
     */
    @Nonnull
    private ObjectName objectName;

    @Nullable
    private String resultAlias;
    /**
     * JMX attributes to collect. As an array for {@link javax.management.MBeanServer#getAttributes(javax.management.ObjectName, String[])}
     */
    @Nonnull
    private Map<String, QueryAttribute> attributesByName = new HashMap<String, QueryAttribute>();
    /**
     * Copy of {@link #attributesByName}'s {@link java.util.Map#entrySet()} for performance optimization
     */
    @Nonnull
    private String[] attributeNames = new String[0];

    /**
     * List of {@linkplain OutputWriter} declared at the {@linkplain Query} level.
     *
     * @see EmbeddedJmxTrans#getOutputWriters()
     * @see #getEffectiveOutputWriters()
     */
    @Nonnull
    private List<OutputWriter> outputWriters = new ArrayList<OutputWriter>();

    /**
     * Store the metrics collected on this {@linkplain Query} (see {@link #collectMetrics()})
     * until they are exported to the target {@linkplain OutputWriter}s (see {@link #exportCollectedMetrics()}.
     */
    @Nonnull
    private BlockingQueue<QueryResult> queryResults = new DiscardingBlockingQueue<QueryResult>(200);

    @Nonnull
    private final AtomicInteger collectedMetricsCount = new AtomicInteger();

    @Nonnull
    private final AtomicLong collectionDurationInNanos = new AtomicLong();

    @Nonnull
    private final AtomicInteger collectionCount = new AtomicInteger();

    @Nonnull
    private final AtomicInteger exportedMetricsCount = new AtomicInteger();

    @Nonnull
    private final AtomicLong exportDurationInNanos = new AtomicLong();

    @Nonnull
    private final AtomicInteger exportCount = new AtomicInteger();

    /**
     * {@link ObjectName} of this {@link QueryMBean}
     */
    @Nullable
    private ObjectName queryMbeanObjectName;

    /**
     * Creates a {@linkplain Query} on the given <code>objectName</code>.
     *
     * @param objectName {@link ObjectName} to query, can contain wildcards ('*' or '?')
     */
    public Query(@Nonnull String objectName) {
        try {
            this.objectName = new ObjectName(objectName);
        } catch (MalformedObjectNameException e) {
            throw new RuntimeException("Exception parsing '" + objectName + "'", e);
        }
    }

    /**
     * Creates a {@linkplain Query} on the given <code>objectName</code>.
     *
     * @param objectName {@link ObjectName} to query, can contain wildcards ('*' or '?')
     */
    public Query(@Nonnull ObjectName objectName) {
        this.objectName = objectName;
    }


    /**
     * Collect the values for this query and store them as {@link QueryResult} in the {@linkplain Query#queryResults} queue
     */
    @Override
    public void collectMetrics() {
        long nanosBefore = System.nanoTime();
        /*
         * Optimisation tip: no need to skip 'mbeanServer.queryNames()' if the ObjectName is not a pattern
         * (i.e. not '*' or '?' wildcard) because the mbeanserver internally performs the check.
         * Seen on com.sun.jmx.interceptor.DefaultMBeanServerInterceptor
         */
        Set<ObjectName> matchingObjectNames = embeddedJmxTrans.getMbeanServer().queryNames(this.objectName, null);
        logger.trace("Query {} returned {}", objectName, matchingObjectNames);

        for (ObjectName matchingObjectName : matchingObjectNames) {
            long epochInMillis = System.currentTimeMillis();
            try {
                AttributeList jmxAttributes = embeddedJmxTrans.getMbeanServer().getAttributes(matchingObjectName, this.attributeNames);
                logger.trace("Query {} returned {}", matchingObjectName, jmxAttributes);
                for (Attribute jmxAttribute : jmxAttributes.asList()) {
                    QueryAttribute queryAttribute = this.attributesByName.get(jmxAttribute.getName());
                    Object value = jmxAttribute.getValue();
                    int count = queryAttribute.collectMetrics(matchingObjectName, value, epochInMillis, this.queryResults);
                    collectedMetricsCount.addAndGet(count);
                }
            } catch (Exception e) {
                logger.warn("Exception processing query {}", this, e);
            }
        }
        collectionCount.incrementAndGet();
        long nanosAfter = System.nanoTime();
        collectionDurationInNanos.addAndGet(nanosAfter - nanosBefore);
    }

    /**
     * Export the collected metrics to the {@linkplain OutputWriter}s associated with this {@linkplain Query}
     * (see {@link #getEffectiveOutputWriters()}).
     * <p/>
     * Metrics are batched according to {@link EmbeddedJmxTrans#getExportBatchSize()}
     *
     * @return the number of exported {@linkplain QueryResult}
     */
    @Override
    public int exportCollectedMetrics() {
        if(queryResults.isEmpty()) {
            return 0;
        }

        int totalExportedMetricsCount = 0;
        long nanosBefore = System.nanoTime();

        List<OutputWriter> effectiveOutputWriters = getEffectiveOutputWriters();
        int exportBatchSize = getEmbeddedJmxTrans().getExportBatchSize();
        List<QueryResult> availableQueryResults = new ArrayList<QueryResult>(exportBatchSize);

        int size;
        while ((size = queryResults.drainTo(availableQueryResults, exportBatchSize)) > 0) {
            totalExportedMetricsCount += size;
            exportedMetricsCount.addAndGet(size);
            for (OutputWriter outputWriter : effectiveOutputWriters) {
                outputWriter.write(availableQueryResults);
            }
            availableQueryResults.clear();
        }
        exportDurationInNanos.addAndGet(System.nanoTime() - nanosBefore);
        exportCount.incrementAndGet();
        return totalExportedMetricsCount;
    }

    /**
     * Start all the {@linkplain OutputWriter}s attached to this {@linkplain Query}
     */
    @PostConstruct
    public void start() throws Exception {
        queryMbeanObjectName = JmxUtils2.registerObject(this, "org.jmxtrans.embedded:Type=Query,id=" + id, getEmbeddedJmxTrans().getMbeanServer());


        for (OutputWriter outputWriter : outputWriters) {
            outputWriter.start();
        }
    }

    /**
     * Stop all the {@linkplain OutputWriter}s attached to this {@linkplain Query}
     */
    @PreDestroy
    public void stop() throws Exception {
        JmxUtils2.unregisterObject(queryMbeanObjectName, embeddedJmxTrans.getMbeanServer());

        for (OutputWriter outputWriter : outputWriters) {
            outputWriter.stop();
        }
    }

    @Override
    @Nonnull
    public ObjectName getObjectName() {
        return objectName;
    }

    @Nonnull
    public Collection<QueryAttribute> getQueryAttributes() {
        return attributesByName.values();
    }

    /**
     * Add the given attribute to the list attributes of this query
     * and maintains the reverse relation (see {@link org.jmxtrans.embedded.QueryAttribute#getQuery()}).
     *
     * @param attribute attribute to add
     * @return this
     */
    @Nonnull
    public Query addAttribute(@Nonnull QueryAttribute attribute) {
        attribute.setQuery(this);
        attributesByName.put(attribute.getName(), attribute);
        attributeNames = attributesByName.keySet().toArray(new String[0]);

        return this;
    }

    /**
     * Create a basic {@link QueryAttribute}, add it to the list attributes of this query
     * and maintains the reverse relation (see {@link org.jmxtrans.embedded.QueryAttribute#getQuery()}).
     *
     * @param attributeName attribute to add
     * @return this
     */
    @Nonnull
    public Query addAttribute(@Nonnull String attributeName) {
        return addAttribute(new QueryAttribute(attributeName, null, null));
    }

    @Nonnull
    public BlockingQueue<QueryResult> getResults() {
        return queryResults;
    }

    /**
     * WARNING: {@linkplain #queryResults} queue should not be changed at runtime as the operation is not thread safe.
     */
    public void setResultsQueue(@Nonnull BlockingQueue<QueryResult> queryResultQueue) {
        this.queryResults = Preconditions.checkNotNull(queryResultQueue);
    }

    public void setResultAlias(@Nullable String resultAlias) {
        this.resultAlias = resultAlias;
    }

    public EmbeddedJmxTrans getEmbeddedJmxTrans() {
        return embeddedJmxTrans;
    }

    public void setEmbeddedJmxTrans(EmbeddedJmxTrans embeddedJmxTrans) {
        this.embeddedJmxTrans = embeddedJmxTrans;
    }

    /**
     * Return the <code>outputWriters</code> to which the collected metrics of this {@linkplain Query} are exported,
     * the <code>outputWriters</code> declared at query level or a the parent level.
     *
     * @see #getOutputWriters()
     * @see EmbeddedJmxTrans#getOutputWriters()
     */
    @Nonnull
    public List<OutputWriter> getEffectiveOutputWriters() {
        // Google Guava predicates would be nicer but we don't include guava to ease embeddability
        List<OutputWriter> result = new ArrayList<OutputWriter>(embeddedJmxTrans.getOutputWriters().size() + outputWriters.size());
        for (OutputWriter outputWriter : embeddedJmxTrans.getOutputWriters()) {
            if (outputWriter.isEnabled()) {
                result.add(outputWriter);
            }
        }
        for (OutputWriter outputWriter : outputWriters) {
            if (outputWriter.isEnabled()) {
                result.add(outputWriter);
            }
        }
        return result;
    }

    @Nonnull
    public List<OutputWriter> getOutputWriters() {
        return outputWriters;
    }

    @Override
    @Nullable
    public String getResultAlias() {
        return resultAlias;
    }

    @Override
    public String toString() {
        return "Query{" +
                "objectName=" + objectName +
                ", resultAlias='" + resultAlias + '\'' +
                ", outputWriters=" + outputWriters +
                ", attributes=" + attributesByName.values() +
                '}';
    }

    @Override
    public int getCollectedMetricsCount() {
        return collectedMetricsCount.get();
    }

    @Override
    public long getCollectionDurationInNanos() {
        return collectionDurationInNanos.get();
    }

    @Override
    public int getCollectionCount() {
        return collectionCount.get();
    }

    @Override
    public int getExportedMetricsCount() {
        return exportedMetricsCount.get();
    }

    @Override
    public long getExportDurationInNanos() {
        return exportDurationInNanos.get();
    }

    @Override
    public int getExportCount() {
        return exportCount.get();
    }

    @Override
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    /**
     * Returns the number of discarded elements in the {@link #queryResults} queue
     * or <code>-1</code> if the queue is not a {@link DiscardingBlockingQueue}.
     */
    @Override
    public int getDiscardedResultsCount() {
        if (queryResults instanceof DiscardingBlockingQueue) {
            DiscardingBlockingQueue discardingBlockingQueue = (DiscardingBlockingQueue) queryResults;
            return discardingBlockingQueue.getDiscardedElementCount();
        } else {
            return -1;
        }
    }
}
TOP

Related Classes of org.jmxtrans.embedded.Query

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.