org.rhq.plugins.jbossas5.script.ScriptComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.rhq.plugins.jbossas5.script.ScriptComponent.java

Source

/*
 * Jopr Management Platform
 * Copyright (C) 2005-2009 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.jbossas5.script;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.content.PackageDetailsKey;
import org.rhq.core.domain.content.PackageType;
import org.rhq.core.domain.content.transfer.ContentResponseResult;
import org.rhq.core.domain.content.transfer.DeployIndividualPackageResponse;
import org.rhq.core.domain.content.transfer.DeployPackageStep;
import org.rhq.core.domain.content.transfer.DeployPackagesResponse;
import org.rhq.core.domain.content.transfer.RemovePackagesResponse;
import org.rhq.core.domain.content.transfer.ResourcePackageDetails;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.content.ContentFacet;
import org.rhq.core.pluginapi.content.ContentServices;
import org.rhq.core.pluginapi.inventory.DeleteResourceFacet;
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.core.util.MessageDigestGenerator;
import org.rhq.plugins.jbossas5.ApplicationServerComponent;
import org.rhq.plugins.jbossas5.ApplicationServerPluginConfigurationProperties;
import org.rhq.plugins.jbossas5.connection.ProfileServiceConnection;
import org.rhq.plugins.jbossas5.deploy.RemoteDownloader;
import org.rhq.plugins.jbossas5.deploy.ScriptDeployer;

/**
 * A JON service that provides the ability to execute a script.
 * 
 * @author Ian Springer
 * @author Lukas Krejci
 */
public class ScriptComponent implements ResourceComponent<ApplicationServerComponent<?>>, OperationFacet,
        DeleteResourceFacet, ContentFacet {
    public static final String TYPE_NAME = "Script";
    public static final String PACKAGE_TYPE = "script";

    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 static final String PACKAGE_VERSION = "none";
    private static final String PACKAGE_ARCHITECTURE = "noarch";

    private final Log log = LogFactory.getLog(this.getClass());

    private ResourceContext<ApplicationServerComponent<?>> resourceContext;

    public void start(ResourceContext<ApplicationServerComponent<?>> 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);
        }
    }

    public void deleteResource() throws Exception {
        String path = resourceContext.getPluginConfiguration().getSimpleValue(PATH_CONFIG_PROP, null);

        File f = new File(path);

        if (!f.delete()) {
            throw new IOException("Failed to delete the file on the configured path: " + path);
        }
    }

    public List<DeployPackageStep> generateInstallationSteps(ResourcePackageDetails packageDetails) {
        return null;
    }

    public DeployPackagesResponse deployPackages(Set<ResourcePackageDetails> packages,
            ContentServices contentServices) {
        DeployPackagesResponse response = new DeployPackagesResponse();
        if (packages.size() != 1) {
            log.warn("Request to deploy a script containing multiple files, which is obivously illegal: "
                    + packages);
            response.setOverallRequestResult(ContentResponseResult.FAILURE);
            response.setOverallRequestErrorMessage("Only one script can be updated at a time.");
            return response;
        }

        try {
            String jbossHomeDir = resourceContext.getParentResourceComponent().getResourceContext()
                    .getPluginConfiguration()
                    .getSimpleValue(ApplicationServerPluginConfigurationProperties.HOME_DIR, null);
            SystemInfo systemInfo = resourceContext.getSystemInformation();
            ProfileServiceConnection profileServiceConnection = resourceContext.getParentResourceComponent()
                    .getConnection();

            ScriptDeployer deployer = new ScriptDeployer(jbossHomeDir, systemInfo,
                    new RemoteDownloader(resourceContext, true, profileServiceConnection));
            ResourcePackageDetails packageDetails = packages.iterator().next();

            DeployIndividualPackageResponse scriptUpdateResult = deployer.update(packageDetails,
                    resourceContext.getResourceType());

            response.setOverallRequestResult(scriptUpdateResult.getResult());
            response.addPackageResponse(scriptUpdateResult);
        } catch (Exception e) {
            response.setOverallRequestErrorMessage(e.getMessage());
            response.setOverallRequestResult(ContentResponseResult.FAILURE);
        }

        return response;
    }

    public RemovePackagesResponse removePackages(Set<ResourcePackageDetails> packages) {
        throw new UnsupportedOperationException("Cannot remove a package backing a script.");
    }

    public Set<ResourcePackageDetails> discoverDeployedPackages(PackageType type) {
        Set<ResourcePackageDetails> results = new HashSet<ResourcePackageDetails>();
        if (PACKAGE_TYPE.equals(type.getName())) {
            String jbossHomeDir = resourceContext.getParentResourceComponent().getResourceContext()
                    .getPluginConfiguration()
                    .getSimpleValue(ApplicationServerPluginConfigurationProperties.HOME_DIR, null);
            File binDirectory = new File(jbossHomeDir, "bin");

            File scriptFile = new File(binDirectory, resourceContext.getResourceKey());

            String sha256 = PACKAGE_VERSION;
            try {
                sha256 = new MessageDigestGenerator(MessageDigestGenerator.SHA_256).calcDigestString(scriptFile);
            } catch (IOException e) {
                log.warn("Failed to compute the SHA256 digest of the script: " + scriptFile.getAbsolutePath(), e);
            }

            PackageDetailsKey key = new PackageDetailsKey(scriptFile.getName(), this.getVersion(sha256),
                    PACKAGE_TYPE, PACKAGE_ARCHITECTURE);
            ResourcePackageDetails details = new ResourcePackageDetails(key);
            details.setDisplayName(scriptFile.getName());
            details.setFileName(scriptFile.getAbsolutePath());
            details.setFileSize(scriptFile.length());
            details.setLocation(scriptFile.getAbsolutePath());
            details.setFileCreatedDate(scriptFile.lastModified());
            details.setInstallationTimestamp(System.currentTimeMillis());
            details.setSHA256(sha256);

            results.add(details);
        }

        return results;
    }

    public InputStream retrievePackageBits(ResourcePackageDetails packageDetails) {
        String jbossHomeDir = resourceContext.getParentResourceComponent().getResourceContext()
                .getPluginConfiguration()
                .getSimpleValue(ApplicationServerPluginConfigurationProperties.HOME_DIR, null);
        File binDir = new File(jbossHomeDir, "bin");

        String scriptName = packageDetails.getKey().getName();

        File script = new File(binDir, scriptName);

        try {
            return new FileInputStream(script);
        } catch (FileNotFoundException e) {
            log.warn("Failed to retrieve package bits for script " + packageDetails, e);
            return null;
        }
    }

    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);
        if (null != cmdLineArgs) {
            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 envVars) {
        Pattern pattern = Pattern.compile("(%([^%]*)%)");
        Matcher matcher = pattern.matcher(envVars);
        Configuration parentPluginConfig = this.resourceContext.getParentResourceComponent().getResourceContext()
                .getPluginConfiguration();
        StringBuffer buffer = new StringBuffer();
        while (matcher.find()) {
            String propName = matcher.group(2);
            PropertySimple prop = parentPluginConfig.getSimple(propName);
            String propPattern = matcher.group(1);
            String replacement = (prop != null) ? prop.getStringValue() : 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);
    }

    private String getVersion(String sha256) {
        return "[sha256=" + sha256 + "]";
    }
}