/** * 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.core; import org.apache.solr.common.SolrException; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrConfig.JmxConfiguration; import javax.management.*; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p> * Responsible for finding (or creating) a MBeanServer from given configuration * and registering all SolrInfoMBean objects with JMX. * </p> * <p/> * <p> * Please see http://wiki.apache.org/solr/SolrJmx for instructions on usage and configuration * </p> * * @version $Id: JmxMonitoredMap.java 945245 2010-05-17 17:18:10Z rmuir $ * @see org.apache.solr.core.SolrConfig.JmxConfiguration * @since solr 1.3 */ public class JmxMonitoredMap<K, V> extends ConcurrentHashMap<String, SolrInfoMBean> { private static final Logger LOG = LoggerFactory.getLogger(JmxMonitoredMap.class .getName()); private MBeanServer server = null; private String jmxRootName; public JmxMonitoredMap(final String coreName, final JmxConfiguration jmxConfig) { jmxRootName = (null != jmxConfig.rootName ? jmxConfig.rootName : ("solr" + (null != coreName ? "/" + coreName : ""))); if (jmxConfig.serviceUrl == null) { List<MBeanServer> servers = null; if (jmxConfig.agentId == null) { // Try to find the first MBeanServer servers = MBeanServerFactory.findMBeanServer(null); } else if (jmxConfig.agentId != null) { // Try to find the first MBean server with the given agentId servers = MBeanServerFactory.findMBeanServer(jmxConfig.agentId); // throw Exception if no servers were found with the given agentId if (servers == null || servers.isEmpty()) throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "No JMX Servers found with agentId: " + jmxConfig.agentId); } if (servers == null || servers.isEmpty()) { LOG.info("No JMX servers found, not exposing Solr information with JMX."); return; } server = servers.get(0); LOG.info("JMX monitoring is enabled. Adding Solr mbeans to JMX Server: " + server); } else { try { // Create a new MBeanServer with the given serviceUrl server = MBeanServerFactory.newMBeanServer(); JMXConnectorServer connector = JMXConnectorServerFactory .newJMXConnectorServer(new JMXServiceURL(jmxConfig.serviceUrl), null, server); connector.start(); LOG.info("JMX monitoring is enabled at " + jmxConfig.serviceUrl); } catch (Exception e) { // Release the reference server = null; throw new RuntimeException("Could not start JMX monitoring ", e); } } } /** * Clears the map and unregisters all SolrInfoMBeans in the map from * MBeanServer */ @Override public void clear() { if (server != null) { for (Map.Entry<String, SolrInfoMBean> entry : entrySet()) { unregister(entry.getKey(), entry.getValue()); } } super.clear(); } /** * Adds the SolrInfoMBean to the map and registers the given SolrInfoMBean * instance with the MBeanServer defined for this core. If a SolrInfoMBean is * already registered with the MBeanServer then it is unregistered and then * re-registered. * * @param key the JMX type name for this SolrInfoMBean * @param infoBean the SolrInfoMBean instance to be registered */ @Override public SolrInfoMBean put(String key, SolrInfoMBean infoBean) { if (server != null && infoBean != null) { try { ObjectName name = getObjectName(key, infoBean); if (server.isRegistered(name)) server.unregisterMBean(name); SolrDynamicMBean mbean = new SolrDynamicMBean(infoBean); server.registerMBean(mbean, name); } catch (Exception e) { LOG.warn( "Failed to register info bean: " + key, e); } } return super.put(key, infoBean); } /** * Removes the SolrInfoMBean object at the given key and unregisters it from * MBeanServer * * @param key the JMX type name for this SolrInfoMBean */ @Override public SolrInfoMBean remove(Object key) { SolrInfoMBean infoBean = get(key); if (infoBean != null) { try { unregister((String) key, infoBean); } catch (RuntimeException e) { LOG.warn( "Failed to unregister info bean: " + key, e); } } return super.remove(key); } private void unregister(String key, SolrInfoMBean infoBean) { if (server == null) return; try { ObjectName name = getObjectName(key, infoBean); if (server.isRegistered(name)) { server.unregisterMBean(name); } else { LOG.info("Failed to unregister mbean: " + key + " because it was not registered"); } } catch (Exception e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Failed to unregister info bean: " + key, e); } } private ObjectName getObjectName(String key, SolrInfoMBean infoBean) throws MalformedObjectNameException { Hashtable<String, String> map = new Hashtable<String, String>(); map.put("type", key); if (infoBean.getName() != null && !"".equals(infoBean.getName())) { map.put("id", infoBean.getName()); } return ObjectName.getInstance(jmxRootName, map); } /** * DynamicMBean is used to dynamically expose all SolrInfoMBean * getStatistics() NameList keys as String getters. */ static class SolrDynamicMBean implements DynamicMBean { private SolrInfoMBean infoBean; private HashSet<String> staticStats; public SolrDynamicMBean(SolrInfoMBean managedResource) { this.infoBean = managedResource; staticStats = new HashSet<String>(); // For which getters are already available in SolrInfoMBean staticStats.add("name"); staticStats.add("version"); staticStats.add("description"); staticStats.add("category"); staticStats.add("sourceId"); staticStats.add("source"); } public MBeanInfo getMBeanInfo() { ArrayList<MBeanAttributeInfo> attrInfoList = new ArrayList<MBeanAttributeInfo>(); for (String stat : staticStats) { attrInfoList.add(new MBeanAttributeInfo(stat, String.class.getName(), null, true, false, false)); } try { NamedList dynamicStats = infoBean.getStatistics(); if (dynamicStats != null) { for (int i = 0; i < dynamicStats.size(); i++) { String name = dynamicStats.getName(i); if (!staticStats.contains(name)) attrInfoList.add(new MBeanAttributeInfo(dynamicStats.getName(i), String.class.getName(), null, true, false, false)); } } } catch (Exception e) { LOG.warn( "Could not getStatistics on info bean " + infoBean.getName(), e); } MBeanAttributeInfo[] attrInfoArr = attrInfoList .toArray(new MBeanAttributeInfo[attrInfoList.size()]); return new MBeanInfo(getClass().getName(), infoBean .getDescription(), attrInfoArr, null, null, null); } public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { Object val; if (staticStats.contains(attribute) && attribute != null && attribute.length() > 0) { try { String getter = "get" + attribute.substring(0, 1).toUpperCase(Locale.ENGLISH) + attribute.substring(1); Method meth = infoBean.getClass().getMethod(getter); val = meth.invoke(infoBean); } catch (Exception e) { throw new AttributeNotFoundException(attribute); } } else { NamedList list = infoBean.getStatistics(); val = list.get(attribute); } if (val != null) return val.toString(); else return val; } public AttributeList getAttributes(String[] attributes) { AttributeList list = new AttributeList(); for (String attribute : attributes) { try { list.add(new Attribute(attribute, getAttribute(attribute))); } catch (Exception e) { LOG.warn("Could not get attibute " + attribute); } } return list; } public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { throw new UnsupportedOperationException("Operation not Supported"); } public AttributeList setAttributes(AttributeList attributes) { throw new UnsupportedOperationException("Operation not Supported"); } public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { throw new UnsupportedOperationException("Operation not Supported"); } } }