All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencastproject.util.ProcessExecutor Maven / Gradle / Ivy

There is a newer version: 16.7
Show newest version
/**
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License
 * at:
 *
 *   http://opensource.org/licenses/ecl2.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */


package org.opencastproject.util;

import static org.opencastproject.util.data.Collections.map;

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Helper class to execute processes on the host system and outside of the java vm. Since there are problems with
 * reading stdin, stdout and stderr that need to be taken into account when running on various platforms, this helper
 * class is used to deal with those.
 *
 * A generic Exception should be used to indicate what types of checked exceptions might be thrown from this process.
 *
 * @deprecated use {@link ProcessRunner} instead
 */
public class ProcessExecutor {
  /** The logging facility */
  private static final Logger logger = LoggerFactory.getLogger(ProcessExecutor.class);

  private final boolean redirectErrorStream;
  private final String[] commandLine;
  private final Map environment;

  protected ProcessExecutor(String commandLine, Map environment, boolean redirectErrorStream) {
    this.commandLine = commandLine.split("\\s+");
    this.environment = environment;
    this.redirectErrorStream = redirectErrorStream;
  }

  protected ProcessExecutor(String command, String options) {
    this.commandLine = mkCommandLine(command, options);
    this.environment = map();
    this.redirectErrorStream = false;
  }

  protected ProcessExecutor(String command, String[] options) {
    List commandLineList = new ArrayList();
    commandLineList.add(command);
    commandLineList.addAll(Arrays.asList(options));
    this.commandLine = commandLineList.toArray(new String[commandLineList.size()]);
    this.environment = map();
    this.redirectErrorStream = false;
  }

  protected ProcessExecutor(String[] commandLine) {
    this.commandLine = commandLine;
    this.environment = map();
    this.redirectErrorStream = false;
  }

  protected ProcessExecutor(String commandLine, boolean redirectErrorStream) {
    this.commandLine = commandLine.split("\\s+");
    this.redirectErrorStream = redirectErrorStream;
    this.environment = map();
  }

  protected ProcessExecutor(String[] commandLine, boolean redirectErrorStream) {
    this.commandLine = commandLine;
    this.redirectErrorStream = redirectErrorStream;
    this.environment = map();
  }

  protected ProcessExecutor(String command, String options, boolean redirectErrorStream) {
    this.commandLine = mkCommandLine(command, options);
    this.redirectErrorStream = redirectErrorStream;
    this.environment = map();
  }

  private static String[] mkCommandLine(String command, String options) {
    final List commandLineList = new ArrayList();
    commandLineList.add(command);
    Collections.addAll(commandLineList, options.split("\\s+"));
    return commandLineList.toArray(new String[commandLineList.size()]);
  }

  // --

  public final void execute() throws ProcessExcecutorException {
    Process process = null;
    StreamHelper errorStreamHelper = null;
    StreamHelper inputStreamHelper = null;
    try {
      // no special working directory is set which means the working directory of the
      // current java process is used.
      final ProcessBuilder pbuilder = new ProcessBuilder(commandLine);
      pbuilder.redirectErrorStream(redirectErrorStream);
      pbuilder.environment().putAll(environment);
      process = pbuilder.start();
      // consume stdin (the process's stdout)
      // Quoting the java doc for {@link Process}:
      //   "Because some native platforms only provide limited buffer size for standard input and output streams,
      //    failure to promptly write the input stream or read the output stream of the subprocess may cause the
      //    subprocess to block, and even deadlock."
      // Since it's more likely that a process writes massive amounts to stdout it should be made
      // sure to read from it as fast as possible.
      // todo use a thread pool to avoid the delay of starting a new thread. Reading late from a
      //   process's stream is a serious issue which can cause the application to crash or hang.
      inputStreamHelper = new StreamHelper(process.getInputStream()) {
        @Override protected void append(String output) {
          onStdout(output);
        }
      };
      // consume stderr if it is not redirected and merged into stdin
      if (!redirectErrorStream) {
        errorStreamHelper = new StreamHelper(process.getErrorStream()) {
          @Override protected void append(String output) {
            onStderr(output);
          }
        };
      }
      // wait until streams have been emptied otherwise relevant information may get lost
      inputStreamHelper.join();
      if (errorStreamHelper != null) {
        errorStreamHelper.join();
      }
      // wait for the process to exit
      process.waitFor();
      int exitCode = process.exitValue();
      // allow subclasses to react to the process result
      onProcessFinished(exitCode);
    } catch (Throwable t) {
      String msg = null;
      if (errorStreamHelper != null && errorStreamHelper.contentBuffer != null) {
        msg = errorStreamHelper.contentBuffer.toString();
      }
      // TODO: What if the error stream has been redirected? Can we still get the error messgae?
      throw new ProcessExcecutorException(msg, t);
    } finally {
      IoSupport.closeQuietly(process);
    }
  }

  /**
   * A line of output has been read from the processe's stderr. Subclasses should override this method in order to deal
   * with process output.
   *
   * @param line
   *         the line from stderr
   */
  protected void onStderr(String line) {
    logger.warn(line);
  }

  /**
   * A line of output has been read from the processe's stdout. Subclasses should override this method in order to deal
   * with process output.
   *
   * @param line
   *         the line from stdout
   */
  protected void onStdout(String line) {
    logger.debug(line);
  }

  protected void onProcessFinished(int exitCode) throws T {
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy