blob: f37aa23419fa8e2b5514f6615576f04a96c6d39a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2018 Red Hat.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat - Initial Contribution
*******************************************************************************/
package org.eclipse.linuxtools.internal.docker.core;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.linuxtools.docker.core.Activator;
import org.eclipse.linuxtools.docker.core.DockerCommandNotFoundException;
import org.eclipse.linuxtools.docker.core.DockerException;
/**
* A utility class to run Java {@link Process} such as {@code docker-machine} or
* {@code docker-compose}.
*/
public class ProcessLauncher {
public static final String COMMAND_LINE = "org.eclipse.linuxtools.docker.compose.commandLine";
/**
* Checks that the given {@code cmdName} exists in the given {@code path}
*
* @param path
* the system {@code PATH} to use to look for the command
* @param cmdName
* the name of the command in the given {@code cmdDir}
* @return <code>true</code> if the command was found, <code>false</code>
* otherwise.
*/
public boolean checkPathToCommand(final String path, final String cmdName) {
return Stream.of(path.split(File.pathSeparator))
.map(pathFragment -> new File(pathFragment, cmdName))
.anyMatch(fullPath -> fullPath.exists());
}
/**
* Entry method to run a command.
*
* @param baseCmdDir
* the base directory containing the command to run
* @param cmdName
* the name of the command to run
* @throws DockerException
* if the {@code baseCmdDir} did not contain the specific
* {@code cmdName}
*/
public FluentProcessBuilder processBuilder(final String baseCmdDir,
final String cmdName, final String[] args) throws DockerException {
return new FluentProcessBuilder(baseCmdDir, cmdName, args);
}
public static class FluentProcessBuilder {
/** the {@link ProcessBuilder} to use to run the command. */
private final ProcessBuilder processBuilder;
/**
* Constructor.
*
* @param baseCmdDir
* the base directory containing the command to run
* @param cmdName
* the name of the command to run
* @throws DockerException
* if the {@code baseCmdDir} did not contain the specific
* {@code cmdName}
*/
private FluentProcessBuilder(final String baseCmdDir,
final String cmdName, final String[] args)
throws DockerException {
// check that the 'cmdName' can be found in the given
// 'baseCmdDir'
final boolean commandExists = new ProcessLauncher()
.checkPathToCommand(baseCmdDir, cmdName);
if (!commandExists) {
throw new DockerCommandNotFoundException(
ProcessMessages.getFormattedString("Command_Not_Found", //$NON-NLS-1$
baseCmdDir, cmdName));
}
final String[] command = new String[args.length + 1];
command[0] = Paths.get(baseCmdDir, cmdName).toString();
System.arraycopy(args, 0, command, 1, args.length);
this.processBuilder = new ProcessBuilder(command);
}
public FluentProcessBuilder extraPath(final String... extraPaths) {
final Map<String, String> environment = processBuilder
.environment();
final StringBuilder path = new StringBuilder();
for (String extraPath : extraPaths) {
path.append(File.pathSeparator).append(extraPath);
}
final String newEnvPath = environment.get("PATH") //$NON-NLS-1$
+ path.toString();
environment.put("PATH", newEnvPath); //$NON-NLS-1$
return this;
}
public FluentProcessBuilder workingDir(final String workingDir) {
this.processBuilder.directory(new File(workingDir));
return this;
}
public FluentProcessBuilder redirectErrorStream(
boolean redirectErrorStream) {
this.processBuilder.redirectErrorStream(redirectErrorStream);
return this;
}
public String getCommand() {
return this.processBuilder.command().stream()
.collect(Collectors.joining(" "));
}
/**
* Starts the process from the current settings.
*
* @return the {@link Process} that was started by this process builder
* @throws DockerException
* if an error occurred
*/
public Process start() throws DockerException {
try {
return this.processBuilder.start();
} catch (Throwable e) {
throw new DockerException(ProcessMessages
.getFormattedString("Process_Start_Exception", //$NON-NLS-1$
processBuilder.command().stream()
.collect(Collectors.joining(" ")),
e.getMessage()),
e);
}
}
/**
* Starts the {@link Process} from the current settings and returns the
* output.
*
* @return the process output once it has completed.
* @throws DockerException
* if an error occurred
*/
public String[] startAndGetResult() throws DockerException {
final Process process = start();
return extractResult(process);
}
private String[] extractResult(final Process process) {
try {
process.waitFor();
if (process.exitValue() == 0) {
final List<String> result = new ArrayList<>();
try (final InputStream inputStream = process.getInputStream();
final BufferedReader buff = new BufferedReader(
new InputStreamReader(inputStream))) {
String line;
while ((line = buff.readLine()) != null) {
result.add(line);
}
}
return result.toArray(new String[0]);
} else {
final StringBuilder errorMessage = new StringBuilder();
try (final InputStream errorStream = process.getErrorStream();
final BufferedReader buff = new BufferedReader(
new InputStreamReader(errorStream))) {
String line;
while ((line = buff.readLine()) != null) {
errorMessage.append(line).append('\n'); // $NON-NLS-1$
}
}
Activator.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID,
ProcessMessages.getFormattedString("Process_Error", //$NON-NLS-1$
this.getCommand(),
process.exitValue(), errorMessage.toString())));
}
} catch (IOException | InterruptedException e) {
Activator.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
ProcessMessages.getFormattedString("Process_Exception", //$NON-NLS-1$
this.getCommand(), e.getMessage()),
e));
}
return new String[0];
}
}
}