PageRenderTime 62ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorComponent.java

https://github.com/metlos/RHQ-old
Java | 289 lines | 156 code | 42 blank | 91 comment | 28 complexity | 3c18cb67d57b5a2c9e7172c1eda75c1f MD5 | raw file
  1. /*
  2. * Jopr Management Platform
  3. * Copyright (C) 2005-2008 Red Hat, Inc.
  4. * All rights reserved.
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License, version 2, as
  8. * published by the Free Software Foundation, and/or the GNU Lesser
  9. * General Public License, version 2.1, also as published by the Free
  10. * Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License and the GNU Lesser General Public License
  16. * for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * and the GNU Lesser General Public License along with this program;
  20. * if not, write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. */
  23. package org.jboss.on.plugins.tomcat;
  24. import java.net.InetAddress;
  25. import java.net.UnknownHostException;
  26. import java.util.List;
  27. import java.util.Set;
  28. import org.apache.commons.logging.Log;
  29. import org.apache.commons.logging.LogFactory;
  30. import org.mc4j.ems.connection.EmsConnection;
  31. import org.mc4j.ems.connection.bean.EmsBean;
  32. import org.mc4j.ems.connection.bean.attribute.EmsAttribute;
  33. import org.rhq.core.domain.configuration.Configuration;
  34. import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
  35. import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
  36. import org.rhq.core.domain.configuration.definition.PropertyDefinition;
  37. import org.rhq.core.domain.measurement.AvailabilityType;
  38. import org.rhq.core.domain.measurement.MeasurementDataNumeric;
  39. import org.rhq.core.domain.measurement.MeasurementReport;
  40. import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
  41. import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
  42. import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
  43. import org.rhq.core.pluginapi.inventory.ResourceContext;
  44. import org.rhq.plugins.jmx.MBeanResourceComponent;
  45. import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
  46. /**
  47. * Plugin component for representing Tomcat connectors. Much of the functionality is left to the super class,
  48. * however the metrics required special handling.
  49. *
  50. * @author Jay Shaughnessy
  51. * @author Jason Dobies
  52. */
  53. public class TomcatConnectorComponent extends MBeanResourceComponent<TomcatServerComponent<?>> {
  54. /**
  55. * property name for the protocol of the connector
  56. */
  57. public static final String CONFIG_PROTOCOL = "protocol";
  58. /**
  59. * V5 only property
  60. */
  61. public static final String CONFIG_V5_KEEP_ALIVE_TIMEOUT = "keepAliveTimeout";
  62. /**
  63. * Plugin property name for the address the connector is bound to.
  64. */
  65. public static final String PLUGIN_CONFIG_ADDRESS = "address";
  66. /**
  67. * Plugin property name for the connector type the connector is bound to.
  68. */
  69. public static final String PLUGIN_CONFIG_CONNECTOR = "connector";
  70. /**
  71. * Plugin property name for the protocol handler. This prefix is used in the associated GlobalRequestProcessor object name.
  72. */
  73. public static final String PLUGIN_CONFIG_HANDLER = "handler";
  74. /**
  75. * Plugin property name for the name.
  76. */
  77. public static final String PLUGIN_CONFIG_NAME = "name";
  78. /**
  79. * Plugin property name for the port the connector is listening on.
  80. */
  81. public static final String PLUGIN_CONFIG_PORT = "port";
  82. /**
  83. * Plugin property name for the optional shared executor the connector is using for its threadpool.
  84. * If this property is left unset, the connector is not using a shared executor.
  85. */
  86. public static final String PLUGIN_CONFIG_SHARED_EXECUTOR = "sharedExecutorName";
  87. public static final String UNKNOWN = "?";
  88. private final Log log = LogFactory.getLog(this.getClass());
  89. @Override
  90. public AvailabilityType getAvailability() {
  91. // First, ensure the underlying mbean for the connector is active
  92. AvailabilityType result = super.getAvailability();
  93. if (AvailabilityType.UP == result) {
  94. // When the connector is stopped its associated GlobalRequestProcessor will not exist. We test
  95. // for availability by checking the existence of objectName Catalina:type=GlobalRequestProcessor,name=%handler%[%address%]-%port%.
  96. String objectName = getGlobalRequestProcessorName();
  97. EmsConnection connection = getEmsConnection();
  98. ObjectNameQueryUtility queryUtility = new ObjectNameQueryUtility(objectName);
  99. List<EmsBean> beans = connection.queryBeans(queryUtility.getTranslatedQuery());
  100. result = (beans.isEmpty()) ? AvailabilityType.DOWN : AvailabilityType.UP;
  101. }
  102. return result;
  103. }
  104. @Override
  105. public void start(ResourceContext<TomcatServerComponent<?>> context) {
  106. if (UNKNOWN.equals(context.getPluginConfiguration().getSimple(PLUGIN_CONFIG_HANDLER).getStringValue())) {
  107. throw new InvalidPluginConfigurationException(
  108. "The connector is not listening for requests on the configured port. This is most likely due to the configured port being in use at Tomcat startup. In some cases (AJP connectors) Tomcat will assign an open port. This happens most often when there are multiple Tomcat servers running on the same platform. Check your Tomcat configuration for conflicts: "
  109. + context.getResourceKey());
  110. }
  111. super.start(context);
  112. }
  113. @Override
  114. public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> requests) {
  115. getEmsConnection(); // reload the EMS connection
  116. for (MeasurementScheduleRequest request : requests) {
  117. String req = request.getName();
  118. req = switchConnectorThreadpoolName(req);
  119. req = getAttributeName(req);
  120. String beanName = req.substring(0, req.lastIndexOf(':'));
  121. String attributeName = req.substring(req.lastIndexOf(':') + 1);
  122. try {
  123. // Bean is cached by EMS, so no problem with getting the bean from the connection on each call
  124. EmsBean eBean = loadBean(beanName);
  125. if (eBean == null) {
  126. log.warn("Bean " + beanName + " not found, skipping ...");
  127. continue;
  128. }
  129. EmsAttribute attribute = eBean.getAttribute(attributeName);
  130. Object valueObject = attribute.refresh();
  131. Number value = (Number) valueObject;
  132. report.addData(new MeasurementDataNumeric(request, value.doubleValue()));
  133. } catch (Exception e) {
  134. log.error("Failed to obtain measurement [" + req + "]", e);
  135. }
  136. }
  137. }
  138. /**
  139. * If the given name represents a property name for a threadpool metric, this will switch
  140. * the name IF the connector is using a shared executor for its threadpool. If the connector is
  141. * not using a shared executor, nothing has to be switched and we can use the original name
  142. * passed to this method.
  143. *
  144. * See BZ 795531.
  145. *
  146. * @param property the metric property name that may need to be switched if it is a threadpool metric
  147. * @return the name for the metric property, switched to use the shared executor name if appropriate
  148. */
  149. private String switchConnectorThreadpoolName(String property) {
  150. Configuration pluginConfiguration = getResourceContext().getPluginConfiguration();
  151. String sharedExecutorName = pluginConfiguration.getSimpleValue(PLUGIN_CONFIG_SHARED_EXECUTOR, "");
  152. if (sharedExecutorName == null || sharedExecutorName.trim().isEmpty()) {
  153. return property; // there is nothing special to do if the connector isn't using a shared executor for its threadpool
  154. }
  155. // 1) Catalina:type=ThreadPool,name=%name%:currentThreadsBusy
  156. // will be replaced with:
  157. // Catalina:type=Executor,name=<name of shared executor>:activeCount
  158. //
  159. // 2) Catalina:type=ThreadPool,name=%name%:currentThreadCount
  160. // will be replaced with:
  161. // Catalina:type=Executor,name=<name of shared executor>:poolSize
  162. //
  163. // 3) Catalina:type=ThreadPool,name=%name%:maxThreads
  164. // will be replaced with
  165. // Catalina:type=Executor,name=<name of shared executor>:maxThreads
  166. final String NON_SHARED_THREADS_ACTIVE = "Catalina:type=ThreadPool,name=%name%:currentThreadsBusy";
  167. final String NON_SHARED_THREADS_ALLOCATED = "Catalina:type=ThreadPool,name=%name%:currentThreadCount";
  168. final String NON_SHARED_THREADS_MAX = "Catalina:type=ThreadPool,name=%name%:maxThreads";
  169. final String SHARED_THREADS_ACTIVE = "Catalina:type=Executor,name=XXX:activeCount";
  170. final String SHARED_THREADS_ALLOCATED = "Catalina:type=Executor,name=XXX:poolSize";
  171. final String SHARED_THREADS_MAX = "Catalina:type=Executor,name=XXX:maxThreads";
  172. if (property.equals(NON_SHARED_THREADS_ACTIVE)) {
  173. property = SHARED_THREADS_ACTIVE;
  174. } else if (property.equals(NON_SHARED_THREADS_ALLOCATED)) {
  175. property = SHARED_THREADS_ALLOCATED;
  176. } else if (property.equals(NON_SHARED_THREADS_MAX)) {
  177. property = SHARED_THREADS_MAX;
  178. } else {
  179. return property; // this isn't one of the names we need to switch, immediate return the original name as-is
  180. }
  181. property = property.replace("XXX", sharedExecutorName);
  182. return property;
  183. }
  184. /**
  185. * Get the real name of the passed property for a concrete connector. The actual object name will begin with:
  186. * Catalina:type=GlobalRequestProcessor,name=handler[-address]-port. We need to
  187. * substitute in the address and port of this particular connector before the value can be read. In the plugin
  188. * descriptor, these are written as %handler%, %address% and %port% respectively, so we can replace on those.
  189. */
  190. @Override
  191. protected String getAttributeName(String property) {
  192. String theProperty = replaceGlobalRequestProcessorNameProps(property);
  193. if (log.isDebugEnabled()) {
  194. log.debug("Finding metrics for: " + theProperty);
  195. }
  196. return theProperty;
  197. }
  198. private String getGlobalRequestProcessorName() {
  199. return replaceGlobalRequestProcessorNameProps("Catalina:type=GlobalRequestProcessor,name=%name%");
  200. }
  201. private String replaceGlobalRequestProcessorNameProps(String property) {
  202. Configuration pluginConfiguration = getResourceContext().getPluginConfiguration();
  203. String name = pluginConfiguration.getSimple(PLUGIN_CONFIG_NAME).getStringValue();
  204. String result = property.replace("%name%", name);
  205. return result;
  206. }
  207. @Override
  208. public void updateResourceConfiguration(ConfigurationUpdateReport report) {
  209. // When starting the component get the connector type specific property keys
  210. ResourceContext<TomcatServerComponent<?>> context = getResourceContext();
  211. ConfigurationDefinition configDef = context.getResourceType().getResourceConfigurationDefinition();
  212. String protocol = report.getConfiguration().getSimpleValue(CONFIG_PROTOCOL, null);
  213. if ((null == protocol) || protocol.toUpperCase().contains("HTTP")) {
  214. // remove AJP only properties
  215. for (PropertyDefinition propDef : configDef.getPropertiesInGroup("AJP")) {
  216. report.getConfiguration().remove(propDef.getName());
  217. }
  218. }
  219. if ((null == protocol) || protocol.toUpperCase().contains("AJP")) {
  220. for (PropertyDefinition propDef : configDef.getPropertiesInGroup("HTTP")) {
  221. report.getConfiguration().remove(propDef.getName());
  222. }
  223. for (PropertyDefinition propDef : configDef.getPropertiesInGroup("HTTP SSL")) {
  224. report.getConfiguration().remove(propDef.getName());
  225. }
  226. }
  227. if (getResourceContext().getParentResourceComponent().getResourceContext().getVersion().startsWith("5")) {
  228. report.getConfiguration().remove(CONFIG_V5_KEEP_ALIVE_TIMEOUT);
  229. }
  230. super.updateResourceConfiguration(report);
  231. // if the mbean update failed, return now
  232. if (ConfigurationUpdateStatus.SUCCESS != report.getStatus()) {
  233. return;
  234. }
  235. // If all went well, persist the changes to the Tomcat server.xml
  236. try {
  237. storeConfig();
  238. } catch (Exception e) {
  239. report
  240. .setErrorMessage("Failed to persist configuration change. Changes will not survive Tomcat restart unless a successful Store Configuration operation is performed.");
  241. }
  242. }
  243. /** Persist local changes to the server.xml */
  244. void storeConfig() throws Exception {
  245. this.getResourceContext().getParentResourceComponent().storeConfig();
  246. }
  247. }