| /******************************************************************************* |
| * 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; |
| } |
| } |