/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.control.agent.server; import java.util.NoSuchElementException; import org.hyperic.hq.bizapp.client.AgentCallbackClientException; import org.hyperic.hq.bizapp.client.ControlCallbackClient; import org.hyperic.hq.bizapp.shared.lather.ControlSendCommandResult_args; import org.hyperic.hq.product.PluginNotFoundException; import org.hyperic.hq.product.PluginException; import org.hyperic.hq.product.ControlPluginManager; import org.hyperic.util.config.ConfigResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Class to run control actions. Control actions should happen infrequently * enough that the simplicity of spawning a new thread each time should * outweigh the performance benefits of using a thread pool. We can always * modify this behavior later. */ class ActionThread extends Thread { private ControlPluginManager manager; private ControlCallbackClient client; private String pluginName; private String pluginType; private String action; private String[] args; private String id; // Time to wait before requeuing a control job that failed because // the plugin was busy with a previous action protected static final int REQUEUE_INTERVAL = 2 * 1000; protected Log log = LogFactory.getLog(ActionThread.class.getName()); ActionThread(String pluginName, String pluginType, String id, String action, String[] args, ControlCallbackClient client, ControlPluginManager manager) { this.pluginName = pluginName; this.pluginType = pluginType; this.id = id; this.action = action; this.args = args; this.client = client; this.manager = manager; } public void run() { int result = -1; String errMsg = null; // add ourselves to the job queue this.log.debug("Adding job " + id + " to " + pluginName + " queue"); this.manager.addJob(pluginName, id); // wait for our turn while (true) { String nextJob; try { nextJob = this.manager.getNextJob(pluginName); if (nextJob.equals(id)) break; } catch (NoSuchElementException e) { // should never happen this.log.error("Job queue empty"); return; } this.log.debug("Plugin busy with job " + nextJob + " requeueing in " + REQUEUE_INTERVAL + "ms"); try { Thread.sleep(REQUEUE_INTERVAL); } catch (InterruptedException e) {} } this.log.debug("Running job " + id); long startTime = System.currentTimeMillis(); final ControlSendCommandResult_args resultsMetadata = this.client.newResultsMetadata(this.pluginName, Integer.parseInt(id), startTime) ; try { this.manager.doAction(this.pluginName, this.action, this.args, resultsMetadata); result = this.manager.getResult(this.pluginName); errMsg = this.manager.getMessage(this.pluginName); } catch (PluginNotFoundException e) { // The plugin has not been configured locally, or the plugin // type is not found on this agent. Try to get the config // from the server try { this.log.info("Fetching plugin configuration from server"); byte[] configBytes = this.client.controlGetPluginConfiguration(this.pluginName); if (configBytes == null || configBytes.length == 0) { // log something or send a message back to the server errMsg = "Plugin configuration not found"; this.log.error(errMsg); return; } ConfigResponse config = ConfigResponse.decode(configBytes); this.log.info("Config finished, running control action"); this.manager.createControlPlugin(this.pluginName, this.pluginType, config); this.manager.doAction(this.pluginName, this.action, this.args, resultsMetadata); result = this.manager.getResult(this.pluginName); errMsg = this.manager.getMessage(this.pluginName); } catch (Exception exc) { errMsg = "Unable to fetch plugin configuration: " + exc.getMessage(); this.log.error(errMsg, exc); } } catch (PluginException e) { errMsg = "Unable to run control action: " +e.getMessage(); this.log.error(errMsg, e); } finally { // Remove this job from the queue try { this.manager.removeNextJob(pluginName); this.log.debug("Removed job " + id + " from the queue"); } catch (NoSuchElementException e) { // will never happen } // Lastly, send the status back to the server try { this.client.controlSendCommandResult(resultsMetadata, result, errMsg); } catch (AgentCallbackClientException e) { this.log.error("Unable to send command result: " + e.getMessage()); } } } }