/* * RHQ Management Platform * Copyright (C) 2005-2013 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 as published by * the Free Software Foundation version 2 of the License. * * 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 for more details. * * You should have received a copy of the GNU 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 static org.rhq.core.pluginapi.util.ResponseTimeConfiguration.RESPONSE_TIME_LOG_FILE_CONFIG_PROP; import static org.rhq.core.pluginapi.util.ResponseTimeLogFinder.FALLBACK_RESPONSE_TIME_LOG_FILE_DIRECTORY; import static org.rhq.core.pluginapi.util.ResponseTimeLogFinder.findResponseTimeLogFileInDirectory; import static org.rhq.plugins.jbossas5.ApplicationServerPluginConfigurationProperties.SERVER_HOME_DIR; import java.io.File; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; 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.jetbrains.annotations.Nullable; import org.jboss.deployers.spi.management.ManagementView; import org.jboss.managed.api.ComponentType; import org.jboss.managed.api.ManagedComponent; import org.jboss.managed.api.ManagedDeployment; import org.jboss.profileservice.spi.NoSuchDeploymentException; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails; import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent; import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext; import org.rhq.core.util.StringUtil; 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 component for discovering the contexts of a WAR - one context per vhost the WAR is deployed to. * * @author Ian Springer */ public class WebApplicationContextDiscoveryComponent implements ResourceDiscoveryComponent<AbstractManagedDeploymentComponent> { private static final String CONTEXT_COMPONENT_NAME = "ContextMO"; // A regex for the names of all MBean:WebApplication components for a WAR // (one component per vhost that WAR is deployed to). private static final String WEB_APPLICATION_COMPONENT_NAMES_REGEX_TEMPLATE = "jboss.web:J2EEApplication=none,J2EEServer=none,j2eeType=WebModule,name=//[^/]+%" + WebApplicationContextComponent.CONTEXT_PATH_PROPERTY + "%"; // The name of the MBean:WebApplicationManager component for a WAR. private static final String WEB_APPLICATION_MANAGER_COMPONENT_NAME_TEMPLATE = "jboss.web:host=%" + WebApplicationContextComponent.VIRTUAL_HOST_PROPERTY + "%," + "path=%" + WebApplicationContextComponent.CONTEXT_PATH_PROPERTY + "%,type=Manager"; // TODO check cluster case private final Log log = LogFactory.getLog(this.getClass()); public Set<DiscoveredResourceDetails> discoverResources( ResourceDiscoveryContext<AbstractManagedDeploymentComponent> discoveryContext) throws Exception { ResourceType resourceType = discoveryContext.getResourceType(); log.trace("Discovering " + resourceType.getName() + " Resources..."); AbstractManagedDeploymentComponent parentWarComponent = discoveryContext.getParentResourceComponent(); ManagementView managementView = parentWarComponent.getConnection().getManagementView(); // TODO (ips): Only refresh the ManagementView *once* per runtime discovery scan, rather than every time this // method is called. Do this by providing a runtime scan id in the ResourceDiscoveryContext. managementView.load(); String contextPath = getContextPath(discoveryContext); Set<ManagedComponent> webApplicationComponents = getWebApplicationComponents(contextPath, managementView); Set<DiscoveredResourceDetails> discoveredResources = new LinkedHashSet(webApplicationComponents.size()); for (ManagedComponent webApplicationComponent : webApplicationComponents) { String virtualHost = getWebApplicationComponentVirtualHost(webApplicationComponent); String resourceName = "//" + virtualHost + contextPath; //noinspection UnnecessaryLocalVariable String resourceKey = virtualHost; String resourceVersion = null; Configuration pluginConfig = discoveryContext.getDefaultPluginConfiguration(); // Make sure to set the "virtualHost" and "contextPath" props before setting the "componentName" props, // since those two props are referenced in the template for the "componentName" property. pluginConfig.put(new PropertySimple(WebApplicationContextComponent.VIRTUAL_HOST_PROPERTY, virtualHost)); pluginConfig.put(new PropertySimple(WebApplicationContextComponent.CONTEXT_PATH_PROPERTY, contextPath)); // e.g. "jboss.web:J2EEApplication=none,J2EEServer=none,j2eeType=WebModule,name=//localhost/jmx-console" String webApplicationManagerComponentName = ResourceComponentUtils.replacePropertyExpressionsInTemplate( WEB_APPLICATION_MANAGER_COMPONENT_NAME_TEMPLATE, pluginConfig); pluginConfig.put(new PropertySimple(ManagedComponentComponent.Config.COMPONENT_NAME, webApplicationManagerComponentName)); discoverResponseTimeLogFile(parentWarComponent, contextPath, pluginConfig); DiscoveredResourceDetails resource = new DiscoveredResourceDetails(resourceType, resourceKey, resourceName, resourceVersion, resourceType.getDescription(), pluginConfig, null); discoveredResources.add(resource); } log.trace("Discovered " + discoveredResources.size() + " " + resourceType.getName() + " Resources."); return discoveredResources; } private void discoverResponseTimeLogFile(AbstractManagedDeploymentComponent parentWarComponent, String contextPath, Configuration pluginConfig) { String rtFilePath = null; // First search in SERVER_HOME_DIR/log/rt directory ApplicationServerComponent applicationServerComponent = ApplicationServerComponent .findApplicationServerComponent(parentWarComponent); if (applicationServerComponent != null) { String serverHomeDir = applicationServerComponent.getResourceContext().getPluginConfiguration() .getSimpleValue(SERVER_HOME_DIR); if (StringUtil.isNotBlank(serverHomeDir)) { File rtDirectory = new File(serverHomeDir + File.separator + "log" + File.separator + "rt"); rtFilePath = findResponseTimeLogFileInDirectory(contextPath, rtDirectory); } } if (rtFilePath == null) { // Search again in FALLBACK_RESPONSE_TIME_LOG_FILE_DIRECTORY rtFilePath = findResponseTimeLogFileInDirectory(contextPath, FALLBACK_RESPONSE_TIME_LOG_FILE_DIRECTORY); } if (rtFilePath != null) { pluginConfig.setSimpleValue(RESPONSE_TIME_LOG_FILE_CONFIG_PROP, rtFilePath); } } /** * Returns the parent WAR's context path (e.g. "/jmx-console"), or <code>null</code> if the WAR is currently * stopped, since stopped WARs are not associated with any contexts. * * @return this WAR's context path (e.g. "/jmx-console"), or <code>null</code> if the WAR is currently stopped, * since stopped WARs are not associated with any contexts * @throws NoSuchDeploymentException if the WAR is no longer deployed */ @Nullable private String getContextPath(ResourceDiscoveryContext<AbstractManagedDeploymentComponent> discoveryContext) throws NoSuchDeploymentException { AbstractManagedDeploymentComponent parentWarComponent = discoveryContext.getParentResourceComponent(); ManagedDeployment deployment = parentWarComponent.getManagedDeployment(); ManagedComponent contextComponent = deployment.getComponent(CONTEXT_COMPONENT_NAME); // e.g. "/jmx-console" if (contextComponent != null) { return (String) ManagedComponentUtils.getSimplePropertyValue(contextComponent, "contextRoot"); } else { return null; } } static Set<String> getVirtualHosts(String contextPath, ManagementView managementView) throws Exception { Set<String> virtualHosts = new HashSet(); Set<ManagedComponent> webApplicationManagerComponents = getWebApplicationComponents(contextPath, managementView); for (ManagedComponent webApplicationManagerComponent : webApplicationManagerComponents) { virtualHosts.add(getWebApplicationComponentVirtualHost(webApplicationManagerComponent)); } return virtualHosts; } private static Set<ManagedComponent> getWebApplicationComponents(String contextPath, ManagementView managementView) throws Exception { if (contextPath == null) { // This means the WAR is stopped, which means it has no contexts associated with it. return Collections.emptySet(); } String webApplicationManagerComponentNamesRegex = WEB_APPLICATION_COMPONENT_NAMES_REGEX_TEMPLATE.replaceAll("%" + WebApplicationContextComponent.CONTEXT_PATH_PROPERTY + "%", contextPath); ComponentType webApplicationComponentType = MoreKnownComponentTypes.MBean.WebApplication.getType(); //return managementView.getMatchingComponents(webApplicationManagerComponentNamesRegex, // webApplicationComponentType, new RegularExpressionNameMatcher()); return ManagedComponentUtils.getManagedComponents(managementView, webApplicationComponentType, webApplicationManagerComponentNamesRegex, new RegularExpressionNameMatcher()); } private static String getWebApplicationComponentVirtualHost(ManagedComponent webApplicationComponent) { ObjectName objectName; try { objectName = new ObjectName(webApplicationComponent.getName()); } catch (MalformedObjectNameException e) { throw new IllegalStateException("'" + webApplicationComponent.getName() + "' is not a valid JMX ObjectName."); } // name like "//localhost/foo/bar", return just the host portion, e.g. "localhost". String hostName = objectName.getKeyProperty("name").substring(2); hostName = hostName.substring(0, hostName.indexOf("/")); return hostName; } }