/*
* Jopr 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, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also 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 and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser 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.jbossas.script;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.measurement.AvailabilityType;
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.pluginapi.util.ProcessExecutionUtility;
import org.rhq.core.system.ProcessExecution;
import org.rhq.core.system.ProcessExecutionResults;
import org.rhq.core.system.SystemInfo;
import org.rhq.plugins.jbossas.JBossASServerComponent;
/**
* A JON service that provides the ability to execute a script.
*
* @author Ian Springer
*/
public class ScriptComponent implements ResourceComponent<JBossASServerComponent<?>>, OperationFacet {
public static final String PATH_CONFIG_PROP = "path";
public static final String ENVIRONMENT_VARIABLES_CONFIG_PROP = "environmentVariables";
public static final String EXECUTE_OPERATION = "execute";
public static final String COMMAND_LINE_ARGUMENTS_PARAM_PROP = "commandLineArguments";
private static final String EXIT_CODE_RESULT_PROP = "exitCode";
private static final String OUTPUT_RESULT_PROP = "output";
private final Log log = LogFactory.getLog(this.getClass());
private ResourceContext<JBossASServerComponent<?>> resourceContext;
public void start(ResourceContext<JBossASServerComponent<?>> resourceContext) {
this.resourceContext = resourceContext;
}
public void stop() {
this.resourceContext = null;
}
public AvailabilityType getAvailability() {
File scriptFile = getScriptFile();
return (scriptFile.exists()) ? AvailabilityType.UP : AvailabilityType.DOWN;
}
public OperationResult invokeOperation(String name, Configuration params) throws Exception {
if (name.equals(EXECUTE_OPERATION)) {
OperationResult operationResult = new OperationResult();
File scriptFile = getScriptFile();
SystemInfo systemInfo = this.resourceContext.getSystemInformation();
ProcessExecution processExecution = ProcessExecutionUtility.createProcessExecution(scriptFile);
processExecution.setWaitForCompletion(1000L * 60 * 60); // 1 hour
processExecution.setCaptureOutput(true);
// TODO: Make the script's cwd configurable, but default it to the directory containing the script.
processExecution.setWorkingDirectory(scriptFile.getParent());
setEnvironmentVariables(processExecution);
setCommandLineArguments(params, processExecution);
if (log.isDebugEnabled()) {
log.debug(processExecution);
}
ProcessExecutionResults processExecutionResults = systemInfo.executeProcess(processExecution);
if (processExecutionResults.getError() != null) {
throw new Exception(processExecutionResults.getError());
}
Integer exitCode = processExecutionResults.getExitCode();
String output = processExecutionResults.getCapturedOutput(); // NOTE: this is stdout + stderr
Configuration complexResults = operationResult.getComplexResults();
complexResults.put(new PropertySimple(EXIT_CODE_RESULT_PROP, exitCode));
complexResults.put(new PropertySimple(OUTPUT_RESULT_PROP, output));
if (exitCode != null && exitCode != 0) {
operationResult.setErrorMessage("Exit code was '" + exitCode + "', see operation results for details");
}
return operationResult;
} else {
throw new IllegalArgumentException("Unsupported operation: " + name);
}
}
private void setCommandLineArguments(Configuration params, ProcessExecution processExecution) {
List<String> processExecutionArguments = processExecution.getArguments();
if (null == processExecutionArguments) {
processExecutionArguments = new ArrayList<String>();
processExecution.setArguments(processExecutionArguments);
}
String cmdLineArgsString = params.getSimpleValue(COMMAND_LINE_ARGUMENTS_PARAM_PROP, null);
List<String> cmdLineArgs = createCommandLineArgumentList(cmdLineArgsString);
processExecutionArguments.addAll(cmdLineArgs);
}
private void setEnvironmentVariables(ProcessExecution processExecution) {
Configuration pluginConfig = this.resourceContext.getPluginConfiguration();
Map<String, String> processExecutionEnvironmentVariables = processExecution.getEnvironmentVariables();
if (null == processExecutionEnvironmentVariables) {
processExecutionEnvironmentVariables = new LinkedHashMap<String, String>();
processExecution.setEnvironmentVariables(processExecutionEnvironmentVariables);
}
String envVars = pluginConfig.getSimpleValue(ENVIRONMENT_VARIABLES_CONFIG_PROP, null);
Map<String, String> envVarsMap = createEnvironmentVariableMap(envVars);
if (null != envVarsMap) {
processExecutionEnvironmentVariables.putAll(envVarsMap);
}
}
@NotNull
private List<String> createCommandLineArgumentList(String cmdLineArgsString) {
if (cmdLineArgsString == null) {
return new ArrayList<String>();
}
StringTokenizer tokenizer = new StringTokenizer(cmdLineArgsString, "\n");
List<String> cmdLineArgs = new ArrayList<String>(tokenizer.countTokens());
while (tokenizer.hasMoreTokens()) {
String cmdLineArg = tokenizer.nextToken().trim();
cmdLineArg = replacePropertyPatterns(cmdLineArg);
cmdLineArgs.add(cmdLineArg);
}
return cmdLineArgs;
}
private Map<String, String> createEnvironmentVariableMap(String envVarsString) {
if (envVarsString == null) {
return null;
}
StringTokenizer tokenizer = new StringTokenizer(envVarsString, "\n");
Map<String, String> envVars = new LinkedHashMap<String, String>(tokenizer.countTokens());
while (tokenizer.hasMoreTokens()) {
String var = tokenizer.nextToken().trim();
int equalsIndex = var.indexOf('=');
if (equalsIndex == -1) {
throw new IllegalStateException("Malformed environment entry: " + var);
}
String varName = var.substring(0, equalsIndex);
String varValue = var.substring(equalsIndex + 1);
varValue = replacePropertyPatterns(varValue);
envVars.put(varName, varValue);
}
return envVars;
}
private String replacePropertyPatterns(String value) {
Pattern pattern = Pattern.compile("(%([^%]*)%)");
Matcher matcher = pattern.matcher(value);
Configuration parentPluginConfig = this.resourceContext.getParentResourceComponent().getPluginConfiguration();
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
String propName = matcher.group(2);
PropertySimple prop = parentPluginConfig.getSimple(propName);
String propValue = ((prop != null) && (prop.getStringValue() != null)) ? prop.getStringValue() : "";
String propPattern = matcher.group(1);
String replacement = (prop != null) ? propValue : propPattern;
matcher.appendReplacement(buffer, Matcher.quoteReplacement(replacement));
}
matcher.appendTail(buffer);
return buffer.toString();
}
private File getScriptFile() {
Configuration pluginConfig = this.resourceContext.getPluginConfiguration();
String scriptFilePath = pluginConfig.getSimple(PATH_CONFIG_PROP).getStringValue();
return new File(scriptFilePath);
}
}