blob: ef637fbc68de9976cb8b6e808ffe9f0dc628ba6d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 ALL4TEC & CEA LIST.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* ALL4TEC & CEA LIST - initial API and implementation
******************************************************************************/
package org.polarsys.esf.core.common.process;
/*
* --------------------------------------------------------------------------- - This class is based on a tool under GNU
* Lesser General Public License : - ---------------------------------------------------------------------------
*
* ProccessLauncher is a tool to launch an extern application in a Java program with stream managed in separate threads.
*
* Copyright (C) 2006 Fabio MARAZZATO, Yann D'ISANTO
*
* This library 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 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 library; if not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.polarsys.esf.core.common.CommonActivator;
/**
* ProcessLauncher allow to launch an external application while its flux are readed in separated threads.
*
* @author $Author: jdumont $
* @version $Revision: 168 $
*/
public class ProcessLauncher {
/** Log msg. */
private static final String PROCESS_INTERRUPTED =
CommonActivator.getMessages().getString("ProcessLauncher.errorprocessinterrupted"); //$NON-NLS-1$
/** Log msg. */
private static final String ERROR_WHILE_READING_THE_DATA_IN_THE_STREAM =
CommonActivator.getMessages().getString("ProcessLauncher.error.datastream"); //$NON-NLS-1$
/** Log msg. */
private static final String PROCESS_LAUNCHED_WITHOUT_TIMEOUT_INTERRUPTED =
CommonActivator.getMessages().getString("ProcessLauncher.error.notimeout.interrupted"); //$NON-NLS-1$
/** Log msg. */
private static final String PROCESS_LAUNCHED_WITH_TIMEOUT_INTERRUPTED =
CommonActivator.getMessages().getString("ProcessLauncher.error.timeout.interrupted"); //$NON-NLS-1$
/** Log msg. */
private static final String PROCESS_NOT_FINISHED_AFTER_THE_TIMEOUT =
CommonActivator.getMessages().getString("ProcessLauncher.error.timeout"); //$NON-NLS-1$
/** Standard output stream. */
private OutputStream mOutputStream = null;
/** Output stream for errors. */
private OutputStream mErrorStream = null;
/** Input stream. */
private InputStream mInputStream = null;
/** Process of the external application launched. */
private Process mExtAppProcess = null;
/**
* Timeout used to specify after how many milliseconds the external application is considered as long and must be
* killed.
*/
private long mTimeout = 0L;
/** Specify if the external application is finished. */
private boolean mIsFinished = false;
/**
* Creates a new ProcessLauncher object.
*/
public ProcessLauncher() {
this(null, null, null, 0L);
}
/**
* Creates a new ProcessLauncher object, with a given OutputStream for standard and error messages.
*
* @param pOutputStream Outputstream used to get the standard messages
* @param pErrorStream Outputstream used to get the error messages
*/
public ProcessLauncher(final OutputStream pOutputStream, final OutputStream pErrorStream) {
this(pOutputStream, pErrorStream, null, 0L);
}
/**
* Creates a new ProcessLauncher object, with a given OutputStream for standard and error messages, and an
* InputStream to send information to the external application.
*
* @param pOutputStream Outputstream used to get the standard messages
* @param pErrorStream Outputstream used to get the error messages
* @param pInputStream InputStream to communicate with the external application
*/
public ProcessLauncher(
final OutputStream pOutputStream,
final OutputStream pErrorStream,
final InputStream pInputStream) {
this(pOutputStream, pErrorStream, pInputStream, 0L);
}
/**
* Creates a new ProcessLauncher object, with a given OutputStream for standard and error messages. Specify also a
* timeout to be sure that the external application won't take too long.
*
* @param pOutputStream Outputstream used to get the standard messages
* @param pErrorStream Outputstream used to get the error messages
* @param pTimeout Timeout for the external application
*/
public ProcessLauncher(final OutputStream pOutputStream, final OutputStream pErrorStream, final long pTimeout) {
this(pOutputStream, pErrorStream, null, pTimeout);
}
/**
* Creates a new ProcessLauncher object, with a given OutputStream for standard and error messages, and an
* InputStream to send information to the external application. Specify also a timeout to be sure that the external
* application won't take too long.
*
* @param pOutputStream Outputstream used to get the standard messages
* @param pErrorStream Outputstream used to get the error messages
* @param pInputStream InputStream to communicate with the external application
* @param pTimeout Timeout for the external application
*/
public ProcessLauncher(
final OutputStream pOutputStream,
final OutputStream pErrorStream,
final InputStream pInputStream,
final long pTimeout) {
this.mOutputStream = pOutputStream;
this.mErrorStream = pErrorStream;
this.mInputStream = pInputStream;
this.mTimeout = (pTimeout < 0) ? 0L : pTimeout;
}
/**
* Execute a command line in a separated thread.
*
* @param pCommandLine Command line to execute
* @return Return value of the process launched
* @throws IOException Exception launched
*/
public final int exec(final String pCommandLine) throws IOException {
mExtAppProcess = Runtime.getRuntime().exec(pCommandLine);
return execute();
}
/**
* Execute a command line in a separated thread.
*
* @param pCommandLine Command line to execute. It's formated in a String array to allow the use of command and
* parameters with spaces
* @return Return value of the process launched
* @throws IOException Exception launched
*/
public final int exec(final String[] pCommandLine) throws IOException {
mExtAppProcess = Runtime.getRuntime().exec(pCommandLine);
return execute();
}
/**
* Execute a command line in a separated thread, with specific environment variables.
*
* @param pCommandLine Command line to execute. It's formated in a String array to allow the use of command and
* parameters with spaces
* @param pEnvVarsArray Array of environment variables
* @return Return value of the process launched
* @throws IOException Exception launched
*/
public final int exec(final String[] pCommandLine, final String[] pEnvVarsArray) throws IOException {
mExtAppProcess = Runtime.getRuntime().exec(pCommandLine, pEnvVarsArray);
return execute();
}
/**
* Execute a command line in a separated thread, with specific environment variables and the working directory.
*
* @param pCommandLine Command line to execute. It's formated in a String array to allow the use of command and
* parameters with spaces
* @param pEnvVarsArray Array of environment variables
* @param pWorkingDirectory Working directory to use
* @return Return value of the process launched
* @throws IOException Exception launched
*/
public final int exec(final String[] pCommandLine, final String[] pEnvVarsArray, final File pWorkingDirectory)
throws IOException {
mExtAppProcess = Runtime.getRuntime().exec(pCommandLine, pEnvVarsArray, pWorkingDirectory);
return execute();
}
/**
* Execute a command line in a separated thread, with specific environment variables.
*
* @param pCommandLine Command line to execute.
* @param pEnvVarsArray Array of environment variables
* @return Return value of the process launched
* @throws IOException Exception launched
*/
public final int exec(final String pCommandLine, final String[] pEnvVarsArray) throws IOException {
mExtAppProcess = Runtime.getRuntime().exec(pCommandLine, pEnvVarsArray);
return execute();
}
/**
* Execute a command line in a separated thread, with specific environment variables and the working directory.
*
* @param pCommandLine Command line to execute.
* @param pEnvVarsArray Array of environment variables
* @param pWorkingDirectory Working directory to use
* @return Return value of the process launched
* @throws IOException Exception launched
*/
public final int exec(final String pCommandLine, final String[] pEnvVarsArray, final File pWorkingDirectory)
throws IOException {
mExtAppProcess = Runtime.getRuntime().exec(pCommandLine, pEnvVarsArray, pWorkingDirectory);
return execute();
}
/**
* Method used to execute the command line, with all the properties given at the creation of this object.
*
* @return Status of execution
* @throws IOException Exception launched
*/
private int execute() throws IOException {
int vStatus = -1;
// Prepare the consumption of error stream in a separated thread
if (mErrorStream == null) {
// Close the error stream of the process if it's not consumed
mExtAppProcess.getErrorStream().close();
} else {
// Link the error stream of the process with the error stream of this object
createStreamThread(mExtAppProcess.getErrorStream(), mErrorStream);
}
// Prepare the consumption of standard stream in a separated thread
if (mOutputStream == null) {
// Close the input stream of the process if it's not consumed
mExtAppProcess.getInputStream().close();
} else {
// Link the input stream of the process with the output stream of this object
createStreamThread(mExtAppProcess.getInputStream(), mOutputStream);
}
// Redirect the standard input of the external application to the given
// input stream, also in a separated thread
if (mInputStream != null) {
// Link the input stream of this object with the output stream of the process
createStreamThread(mInputStream, mExtAppProcess.getOutputStream());
}
// Check if a timeout is specified. When it's the case, the external
// application launched must be finished before this timeout or it will
// be killed
if (mTimeout > 0L) {
// Use a timeout ...
// Create a thread for the external application and start it
Thread vProcessThread = createProcessThread(mExtAppProcess);
vProcessThread.start();
try {
// Wait to the timeout that this thread die
vProcessThread.join(mTimeout);
try {
// Get the return value of the process. If the process is not
// finished, throws an IllegalThreadStateException
vStatus = mExtAppProcess.exitValue();
} catch (final IllegalThreadStateException pException) {
CommonActivator.logError(PROCESS_NOT_FINISHED_AFTER_THE_TIMEOUT, pException);
// Kill the process
mExtAppProcess.destroy();
// Get its return value
vStatus = mExtAppProcess.exitValue();
}
} catch (final InterruptedException pException) {
CommonActivator.logError(PROCESS_LAUNCHED_WITH_TIMEOUT_INTERRUPTED, pException);
}
} else {
// Don't use a timeout ...
try {
// Simply launch the external application and wait is end
vStatus = mExtAppProcess.waitFor();
} catch (final InterruptedException pException) {
CommonActivator.logError(PROCESS_LAUNCHED_WITHOUT_TIMEOUT_INTERRUPTED, pException);
}
}
mIsFinished = true;
return vStatus;
}
/**
* Method used to create a separated thread for a stream, and start it. It links an "input" stream and an "output"
* stream. The "input" stream will be the consumer of the "output" stream.
*
* @param pInputStream InputStream to read
* @param pOutputStream OutputStream to write in
*/
private void createStreamThread(final InputStream pInputStream, final OutputStream pOutputStream) {
// Create the separated thread and launch it
new Thread(new Runnable() {
/**
* Method used to define what the thread will do when it's run
*/
public void run() {
// Create a buffered stream from the input stream
BufferedInputStream vBuffInputStream = new BufferedInputStream(pInputStream);
// Create a buffered stream from the output stream
BufferedOutputStream vBuffOutputStream = null;
if (pOutputStream != null) {
vBuffOutputStream = new BufferedOutputStream(pOutputStream);
}
// Initialise a byte array, used to read the data
byte[] vDataArray = new byte[2048];
try {
int vNbBytesRead = vBuffInputStream.read(vDataArray);
// Loop to read the data
while (!mIsFinished && (vNbBytesRead > 0)) {
// If the output stream is specified, write the data in it
if (vBuffOutputStream != null) {
vBuffOutputStream.write(vDataArray, 0, vNbBytesRead);
vBuffOutputStream.flush();
}
vNbBytesRead = vBuffInputStream.read(vDataArray);
}
} catch (final IOException pException) {
CommonActivator.logError(ERROR_WHILE_READING_THE_DATA_IN_THE_STREAM, pException);
}
}
}).start();
}
/**
* Method used to create a separated thread for the external application process.
*
* @param pProcess Process used for the external application
* @return Thread, the separated thread created
*/
private Thread createProcessThread(final Process pProcess) {
// Create the separated thread
return new Thread() {
/**
* Method used to define what the thread will do when it's run
*/
public void run() {
try {
// Launch the process for the external application and wait for it
pProcess.waitFor();
} catch (final InterruptedException pException) {
CommonActivator.logError(PROCESS_INTERRUPTED, pException);
}
}
};
}
/**
* Get the OutputStream to which the error stream of external application has been redirected.
*
* @return OutputStream
*/
public final OutputStream getErrorStream() {
return mErrorStream;
}
/**
* Get the InputStream from which the information are sent to the input stream of external application.
*
* @return InputStream
*/
public final InputStream getInputStream() {
return mInputStream;
}
/**
* Get the OutputStream to which the standard output stream of external application has been redirected.
*
* @return OutputStream
*/
public final OutputStream getOutputStream() {
return mOutputStream;
}
/**
* Get the timeout value, in ms.
*
* @return long, Timeout value
*/
public final long getTimeout() {
return mTimeout;
}
/**
* Set the OutputStream to which the error stream of external application will be redirected.
*
* @param pErrorStream The error stream to use
*/
public final void setErrorStream(final OutputStream pErrorStream) {
this.mErrorStream = pErrorStream;
}
/**
* Set the InputStream from which the information will be sent to the input stream of external application.
*
* @param pInputStream The input stream to use
*/
public final void setInputStream(final InputStream pInputStream) {
this.mInputStream = pInputStream;
}
/**
* Set the OutputStream to which the standard output stream of external application will be redirected.
*
* @param pOutputStream The output stream to use
*/
public final void setOutputStream(final OutputStream pOutputStream) {
this.mOutputStream = pOutputStream;
}
/**
* Set the timeout value, in ms. If it's set to 0, the application will never be forced to shutdown.
*
* @param pTimeout Timeout value
*/
public final void setTimeout(final long pTimeout) {
this.mTimeout = pTimeout;
}
}