net.tzolov.geode.jmx.JmxInfluxLoader.java Source code

Java tutorial

Introduction

Here is the source code for net.tzolov.geode.jmx.JmxInfluxLoader.java

Source

/*
 * Copyright 2012-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.tzolov.geode.jmx;

import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.MalformedObjectNameException;
import javax.management.NotificationListener;
import javax.management.ObjectName;

import org.influxdb.InfluxDB;
import org.influxdb.InfluxDB.ConsistencyLevel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Injector;
import com.googlecode.jmxtrans.JmxTransformer;
import com.googlecode.jmxtrans.cli.JmxTransConfiguration;
import com.googlecode.jmxtrans.guice.JmxTransModule;
import com.googlecode.jmxtrans.model.JmxProcess;
import com.googlecode.jmxtrans.model.Query;
import com.googlecode.jmxtrans.model.ResultAttribute;
import com.googlecode.jmxtrans.model.Server;
import com.googlecode.jmxtrans.model.Server.Builder;
import com.googlecode.jmxtrans.model.ServerFixtures;
import com.googlecode.jmxtrans.model.output.InfluxDbWriter;
import net.tzolov.geode.jmx.util.JmxTransformerDynamic;

@Service
public class JmxInfluxLoader {

    private static final Logger log = LoggerFactory.getLogger(JmxInfluxLoader.class);

    public static final String GEM_FIRE_SERVICE_SYSTEM_TYPE_DISTRIBUTED = "GemFire:service=System,type=Distributed";
    private static ImmutableSet<ResultAttribute> resultAttributesToWriteAsTags = ImmutableSet.of();

    private final MBeanServerConnection jmxConnection;
    private final InfluxDB influxDB;
    private GeodeDistributedSystem distributedSystemMXBean;
    private String mbeanHostName;
    private String mbeanPort;
    private String influxDatabaseName;

    private String cronExpression;

    private boolean cleanDatabaseOnStart;
    private String influxRetentionPolicy;
    private JmxTransformer transformer;

    @Autowired
    public JmxInfluxLoader(InfluxDB influxDB, MBeanServerConnection jmxConnection,
            @Value("${cleanDatabaseOnStart}") boolean cleanDatabaseOnStart,
            @Value("${influxRetentionPolicy}") String influxRetentionPolicy,
            @Value("${mbeanHostName}") String mbeanHostName, @Value("${mbeanPort}") String mbeanPort,
            @Value("${influxDatabaseName}") String influxDatabaseName,
            @Value("${cronExpression}") String cronExpression) {
        this.influxDB = influxDB;
        this.jmxConnection = jmxConnection;
        this.cleanDatabaseOnStart = cleanDatabaseOnStart;
        this.influxRetentionPolicy = influxRetentionPolicy;
        this.mbeanHostName = mbeanHostName;
        this.mbeanPort = mbeanPort;
        this.influxDatabaseName = influxDatabaseName;
        this.cronExpression = cronExpression;

        log.info("cronExpression: " + cronExpression);

        if (this.cleanDatabaseOnStart) {
            influxDB.deleteDatabase(influxDatabaseName);
            influxDB.createDatabase(this.influxDatabaseName);
        }
    }

    public void start() throws Exception {

        JmxProcess process = new JmxProcess(createServer());
        Injector injector = JmxTransModule.createInjector(new JmxTransConfiguration());
        transformer = injector.getInstance(JmxTransformerDynamic.class);
        transformer.executeStandalone(process);

        addNotificationListener((notification, handback) -> {
            switch (notification.getType()) {
            case "gemfire.distributedsystem.cache.member.departed":
            case "gemfire.distributedsystem.cache.member.joined":
            case "gemfire.distributedsystem.cache.region.created":
            case "gemfire.distributedsystem.cache.region.closed]": {
                try {
                    log.info("Reload Geode JMX definitions on event:" + notification.getType());
                    JmxInfluxLoader.this.jmxSourceInitialize();
                } catch (Exception e) {
                    log.error("Failed to reload the Geode JMXdefintiti", e);
                }
                break;
            }
            }
        });

    }

    public interface GeodeDistributedSystem {
        String[] listMembers();

        String[] listRegions();

        ObjectName[] listDistributedRegionObjectNames();
    }

    // "0 0/1 * * * ?" - every minute
    private Server createServer() {
        Builder serverBuilder = Server.builder().setHost(mbeanHostName).setPort(mbeanPort).setAlias("GeodeServers")
                .setCronExpression(cronExpression).setPool(ServerFixtures.createPool());

        for (String member : getDistributedSystemMXBean().listMembers()) {
            serverBuilder.addQuery(memberQuery(influxDB, jmxConnection, member));
        }

        // Build DistributedRegionMXBean for each region
        for (String region : getDistributedSystemMXBean().listRegions()) {
            serverBuilder.addQuery(distributedRegionQuery(influxDB, jmxConnection, region));
        }

        serverBuilder.addQuery(distributedSystemQuery(influxDB, jmxConnection));

        return serverBuilder.build();
    }

    public void jmxSourceInitialize() throws Exception {
        if (transformer != null) {
            ((JmxTransformerDynamic) transformer).reloadGeodeJmxMBeans(ImmutableList.of(createServer()));
        }
    }

    private Query distributedSystemQuery(InfluxDB influxDB, MBeanServerConnection jmxConnection) {
        ImmutableMap<String, String> tags = ImmutableMap.of();
        String objectName = "GemFire:service=System,type=Distributed";
        return createQuery(influxDB, jmxConnection, tags, objectName, "DistributedSystem");
    }

    private Query memberQuery(InfluxDB influxDB, MBeanServerConnection jmxConnection, String memberName) {
        ImmutableMap<String, String> tags = ImmutableMap.<String, String>builder().put("member", memberName)
                .build();
        String objectName = "GemFire:type=Member,member=" + memberName.replace(":", "-");
        return createQuery(influxDB, jmxConnection, tags, objectName, "Member");
    }

    private Query distributedRegionQuery(InfluxDB influxDB, MBeanServerConnection jmxConnection,
            String regionName) {
        ImmutableMap<String, String> tags = ImmutableMap.<String, String>builder().put("region", regionName)
                .build();
        String objectName = "GemFire:service=Region,name=/" + regionName.trim() + ",type=Distributed";
        return createQuery(influxDB, jmxConnection, tags, objectName, "DistributedRegion");
    }

    private Query createQuery(InfluxDB influxDB, MBeanServerConnection jmxConnection,
            ImmutableMap<String, String> tags, String objectName, String measurementName) {

        InfluxDbWriter influxDbWriter = new InfluxDbWriter(influxDB, influxDatabaseName, ConsistencyLevel.ALL,
                influxRetentionPolicy, tags, resultAttributesToWriteAsTags, false);

        Query query = Query.builder().setObj(objectName)
                .addAttr(attributeNames(jmxConnection, objectName, new PrimitiveTypesFilter()))
                .setResultAlias(measurementName).addOutputWriters(ImmutableSet.of(influxDbWriter)).build();

        return query;
    }

    private GeodeDistributedSystem getDistributedSystemMXBean() {
        if (distributedSystemMXBean == null) {

            try {
                distributedSystemMXBean = MBeanServerInvocationHandler.newProxyInstance(jmxConnection,
                        new ObjectName(GEM_FIRE_SERVICE_SYSTEM_TYPE_DISTRIBUTED), GeodeDistributedSystem.class,
                        false);
            } catch (MalformedObjectNameException e) {
                log.error("", e);
            }
        }

        return distributedSystemMXBean;
    }

    public void addNotificationListener(NotificationListener listener) {
        try {
            this.jmxConnection.addNotificationListener(new ObjectName(GEM_FIRE_SERVICE_SYSTEM_TYPE_DISTRIBUTED),
                    listener, null, null);
        } catch (Exception e) {
            log.error("Failed to register JMX notification listener: " + listener, e);
        }
    }

    private String[] attributeNames(MBeanServerConnection connection, String objectName,
            MBeanAttributeInfoFilter attributeFilter) {

        try {
            ImmutableList.Builder<String> builder = ImmutableList.builder();
            for (MBeanAttributeInfo attr : connection.getMBeanInfo(new ObjectName(objectName)).getAttributes()) {
                if (!attributeFilter.filter(attr)) {
                    builder.add(attr.getName());
                }
            }
            ImmutableList<String> names = builder.build();
            return names.toArray(new String[names.size()]);
        } catch (Exception ex) {
            throw new RuntimeException((ex));
        }
    }

    public interface MBeanAttributeInfoFilter {
        boolean filter(MBeanAttributeInfo attributeInfo);
    }

    public static class PrimitiveTypesFilter implements MBeanAttributeInfoFilter {
        @Override
        public boolean filter(MBeanAttributeInfo attributeInfo) {
            switch (attributeInfo.getType()) {
            case "java.lang.String":
            case "long":
            case "float":
            case "boolean":
            case "double":
            case "int":
                return false;
            default:
                return true;
            }
        }
    }
}