/** * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the * COPYRIGHT.txt in the distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation; either version 3.0 of the * License, or (at your option) any later version. * * This software 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.waarp.gateway.kernel.exec; import java.io.File; import java.io.IOException; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteException; import org.apache.commons.exec.ExecuteWatchdog; import org.apache.commons.exec.PumpStreamHandler; import org.waarp.common.command.exception.Reply421Exception; import org.waarp.common.future.WaarpFuture; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; /** * ExecuteExecutor class. The given argument will be executed after replacements. * * * <br> * The following replacement are done dynamically before the command is executed:<br> * - #BASEPATH# is replaced by the full path for the root of FTP Directory<br> * - #FILE# is replaced by the current file path relative to FTP Directory (so #BASEPATH##FILE# is * the full path of the file)<br> * - #USER# is replaced by the username<br> * - #ACCOUNT# is replaced by the account<br> * - #COMMAND# is replaced by the command issued for the file<br> * - #SPECIALID# is replaced by the FTP id of the transfer (whatever in or out)<br> * - #UUID# is replaced by a special UUID globally unique for the transfer, in general to be placed in -info part (for instance ##UUID## giving #uuid#)<br> * * @author Frederic Bregier * */ public class ExecuteExecutor extends AbstractExecutor { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory .getLogger(ExecuteExecutor.class); private final String[] args; private final String arg; private final WaarpFuture futureCompletion; private final long delay; /** * * @param command * @param delay * @param futureCompletion */ public ExecuteExecutor(String command, long delay, WaarpFuture futureCompletion) { this.args = command.split(" "); this.arg = command; this.futureCompletion = futureCompletion; this.delay = delay; } public void run() throws Reply421Exception { // Check if the execution will be done through LocalExec daemon if (AbstractExecutor.useLocalExec) { LocalExecClient localExecClient = new LocalExecClient(); if (localExecClient.connect()) { localExecClient.runOneCommand(arg, delay, futureCompletion); localExecClient.disconnect(); return; }// else continue } // Execution is done internally File exec = new File(args[0]); if (exec.isAbsolute()) { if (!exec.canExecute()) { logger.error("Exec command is not executable: " + args[0]); throw new Reply421Exception("Pre Exec command is not executable"); } } CommandLine commandLine = new CommandLine(args[0]); for (int i = 1; i < args.length; i++) { commandLine.addArgument(args[i]); } DefaultExecutor defaultExecutor = new DefaultExecutor(); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(null, null); defaultExecutor.setStreamHandler(pumpStreamHandler); int[] correctValues = { 0, 1 }; defaultExecutor.setExitValues(correctValues); ExecuteWatchdog watchdog = null; if (delay > 0) { watchdog = new ExecuteWatchdog(delay); defaultExecutor.setWatchdog(watchdog); } int status = -1; try { status = defaultExecutor.execute(commandLine); } catch (ExecuteException e) { if (e.getExitValue() == -559038737) { // Cannot run immediately so retry once try { Thread.sleep(10); } catch (InterruptedException e1) { } try { status = defaultExecutor.execute(commandLine); } catch (ExecuteException e2) { try { pumpStreamHandler.stop(); } catch (IOException e1) { } logger.error("System Exception: " + e.getMessage() + "\n Exec cannot execute command " + commandLine.toString()); throw new Reply421Exception("Cannot execute Pre command"); } catch (IOException e2) { try { pumpStreamHandler.stop(); } catch (IOException e1) { } logger.error("Exception: " + e.getMessage() + "\n Exec in error with " + commandLine.toString()); throw new Reply421Exception("Cannot execute Pre command"); } logger.info("System Exception: " + e.getMessage() + " but finally get the command executed " + commandLine.toString()); } else { try { pumpStreamHandler.stop(); } catch (IOException e1) { } logger.error("Exception: " + e.getMessage() + "\n Exec in error with " + commandLine.toString()); throw new Reply421Exception("Cannot execute Pre command"); } } catch (IOException e) { try { pumpStreamHandler.stop(); } catch (IOException e1) { } logger.error("Exception: " + e.getMessage() + "\n Exec in error with " + commandLine.toString()); throw new Reply421Exception("Cannot execute Pre command"); } try { pumpStreamHandler.stop(); } catch (IOException e1) { } if (watchdog != null && watchdog.killedProcess()) { // kill by the watchdoc (time out) logger.error("Exec is in Time Out"); status = -1; } if (status == 0) { futureCompletion.setSuccess(); logger.info("Exec OK with {}", commandLine); } else if (status == 1) { logger.warn("Exec in warning with {}", commandLine); futureCompletion.setSuccess(); } else { logger.debug("Status: " + status + (status == -1 ? " Tiemout" : "") + " Exec in error with " + commandLine.toString()); throw new Reply421Exception("Pre command executed in error"); } } }