/** * 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.internal; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.configuration.ComponentSSLConfiguration; import org.apache.ambari.server.controller.jmx.JMXHostProvider; import org.apache.ambari.server.controller.jmx.JMXPropertyProvider; import org.apache.ambari.server.controller.metrics.MetricHostProvider; import org.apache.ambari.server.controller.metrics.MetricPropertyProviderFactory; import org.apache.ambari.server.controller.metrics.MetricsPropertyProvider; import org.apache.ambari.server.controller.metrics.MetricsServiceProvider; import org.apache.ambari.server.controller.metrics.RestMetricsPropertyProvider; import org.apache.ambari.server.controller.metrics.timeline.cache.TimelineMetricCacheProvider; import org.apache.ambari.server.controller.spi.Predicate; import org.apache.ambari.server.controller.spi.PropertyProvider; import org.apache.ambari.server.controller.spi.Request; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.controller.spi.SystemException; import org.apache.ambari.server.controller.utilities.StreamProvider; import org.apache.ambari.server.security.authorization.AuthorizationException; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.stack.Metric; import org.apache.ambari.server.state.stack.MetricDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Injector; /** * This class analyzes a service's metrics to determine if additional * metrics should be fetched. It's okay to maintain state here since these * are done per-request. */ public class StackDefinedPropertyProvider implements PropertyProvider { private static final Logger LOG = LoggerFactory.getLogger(StackDefinedPropertyProvider.class); @Inject private static Clusters clusters = null; @Inject private static AmbariMetaInfo metaInfo = null; @Inject private static Injector injector = null; /** * A factory used to retrieve Guice-injected instances of a metric * {@link PropertyProvider}. */ @Inject private static MetricPropertyProviderFactory metricPropertyProviderFactory; private Resource.Type type = null; private String clusterNamePropertyId = null; private String hostNamePropertyId = null; private String componentNamePropertyId = null; private String resourceStatePropertyId = null; private ComponentSSLConfiguration sslConfig = null; private URLStreamProvider streamProvider = null; private JMXHostProvider jmxHostProvider; private PropertyProvider defaultJmx = null; private PropertyProvider defaultGanglia = null; private final MetricHostProvider metricHostProvider; private final MetricsServiceProvider metricsServiceProvider; private TimelineMetricCacheProvider cacheProvider; /** * PropertyHelper/AbstractPropertyProvider expect map of maps, * that's why we wrap metrics into map */ public static final String WRAPPED_METRICS_KEY = "WRAPPED_METRICS_KEY"; @Inject public static void init(Injector injector) { clusters = injector.getInstance(Clusters.class); metaInfo = injector.getInstance(AmbariMetaInfo.class); metricPropertyProviderFactory = injector.getInstance(MetricPropertyProviderFactory.class); StackDefinedPropertyProvider.injector = injector; } public StackDefinedPropertyProvider(Resource.Type type, JMXHostProvider jmxHostProvider, MetricHostProvider metricHostProvider, MetricsServiceProvider serviceProvider, URLStreamProvider streamProvider, String clusterPropertyId, String hostPropertyId, String componentPropertyId, String resourceStatePropertyId, PropertyProvider defaultJmxPropertyProvider, PropertyProvider defaultGangliaPropertyProvider) { this.metricHostProvider = metricHostProvider; metricsServiceProvider = serviceProvider; if (null == clusterPropertyId) { throw new NullPointerException("Cluster name property id cannot be null"); } if (null == componentPropertyId) { throw new NullPointerException("Component name property id cannot be null"); } this.type = type; clusterNamePropertyId = clusterPropertyId; hostNamePropertyId = hostPropertyId; componentNamePropertyId = componentPropertyId; this.resourceStatePropertyId = resourceStatePropertyId; this.jmxHostProvider = jmxHostProvider; sslConfig = ComponentSSLConfiguration.instance(); this.streamProvider = streamProvider; defaultJmx = defaultJmxPropertyProvider; defaultGanglia = defaultGangliaPropertyProvider; cacheProvider = injector.getInstance(TimelineMetricCacheProvider.class); } @Override public Set<Resource> populateResources(Set<Resource> resources, Request request, Predicate predicate) throws SystemException { // only arrange for one instance of Ganglia and JMX instantiation Map<String, Map<String, PropertyInfo>> gangliaMap = new HashMap<>(); Map<String, Map<String, PropertyInfo>> jmxMap = new HashMap<>(); List<PropertyProvider> additional = new ArrayList<>(); try { for (Resource r : resources) { String clusterName = r.getPropertyValue(clusterNamePropertyId).toString(); String componentName = r.getPropertyValue(componentNamePropertyId).toString(); Cluster cluster = clusters.getCluster(clusterName); StackId stack = cluster.getDesiredStackVersion(); String svc = metaInfo.getComponentToService(stack.getStackName(), stack.getStackVersion(), componentName); List<MetricDefinition> defs = metaInfo.getMetrics( stack.getStackName(), stack.getStackVersion(), svc, componentName, type.name()); if (null == defs || 0 == defs.size()) { continue; } for (MetricDefinition m : defs) { if (m.getType().equals("ganglia")) { gangliaMap.put(componentName, getPropertyInfo(m)); } else if (m.getType().equals("jmx")) { jmxMap.put(componentName, getPropertyInfo(m)); } else { PropertyProvider pp = getDelegate(m, streamProvider, metricHostProvider, clusterNamePropertyId, hostNamePropertyId, componentNamePropertyId, resourceStatePropertyId, componentName); if (pp == null) { pp = getDelegate(m); } if (pp != null) { additional.add(pp); } } } } if (gangliaMap.size() > 0) { PropertyProvider propertyProvider = MetricsPropertyProvider.createInstance(type, gangliaMap, streamProvider, sslConfig, cacheProvider, metricHostProvider, metricsServiceProvider, clusterNamePropertyId, hostNamePropertyId, componentNamePropertyId); propertyProvider.populateResources(resources, request, predicate); } else { defaultGanglia.populateResources(resources, request, predicate); } if (jmxMap.size() > 0) { JMXPropertyProvider jpp = metricPropertyProviderFactory.createJMXPropertyProvider(jmxMap, streamProvider, jmxHostProvider, metricHostProvider, clusterNamePropertyId, hostNamePropertyId, componentNamePropertyId, resourceStatePropertyId); jpp.populateResources(resources, request, predicate); } else { defaultJmx.populateResources(resources, request, predicate); } for (PropertyProvider pp : additional) { pp.populateResources(resources, request, predicate); } } catch (AuthorizationException e) { // Need to rethrow the catched 'AuthorizationException'. throw e; } catch (Exception e) { LOG.error("Error loading deferred resources", e); throw new SystemException("Error loading deferred resources", e); } return resources; } @Override public Set<String> checkPropertyIds(Set<String> propertyIds) { return Collections.emptySet(); } /** * @param def the metric definition * @return the converted Map required for JMX or Ganglia execution. * Format: <metric name, property info> */ public static Map<String, PropertyInfo> getPropertyInfo(MetricDefinition def) { Map<String, PropertyInfo> defs = new HashMap<>(); for (Entry<String, Metric> entry : def.getMetrics().entrySet()) { Metric metric = entry.getValue(); if (metric.getName() != null) { PropertyInfo propertyInfo = new PropertyInfo(metric.getName(), metric.isTemporal(), metric.isPointInTime()); propertyInfo.setAmsHostMetric(metric.isAmsHostMetric()); propertyInfo.setUnit(metric.getUnit()); defs.put(entry.getKey(), propertyInfo); } } return defs; } /** * @param definition metric definition for a component and resource type combination * @return the custom property provider */ private PropertyProvider getDelegate(MetricDefinition definition) { try { Class<?> clz = Class.forName(definition.getType()); // singleton/factory try { Method m = clz.getMethod("getInstance", Map.class, Map.class); Object o = m.invoke(null, definition.getProperties(), definition.getMetrics()); return PropertyProvider.class.cast(o); } catch (Exception e) { LOG.info("Could not load singleton or factory method for type '" + definition.getType()); } // try maps constructor try { Constructor<?> ct = clz.getConstructor(Map.class, Map.class); Object o = ct.newInstance(definition.getProperties(), definition.getMetrics()); return PropertyProvider.class.cast(o); } catch (Exception e) { LOG.info("Could not find contructor for type '" + definition.getType()); } // just new instance return PropertyProvider.class.cast(clz.newInstance()); } catch (Exception e) { LOG.error("Could not load class " + definition.getType()); return null; } } /** * * @param definition the metric definition for a component * @param streamProvider the stream provider * @param metricsHostProvider the metrics host provider * @param clusterNamePropertyId the cluster name property id * @param hostNamePropertyId the host name property id * @param componentNamePropertyId the component name property id * @param statePropertyId the state property id * @return the custom property provider */ private PropertyProvider getDelegate(MetricDefinition definition, StreamProvider streamProvider, MetricHostProvider metricsHostProvider, String clusterNamePropertyId, String hostNamePropertyId, String componentNamePropertyId, String statePropertyId, String componentName) { Map<String, PropertyInfo> metrics = getPropertyInfo(definition); HashMap<String, Map<String, PropertyInfo>> componentMetrics = new HashMap<>(); componentMetrics.put(WRAPPED_METRICS_KEY, metrics); try { Class<?> clz = Class.forName(definition.getType()); // use a Factory for the REST provider if (clz.equals(RestMetricsPropertyProvider.class)) { return metricPropertyProviderFactory.createRESTMetricsPropertyProvider( definition.getProperties(), componentMetrics, streamProvider, metricsHostProvider, clusterNamePropertyId, hostNamePropertyId, componentNamePropertyId, statePropertyId, componentName); } try { /* * Warning: this branch is already used, that's why please adjust * all implementations when modifying constructor interface */ Constructor<?> ct = clz.getConstructor(Map.class, Map.class, StreamProvider.class, MetricHostProvider.class, String.class, String.class, String.class, String.class, String.class); Object o = ct.newInstance( injector, definition.getProperties(), componentMetrics, streamProvider, metricsHostProvider, clusterNamePropertyId, hostNamePropertyId, componentNamePropertyId, statePropertyId, componentName); return PropertyProvider.class.cast(o); } catch (Exception e) { LOG.info("Could not find contructor for type '" + definition.getType()); } // just new instance return PropertyProvider.class.cast(clz.newInstance()); } catch (Exception e) { LOG.error("Could not load class " + definition.getType()); return null; } } }