/* * 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.enterprise.agent; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.prefs.BackingStoreException; import javax.management.MBeanServer; import javax.management.MBeanServerInvocationHandler; import org.rhq.core.util.stream.StreamUtil; import org.rhq.enterprise.communications.ServiceContainerMetricsMBean; import org.rhq.enterprise.communications.command.client.ClientCommandSenderMetrics; /** * The class that tests can use to configure and create agents. This class also provides the output of the agent as well * as allowing input to the agent. * * @author John Mazzitelli */ public class AgentTestClass { /** * The default configuration file this test class will use if one isn't specified. */ public static final String DEFAULT_CONFIG_FILE = "test-agent-configuration.xml"; /** * The default preference node used by the configuration file - this is used if the node name isn't specified. */ public static final String DEFAULT_CONFIG_PREF_NODE = "test"; private String m_configFilePath = DEFAULT_CONFIG_FILE; private String m_preferenceNodeName = DEFAULT_CONFIG_PREF_NODE; private Properties m_configOverrides = null; private List m_inputCommands = null; private File m_outputFile = null; private File m_inputFile = null; private boolean m_captureOutput = false; private AgentMain m_agent = null; /** * Constructor for {@link AgentTestClass}. */ public AgentTestClass() { } /** * Sets the location of the configuration file and the name of the preferences node that is in the configuration * file. The file path can be a path in the classloader or an absolute file system path. * * @param file_path the preferences configuration file * @param pref_node_name the preferences node name that corresponds to the node name in the configuration file */ public void setConfigurationFile(String file_path, String pref_node_name) { assert file_path != null : "File path must not be null"; assert pref_node_name != null : "Preference node name must not be null"; m_configFilePath = file_path; m_preferenceNodeName = pref_node_name; } /** * Sets some properties that can be used to override those properties in the * {@link #setConfigurationFile(String, String) configuration file}. This is useful when a series of tests want to * use mostly the same configuration with one or two config properties that vary. * * @param props properties that override those found in the configuration file */ public void setConfigurationOverrides(Properties props) { m_configOverrides = props; } /** * This sets a list of commands that will be input to the agent - the commands will be stored to a temporary file * and passed to the agent via its "-i" argument. * * @param commands set of commands for the agent to execute */ public void setInputCommands(List commands) { m_inputCommands = commands; } /** * Sets the flag to indicate if the agent's output should be captured. If a test wants to be able to get the * {@link #getAgentOutput() agent output}, it must set this flag to <code>true</code>. * * @param flag if <code>true</code>, the agent's output will be redirected to a file that will later be read in via * {@link #getAgentOutput()}. */ public void setCaptureOutput(boolean flag) { m_captureOutput = flag; } /** * This creates an agent with the configuration specified by the * {@link #setConfigurationFile(String, String) config file} and * {@link #setConfigurationOverrides(Properties) override properties}. * * @param start_it if <code>true</code>, the agent will be started before it is returned * * @return the newly created agent instance * * @throws Exception if an error occurred while created or starting the agent */ public AgentMain createAgent(boolean start_it) throws Exception { // make sure we clean up any previously created agent if (m_agent != null) { m_agent.shutdown(); m_agent = null; } assert m_configFilePath != null : "No configuration file has been specified for the agent"; ArrayList<String> args = new ArrayList<String>(); if (m_captureOutput) { m_outputFile = createOutputFile(); args.add("-o"); args.add(m_outputFile.getAbsolutePath()); } if ((m_inputCommands != null) && (m_inputCommands.size() > 0)) { m_inputFile = createInputFile(); args.add("-i"); args.add(m_inputFile.getAbsolutePath()); } args.add("-d"); // put it daemon mode, otherwise, the agent main input loop thread never dies in our tests args.add("-l"); // clear out any old preferences args.add("-c"); args.add(m_configFilePath); args.add("-p"); args.add(m_preferenceNodeName); if ((m_configOverrides != null) && (m_configOverrides.size() > 0)) { for (Iterator iter = m_configOverrides.entrySet().iterator(); iter.hasNext();) { Map.Entry entry = (Map.Entry) iter.next(); args.add("-D" + entry.getKey() + "=" + entry.getValue()); } } m_agent = new AgentMain(args.toArray(new String[0])); if (start_it) { // start the agent *and* allow it to send messages immediately m_agent.start(); m_agent.getClientCommandSender().getRemoteCommunicator().setInitializeCallback(null); m_agent.getClientCommandSender().startSending(); } return m_agent; } /** * Returns the agent that was created by this class. If this class hasn't created an agent yet, <code>null</code> is * returned. * * @return the created agent or <code>null</code> */ public AgentMain getAgent() { return m_agent; } /** * Returns a proxy to the server metrics MBean. * * @return metrics mbean that contains server-side metrics * * @throws RuntimeException if can't create the MBean proxy */ public ServiceContainerMetricsMBean getServerMetrics() { MBeanServer mbs = getAgent().getServiceContainer().getMBeanServer(); ServiceContainerMetricsMBean metrics; try { metrics = (ServiceContainerMetricsMBean) MBeanServerInvocationHandler.newProxyInstance(mbs, ServiceContainerMetricsMBean.OBJECTNAME_METRICS, ServiceContainerMetricsMBean.class, false); } catch (Exception e) { throw new RuntimeException(e); } return metrics; } /** * Returns the set of metrics for the agent's client sender. * * @return metrics */ public ClientCommandSenderMetrics getClientMetrics() { return getAgent().getClientCommandSender().getMetrics(); } /** * This will clear any and all agent configuration preferences. After this method returns, only default values will * remain in effect. An exception is thrown if an agent has not yet been {@link #createAgent(boolean) created}. * * @throws RuntimeException if the agent hasn't been created yet or if the configuration failed to get cleared */ public void clearAgentConfiguration() { if (m_agent == null) { throw new RuntimeException("Cannot clear configuration - the agent has not yet been created"); } try { m_agent.getConfiguration().getPreferences().clear(); } catch (BackingStoreException e) { throw new RuntimeException("Failed to clear the agent configuration", e); } } /** * Call this when you want to delete the temporary input and output files this class created. */ public void cleanUpFiles() { if (m_outputFile != null) { getAgent().getOut().close(); m_outputFile.delete(); } if (m_inputFile != null) { m_inputFile.delete(); } return; } /** * This returns the agent's previous output. * * @return agent output as one big string * * @throws FileNotFoundException if the agent was not told to capture output or the output file cannot be opened for * some reason */ public String getAgentOutput() throws FileNotFoundException { String output = null; if (m_outputFile != null) { FileInputStream fis = new FileInputStream(m_outputFile); output = new String(StreamUtil.slurp(fis)); } else { throw new FileNotFoundException("The agent was not told to capture its output - no output available"); } return output; } /** * Creates the input file with the set of {@link #setInputCommands(List) input commands}. The file will be created * in a temporary location and will be deleted on exit. * * @return the file that was created * * @throws Exception if failed to create the file */ private File createInputFile() throws Exception { File file = File.createTempFile("input-commands", null); file.deleteOnExit(); PrintWriter pw = new PrintWriter(new FileWriter(file)); for (Iterator iter = m_inputCommands.iterator(); iter.hasNext();) { String cmd = (String) iter.next(); pw.println(cmd); } pw.close(); return file; } /** * Creates the output file where the agent will dump its output. The file will be created in a temporary location * and will be deleted on exit. * * @return the file that was created * * @throws Exception if failed to create the file */ private File createOutputFile() throws Exception { File file = File.createTempFile("agent-output", null); file.deleteOnExit(); return file; } }