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