All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openesb.management.jmx.JvmMetricsServiceImpl Maven / Gradle / Ivy

There is a newer version: 1.0.4
Show newest version
package net.openesb.management.jmx;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import net.openesb.management.api.JvmMetricsService;
import net.openesb.model.api.metric.Gauge;
import net.openesb.model.api.metric.Metric;
import net.openesb.model.api.metric.RatioGauge;

/**
 *
 * @author David BRASSELY (brasseld at gmail.com)
 * @author OpenESB Community
 */
public class JvmMetricsServiceImpl implements JvmMetricsService {

    // do not compute stack traces.
    private final static int STACK_TRACE_DEPTH = 0;
    private static final int MAX_STACK_TRACE_DEPTH = 100;
    private static final Pattern WHITESPACE = Pattern.compile("[\\s]+");
    private final List garbageCollectors;
    private final ThreadMXBean threads;
    private final RuntimeMXBean runtimeMxBean;
    private final MemoryMXBean memoryMxBean;
    private final List memoryPools;

    public JvmMetricsServiceImpl() {
        this.threads = ManagementFactory.getThreadMXBean();
        this.garbageCollectors = new ArrayList(
                ManagementFactory.getGarbageCollectorMXBeans());
        this.runtimeMxBean = ManagementFactory.getRuntimeMXBean();
        this.memoryMxBean = ManagementFactory.getMemoryMXBean();
        this.memoryPools = ManagementFactory.getMemoryPoolMXBeans();
    }

    @Override
    public Map getInformations() {
        final Map infos = new HashMap();

        infos.put("name", runtimeMxBean.getName());
        infos.put("spec.name", runtimeMxBean.getSpecName());
        infos.put("spec.vendor", runtimeMxBean.getSpecVendor());
        infos.put("spec.version", runtimeMxBean.getSpecVersion());
        infos.put("vm.name", runtimeMxBean.getVmName());
        infos.put("vm.vendor", runtimeMxBean.getVmVendor());
        infos.put("vm.version", runtimeMxBean.getVmVersion());
        infos.put("mgmt.spec.version", runtimeMxBean.getManagementSpecVersion());
        infos.put("system.properties", runtimeMxBean.getSystemProperties());

        return Collections.unmodifiableMap(infos);
    }

    @Override
    public Map getMemoryUsage() {
        final Map gauges = new HashMap();

        gauges.put("total.init", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getHeapMemoryUsage().getInit()
                        + memoryMxBean.getNonHeapMemoryUsage().getInit();
            }
        });

        gauges.put("total.used", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getHeapMemoryUsage().getUsed()
                        + memoryMxBean.getNonHeapMemoryUsage().getUsed();
            }
        });

        gauges.put("total.max", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getHeapMemoryUsage().getMax()
                        + memoryMxBean.getNonHeapMemoryUsage().getMax();
            }
        });

        gauges.put("total.committed", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getHeapMemoryUsage().getCommitted()
                        + memoryMxBean.getNonHeapMemoryUsage().getCommitted();
            }
        });


        gauges.put("heap.init", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getHeapMemoryUsage().getInit();
            }
        });

        gauges.put("heap.used", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getHeapMemoryUsage().getUsed();
            }
        });

        gauges.put("heap.max", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getHeapMemoryUsage().getMax();
            }
        });

        gauges.put("heap.committed", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getHeapMemoryUsage().getCommitted();
            }
        });

        gauges.put("heap.usage", new RatioGauge() {
            @Override
            protected RatioGauge.Ratio getRatio() {
                final MemoryUsage usage = memoryMxBean.getHeapMemoryUsage();
                return RatioGauge.Ratio.of(usage.getUsed(), usage.getMax());
            }
        });

        gauges.put("non-heap.init", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getNonHeapMemoryUsage().getInit();
            }
        });

        gauges.put("non-heap.used", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getNonHeapMemoryUsage().getUsed();
            }
        });

        gauges.put("non-heap.max", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getNonHeapMemoryUsage().getMax();
            }
        });

        gauges.put("non-heap.committed", new Gauge() {
            @Override
            public Long getValue() {
                return memoryMxBean.getNonHeapMemoryUsage().getCommitted();
            }
        });

        gauges.put("non-heap.usage", new RatioGauge() {
            @Override
            protected RatioGauge.Ratio getRatio() {
                final MemoryUsage usage = memoryMxBean.getNonHeapMemoryUsage();
                return RatioGauge.Ratio.of(usage.getUsed(), usage.getMax());
            }
        });

        for (final MemoryPoolMXBean pool : memoryPools) {
            final String poolName = name("pools", WHITESPACE.matcher(pool.getName()).replaceAll("-"));

            gauges.put(name(poolName, "usage"),
                    new RatioGauge() {
                @Override
                protected RatioGauge.Ratio getRatio() {
                    final long max = pool.getUsage().getMax() == -1
                            ? pool.getUsage().getCommitted()
                            : pool.getUsage().getMax();
                    return RatioGauge.Ratio.of(pool.getUsage().getUsed(), max);
                }
            });

            gauges.put(name(poolName, "max"), new Gauge() {
                @Override
                public Long getValue() {
                    return pool.getUsage().getMax();
                }
            });

            gauges.put(name(poolName, "used"), new Gauge() {
                @Override
                public Long getValue() {
                    return pool.getUsage().getUsed();
                }
            });

            gauges.put(name(poolName, "committed"), new Gauge() {
                @Override
                public Long getValue() {
                    return pool.getUsage().getCommitted();
                }
            });

            gauges.put(name(poolName, "init"), new Gauge() {
                @Override
                public Long getValue() {
                    return pool.getUsage().getInit();
                }
            });
        }

        return Collections.unmodifiableMap(gauges);
    }

    @Override
    public Map getGarbageCollector() {
        final Map gauges = new HashMap();
        for (final GarbageCollectorMXBean gc : garbageCollectors) {
            final String name = WHITESPACE.matcher(gc.getName()).replaceAll("-");
            gauges.put(name(name, "count"), new Gauge() {
                @Override
                public Long getValue() {
                    return gc.getCollectionCount();
                }
            });

            gauges.put(name(name, "time"), new Gauge() {
                @Override
                public Long getValue() {
                    return gc.getCollectionTime();
                }
            });
        }
        return Collections.unmodifiableMap(gauges);
    }

    @Override
    public Map getThreadStates() {
        final Map gauges = new HashMap();

        for (final Thread.State state : Thread.State.values()) {
            gauges.put(name(state.toString().toLowerCase(), "count"),
                    new Gauge() {
                @Override
                public Object getValue() {
                    return getThreadCount(state);
                }
            });
        }

        gauges.put("count", new Gauge() {
            @Override
            public Integer getValue() {
                return threads.getThreadCount();
            }
        });

        gauges.put("daemon.count", new Gauge() {
            @Override
            public Integer getValue() {
                return threads.getDaemonThreadCount();
            }
        });

        gauges.put("deadlock.count", new Gauge() {
            @Override
            public Integer getValue() {
                return getDeadlockedThreads().size();
            }
        });

        gauges.put("deadlocks", new Gauge>() {
            @Override
            public Set getValue() {
                return getDeadlockedThreads();
            }
        });

        return Collections.unmodifiableMap(gauges);
    }

    private int getThreadCount(Thread.State state) {
        final ThreadInfo[] allThreads = getThreadInfo();
        int count = 0;
        for (ThreadInfo info : allThreads) {
            if (info != null && info.getThreadState() == state) {
                count++;
            }
        }
        return count;
    }

    ThreadInfo[] getThreadInfo() {
        return threads.getThreadInfo(threads.getAllThreadIds(), STACK_TRACE_DEPTH);
    }

    /**
     * Returns a set of diagnostic stack traces for any deadlocked threads. If
     * no threads are deadlocked, returns an empty set.
     *
     * @return stack traces for deadlocked threads or an empty set
     */
    private Set getDeadlockedThreads() {
        final long[] ids = threads.findDeadlockedThreads();
        if (ids != null) {
            final Set deadlocks = new HashSet();
            for (ThreadInfo info : threads.getThreadInfo(ids, MAX_STACK_TRACE_DEPTH)) {
                final StringBuilder stackTrace = new StringBuilder();
                for (StackTraceElement element : info.getStackTrace()) {
                    stackTrace.append("\t at ")
                            .append(element.toString())
                            .append(String.format("%n"));
                }

                deadlocks.add(
                        String.format("%s locked on %s (owned by %s):%n%s",
                        info.getThreadName(),
                        info.getLockName(),
                        info.getLockOwnerName(),
                        stackTrace.toString()));
            }
            return Collections.unmodifiableSet(deadlocks);
        }
        return Collections.emptySet();
    }

    private static String name(String name, String... names) {
        final StringBuilder builder = new StringBuilder();
        append(builder, name);
        if (names != null) {
            for (String s : names) {
                append(builder, s);
            }
        }
        return builder.toString();
    }

    private static void append(StringBuilder builder, String part) {
        if (part != null && !part.isEmpty()) {
            if (builder.length() > 0) {
                builder.append('.');
            }
            builder.append(part);
        }
    }
}