/* * Jopr Management Platform * Copyright (C) 2005-2009 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.jbossas5; import java.io.File; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.deployers.spi.management.ManagementView; import org.jboss.managed.api.ComponentType; import org.jboss.managed.api.ManagedComponent; import org.jboss.managed.api.ManagedProperty; import org.rhq.core.domain.configuration.Configuration; 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.domain.measurement.calltime.CallTimeData; import org.rhq.core.pluginapi.inventory.ResourceContext; import org.rhq.core.pluginapi.util.ResponseTimeConfiguration; import org.rhq.core.pluginapi.util.ResponseTimeLogParser; import org.rhq.plugins.jbossas5.helper.MoreKnownComponentTypes; import org.rhq.plugins.jbossas5.util.ManagedComponentUtils; import org.rhq.plugins.jbossas5.util.RegularExpressionNameMatcher; import org.rhq.plugins.jbossas5.util.ResourceComponentUtils; /** * A Resource component for web application contexts (e.g. "//localhost/jmx-console"). * * @author Ian Springer */ public class WebApplicationContextComponent extends ManagedComponentComponent { public static final String VIRTUAL_HOST_PROPERTY = "virtualHost"; public static final String CONTEXT_PATH_PROPERTY = "contextPath"; private static final String RESPONSE_TIME_METRIC = "responseTime"; public static final String RESPONSE_TIME_LOG_FILE_CONFIG_PROP = "responseTimeLogFile"; private static final String VIRTUAL_HOST_TRAIT = "virtualHost"; private static final String CLUSTERED_TRAIT = "clustered"; private static final String DISTRIBUTABLE_MANAGED_PROPERTY = "Distributable"; // A regex for the names of all MBean:Servlet components for a WAR. private static final String SERVLET_COMPONENT_NAMES_REGEX_TEMPLATE = "jboss.web:J2EEApplication=none,J2EEServer=none," + "WebModule=//%" + VIRTUAL_HOST_PROPERTY + "%" + "%" + CONTEXT_PATH_PROPERTY + "%,j2eeType=Servlet,name=[^,]+"; private static final String SERVLET_METRIC_PREFIX = "Servlet."; private static final String SERVLET_MAXIMUM_RESPONSE_TIME_METRIC = "Servlet.maximumResponseTime"; private static final String SERVLET_MINIMUM_RESPONSE_TIME_METRIC = "Servlet.minimumResponseTime"; private static final String SERVLET_AVERAGE_RESPONSE_TIME_METRIC = "Servlet.averageResponseTime"; private static final String SERVLET_TOTAL_RESPONSE_TIME_METRIC = "Servlet.totalResponseTime"; private static final String SERVLET_REQUEST_COUNT_METRIC = "Servlet.requestCount"; private static final String SERVLET_ERROR_COUNT_METRIC = "Servlet.errorCount"; private final Log log = LogFactory.getLog(this.getClass()); private String servletComponentNamesRegex; private ResponseTimeLogParser logParser; private Boolean clustered; @Override public void start(ResourceContext<ProfileServiceComponent<?>> resourceContext) throws Exception { super.start(resourceContext); Configuration pluginConfig = getResourceContext().getPluginConfiguration(); this.servletComponentNamesRegex = ResourceComponentUtils.replacePropertyExpressionsInTemplate( SERVLET_COMPONENT_NAMES_REGEX_TEMPLATE, pluginConfig); ResponseTimeConfiguration responseTimeConfig = new ResponseTimeConfiguration(pluginConfig); File logFile = responseTimeConfig.getLogFile(); if (logFile != null) { this.logParser = new ResponseTimeLogParser(logFile); this.logParser.setExcludes(responseTimeConfig.getExcludes()); this.logParser.setTransforms(responseTimeConfig.getTransforms()); } } @Override public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> requests) throws Exception { ProfileServiceComponent warComponent = getResourceContext().getParentResourceComponent(); ManagementView managementView = warComponent.getConnection().getManagementView(); ManagedComponent component = getManagedComponent(); for (MeasurementScheduleRequest request : requests) { String metricName = request.getName(); try { if (metricName.startsWith(SERVLET_METRIC_PREFIX)) { Double value = getServletMetric(managementView, metricName); MeasurementDataNumeric metric = new MeasurementDataNumeric(request, value); report.addData(metric); } else if (metricName.equals(VIRTUAL_HOST_TRAIT)) { Configuration pluginConfig = getResourceContext().getPluginConfiguration(); String virtualHost = pluginConfig.getSimple(VIRTUAL_HOST_PROPERTY).getStringValue(); MeasurementDataTrait trait = new MeasurementDataTrait(request, virtualHost); report.addData(trait); } else if (metricName.equals(RESPONSE_TIME_METRIC)) { if (this.logParser != null) { try { CallTimeData callTimeData = new CallTimeData(request); this.logParser.parseLog(callTimeData); report.addData(callTimeData); } catch (Exception e) { log.error("Failed to retrieve HTTP call-time data.", e); } } else { log.error("The '" + RESPONSE_TIME_METRIC + "' metric is enabled for WAR resource '" + getComponentName() + "', but no value is defined for the '" + RESPONSE_TIME_LOG_FILE_CONFIG_PROP + "' connection property."); // TODO: Communicate this error back to the server for display in the GUI. } } else if (metricName.equals(CLUSTERED_TRAIT)) { if(clustered == null){ retrieveClusteredProperty(); } if (clustered != null) { MeasurementDataTrait trait = new MeasurementDataTrait(request, clustered.toString()); report.addData(trait); } } else { String value = null; /* * Bug 737996 - Working around the property names inconsistency in the clustered web app contexts. * If clustered we need to capitalize the first letter of the metric name to workaround JBPAPP-7172. * If null is returned then we can assume that JBPAPP-7172 does not apply here and use the expected * metric name. */ if (clustered == null) { retrieveClusteredProperty(); } if (clustered != null && clustered && !"runState".equals(metricName)) { value = getMeasurement(component, toUpperCaseFirstLetter(metricName)); } // value should be null unless app is clustered and JBPAPP-7172 applies if (value == null) { value = getMeasurement(component, metricName); } addValueToMeasurementReport(report, request, value); } } catch (Exception e) { // Don't let one bad apple spoil the barrel. log.error("Failed to collect metric '" + metricName + "' for " + getResourceContext().getResourceType() + " Resource with key " + getResourceContext().getResourceKey() + ".", e); } } } /** * @deprecated The clustered property should be retrieved by the individual component in the * specific use case that requires it. Leaving this method for backwards compatibility * with plugins that use the AS5 plugin and also use reflection. */ @Deprecated public boolean isClustered() { if (clustered == null) { return false; } return clustered; } private void retrieveClusteredProperty() { try { ManagedProperty distributableProp = getManagedComponent().getProperty(DISTRIBUTABLE_MANAGED_PROPERTY); if (distributableProp != null) { Boolean distributable = (Boolean) getInnerValue(distributableProp.getValue()); clustered = distributable != null && distributable.booleanValue(); } } catch (Exception e) { log.warn("Failed to determine whether the web app context " + this.getResourceContext().getResourceKey() + " is clustered or not.", e); } } private Double getServletMetric(ManagementView managementView, String metricName) throws Exception { ComponentType servletComponentType = MoreKnownComponentTypes.MBean.Servlet.getType(); //Set<ManagedComponent> servletComponents = managementView.getMatchingComponents(this.servletComponentNamesRegex, // servletComponentType, new RegularExpressionNameMatcher()); Set<ManagedComponent> servletComponents = ManagedComponentUtils.getManagedComponents(managementView, servletComponentType, this.servletComponentNamesRegex, new RegularExpressionNameMatcher()); long min = Long.MAX_VALUE; long max = 0; long processingTime = 0; int requestCount = 0; int errorCount = 0; for (ManagedComponent servletComponent : servletComponents) { if (metricName.equals(SERVLET_MINIMUM_RESPONSE_TIME_METRIC)) { Long longValue = (Long) ManagedComponentUtils.getSimplePropertyValue(servletComponent, "minTime"); if (longValue < min) min = longValue; } else if (metricName.equals(SERVLET_MAXIMUM_RESPONSE_TIME_METRIC)) { Long longValue = (Long) ManagedComponentUtils.getSimplePropertyValue(servletComponent, "maxTime"); if (longValue > max) max = longValue; } else if (metricName.equals(SERVLET_AVERAGE_RESPONSE_TIME_METRIC)) { Long longValue = (Long) ManagedComponentUtils .getSimplePropertyValue(servletComponent, "processingTime"); processingTime += longValue; Integer intValue = (Integer) ManagedComponentUtils.getSimplePropertyValue(servletComponent, "requestCount"); requestCount += intValue; } else if (metricName.equals(SERVLET_REQUEST_COUNT_METRIC)) { Integer intValue = (Integer) ManagedComponentUtils.getSimplePropertyValue(servletComponent, "requestCount"); requestCount += intValue; } else if (metricName.equals(SERVLET_ERROR_COUNT_METRIC)) { Integer intValue = (Integer) ManagedComponentUtils.getSimplePropertyValue(servletComponent, "errorCount"); errorCount += intValue; } else if (metricName.equals(SERVLET_TOTAL_RESPONSE_TIME_METRIC)) { Long longValue = (Long) ManagedComponentUtils .getSimplePropertyValue(servletComponent, "processingTime"); processingTime += longValue; } } Double result; if (metricName.equals(SERVLET_AVERAGE_RESPONSE_TIME_METRIC)) { result = (requestCount > 0) ? ((double) processingTime / (double) requestCount) : Double.NaN; } else if (metricName.equals(SERVLET_MINIMUM_RESPONSE_TIME_METRIC)) { result = (min != Long.MAX_VALUE) ? (double) min : Double.NaN; } else if (metricName.equals(SERVLET_MAXIMUM_RESPONSE_TIME_METRIC)) { result = (max != 0) ? (double) max : Double.NaN; } else if (metricName.equals(SERVLET_ERROR_COUNT_METRIC)) { result = (double) errorCount; } else if (metricName.equals(SERVLET_REQUEST_COUNT_METRIC)) { result = (double) requestCount; } else if (metricName.equals(SERVLET_TOTAL_RESPONSE_TIME_METRIC)) { result = (double) processingTime; } else { // fallback result = Double.NaN; } return result; } private static String toUpperCaseFirstLetter(String name) { if (name == null || name.length() == 0) { return name; } char first = name.charAt(0); return Character.toUpperCase(first) + name.substring(1); } }