/** * Copyright (c) 2009-2011 VMware, Inc. All Rights Reserved. * * 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.springsource.insight.plugin.jmx; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.management.MalformedObjectNameException; import com.springsource.insight.collection.method.custom.CustomScoreGenerator; import com.springsource.insight.collection.method.custom.ScoreGeneratorsFactory; import com.springsource.insight.intercept.endpoint.AbstractSingleTypeEndpointAnalyzer; import com.springsource.insight.intercept.endpoint.EndPointAnalysis; import com.springsource.insight.intercept.endpoint.EndPointName; import com.springsource.insight.intercept.operation.Operation; import com.springsource.insight.intercept.operation.OperationList; import com.springsource.insight.intercept.operation.OperationUtils; import com.springsource.insight.intercept.operation.method.JoinPointBreakDown; import com.springsource.insight.intercept.plugin.CollectionSettingName; import com.springsource.insight.intercept.plugin.CollectionSettingsRegistry; import com.springsource.insight.intercept.plugin.CollectionSettingsUpdateListener; import com.springsource.insight.intercept.resource.ResourceKey; import com.springsource.insight.intercept.trace.Frame; import com.springsource.insight.util.ArrayUtil; import com.springsource.insight.util.StringFormatterUtils; import com.springsource.insight.util.StringUtil; import com.springsource.insight.util.logging.InsightLogManager; import com.springsource.insight.util.logging.InsightLogger; import com.springsource.insight.util.props.AggregateNamedPropertySource; import com.springsource.insight.util.props.NamedPropertySource; import com.springsource.insight.util.props.PropertiesUtil; /** * */ public class JmxInvocationEndPointAnalyzer extends AbstractSingleTypeEndpointAnalyzer implements CollectionSettingsUpdateListener { public static final String DOMAIN_NAME_PROP = ResourceKey.DOMAIN_NAME_PROP, METHOD_NAME_PROP = "invokedMethod", SIGNATURE_NAME_PROP = "invokedSignature", INVOCATION_ARGS_PROP = "invocationArgs"; public static final String NAME_KEY = "name", TYPE_KEY = "type"; public static final String SIMPLE_BEAN_NAME_FORMAT = "${" + NAME_KEY + PropertiesUtil.PROP_NAME_CHOICE_STRING + TYPE_KEY + "}"; public static final CollectionSettingName ENDPOINT_FORMAT = new CollectionSettingName("endpoint.format", JmxPluginRuntimeDescriptor.PLUGIN_NAME, "Formats the endpoint value"); public static final String DEFAULT_ENDPOINT_FORMAT = "${" + DOMAIN_NAME_PROP + "}" + "." + SIMPLE_BEAN_NAME_FORMAT + "#${" + METHOD_NAME_PROP + "}" + "(${" + SIGNATURE_NAME_PROP + "})"; public static final CollectionSettingName LABEL_FORMAT = new CollectionSettingName("label.format", JmxPluginRuntimeDescriptor.PLUGIN_NAME, "Formats the endpoint label"); public static final String DEFAULT_LABEL_FORMAT = SIMPLE_BEAN_NAME_FORMAT + "#${" + METHOD_NAME_PROP + "}" + "(${" + SIGNATURE_NAME_PROP + "})"; public static final CollectionSettingName EXAMPLE_FORMAT = new CollectionSettingName("example.format", JmxPluginRuntimeDescriptor.PLUGIN_NAME, "Formats the endpoint example text"); public static final String DEFAULT_EXAMPLE_FORMAT = DEFAULT_LABEL_FORMAT; public static final CollectionSettingName SCORE_VALUE = new CollectionSettingName("score.value", JmxPluginRuntimeDescriptor.PLUGIN_NAME, "Formats the endpoint score"); public static final String DEFAULT_SCORE_FORMAT = ScoreGeneratorsFactory.DEFAULT_SCORE; public static final List<CollectionSettingName> SETTINGS = Collections.unmodifiableList( Arrays.asList(ENDPOINT_FORMAT, LABEL_FORMAT, EXAMPLE_FORMAT, SCORE_VALUE)); private final Map<CollectionSettingName, String> formatsMap = Collections.synchronizedMap( new TreeMap<CollectionSettingName, String>(CollectionSettingName.BY_KEY_COMPARATOR)); private static class LazyFieldHolder { @SuppressWarnings("synthetic-access") private static final JmxInvocationEndPointAnalyzer INSTANCE = new JmxInvocationEndPointAnalyzer(); } private JmxInvocationEndPointAnalyzer() { this(CollectionSettingsRegistry.getInstance()); } // jUnit tests JmxInvocationEndPointAnalyzer(CollectionSettingsRegistry registry) { super(JmxPluginRuntimeDescriptor.INVOKE); if (registry == null) { throw new IllegalArgumentException("No registry"); } registry.addListener(this); registry.register(ENDPOINT_FORMAT, DEFAULT_ENDPOINT_FORMAT); registry.register(LABEL_FORMAT, DEFAULT_LABEL_FORMAT); registry.register(EXAMPLE_FORMAT, DEFAULT_EXAMPLE_FORMAT); registry.register(SCORE_VALUE, DEFAULT_SCORE_FORMAT); } @SuppressWarnings("synthetic-access") public static final JmxInvocationEndPointAnalyzer getInstance() { return LazyFieldHolder.INSTANCE; } @Override protected EndPointAnalysis makeEndPoint(Frame frame, int depth) { Operation operation = frame.getOperation(); NamedPropertySource props = toPropertySource(operation); String endpointName = PropertiesUtil.format(getSettingFormat(ENDPOINT_FORMAT), props); return new EndPointAnalysis(EndPointName.valueOf(endpointName), PropertiesUtil.format(getSettingFormat(LABEL_FORMAT), props), PropertiesUtil.format(getSettingFormat(EXAMPLE_FORMAT), props), getOperationScore(operation, depth), operation); } public String getSettingFormat(CollectionSettingName name) { if (name == null) { return null; } else { return formatsMap.get(name); } } @Override protected int getOperationScore(Operation op, int depth) { Number score = resolveOperationScore(op, getSettingFormat(SCORE_VALUE)); if (score != null) { // check if have an override or a formatted value return score.intValue(); } else { return super.getOperationScore(op, depth); } } public void incrementalUpdate(CollectionSettingName name, Serializable value) { if ((name == null) || (value == null) || (!SETTINGS.contains(name))) { return; } String formatValue = value.toString(), prevValue = formatsMap.put(name, formatValue); if (StringUtil.safeCompare(formatValue, prevValue) != 0) { _logger.info("incrementalUpdate(" + name + ") " + prevValue + " => " + formatValue); } } public static final Number resolveOperationScore(Operation op, String scoreMode) { Number score = op.get(EndPointAnalysis.SCORE_FIELD, Number.class); if (score != null) { // check if have an override in the operation return score; } String scoreType = scoreMode; if (StringUtil.isEmpty(scoreType)) { scoreType = DEFAULT_SCORE_FORMAT; } ScoreGeneratorsFactory factory = ScoreGeneratorsFactory.getInstance(); try { CustomScoreGenerator gen = factory.resolveCustomScoreGenerator(scoreType); // NOTE: the default score generator returns null if ((score = gen.calculateOperationScore(op, null, JmxInvocationEndPointAnalyzer.class.getSimpleName())) != null) { return score; } } catch (RuntimeException e) { InsightLogger logger = InsightLogManager.getLogger(JmxInvocationEndPointAnalyzer.class.getName()); logger.warning("resolveOperationScore(" + scoreType + ")" + " failed (" + e.getClass().getSimpleName() + ")" + " to generate score: " + e.getMessage()); } return null; } public static final Operation updateOperation(Operation op, String methodName, String[] signature, Object[] argVals) { op.type(JmxPluginRuntimeDescriptor.INVOKE) .label(JoinPointBreakDown.getMethodStringFromArgs(methodName, signature)) .putAnyNonEmpty(METHOD_NAME_PROP, methodName) .putAnyNonEmpty(SIGNATURE_NAME_PROP, JoinPointBreakDown.createMethodParamsSignature(signature)) ; updateInvocationArgs(op, argVals); return op; } public static final OperationList updateInvocationArgs(Operation op, Object... argVals) { return updateInvocationArgs(op.createList(INVOCATION_ARGS_PROP), argVals); } public static final OperationList updateInvocationArgs(OperationList op, Object... argVals) { if (ArrayUtil.length(argVals) <= 0) { return op; } for (Object v : argVals) { op.add(StringFormatterUtils.formatObject(v)); } return op; } public static final NamedPropertySource toPropertySource(final Operation op) { if (op == null) { return PropertiesUtil.EMPTY_SOURCE; } NamedPropertySource opProps = OperationUtils.toPropertySource(op); String beanName = op.get(JmxPluginRuntimeDescriptor.BEAN_NAME_PROP, String.class); if (StringUtil.isEmpty(beanName)) { return opProps; } try { NamedPropertySource beanProps = ResourceKey.toPropertySource(beanName); // NOTE: order is important - especially for the 'type' property return new AggregateNamedPropertySource(beanProps, opProps); } catch (MalformedObjectNameException e) { InsightLogger logger = InsightLogManager.getLogger(JmxInvocationEndPointAnalyzer.class.getName()); logger.warning("Failed (" + e.getClass().getSimpleName() + ")" + " to convert " + beanName + " to property source: " + e.getMessage()); return opProps; } } }