blob: 6914670262f2b23d53d5c59bf1914269d682128b [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.ambari.server.controller.jmx;
import org.apache.ambari.server.controller.internal.AbstractPropertyProvider;
import org.apache.ambari.server.controller.internal.PropertyInfo;
import org.apache.ambari.server.controller.spi.*;
import org.apache.ambari.server.controller.utilities.StreamProvider;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Property provider implementation for JMX sources.
*/
public class JMXPropertyProvider extends AbstractPropertyProvider {
private static final String NAME_KEY = "name";
private static final String PORT_KEY = "tag.port";
private final StreamProvider streamProvider;
private final JMXHostProvider jmxHostProvider;
private static final Map<String, String> DEFAULT_JMX_PORTS = new HashMap<String, String>();
private final String clusterNamePropertyId;
private final String hostNamePropertyId;
private final String componentNamePropertyId;
private final static ObjectReader objectReader;
static {
DEFAULT_JMX_PORTS.put("NAMENODE", "50070");
DEFAULT_JMX_PORTS.put("DATANODE", "50075");
DEFAULT_JMX_PORTS.put("JOBTRACKER", "50030");
DEFAULT_JMX_PORTS.put("TASKTRACKER", "50060");
DEFAULT_JMX_PORTS.put("HBASE_MASTER", "60010");
DEFAULT_JMX_PORTS.put("HBASE_REGIONSERVER", "60030");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationConfig.Feature.USE_ANNOTATIONS, false);
objectReader = objectMapper.reader(JMXMetricHolder.class);
}
protected final static Logger LOG =
LoggerFactory.getLogger(JMXPropertyProvider.class);
// ----- Constructors ------------------------------------------------------
/**
* Create a JMX property provider.
*
* @param componentMetrics the map of supported metrics
* @param streamProvider the stream provider
* @param jmxHostProvider the host mapping
* @param clusterNamePropertyId the cluster name property id
* @param hostNamePropertyId the host name property id
* @param componentNamePropertyId the component name property id
*/
public JMXPropertyProvider(Map<String, Map<String, PropertyInfo>> componentMetrics,
StreamProvider streamProvider,
JMXHostProvider jmxHostProvider,
String clusterNamePropertyId,
String hostNamePropertyId,
String componentNamePropertyId) {
super(componentMetrics);
this.streamProvider = streamProvider;
this.jmxHostProvider = jmxHostProvider;
this.clusterNamePropertyId = clusterNamePropertyId;
this.hostNamePropertyId = hostNamePropertyId;
this.componentNamePropertyId = componentNamePropertyId;
}
// ----- PropertyProvider --------------------------------------------------
@Override
public Set<Resource> populateResources(Set<Resource> resources, Request request, Predicate predicate)
throws SystemException {
Set<Resource> keepers = new HashSet<Resource>();
for (Resource resource : resources) {
if (populateResource(resource, request, predicate)) {
keepers.add(resource);
}
}
return keepers;
}
// ----- helper methods ----------------------------------------------------
/**
* Get the spec to locate the JMX stream from the given host and port
*
* @param hostName the host name
* @param port the port
*
* @return the spec
*/
protected String getSpec(String hostName, String port) {
return "http://" + hostName + ":" + port + "/jmx";
}
/**
* Populate a resource by obtaining the requested JMX properties.
*
* @param resource the resource to be populated
* @param request the request
* @param predicate the predicate
*
* @return true if the resource was successfully populated with the requested properties
*/
private boolean populateResource(Resource resource, Request request, Predicate predicate)
throws SystemException {
Set<String> ids = getRequestPropertyIds(request, predicate);
if (ids.isEmpty()) {
return true;
}
String componentName = (String) resource.getPropertyValue(componentNamePropertyId);
if (getComponentMetrics().get(componentName) == null) {
// If there are no metrics defined for the given component then there is nothing to do.
return true;
}
String clusterName = (String) resource.getPropertyValue(clusterNamePropertyId);
String port = getPort(clusterName, componentName);
if (port == null) {
String error = "Unable to get JMX metrics. No port value for " + componentName;
logError(error, null);
throw new SystemException(error, null);
}
String hostName = getHost(resource, clusterName, componentName);
if (hostName == null) {
String error = "Unable to get JMX metrics. No host name for " + componentName;
logError(error, null);
throw new SystemException(error, null);
}
String spec = getSpec(hostName, port);
InputStream in = null;
try {
in = streamProvider.readFrom(spec);
JMXMetricHolder metricHolder = objectReader.readValue(in);
Map<String, Map<String, Object>> categories = new HashMap<String, Map<String, Object>>();
for (Map<String, Object> bean : metricHolder.getBeans()) {
String category = getCategory(bean);
if (category != null) {
categories.put(category, bean);
}
}
for (String propertyId : ids) {
Map<String, PropertyInfo> propertyInfoMap = getPropertyInfoMap(componentName, propertyId);
for (Map.Entry<String, PropertyInfo> entry : propertyInfoMap.entrySet()) {
PropertyInfo propertyInfo = entry.getValue();
propertyId = entry.getKey();
if (propertyInfo.isPointInTime()) {
String property = propertyInfo.getPropertyId();
String category = "";
List<String> keyList = new LinkedList<String>();
int keyStartIndex = property.indexOf('[', 0);
int firstKeyIndex = keyStartIndex > -1 ? keyStartIndex : property.length();
while (keyStartIndex > -1) {
int keyEndIndex = property.indexOf(']', keyStartIndex);
if (keyEndIndex > -1 & keyEndIndex > keyStartIndex) {
keyList.add(property.substring(keyStartIndex + 1, keyEndIndex));
keyStartIndex = property.indexOf('[', keyEndIndex);
}
else {
keyStartIndex = -1;
}
}
int dotIndex = property.lastIndexOf('.', firstKeyIndex - 1);
if (dotIndex != -1){
category = property.substring(0, dotIndex);
property = property.substring(dotIndex + 1, firstKeyIndex);
}
Map<String, Object> properties = categories.get(category);
if (properties != null && properties.containsKey(property)) {
Object value = properties.get(property);
if (keyList.size() > 0 && value instanceof Map) {
Map map = (Map) value;
for (String key : keyList) {
value = map.get(key);
if (value instanceof Map) {
map = (Map) value;
}
else {
break;
}
}
}
resource.setProperty(propertyId, value);
}
}
}
}
} catch (IOException e) {
logError(spec, e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
logError("Unable to close http input steam : spec=" + spec, e);
}
}
}
return true;
}
private String getPort(String clusterName, String componentName) throws SystemException {
String port = jmxHostProvider.getPort(clusterName, componentName);
return port == null ? DEFAULT_JMX_PORTS.get(componentName) : port;
}
private String getHost(Resource resource, String clusterName, String componentName) throws SystemException {
return hostNamePropertyId == null ?
jmxHostProvider.getHostName(clusterName, componentName) :
(String) resource.getPropertyValue(hostNamePropertyId);
}
private String getCategory(Map<String, Object> bean) {
if (bean.containsKey(NAME_KEY)) {
String name = (String) bean.get(NAME_KEY);
if (bean.containsKey(PORT_KEY)) {
String port = (String) bean.get(PORT_KEY);
name = name.replace("ForPort" + port, "");
}
return name;
}
return null;
}
private static void logError(String error, IOException e) {
if (LOG.isErrorEnabled()) {
if (e == null) {
LOG.error("Caught exception getting JMX metrics : spec=" + error);
} else {
LOG.error("Caught exception getting JMX metrics : spec=" + error, e);
}
}
}
}