com.ubershy.streamsis.actions.RunProgramAction.java Source code

Java tutorial

Introduction

Here is the source code for com.ubershy.streamsis.actions.RunProgramAction.java

Source

/** 
 * StreamSis
 * Copyright (C) 2015 Eva Balycheva
 * 
 * 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, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * 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, see <http://www.gnu.org/licenses/>.
 */
package com.ubershy.streamsis.actions;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.ubershy.streamsis.Util;
import com.ubershy.streamsis.project.AbstractCuteNode;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

/**
 * Run Program Action. <br>
 * This {@link Action} can execute a program or script by using path, arguments and working
 * directory provided by the user.
 */
@SuppressWarnings("unchecked")
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "actnType")
public class RunProgramAction extends AbstractCuteNode implements Action {

    /** The Constant logger. */
    static final Logger logger = LoggerFactory.getLogger(RunProgramAction.class);

    /**
     * The arguments which will be passed to the program.
     */
    @JsonIgnore
    protected StringProperty argumentsProperty = new SimpleStringProperty("");

    // /** The environment path. */
    // private String environmentPATH = System.getenv("PATH");

    /**
     * Determines if the program must be killed by StreamSis if it's still running from previous
     * {@link #execute()}, so it can be started again.
     */
    @JsonIgnore
    protected BooleanProperty killIfStillRunningProperty = new SimpleBooleanProperty(false);

    /**
     * The path of the program/script to execute.
     */
    @JsonIgnore
    protected StringProperty pathProperty = new SimpleStringProperty("");

    /** The process. */
    private Process process;

    /**
     * The working directory path of program/script execution.
     */
    @JsonIgnore
    protected StringProperty workingDirProperty = new SimpleStringProperty("");

    /**
     * Instantiates a new {@link RunProgramAction}.
     */
    public RunProgramAction() {
    }

    /**
     * Instantiates a new {@link RunProgramAction} by {@link #pathProperty path} and
     * {@link #argumentsProperty arguments}.
     *
     * @param path
     *            the path of the program/script to execute
     * @param arguments
     *            the arguments to be passed to the program
     * @param workingDir
     *            the working directory path of program execution
     * @param shouldKill
     *            should the program be killed on #execute() if it's still running from previous
     *            {@link #execute()}
     */
    @JsonCreator
    public RunProgramAction(@JsonProperty("path") String path, @JsonProperty("arguments") String arguments,
            @JsonProperty("workingDir") String workingDir, @JsonProperty("killIfStillRunning") boolean shouldKill) {
        this.pathProperty.set(path);
        this.argumentsProperty.set(arguments);
        this.workingDirProperty.set(workingDir);
        this.killIfStillRunningProperty.set(shouldKill);
    }

    /**
     * Returns {@link #argumentsProperty}.
     *
     * @return the string property
     */
    public StringProperty argumentsProperty() {
        return argumentsProperty;
    }

    public boolean checkIfPathIsAbsoluteAndFileExists(String path) {
        if ((new File(path).isAbsolute())) { // the path provided is absolute
            if (Util.checkifSingleFileExists(path)) {
                return true;
            }
        }
        return false;
    }

    /*
     * @inheritDoc
     */
    @Override
    public void execute() {
        if (elementInfo.canWork()) {
            elementInfo.setAsWorking();
            String toExecute = getPath() + " " + getArguments();
            boolean skipSettingStatus = false;
            boolean success = true;
            if (process != null) {
                logger.info("Checking if the program is still running: " + toExecute);
                if (process.isAlive()) {
                    if (getkillIfStillRunning()) {
                        logger.info("Terminating the program, because it's still running");
                        process.destroy();
                        logger.info("The program was violently terminated");
                    } else {
                        elementInfo.setAsSick("Can't run the program again while it's still running. "
                                + "If you think it's bad and the program is not responding"
                                + ", use task manager to terminate it");
                        success = false;
                    }
                }
            }
            if (success != false) {
                try {
                    logger.info("Running the program: " + toExecute);
                    process = Runtime.getRuntime().exec(toExecute, null, new File(getWorkingDir()));
                    int exitCode = process.waitFor();
                    if (exitCode == 1) {
                        BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                        String stringToAdd;
                        StringBuilder errorLines = new StringBuilder("");
                        try {
                            while ((stringToAdd = br.readLine()) != null) {
                                errorLines.append(stringToAdd).append("\n");
                            }
                        } catch (IOException e) {
                            logger.error("Problem reading error buffer", e);
                        }
                        // Better to break the RunProgramAction in this case.
                        // So the project will stop.
                        // Better be safe than sorry.
                        elementInfo.setAsBroken("Run of the program with specified path and parameters "
                                + "ended with an error: \n" + errorLines);
                        success = false;
                    }
                } catch (IOException e) {
                    elementInfo.setAsBroken("Something is wrong. "
                            + "Is it really a program(not an ordinary file) located on the " + "chosen path?");
                    success = false;
                } catch (InterruptedException e) {
                    skipSettingStatus = true;
                }
            }
            if (!skipSettingStatus) {
                if (success) {
                    elementInfo.setSuccessfulResult();
                } else {
                    elementInfo.setFailedResult();
                }
            }
        }
    }

    /**
     * Gets the {@link #argumentsProperty()} value.
     *
     * @return the arguments
     */
    @JsonProperty("arguments")
    public String getArguments() {
        return argumentsProperty.get();
    }

    /**
     * Gets the {@link #killIfStillRunningProperty} value.
     */
    @JsonProperty("killIfStillRunning")
    public boolean getkillIfStillRunning() {
        return this.killIfStillRunningProperty.get();
    }

    /**
     * Gets the {@link #pathProperty()} value.
     *
     * @return the path
     */
    @JsonProperty("path")
    public String getPath() {
        return pathProperty.get();
    }

    /**
     * Gets the {@link #workingDirProperty} value.
     */
    @JsonProperty("workingDir")
    public String getWorkingDir() {
        return this.workingDirProperty.get();
    }

    /*
     * @inheritDoc
     */
    @Override
    public void init() {
        elementInfo.setAsReadyAndHealthy();
        if (getPath().isEmpty()) {
            elementInfo.setAsBroken("The path to the program is empty");
            return;
        }
        // TODO: improve algorithms below. I was in hurry. =/
        String absolutePath = null;
        if (checkIfPathIsAbsoluteAndFileExists(getPath())) { // the path provided is absolute
            absolutePath = getPath();
        }
        // Too hard to implement ability to support non-absolute paths of executables.
        // TODO: implement someday. Now I'm in hurry. =/
        // else { // the path provided is not absolute
        // if ("cmd.exe".equals(getPath())) {
        // absolutePath = "C:\\Windows\\system32\\" + getPath();
        // return;
        // }
        // // lets search in environment variable "PATH"
        // String[] envDirs = environmentPATH.split(";");
        // for (String envDir : envDirs) {
        // File possibleFile = new File(envDir, getPath());
        // String possibleAbsolutePath = possibleFile.getAbsolutePath();
        // if (Util.checkifSingleFileExists(possibleAbsolutePath)) {
        // absolutePath = possibleAbsolutePath;
        // return;
        // }
        // }
        // }
        if (absolutePath == null) {
            elementInfo.setAsBroken("The path to the program seems invalid. "
                    + "Please check if the program really exists on this path");
            return;
        }
        File pathFile = new File(absolutePath);
        try {
            if (!pathFile.canExecute()) {
                elementInfo.setAsBroken("The path to the program seems invalid. "
                        + "Please check if the program really exists on this path");
                return;
            }
        } catch (SecurityException e) {
            elementInfo.setAsBroken("You don't have enough rights to run this program");
            return;
        }

        if (getWorkingDir().isEmpty()) { // if it's empty, lets initialize workingDir by the
            // program directory
            File parentToPathFile = pathFile.getAbsoluteFile().getParentFile();
            if (parentToPathFile != null) {
                setWorkingDir(parentToPathFile.getAbsolutePath());
            } else {
                throw new RuntimeException("Can't find parent directory of the program");
            }
        } else { // Means the user has specified the working directory
            if (!Util.checkDirectory(getWorkingDir())) {
                elementInfo.setAsBroken("The working directory you have specified does not exist");
            }
        }
    }

    /**
     * Returns {@link #killIfStillRunningProperty}.
     *
     * @return the boolean property
     */
    public BooleanProperty killIfStillRunningProperty() {
        return killIfStillRunningProperty;
    }

    /**
     * Returns {@link #pathProperty}.
     *
     * @return the string property
     */
    public StringProperty pathProperty() {
        return pathProperty;
    }

    /**
     * Sets the {@link #argumentsProperty()} value.
     *
     * @param arguments
     *            the new arguments
     */
    @JsonProperty("arguments")
    public void setArguments(String arguments) {
        this.argumentsProperty.set(arguments);
    }

    /**
     * Sets the {@link #killIfStillRunningProperty} value.
     *
     * @param shouldKill
     *            if the program must be killed if it's still running
     */
    @JsonProperty("killIfStillRunning")
    public void setkillIfStillRunning(boolean shouldKill) {
        this.killIfStillRunningProperty.set(shouldKill);
    }

    /**
     * Sets the {@link #pathProperty()} value.
     *
     * @param path
     *            the new path of the program to execute
     */
    @JsonProperty("path")
    public void setPath(String path) {
        this.pathProperty.set(path);
    }

    /**
     * Sets the {@link #workingDirProperty()} value.
     *
     * @param workingDir
     *            the working directory of execution
     */
    @JsonProperty("workingDir")
    public void setWorkingDir(String workingDir) {
        this.workingDirProperty.set(workingDir);
    }

    /**
     * Returns {@link #workingDirProperty}.
     *
     * @return the string property
     */
    public StringProperty workingDirProperty() {
        return workingDirProperty;
    }

}