blob: 7c84e7e1888bd128cfd0a44814cf12830b252355 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Red Hat, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Red Hat Inc. - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.wst.jsdt.js.cli.internal.util;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.IStreamListener;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.wst.jsdt.js.cli.CLIPlugin;
/**
* Utilities for calling and processing the output from external executables.
*
* @author Gorkem Ercan
*
*/
public class ExternalProcessUtility {
/**
* Executes the given commands asynchronously.
*
* <p>
* If the workingDirectory is null, the current directory for process is used.
* </p>
* @param command the command line can not be null or empty
* @param workingDirectory working directory for the executed command can be null
* @param outStreamListener listener for output, can be null
* @param errorStreamListene listener for error output, can be null
* @param envp environment variables to set in the process can be null
* @throws CoreException if execution fails
* @throws IllegalArgumentException
* <ul>
* <li>If command is null or empty</li>
* <li>If specified workingDirectory does not exist or not a directory</li>
* </ul>
*/
public void execAsync (String [] command, File workingDirectory,
IStreamListener outStreamListener,
IStreamListener errorStreamListener, String[] envp) throws CoreException{
CLIPlugin.logInfo("Async Execute command line: " + Arrays.toString(command)); //$NON-NLS-1$
IProcess prcs = exec(command, workingDirectory, new NullProgressMonitor(), envp, null);
setTracing(command, outStreamListener, errorStreamListener, prcs);
}
/**
* Convenience method to specify command line as a string for {@link #execAsync(String[], File, IStreamListener, IStreamListener, String[])}
*/
public void execAsync ( String commandLine, File workingDirectory,
IStreamListener outStreamListener,
IStreamListener errorStreamListener, String[] envp) throws CoreException{
checkCommandLine(commandLine);
this.execAsync(DebugPlugin.parseArguments(commandLine),
workingDirectory, outStreamListener, errorStreamListener, envp);
}
/**
* Executes the given commands synchronously.
*
* <p>
* If the workingDirectory is null, the current directory for process is used.
* </p>
* @param command the command line can not be null or empty
* @param workingDirectory working directory for the executed command, can be null
* @param outStreamListener listener for output, can be null
* @param errorStreamListene listener for error output, can be null
* @param envp environment variables to set in the process, can be null
* @param launchConfiguration the launch to add as part of this call, can be null
* @return the exit code for the process
* @throws CoreException if the execution fails
*/
public int execSync ( String[] command, File workingDirectory,
IStreamListener outStreamListener,
IStreamListener errorStreamListener, IProgressMonitor monitor,
String[] envp, ILaunchConfiguration launchConfiguration) throws CoreException{
if(monitor == null){
monitor = new NullProgressMonitor();
}
CLIPlugin.logInfo("Sync Execute command line: " + Arrays.toString(command)); //$NON-NLS-1$
IProcess prcs = exec(command, workingDirectory, monitor, envp, launchConfiguration);
if(prcs == null ){
return 0;
}
setTracing(command, outStreamListener, errorStreamListener, prcs);
while (!prcs.isTerminated()) {
try {
if (monitor.isCanceled()) {
prcs.terminate();
break;
}
Thread.sleep(50);
} catch (InterruptedException e) {
CLIPlugin.logError(e, "Exception waiting for process to terminate"); //$NON-NLS-1$
}
}
return prcs.getExitValue();
}
/**
* Executes the given command and returns a handle to the
* process. Should only be used if access to {@link IProcess}
* instance is desired.
*
* @param command
* @param workingDirectory
* @param monitor
* @param envp
* @param launchConfiguration
* @return the process
* @throws CoreException
*/
public IProcess exec(String[] command, File workingDirectory,
IProgressMonitor monitor, String[] envp,
ILaunchConfiguration launchConfiguration ) throws CoreException{
checkCommands(command);
checkWorkingDirectory(workingDirectory);
if(monitor == null ){
monitor = new NullProgressMonitor();
}
if (envp == null && launchConfiguration != null ){
envp = DebugPlugin.getDefault().getLaunchManager().getEnvironment(launchConfiguration);
}
if (monitor.isCanceled()) {
return null;
}
Process process = DebugPlugin.exec(command, workingDirectory, envp);
Map<String, String> processAttributes = generateProcessAttributes(command, launchConfiguration);
Launch launch = new Launch(launchConfiguration, "run", null); //$NON-NLS-1$
IProcess prcs = DebugPlugin.newProcess(launch, process, command[0], processAttributes);
DebugPlugin.getDefault().getLaunchManager().addLaunch(launch);
return prcs;
}
/**
* Convenience method to specify command line as a String
* for {@link #execSync(String[], File, IStreamListener, IStreamListener, IProgressMonitor, String[], ILaunchConfiguration)}
*/
public int execSync ( String commandLine, File workingDirectory,
IStreamListener outStreamListener,
IStreamListener errorStreamListener, IProgressMonitor monitor, String[] envp, ILaunchConfiguration launchConfiguration) throws CoreException{
String[] cmd = DebugPlugin.parseArguments(commandLine);
return this.execSync(cmd, workingDirectory, outStreamListener, errorStreamListener, monitor, envp, launchConfiguration);
}
private Map<String, String> generateProcessAttributes(String[] command, ILaunchConfiguration launchConfiguration)
throws CoreException {
Map<String, String> processAttributes = new HashMap<String, String>();
processAttributes.put(IProcess.ATTR_PROCESS_TYPE, command[0]);
processAttributes.put(IProcess.ATTR_CMDLINE, generateCommandLine(command));
if(launchConfiguration != null ){
processAttributes.put(IProcess.ATTR_PROCESS_LABEL,
launchConfiguration.getAttribute(IProcess.ATTR_PROCESS_LABEL, command[0]));
}
return processAttributes;
}
private void checkWorkingDirectory(File workingDirectory) {
if(workingDirectory != null && !workingDirectory.isDirectory()){
throw new IllegalArgumentException(workingDirectory.toString()+ " is not a valid directory"); //$NON-NLS-1$
}
}
private void setTracing(String[] command, IStreamListener outStreamListener, IStreamListener errorStreamListener,
IProcess prcs) {
// TODO: Handle Debug later
// if(HybridCore.DEBUG){
// HybridCore.trace("Creating TracingStreamListeners for " + Arrays.toString(command));
// outStreamListener = new TracingStreamListener(outStreamListener);
// errorStreamListener = new TracingStreamListener(outStreamListener);
// }
if( outStreamListener != null ){
prcs.getStreamsProxy().getOutputStreamMonitor().addListener(outStreamListener);
}
if( errorStreamListener != null ){
prcs.getStreamsProxy().getErrorStreamMonitor().addListener(errorStreamListener);
}
}
private void checkCommandLine(String commandLine) {
if(commandLine == null || commandLine.isEmpty()){
throw new IllegalArgumentException("Missing command line"); //$NON-NLS-1$
}
}
private void checkCommands(String[] command) {
if(command == null || command.length <1 ){
throw new IllegalArgumentException("Empty commands array"); //$NON-NLS-1$
}
}
private String generateCommandLine(final String[] commandLine) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < commandLine.length; i++) {
buf.append(' ');
char[] characters = commandLine[i].toCharArray();
StringBuffer command = new StringBuffer();
boolean containsSpace = false;
for (int j = 0; j < characters.length; j++) {
char character = characters[j];
if (character == '\"') {
command.append('\\');
} else if (character == ' ') {
containsSpace = true;
}
command.append(character);
}
if (containsSpace) {
buf.append('\"');
buf.append(command);
buf.append('\"');
} else {
buf.append(command);
}
}
return buf.toString();
}
}