/* * Copyright 2015-2017 JKOOL, LLC. * * 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 com.jkoolcloud.tnt4j.stream.jmx; import java.io.IOException; import java.io.PrintStream; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.management.MBeanAttributeInfo; import javax.management.ObjectName; import javax.management.openmbean.CompositeData; import javax.management.openmbean.TabularData; import com.jkoolcloud.tnt4j.core.Activity; import com.jkoolcloud.tnt4j.core.PropertySnapshot; import com.jkoolcloud.tnt4j.stream.jmx.conditions.AttributeSample; import com.jkoolcloud.tnt4j.stream.jmx.core.SampleContext; import com.jkoolcloud.tnt4j.stream.jmx.core.SampleListener; import com.jkoolcloud.tnt4j.stream.jmx.core.UnsupportedAttributeException; /** * <p> * This class provide a default implementation of a {@link SampleListener} * </p> * * @version $Revision: 1 $ * * @see SampleListener */ public class DefaultSampleListener implements SampleListener { public static String STAT_TRACE_MODE = "listener.trace.mode"; public static String STAT_EXCLUDE_SET_COUNT = "listener.exclude.set.count"; boolean trace = false; PrintStream out; HashSet<MBeanAttributeInfo> excAttrs = new HashSet<MBeanAttributeInfo>(89); /** * Create an instance of {@code DefaultSampleListener} with a a given print stream and trace mode * * @param pStream print stream instance for tracing * @param trace mode */ public DefaultSampleListener(PrintStream pStream, boolean trace) { this.trace = trace; this.out = pStream == null ? System.out : pStream; } /** * Determine if a given attribute to be excluded from sampling. * * @param attr MBean attribute info * @return true when attribute should be excluded, false otherwise */ protected boolean isExcluded(MBeanAttributeInfo attr) { return excAttrs.contains(attr); } /** * Mark a given attribute to be excluded from sampling. * * @param attr MBean attribute info */ protected void exclude(MBeanAttributeInfo attr) { excAttrs.add(attr); } @Override public void pre(SampleContext context, Activity activity) { if (trace) { out.println("Pre: " + activity.getName() + ": sample.count=" + context.getSampleCount() + ", mbean.count=" + getMBeanCount(context) + ", sample.mbeans.count=" + context.getMBeanCount() + ", exclude.attr.set=" + excAttrs.size() + ", total.noop.count=" + context.getTotalNoopCount() + ", total.exclude.count=" + context.getExcludeAttrCount() + ", total.error.count=" + context.getTotalErrorCount() + ", tracking.id=" + activity.getTrackingId() + ", mbean.server=" + context.getMBeanServer() ); } } @Override public void pre(SampleContext context, AttributeSample sample) { sample.excludeNext(!sample.getAttributeInfo().isReadable() || isExcluded(sample.getAttributeInfo())); } @Override public void post(SampleContext context, AttributeSample sample) throws UnsupportedAttributeException { MBeanAttributeInfo jinfo = sample.getAttributeInfo(); PropertySnapshot snapshot = sample.getSnapshot(); processAttrValue(snapshot, jinfo, jinfo.getName(), sample.get()); } @Override public void post(SampleContext context, Activity activity) { if (trace) { out.println("Post: " + activity.getName() + ": sample.count=" + context.getSampleCount() + ", mbean.count=" + getMBeanCount(context) + ", elapsed.usec=" + activity.getElapsedTimeUsec() + ", snap.count=" + activity.getSnapshotCount() + ", id.count=" + activity.getIdCount() + ", sample.mbeans.count=" + context.getMBeanCount() + ", sample.metric.count=" + context.getLastMetricCount() + ", sample.time.usec=" + context.getLastSampleUsec() + ", exclude.attr.set=" + excAttrs.size() + ", total.noop.count=" + context.getTotalNoopCount() + ", total.exclude.count=" + context.getExcludeAttrCount() + ", total.error.count=" + context.getTotalErrorCount() + ", tracking.id=" + activity.getTrackingId() + ", mbean.server=" + context.getMBeanServer() ); } } private static String getMBeanCount(SampleContext context) { try { return String.valueOf(context.getMBeanServer().getMBeanCount()); } catch (IOException exc) { return exc.getLocalizedMessage(); } } @Override public void error(SampleContext context, AttributeSample sample) { sample.excludeNext(true); if (trace) { out.println("Failed to sample: " + sample.getAttributeInfo() + ", exclude=" + sample.excludeNext() + ", ex=" + sample.getError()); sample.getError().printStackTrace(out); } if (sample.excludeNext()) { exclude(sample.getAttributeInfo()); } } @Override public void getStats(SampleContext context, Map<String, Object> stats) { stats.put(STAT_TRACE_MODE, trace); stats.put(STAT_EXCLUDE_SET_COUNT, excAttrs.size()); } @Override public void register(SampleContext context, ObjectName oName) { if (trace) { out.println("Register mbean: " + oName + ", mbean.server=" + context.getMBeanServer()); } } @Override public void unregister(SampleContext context, ObjectName oName) { if (trace) { out.println("Unregister mbean: " + oName + ", mbean.server=" + context.getMBeanServer()); } } @Override public void error(SampleContext context, Throwable ex) { out.println("Unexpected error when sampling mbean.server=" + context.getMBeanServer()); ex.printStackTrace(out); } /** * Process/extract value from a given MBean attribute * * @param snapshot instance where extracted attribute is stored * @param jinfo attribute info * @param propName name to be assigned to given attribute value * @param value associated with attribute * @return snapshot instance where all attributes are contained */ private PropertySnapshot processAttrValue(PropertySnapshot snapshot, MBeanAttributeInfo jinfo, String propName, Object value) { if (value instanceof CompositeData) { CompositeData cdata = (CompositeData) value; Set<String> keys = cdata.getCompositeType().keySet(); for (String key : keys) { Object cVal = cdata.get(key); processAttrValue(snapshot, jinfo, propName + "\\" + key, cVal); } } else if (value instanceof TabularData) { TabularData tData = (TabularData) value; Collection<?> values = tData.values(); int row = 0; for (Object tVal : values) { processAttrValue(snapshot, jinfo, propName + "\\" + padNumber(++row), tVal); } } else { snapshot.add(propName, value); } return snapshot; } private static String padNumber(int idx) { return idx < 10 ? "0" + idx : String.valueOf(idx); } }