Skip to content

Commit 8e57eaf

Browse files
authored
Improve handling of spaces in command execution utility on Windows (#757)
1 parent 9c523fc commit 8e57eaf

File tree

2 files changed

+27
-21
lines changed

2 files changed

+27
-21
lines changed

build-info-extractor/src/main/java/org/jfrog/build/extractor/executor/CommandExecutor.java

+26-20
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.concurrent.ExecutorService;
1313
import java.util.concurrent.Executors;
1414
import java.util.concurrent.TimeUnit;
15+
import java.util.stream.Collectors;
1516

1617
import static java.lang.String.format;
1718
import static java.lang.String.join;
@@ -25,7 +26,7 @@ public class CommandExecutor implements Serializable {
2526
private static final int READER_SHUTDOWN_TIMEOUT_SECONDS = 30;
2627
private static final int PROCESS_TERMINATION_TIMEOUT_SECONDS = 30;
2728
private static final int EXECUTION_TIMEOUT_MINUTES = 120;
28-
private final String[] env;
29+
private Map<String, String> env;
2930
private final String executablePath;
3031

3132
/**
@@ -40,7 +41,7 @@ public CommandExecutor(String executablePath, Map<String, String> env) {
4041
fixPathEnv(fixedEnvMap);
4142
finalEnvMap.putAll(fixedEnvMap);
4243
}
43-
this.env = finalEnvMap.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).toArray(String[]::new);
44+
this.env = new HashMap<>(finalEnvMap);
4445
}
4546

4647
/**
@@ -62,19 +63,6 @@ private void fixPathEnv(Map<String, String> env) {
6263
env.replace("PATH", path);
6364
}
6465

65-
/**
66-
* Escape spaces in the input executable path and trim leading and trailing whitespaces.
67-
*
68-
* @param executablePath - the executable path to process
69-
* @return escaped and trimmed executable path.
70-
*/
71-
private static String escapeSpacesInPath(String executablePath) {
72-
if (executablePath == null) {
73-
return null;
74-
}
75-
return executablePath.trim().replaceAll(" ", SystemUtils.IS_OS_WINDOWS ? "^ " : "\\\\ ");
76-
}
77-
7866
/**
7967
* Fix the PATH value to be valid for execution on a Windows machine.
8068
* Take care of a case when either non-Windows or Windows environment-variables are received.
@@ -144,10 +132,11 @@ public CommandResults exeCommand(File execDir, List<String> args, List<String> c
144132
* @return CommandResults object
145133
*/
146134
public CommandResults exeCommand(File execDir, List<String> args, List<String> credentials, Log logger, long timeout, TimeUnit unit) throws InterruptedException, IOException {
147-
args.add(0, executablePath);
135+
List<String> command = new ArrayList<>(args);
136+
command.add(0, executablePath);
148137
ExecutorService service = Executors.newFixedThreadPool(2);
149138
try {
150-
Process process = runProcess(execDir, args, credentials, env, logger);
139+
Process process = runProcess(execDir, command, credentials, env, logger);
151140
// The output stream is not necessary in non-interactive scenarios, therefore we can close it now.
152141
process.getOutputStream().close();
153142
try (InputStream inputStream = process.getInputStream(); InputStream errorStream = process.getErrorStream()) {
@@ -159,7 +148,7 @@ public CommandResults exeCommand(File execDir, List<String> args, List<String> c
159148
service.shutdown();
160149
boolean outputReaderTerminatedProperly = service.awaitTermination(READER_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS);
161150
boolean terminatedProperly = executionTerminatedProperly && outputReaderTerminatedProperly;
162-
return getCommandResults(terminatedProperly, args, inputStreamReader.getOutput(), errorStreamReader.getOutput(), process.exitValue());
151+
return getCommandResults(terminatedProperly, command, inputStreamReader.getOutput(), errorStreamReader.getOutput(), process.exitValue());
163152
} finally {
164153
// Ensure termination of the subprocess we have created.
165154
if (process.isAlive()) {
@@ -177,6 +166,7 @@ public CommandResults exeCommand(File execDir, List<String> args, List<String> c
177166
}
178167
}
179168

169+
180170
private CommandResults getCommandResults(boolean terminatedProperly, List<String> args, String output, String error, int exitValue) {
181171
CommandResults commandRes = new CommandResults();
182172
if (!terminatedProperly) {
@@ -189,7 +179,7 @@ private CommandResults getCommandResults(boolean terminatedProperly, List<String
189179
return commandRes;
190180
}
191181

192-
private static Process runProcess(File execDir, List<String> args, List<String> credentials, String[] env, Log logger) throws IOException {
182+
private static Process runProcess(File execDir, List<String> args, List<String> credentials, Map<String, String> env, Log logger) throws IOException {
193183
if (credentials != null) {
194184
args.addAll(credentials);
195185
}
@@ -204,7 +194,23 @@ private static Process runProcess(File execDir, List<String> args, List<String>
204194
}};
205195
}
206196
logCommand(logger, args, credentials);
207-
return Runtime.getRuntime().exec(args.toArray(new String[0]), env, execDir);
197+
ProcessBuilder processBuilder = new ProcessBuilder(args)
198+
.directory(execDir);
199+
processBuilder.environment().putAll(env);
200+
return processBuilder.start();
201+
}
202+
203+
/**
204+
* Escape spaces in the input executable path and trim leading and trailing whitespaces.
205+
*
206+
* @param executablePath - the executable path to process
207+
* @return escaped and trimmed executable path.
208+
*/
209+
private static String escapeSpacesInPath(String executablePath) {
210+
if (executablePath == null) {
211+
return null;
212+
}
213+
return executablePath.trim().replaceAll(" ", SystemUtils.IS_OS_WINDOWS ? "^ " : "\\\\ ");
208214
}
209215

210216
private static void logCommand(Log logger, List<String> args, List<String> credentials) {

build-info-extractor/src/testFixtures/java/org/jfrog/build/IntegrationTestsBase.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private void createStringSubstitutor() {
116116

117117
protected String readParam(Properties props, String paramName, String defaultValue) {
118118
String paramValue = null;
119-
if (props.size() > 0) {
119+
if (!props.isEmpty()) {
120120
paramValue = props.getProperty(BITESTS_PROPERTIES_PREFIX + paramName);
121121
}
122122
if (StringUtils.isBlank(paramValue)) {

0 commit comments

Comments
 (0)