/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.solr.metrics.reporters; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import java.util.Arrays; import com.codahale.metrics.ObjectNameFactory; import org.apache.solr.metrics.SolrMetricInfo; /** * Factory to create MBean names for a given metric. */ public class JmxObjectNameFactory implements ObjectNameFactory { private final String domain; private final String[] subdomains; private final String reporterName; private final String[] props; /** * Create ObjectName factory. * @param reporterName name of the reporter * @param domain JMX domain name * @param additionalProperties additional properties as key, value pairs. */ public JmxObjectNameFactory(String reporterName, String domain, String... additionalProperties) { this.reporterName = reporterName.replaceAll(":", "_"); this.domain = domain; this.subdomains = domain.replaceAll(":", "_").split("\\."); if (additionalProperties != null && (additionalProperties.length % 2) != 0) { throw new IllegalArgumentException("additionalProperties length must be even: " + Arrays.toString(additionalProperties)); } this.props = additionalProperties; } /** * Return current domain. */ public String getDomain() { return domain; } /** * Return current reporterName. */ public String getReporterName() { return reporterName; } /** * Create a hierarchical name. * * @param type metric class, eg. "counters", may be null for non-metric MBeans * @param currentDomain JMX domain * @param name object name */ @Override public ObjectName createName(String type, String currentDomain, String name) { SolrMetricInfo metricInfo = SolrMetricInfo.of(name); String safeName = metricInfo != null ? metricInfo.name : name; safeName = safeName.replaceAll(":", "_"); // It turns out that ObjectName(String) mostly preserves key ordering // as specified in the constructor (except for the 'type' key that ends // up at top level) - unlike ObjectName(String, Map) constructor // that seems to have a mind of its own... StringBuilder sb = new StringBuilder(); if (domain.equals(currentDomain)) { if (subdomains != null && subdomains.length > 1) { // use only first segment as domain sb.append(subdomains[0]); sb.append(':'); // use remaining segments as properties for (int i = 1; i < subdomains.length; i++) { if (i > 1) { sb.append(','); } sb.append("dom"); sb.append(String.valueOf(i)); sb.append('='); sb.append(subdomains[i]); } sb.append(','); // separate from other properties } else { sb.append(currentDomain.replaceAll(":", "_")); sb.append(':'); } } else { sb.append(currentDomain); sb.append(':'); } if (props != null && props.length > 0) { boolean added = false; for (int i = 0; i < props.length; i += 2) { if (props[i] == null || props[i].isEmpty()) { continue; } if (props[i + 1] == null || props[i + 1].isEmpty()) { continue; } sb.append(','); sb.append(props[i]); sb.append('='); sb.append(props[i + 1]); added = true; } if (added) { sb.append(','); } } if (metricInfo != null) { sb.append("category="); sb.append(metricInfo.category.toString()); if (metricInfo.scope != null) { sb.append(",scope="); sb.append(metricInfo.scope); } // we could also split by type, but don't call it 'type' :) // if (type != null) { // sb.append(",class="); // sb.append(type); // } sb.append(",name="); sb.append(safeName); } else { // make dotted names into hierarchies String[] path = safeName.split("\\."); for (int i = 0; i < path.length - 1; i++) { if (i > 0) { sb.append(','); } sb.append("name"); sb.append(String.valueOf(i)); sb.append('='); sb.append(path[i]); } if (path.length > 1) { sb.append(','); } // split by type // if (type != null) { // sb.append("class="); // sb.append(type); // } sb.append("name="); sb.append(path[path.length - 1]); } ObjectName objectName; try { objectName = new ObjectName(sb.toString()); } catch (MalformedObjectNameException e) { throw new RuntimeException(sb.toString(), e); } return objectName; } }