/* * Jopr Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.plugins.jbosscache; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdom.Attribute; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.mc4j.ems.connection.EmsConnection; import org.mc4j.ems.connection.bean.EmsBean; import org.mc4j.ems.connection.bean.attribute.EmsAttribute; import org.mc4j.ems.connection.bean.operation.EmsOperation; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.ConfigurationUpdateStatus; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.domain.configuration.definition.ConfigurationDefinition; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.domain.measurement.DataType; import org.rhq.core.domain.measurement.MeasurementDataNumeric; import org.rhq.core.domain.measurement.MeasurementDataTrait; import org.rhq.core.domain.measurement.MeasurementReport; import org.rhq.core.domain.measurement.MeasurementScheduleRequest; import org.rhq.core.pluginapi.configuration.ConfigurationFacet; import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport; import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException; import org.rhq.core.pluginapi.inventory.ResourceComponent; import org.rhq.core.pluginapi.inventory.ResourceContext; import org.rhq.core.pluginapi.measurement.MeasurementFacet; import org.rhq.core.pluginapi.operation.OperationFacet; import org.rhq.core.pluginapi.operation.OperationResult; import org.rhq.plugins.jbossas.util.DeploymentUtility; import org.rhq.plugins.jmx.JMXComponent; import org.rhq.plugins.jmx.util.ObjectNameQueryUtility; /** * Get statistic for JBossCache instances * @author Heiko W. Rupp * */ public class JBossCacheComponent implements ResourceComponent<JMXComponent<?>>, MeasurementFacet, OperationFacet, ConfigurationFacet { private final static Log log = LogFactory.getLog(JBossCacheComponent.class); private String baseObjectName; JBossCacheSubsystemComponent parentServer; boolean isTreeCache = false; List<EmsBean> interceptors = new ArrayList<EmsBean>(); ResourceContext context; public void start(ResourceContext context) throws Exception { PropertySimple objectName = context.getPluginConfiguration().getSimple("objectName"); baseObjectName = objectName.getStringValue(); this.context = context; PropertySimple tcProp = context.getPluginConfiguration().getSimple("isTreeCache"); if (tcProp == null || tcProp.getBooleanValue() == null) throw new InvalidPluginConfigurationException("Cache flavour not provided"); else isTreeCache = tcProp.getBooleanValue(); parentServer = (JBossCacheSubsystemComponent) context.getParentResourceComponent(); String query = baseObjectName + ","; if (isTreeCache) query += "tree"; query += "cache-interceptor=%name%"; ObjectNameQueryUtility util = new ObjectNameQueryUtility(query); query = util.getTranslatedQuery(); EmsConnection connection = parentServer.getEmsConnection(); if (connection != null) interceptors = connection.queryBeans(query); } public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> requests) { for (MeasurementScheduleRequest metric : requests) { String name = metric.getName(); if (log.isDebugEnabled()) log.debug("Trying to get metric " + name); int pos = name.indexOf(":"); if (pos > -1) { String bean; bean = baseObjectName; if (pos > 0) { bean += ","; if (isTreeCache) bean += "tree"; bean += "cache-interceptor="; bean += name.substring(0, pos); } String attr = name.substring(pos + 1); EmsConnection conn = parentServer.getEmsConnection(); EmsBean eBean = conn.getBean(bean); if (eBean != null) { List<String> attrs = new ArrayList<String>(); eBean.refreshAttributes(attrs); // only refresh selecte attrs, as there might be non-serializable ones EmsAttribute eAttr = eBean.getAttribute(attr); if (metric.getDataType() == DataType.MEASUREMENT) { Double val = ((Number) (eAttr.getValue())).doubleValue(); MeasurementDataNumeric ret = new MeasurementDataNumeric(metric, val); report.addData(ret); } else if (metric.getDataType() == DataType.TRAIT) { MeasurementDataTrait ret = new MeasurementDataTrait(metric, String.valueOf(eAttr.getValue())); report.addData(ret); } else log.warn("Unknown data type " + metric); } else if (log.isDebugEnabled()) log.debug("Bean " + bean + " not found "); } else log.warn("illegal metric " + metric + " skipping .."); } } public AvailabilityType getAvailability() { try { EmsConnection connection = parentServer.getEmsConnection(); if (connection == null) return AvailabilityType.DOWN; boolean up = connection.getBean(baseObjectName).isRegistered(); return up ? AvailabilityType.UP : AvailabilityType.DOWN; } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Can not determine availability for " + baseObjectName + ": " + e.getMessage()); return AvailabilityType.DOWN; } } public void stop() { // TODO Auto-generated method stub } public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException, Exception { OperationResult result = null; if ("resetStatistics".equals(name)) { for (EmsBean bean : interceptors) { EmsOperation ops = bean.getOperation("resetStatistics"); if (ops != null) // base bean has no resetStatistics ops.invoke(new Object[] {}); } result = null; // no result } else if ("listAssociatedMBeans".equals(name)) { StringBuilder sb = new StringBuilder(); for (EmsBean bean : interceptors) { sb.append(bean.getBeanName().getCanonicalName()); sb.append(" "); } result = new OperationResult(sb.toString()); } return result; } /** * Load the configuration from the actual resource */ public Configuration loadResourceConfiguration() throws Exception { File file = DeploymentUtility.getDescriptorFile(parentServer.getEmsConnection(), context.getResourceKey()); if (file == null) { log.warn("Can not find the deployment descriptor for this cache "); return null; } try { SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(file); // Get the root element Element root = doc.getRootElement(); // First look for the right mbean of *our* cache - the file may contain more than one Configuration config = new Configuration(); for (Object mbeanObj : root.getChildren("mbean")) { if (mbeanObj instanceof Element) { Element mbean = (Element) mbeanObj; // normalize the content of 'name' String nameAttrib = mbean.getAttributeValue("name"); try { ObjectName on = new ObjectName(nameAttrib); nameAttrib = on.getCanonicalName(); } catch (MalformedObjectNameException e) { log.warn("Can't canonicalize " + nameAttrib); } if (nameAttrib.equals(context.getResourceKey())) { // found ours, let the fun begin ConfigurationDefinition configDef = context.getResourceType().getResourceConfigurationDefinition(); fillAttributesInConfig(mbean, config, configDef); Attribute code = mbean.getAttribute("code"); PropertySimple flavour = new PropertySimple(); flavour.setName("Flavour"); if (code.getValue().contains("Tree")) { flavour.setStringValue("treecache"); } else { flavour.setStringValue("cache"); } config.put(flavour); } } } return config; } catch (IOException e) { log.error("IO error occurred while reading file: " + file, e); } catch (JDOMException e) { log.error("Parsing error occurred while reading file: " + file, e); } return null; } EmsConnection getEmsConnection() { return parentServer.getEmsConnection(); } /** * Fill all the <attribute name="XXX"> elements found under mbean into the passed config. * * @param mbean The >mbean< element that builds the root of the JBossCache config * @param config The configuration object to fill stuff in * @param configDef */ private void fillAttributesInConfig(Element mbean, Configuration config, ConfigurationDefinition configDef) { List children = mbean.getChildren("attribute"); for (Object childObj : children) { if (childObj instanceof Element) { Element child = (Element) childObj; String name = child.getAttributeValue("name"); // Only add a prop to the config if the mbean attribute has a corresponding prop def in the config def. if (configDef.getPropertyDefinitionSimple(name) != null) { String value = child.getText(); PropertySimple prop = new PropertySimple(name, value); config.put(prop); } } } } /** * Write an updated version of the configuration to the resource */ public void updateResourceConfiguration(ConfigurationUpdateReport report) { Configuration newConfig = report.getConfiguration(); // Remove any props in the config that don't have corresponding prop defs in the config def - this works // around a bug in versions of the plugin prior to RHQ 4.2. ConfigurationDefinition configDef = context.getResourceType().getResourceConfigurationDefinition(); for (String propName : newConfig.getSimpleProperties().keySet()) { if (configDef.getPropertyDefinitionSimple(propName) == null) { newConfig.remove(propName); } } String mbeanName = context.getResourceKey(); File file = DeploymentUtility.getDescriptorFile(parentServer.getEmsConnection(), mbeanName); if (file == null) { report.setStatus(ConfigurationUpdateStatus.FAILURE); report.setErrorMessage("Failed to determine the deployment descriptor file for mbean '" + mbeanName + "'."); return; } CacheConfigurationHelper helper = new CacheConfigurationHelper(); try { helper.writeConfig(file, newConfig, mbeanName, true); report.setStatus(ConfigurationUpdateStatus.SUCCESS); } catch (Exception e) { log.error(e); // TODO do more? report.setStatus(ConfigurationUpdateStatus.FAILURE); report.setErrorMessageFromThrowable(e); } } }