/* * RHQ 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.plugins.jdbctrace; import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.byteman.agent.submit.Submit; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.ConfigurationUpdateStatus; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.pluginapi.configuration.ConfigurationFacet; import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport; import org.rhq.core.pluginapi.inventory.ResourceComponent; import org.rhq.core.pluginapi.inventory.ResourceContext; import org.rhq.core.pluginapi.operation.OperationFacet; import org.rhq.core.pluginapi.operation.OperationResult; import org.rhq.core.util.exception.ThrowableUtil; import org.rhq.plugins.byteman.BytemanAgentComponent; /** * Component that can trace JDBC calls via byte-code maniuplation. * * @author John Mazzitelli */ public class JdbcTracerComponent implements ResourceComponent<BytemanAgentComponent>, OperationFacet, ConfigurationFacet { private final Log log = LogFactory.getLog(JdbcTracerComponent.class); private Configuration resourceConfiguration; private ResourceContext<BytemanAgentComponent> resourceContext; public void start(ResourceContext<BytemanAgentComponent> context) { this.resourceContext = context; try { prepareRules(true); } catch (Exception e) { log.warn("Failed to prepare the jdbc trace rules in the remote byteman agent", e); } return; } public void stop() { this.resourceContext = null; } public AvailabilityType getAvailability() { try { prepareRules(false); // the above always makes sure the remote byteman agent has the rules loaded; so we can always return UP here return AvailabilityType.UP; } catch (Exception e) { log.debug("Failed to prepare the jdbc trace rules in the remote byteman agent during avail check", e); return AvailabilityType.DOWN; } } public OperationResult invokeOperation(String name, Configuration configuration) { OperationResult result = new OperationResult(); try { if ("refresh".equals(name)) { prepareRules(true); } else { throw new UnsupportedOperationException(name); } } catch (Exception e) { result.setErrorMessage(ThrowableUtil.getAllMessages(e)); } return result; } public Configuration loadResourceConfiguration() { // here we simulate the loading of the managed resource's configuration if (resourceConfiguration == null) { // for this example, we will create a simple dummy configuration to start with. // note that it is empty, so we're assuming there are no required configs in the plugin descriptor. resourceConfiguration = new Configuration(); } Configuration config = resourceConfiguration; return config; } public void updateResourceConfiguration(ConfigurationUpdateReport report) { // this simulates the plugin taking the new configuration and reconfiguring the managed resource resourceConfiguration = report.getConfiguration().deepCopy(); report.setStatus(ConfigurationUpdateStatus.SUCCESS); } /** * Returns <code>true</code> if this component's plugin configuration tells us the JDBC * tracing is to be enabled. If <code>true</code>, the remote Byteman agent will (or should) have * the JDBC trace rules installed. * * @return <code>true</code> if this component's JDBC tracing is enabled */ private boolean isEnabled() { Configuration pc = this.resourceContext.getPluginConfiguration(); String enabledString = pc.getSimpleValue(JdbcTracerUtil.PLUGINCONFIG_ENABLED, "true"); return Boolean.parseBoolean(enabledString); } /** * If this resource has been "enabled" (see its plugin configuration), this will ensure that the script * is added to the remote Byteman agent; if it is not enabled, the script will be deleted from the * remote Byteman agent. * * @param refresh if <code>true</code>, this will ensure the byteman agent has up to date rules. This means * that even if the remote byteman agent already has the script installed, this method will * delete the script and immediately re-install the current one. This parameter is meaningless * if this resource has not been enabled (because, in that case, the script is deleted from * the byteman agent so there is nothing to keep up to date) * * @throws Exception */ private void prepareRules(boolean refresh) throws Exception { Configuration pc = this.resourceContext.getPluginConfiguration(); String scriptName = pc.getSimpleValue(JdbcTracerUtil.PLUGINCONFIG_SCRIPTNAME, JdbcTracerUtil.DEFAULT_JDBC_TRACER_SCRIPT_NAME); String helperJarFileName = JdbcTracerUtil.DEFAULT_JDBC_TRACER_HELPER_JAR; JdbcTracerUtil jdbcTracerUtil = new JdbcTracerUtil(); BytemanAgentComponent bytemanAgentResource = this.resourceContext.getParentResourceComponent(); File script = jdbcTracerUtil.getJdbcTraceRulesScriptFile(bytemanAgentResource, scriptName); String scriptAbsolutePath = script.getAbsolutePath(); File helper = jdbcTracerUtil.getHelperJarFile(bytemanAgentResource, helperJarFileName); String helperAbsolutePath = helper.getAbsolutePath(); // see if there are already jdbc trace rules installed in the byteman agent // note that we talk directly to the remote byteman agent to get the info; our parent resource might not have our script yet in its cache Submit client = bytemanAgentResource.getBytemanClient(); Map<String, String> allKnownScripts = client.getAllRules(); String existingRules = null; if (allKnownScripts != null) { existingRules = allKnownScripts.get(scriptAbsolutePath); } // add or remove the rules as appropriate if (isEnabled()) { // if we are to refresh the script, remove the current script that is loaded (if one is loaded) if (existingRules != null && refresh) { Map<String, String> doomed = new HashMap<String, String>(1); doomed.put(scriptAbsolutePath, existingRules); client.deleteRules(doomed); existingRules = null; } if (refresh || !helper.exists()) { // the helper was deleted previously, we need to write it back out again jdbcTracerUtil.extractHelperJarFile(bytemanAgentResource, helperJarFileName); } if (refresh || !client.getLoadedSystemClassloaderJars().contains(helperAbsolutePath)) { client.addJarsToSystemClassloader(Arrays.asList(helperAbsolutePath)); } if (existingRules == null) { if (!script.exists()) { // the script was deleted previously, we need to write it back out again jdbcTracerUtil.extractJdbcTraceRulesScriptFile(bytemanAgentResource, scriptName); } client.addRulesFromFiles(Arrays.asList(scriptAbsolutePath)); } } else { if (existingRules != null) { Map<String, String> doomed = new HashMap<String, String>(1); doomed.put(scriptAbsolutePath, existingRules); client.deleteRules(doomed); } } return; } }