/** * Arara, the cool TeX automation tool * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the project's author nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package com.github.cereda.arara.utils; import com.github.cereda.arara.controller.ConfigurationController; import com.github.cereda.arara.controller.LanguageController; import com.github.cereda.arara.model.AraraException; import com.github.cereda.arara.model.Argument; import com.github.cereda.arara.model.Command; import com.github.cereda.arara.model.Conditional; import com.github.cereda.arara.model.Messages; import com.github.cereda.arara.model.Rule; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Transformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.listener.ShutdownHookProcessDestroyer; /** * Implements interpreter utilitary methods. * @author Paulo Roberto Massa Cereda * @version 4.0 * @since 4.0 */ public class InterpreterUtils { // the application messages obtained from the // language controller private static final LanguageController messages = LanguageController.getInstance(); // get the logger context from a factory private static final Logger logger = LoggerFactory.getLogger(InterpreterUtils.class); /** * Gets a list of all rule arguments. * @param rule The provided rule. * @return A list of strings containing all rule arguments. */ public static List getRuleArguments(Rule rule) { Collection result = CollectionUtils.collect( rule.getArguments(), new Transformer() { public String transform(Argument input) { return input.getIdentifier(); } }); return new ArrayList(result); } /** * Checks if the current conditional has a prior evaluation. * @param conditional The current conditional object. * @return A boolean value indicating if the current conditional has a prior * evaluation. */ public static boolean runPriorEvaluation(Conditional conditional) { if (((Boolean) ConfigurationController.getInstance(). get("execution.dryrun")) == true) { return false; } switch (conditional.getType()) { case IF: case WHILE: case UNLESS: return true; default: return false; } } /** * Runs the command in the underlying operating system. * @param command An object representing the command. * @return An integer value representing the exit code. * @throws AraraException Something wrong happened, to be caught in the * higher levels. */ public static int run(Object command) throws AraraException { boolean verbose = (Boolean) ConfigurationController. getInstance().get("execution.verbose"); boolean timeout = (Boolean) ConfigurationController. getInstance().get("execution.timeout"); long value = (Long) ConfigurationController. getInstance().get("execution.timeout.value"); TimeUnit unit = (TimeUnit) ConfigurationController. getInstance().get("execution.timeout.unit"); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ProcessExecutor executor = new ProcessExecutor(); if (CommonUtils.checkClass(Command.class, command)) { executor = executor.command(((Command) command).getElements()); if (((Command) command).hasWorkingDirectory()) { executor = executor.directory( ((Command) command).getWorkingDirectory() ); } } else { executor = executor.commandSplit((String) command); } if (timeout) { if (value == 0) { throw new AraraException( messages.getMessage( Messages.ERROR_RUN_TIMEOUT_INVALID_RANGE ) ); } executor = executor.timeout(value, unit); } TeeOutputStream tee; if (verbose) { tee = new TeeOutputStream(System.out, buffer); executor = executor.redirectInput(System.in); } else { tee = new TeeOutputStream(buffer); } executor = executor.redirectOutput(tee).redirectError(tee); ShutdownHookProcessDestroyer hook = new ShutdownHookProcessDestroyer(); executor = executor.addDestroyer(hook); try { int exit = executor.execute().getExitValue(); logger.info(DisplayUtils.displayOutputSeparator( messages.getMessage( Messages.LOG_INFO_BEGIN_BUFFER ) )); logger.info(buffer.toString()); logger.info(DisplayUtils.displayOutputSeparator( messages.getMessage( Messages.LOG_INFO_END_BUFFER ) )); return exit; } catch (IOException ioexception) { throw new AraraException( messages.getMessage( Messages.ERROR_RUN_IO_EXCEPTION ), ioexception ); } catch (InterruptedException iexception) { throw new AraraException( messages.getMessage( Messages.ERROR_RUN_INTERRUPTED_EXCEPTION ), iexception ); } catch (InvalidExitValueException ievexception) { throw new AraraException( messages.getMessage( Messages.ERROR_RUN_INVALID_EXIT_VALUE_EXCEPTION ), ievexception ); } catch (TimeoutException texception) { throw new AraraException( messages.getMessage( Messages.ERROR_RUN_TIMEOUT_EXCEPTION ), texception ); } catch (Exception exception) { throw new AraraException( messages.getMessage( Messages.ERROR_RUN_GENERIC_EXCEPTION ), exception ); } } /** * Builds the rule path based on the rule name and returns the corresponding * file location. * @param name The rule name. * @return The rule file. * @throws AraraException Something wrong happened, to be caught in the * higher levels. */ public static File buildRulePath(String name) throws AraraException { @SuppressWarnings("unchecked") List paths = (List) ConfigurationController. getInstance().get("execution.rule.paths"); for (String path : paths) { File location = new File(construct(path, name)); if (location.exists()) { return location; } } return null; } /** * Constructs the path given the current path and the rule name. * @param path The current path. * @param name The rule name. * @return The constructed path. * @throws AraraException Something wrong happened, to be caught in the * higher levels. */ public static String construct(String path, String name) throws AraraException { name = name.concat(".yaml"); File location = new File(path); if (location.isAbsolute()) { return CommonUtils.buildPath(path, name); } else { File reference = (File) ConfigurationController. getInstance().get("execution.reference"); String parent = CommonUtils.buildPath( CommonUtils.getParentCanonicalPath(reference), path); return CommonUtils.buildPath(parent, name); } } }