View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.openr66.context.task;
21  
22  import org.apache.commons.exec.CommandLine;
23  import org.apache.commons.exec.DefaultExecutor;
24  import org.apache.commons.exec.ExecuteWatchdog;
25  import org.apache.commons.exec.PumpStreamHandler;
26  import org.waarp.commandexec.utils.LocalExecResult;
27  import org.waarp.common.command.exception.CommandAbstractException;
28  import org.waarp.common.logging.WaarpLogger;
29  import org.waarp.common.logging.WaarpLoggerFactory;
30  import org.waarp.common.utility.ParametersChecker;
31  import org.waarp.openr66.context.ErrorCode;
32  import org.waarp.openr66.context.R66Result;
33  import org.waarp.openr66.context.R66Session;
34  import org.waarp.openr66.context.task.localexec.LocalExecClient;
35  import org.waarp.openr66.protocol.configuration.Configuration;
36  
37  import java.io.File;
38  import java.io.PipedInputStream;
39  import java.io.PipedOutputStream;
40  
41  /**
42   * Execute an external command and Rename the file (using the new name from the
43   * result).<br>
44   * <p>
45   * The move of the file (if any) should be done by the external command
46   * itself.<br>
47   * <br>
48   * <p>
49   * waitForValidation (#NOWAIT#) must not be set since it will prevent to have
50   * the MOVE TASK to occur normally.
51   * So even if set, the #NOWAIT# will be ignored.
52   */
53  public class ExecMoveTask extends AbstractExecTask {
54    /**
55     * Internal Logger
56     */
57    private static final WaarpLogger logger =
58        WaarpLoggerFactory.getLogger(ExecMoveTask.class);
59  
60    /**
61     * @param argRule
62     * @param delay
63     * @param argTransfer
64     * @param session
65     */
66    public ExecMoveTask(final String argRule, final int delay,
67                        final String argTransfer, final R66Session session) {
68      super(TaskType.EXECMOVE, delay, argRule, argTransfer, session);
69    }
70  
71    @Override
72    public final void run() {
73      /*
74       * First apply all replacements and format to argRule from context and argTransfer. Will call exec (from first
75       * element of resulting string) with arguments as the following value from the replacements. Return 0 if OK,
76       * else 1 for a warning else as an error. The last line of stdout will be the new name given to the R66File in
77       * case of status 0. The previous file should be deleted by the script or will be deleted in case of status 0.
78       * If the status is 1, no change is made to the file.
79       */
80      logger.info("ExecMove with {}:{} and {}", argRule, argTransfer, session);
81      final String finalname = applyTransferSubstitutions(argRule);
82  
83      // Force the WaitForValidation
84      waitForValidation = true;
85      if (Configuration.configuration.isUseLocalExec() && useLocalExec) {
86        final LocalExecClient localExecClient = new LocalExecClient();
87        if (localExecClient.connect()) {
88          localExecClient.runOneCommand(finalname, delay, waitForValidation,
89                                        futureCompletion);
90          final LocalExecResult result = localExecClient.getLocalExecResult();
91          move(result.getStatus(), result.getResult(), finalname);
92          localExecClient.disconnect();
93          return;
94        } // else continue
95      }
96  
97      final PrepareCommandExec prepareCommandExec =
98          new PrepareCommandExec(this, finalname, false,
99                                 waitForValidation).invoke();
100     if (prepareCommandExec.isError()) {
101       return;
102     }
103     final CommandLine commandLine = prepareCommandExec.getCommandLine();
104     final DefaultExecutor defaultExecutor =
105         prepareCommandExec.getDefaultExecutor();
106     final PipedInputStream inputStream = prepareCommandExec.getInputStream();
107     final PipedOutputStream outputStream = prepareCommandExec.getOutputStream();
108     final PumpStreamHandler pumpStreamHandler =
109         prepareCommandExec.getPumpStreamHandler();
110     final ExecuteWatchdog watchdog = prepareCommandExec.getWatchdog();
111     final LastLineReader lastLineReader = new LastLineReader(inputStream);
112     lastLineReader.setName(
113         "LastLineReader" + session.getRunner().getSpecialId());
114     lastLineReader.setDaemon(true);
115     Configuration.configuration.getExecutorService().execute(lastLineReader);
116     final ExecuteCommand executeCommand =
117         new ExecuteCommand(this, commandLine, defaultExecutor, inputStream,
118                            outputStream, pumpStreamHandler,
119                            lastLineReader).invoke();
120     if (executeCommand.isError()) {
121       return;
122     }
123     int status = executeCommand.getStatus();
124     final String newname;
125     if (defaultExecutor.isFailure(status) && watchdog != null &&
126         watchdog.killedProcess()) {
127       // kill by the watchdoc (time out)
128       status = -1;
129       newname = "TimeOut";
130     } else {
131       newname = lastLineReader.getLastLine();
132       if (status == 0 && ParametersChecker.isEmpty(newname)) {
133         status = 1;
134       }
135     }
136     move(status, newname, commandLine.toString());
137   }
138 
139   private void move(final int status, final String newName,
140                     final String commandLine) {
141     if (newName == null) {
142       logger.error("Status: " + status + " Exec in error with " + commandLine +
143                    " returns no line");
144       futureCompletion.cancel();
145       return;
146     }
147     final String newname = newName.replace('\\', '/');
148     if (status == 0) {
149       if (newname.indexOf(' ') > 0) {
150         logger.warn("Exec returns a multiple string in final line: " + newname);
151         // XXX FIXME: should not split String[] args = newname.split(" ")
152         // newname = args[args.length - 1]
153       }
154       // now test if the previous file was deleted (should be)
155       final File file = new File(newname);
156       if (!file.exists()) {
157         logger.warn(
158             "New file does not exist at the end of the exec: " + newname);
159       }
160       // now replace the file with the new one
161       try {
162         session.getFile().replaceFilename(newname, true);
163       } catch (final CommandAbstractException e) {
164         logger.warn("Exec in warning with " + commandLine + " : {}",
165                     e.getMessage());
166       }
167       session.getRunner().setFileMoved(newname, true);
168       final R66Result result =
169           new R66Result(session, true, ErrorCode.CompleteOk,
170                         session.getRunner());
171       result.setOther(newname);
172       futureCompletion.setResult(result);
173       futureCompletion.setSuccess();
174       logger.info("Exec OK with {} returns {}", commandLine, newname);
175     } else if (status == 1) {
176       logger.warn(
177           "Exec in warning with " + commandLine + " returns " + newname);
178       session.getRunner().setErrorExecutionStatus(ErrorCode.Warning);
179       final R66Result result =
180           new R66Result(session, true, ErrorCode.Warning, session.getRunner());
181       result.setOther(newname);
182       futureCompletion.setResult(result);
183       futureCompletion.setSuccess();
184     } else {
185       logger.error("Status: " + status + " Exec in error with " + commandLine +
186                    " returns " + newname);
187       futureCompletion.cancel();
188     }
189   }
190 }