blob: 36eb83a88e1e7b609bb3adf9fe406a92a90fc6b4 [file] [log] [blame]
/*
* 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.jmx;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import java.util.Arrays;
import com.codahale.metrics.jmx.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;
}
}