Skip to content

Commit 5a5dfb7

Browse files
authored
CommandLine module upgrades (#560)
* command lines module upgrades * new tests * missing dep * missing dep * release notes * [Gradle Release Plugin] - new version commit: '3.27.0-snapshot'.
1 parent 3992930 commit 5a5dfb7

16 files changed

+589
-56
lines changed

RELEASE-NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 3.27.0
2+
* Commandline module upgrade to be able to execute new usecases when creating new native int tests. #533
3+
14
## 3.26.0
25
* Option to set config file path from the ENV, see the docs.
36

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=3.26.0-snapshot
1+
version=3.27.0-snapshot
Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
package com.mageddo.commons.exec;
22

3-
import lombok.Builder;
4-
import lombok.Getter;
5-
import lombok.NonNull;
6-
import lombok.ToString;
3+
import lombok.extern.slf4j.Slf4j;
74
import org.apache.commons.exec.CommandLine;
8-
import org.apache.commons.exec.DaemonExecutor;
95
import org.apache.commons.exec.ExecuteException;
6+
import org.apache.commons.exec.ExecuteResultHandler;
107
import org.apache.commons.exec.ExecuteWatchdog;
11-
import org.apache.commons.exec.Executor;
12-
import org.apache.commons.exec.PumpStreamHandler;
138

14-
import java.io.ByteArrayOutputStream;
159
import java.io.IOException;
1610
import java.io.UncheckedIOException;
11+
import java.time.Duration;
1712

13+
@Slf4j
1814
public class CommandLines {
1915

2016
public static Result exec(String commandLine, Object... args) {
2117
return exec(CommandLine.parse(String.format(commandLine, args)),
22-
ExecuteWatchdog.INFINITE_TIMEOUT
18+
ExecuteWatchdog.INFINITE_TIMEOUT
2319
);
2420
}
2521

@@ -32,56 +28,62 @@ public static Result exec(CommandLine commandLine) {
3228
}
3329

3430
public static Result exec(CommandLine commandLine, long timeout) {
35-
final var out = new ByteArrayOutputStream();
36-
final var executor = new DaemonExecutor();
37-
final var streamHandler = new PumpStreamHandler(out);
38-
executor.setStreamHandler(streamHandler);
39-
int exitCode;
31+
return exec(
32+
Request.builder()
33+
.commandLine(commandLine)
34+
.timeout(Duration.ofMillis(timeout))
35+
.build()
36+
);
37+
}
38+
39+
private static void registerProcessWatch(ProcessAccessibleDaemonExecutor executor) {
40+
ProcessesWatchDog.instance()
41+
.watch(executor::getProcess)
42+
;
43+
}
44+
45+
public static Result exec(CommandLine commandLine, ExecuteResultHandler handler) {
46+
return exec(Request
47+
.builder()
48+
.commandLine(commandLine)
49+
.handler(handler)
50+
.build()
51+
);
52+
}
53+
54+
public static Result exec(Request request) {
55+
final var executor = createExecutor();
56+
executor.setStreamHandler(request.getStreamHandler());
57+
Integer exitCode = null;
4058
try {
41-
executor.setWatchdog(new ExecuteWatchdog(timeout));
42-
exitCode = executor.execute(commandLine);
59+
executor.setWatchdog(new ExecuteWatchdog(request.getTimeoutInMillis()));
60+
if (request.getHandler() != null) {
61+
executor.execute(request.getCommandLine(), request.getEnv(), request.getHandler());
62+
registerProcessWatch(executor);
63+
} else {
64+
exitCode = executor.execute(request.getCommandLine(), request.getEnv());
65+
}
4366
} catch (ExecuteException e) {
44-
exitCode = e.getExitValue();
67+
if (request.getHandler() != null) {
68+
request.getHandler().onProcessFailed(e);
69+
} else {
70+
exitCode = e.getExitValue();
71+
}
4572
} catch (IOException e) {
4673
throw new UncheckedIOException(e);
4774
}
4875
return Result
49-
.builder()
50-
.executor(executor)
51-
.out(out)
52-
.exitCode(exitCode)
53-
.build();
76+
.builder()
77+
.executor(executor)
78+
.processSupplier(executor::getProcess)
79+
.out(request.getBestOut())
80+
.exitCode(exitCode)
81+
.request(request)
82+
.build();
5483
}
5584

56-
@Getter
57-
@Builder
58-
@ToString(of = {"exitCode"})
59-
public static class Result {
60-
61-
@NonNull
62-
private Executor executor;
63-
64-
@NonNull
65-
private ByteArrayOutputStream out;
66-
67-
private int exitCode;
68-
69-
public String getOutAsString() {
70-
return this.out.toString();
71-
}
72-
73-
public Result checkExecution() {
74-
if (this.executor.isFailure(this.getExitCode())) {
75-
throw new ExecutionValidationFailedException(this);
76-
}
77-
return this;
78-
}
79-
80-
public String toString(boolean printOut) {
81-
return String.format(
82-
"code=%d, out=%s",
83-
this.exitCode, printOut ? this.getOutAsString() : null
84-
);
85-
}
85+
private static ProcessAccessibleDaemonExecutor createExecutor() {
86+
return new ProcessAccessibleDaemonExecutor();
8687
}
88+
8789
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.mageddo.commons.exec;
2+
3+
import java.io.IOException;
4+
import java.io.OutputStream;
5+
import java.util.List;
6+
import java.util.stream.Stream;
7+
8+
public class DelegateOutputStream extends OutputStream {
9+
10+
private final List<OutputStream> delegateOuts;
11+
12+
public DelegateOutputStream(OutputStream... delegateOuts) {
13+
this.delegateOuts = Stream.of(delegateOuts).toList();
14+
}
15+
16+
public DelegateOutputStream(List<OutputStream> delegateOuts) {
17+
this.delegateOuts = delegateOuts;
18+
}
19+
20+
@Override
21+
public void write(int b) throws IOException {
22+
for (final var delegateOut : this.delegateOuts) {
23+
delegateOut.write(b);
24+
}
25+
}
26+
27+
@Override
28+
public void close() throws IOException {
29+
for (final var out : this.delegateOuts) {
30+
try {
31+
out.close();
32+
} catch (IOException e) {
33+
}
34+
}
35+
}
36+
}
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package com.mageddo.commons.exec;
22

33
public class ExecutionValidationFailedException extends RuntimeException {
4-
private final CommandLines.Result result;
4+
private final Result result;
55

6-
public ExecutionValidationFailedException(CommandLines.Result result) {
6+
public ExecutionValidationFailedException(Result result) {
77
super(String.format("error, code=%d, error=%s", result.getExitCode(), result.getOutAsString()));
88
this.result = result;
99
}
1010

11-
public CommandLines.Result result() {
11+
public Result result() {
1212
return this.result;
1313
}
14+
15+
public int getExitCode() {
16+
return this.result.getExitCode();
17+
}
1418
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.mageddo.commons.exec;
2+
3+
import org.apache.commons.exec.ExecuteException;
4+
import org.apache.commons.exec.ExecuteResultHandler;
5+
6+
public class NopResultHandler implements ExecuteResultHandler {
7+
@Override
8+
public void onProcessComplete(int exitValue) {
9+
10+
}
11+
12+
@Override
13+
public void onProcessFailed(ExecuteException e) {
14+
15+
}
16+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.mageddo.commons.exec;
2+
3+
import lombok.Getter;
4+
5+
import java.io.IOException;
6+
import java.io.OutputStream;
7+
import java.io.PipedInputStream;
8+
import java.io.PipedOutputStream;
9+
import java.io.UncheckedIOException;
10+
11+
public class PipedStream extends OutputStream {
12+
13+
@Getter
14+
private final PipedInputStream pipedIn;
15+
16+
private final DelegateOutputStream delegateOut;
17+
private final OutputStream originalOut;
18+
19+
public PipedStream(final OutputStream out) {
20+
try {
21+
this.pipedIn = new PipedInputStream();
22+
this.originalOut = out;
23+
final var pout = new PipedOutputStream(this.pipedIn);
24+
this.delegateOut = new DelegateOutputStream(out, pout);
25+
} catch (IOException e) {
26+
throw new UncheckedIOException(e);
27+
}
28+
}
29+
30+
@Override
31+
public void write(int b) throws IOException {
32+
this.delegateOut.write(b);
33+
}
34+
35+
public void close() throws IOException {
36+
this.delegateOut.close();
37+
}
38+
39+
OutputStream getOriginalOut() {
40+
return originalOut;
41+
}
42+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.mageddo.commons.exec;
2+
3+
import lombok.Getter;
4+
import org.apache.commons.exec.CommandLine;
5+
import org.apache.commons.exec.DaemonExecutor;
6+
7+
import java.io.File;
8+
import java.io.IOException;
9+
import java.util.Map;
10+
11+
@Getter
12+
class ProcessAccessibleDaemonExecutor extends DaemonExecutor {
13+
14+
private Process process = null;
15+
16+
@Override
17+
protected Process launch(CommandLine command, Map<String, String> env, File dir) throws IOException {
18+
return this.process = super.launch(command, env, dir);
19+
}
20+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.mageddo.commons.exec;
2+
3+
import com.mageddo.commons.lang.Singletons;
4+
import lombok.extern.slf4j.Slf4j;
5+
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.Objects;
9+
import java.util.function.Supplier;
10+
11+
@Slf4j
12+
public class ProcessesWatchDog {
13+
14+
private List<Supplier<Process>> processes = new ArrayList<>();
15+
16+
public static ProcessesWatchDog instance() {
17+
return Singletons.createOrGet(ProcessesWatchDog.class, ProcessesWatchDog::new);
18+
}
19+
20+
public void watch(Supplier<Process> sup) {
21+
this.processes.add(sup);
22+
}
23+
24+
public void watch(Process process) {
25+
this.processes.add(() -> process);
26+
}
27+
28+
public void killAllProcesses() {
29+
final var validProcesses = this.findValidProcesses();
30+
31+
log.debug("status=killing all processes, processes={}, valid={}", this.processes.size(), validProcesses.size());
32+
33+
validProcesses.forEach(process -> {
34+
try {
35+
process.destroy();
36+
log.trace("status=killed, pid={}", process.pid());
37+
} catch (Exception e) {
38+
log.warn("status=unable to destroy, processId={}, msg={}", process.pid(), e.getMessage(), e);
39+
}
40+
});
41+
}
42+
43+
private List<Process> findValidProcesses() {
44+
return this.processes.stream()
45+
.map(Supplier::get)
46+
.filter(Objects::nonNull)
47+
.toList();
48+
}
49+
}

0 commit comments

Comments
 (0)