##// END OF EJS Templates
Refactoring exec classes
cin -
r7:4ba61464d214 default
parent child
Show More
@@ -0,0 +1,16
1 package org.implab.gradle.common.exec;
2
3 /** Shell used to start processes */
4 public interface ExecShell {
5
6 /** Creates new process builder {@link ExecBuilder} */
7 ExecBuilder builder();
8
9 /**
10 * Creates default process execution "shell" which will execute processes
11 * directly.
12 */
13 static ExecShell system() {
14 return () -> new SystemExecBuilder();
15 }
16 }
@@ -1,78 +1,78
1 1 package org.implab.gradle.common.dsl;
2 2
3 3 import java.io.File;
4 4 import java.util.ArrayList;
5 5 import java.util.Collection;
6 6 import java.util.List;
7 7
8 import org.implab.gradle.common.exec.Executor;
8 import org.implab.gradle.common.exec.ExecBuilder;
9 9 import org.implab.gradle.common.exec.RedirectFrom;
10 10 import org.implab.gradle.common.exec.RedirectTo;
11 11
12 12 public class ProcessSpec {
13 13 private RedirectFrom inputRedirect;
14 14
15 15 private RedirectTo outputRedirect;
16 16
17 17 private RedirectTo errorRedirect;
18 18
19 19 private List<String> command = new ArrayList<>();
20 20
21 21 private File directory;
22 22
23 23 public ProcessSpec command(String... args) {
24 24 command = new ArrayList<>();
25 25 args(args);
26 26 return this;
27 27 }
28 28
29 29 public ProcessSpec directory(File workingDir) {
30 30 directory = workingDir;
31 31 return this;
32 32 }
33 33
34 34 public ProcessSpec command(Collection<String> args) {
35 35 command = new ArrayList<>();
36 36 args(args);
37 37 return this;
38 38 }
39 39
40 40 public ProcessSpec args(String... args) {
41 41 for (String arg : args)
42 42 command.add(arg);
43 43 return this;
44 44 }
45 45
46 46 public ProcessSpec args(Collection<String> args) {
47 47 command.addAll(args);
48 48 return this;
49 49 }
50 50
51 51 public ProcessSpec stderr(RedirectTo to) {
52 52 errorRedirect = to;
53 53 return this;
54 54 }
55 55
56 56 public ProcessSpec stdin(RedirectFrom from) {
57 57 inputRedirect = from;
58 58 return this;
59 59 }
60 60
61 61 public ProcessSpec stdout(RedirectTo to) {
62 62 outputRedirect = to;
63 63 return this;
64 64 }
65 65
66 public void accept(Executor executor) {
66 public void accept(ExecBuilder executor) {
67 67 command.forEach(executor::argument);
68 68 executor.directory(directory);
69 69 if (inputRedirect != null)
70 70 executor.stdin(inputRedirect);
71 71 if (outputRedirect != null)
72 72 executor.stdout(outputRedirect);
73 73 if (errorRedirect != null)
74 74 executor.stderr(errorRedirect);
75 75 }
76 76
77 77
78 78 }
@@ -1,25 +1,61
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.util.concurrent.CompletableFuture;
4 4
5 import org.eclipse.jdt.annotation.NonNullByDefault;
6
5 7 import java.io.File;
6 8 import java.io.IOException;
7 9
8 /** Interface for a command line execution. */
9 public interface Executor {
10 /** Command line builder */
11 @NonNullByDefault
12 public interface ExecBuilder {
13
14 /** Sets command line, clears previous one if any */
15 void command(String cmd, String... args);
16
17 /** Adds an argument to the command line */
10 18 void argument(String arg);
11 19
20 /** Sets the working directory */
12 21 void directory(File directory);
13 22
23 /**
24 * Sets the environment value. The value cannot be null.
25 *
26 * @param envVar The name of the environment variable
27 * @param value The value to set,
28 */
29 void setEnvironment(String envVar, String value);
30
31 void unsetEnvironment(String envVar);
32
33 /**
34 * Sets redirection for the stdin, {@link RedirectFrom} will be applied
35 * every time the process is started.
36 *
37 * <p>If redirection
38 */
14 39 void stdin(RedirectFrom from);
15 40
41 /**
42 * Sets redirection for the stdout, {@link RedirectTo} will be applied
43 * every time the process is started.
44 */
16 45 void stdout(RedirectTo out);
17 46
47 /**
48 * Sets redirection for the stderr, {@link RedirectTo} will be applied
49 * every time the process is started.
50 */
18 51 void stderr(RedirectTo err);
19
52
53 /**
54 * Creates and starts new process and returns {@link CompletableFuture}. The
55 * process may be a remote process, or a shell process or etc.
56 * @return
57 * @throws IOException
58 */
20 59 CompletableFuture<Integer> start() throws IOException;
21 60
22 public static Executor system() {
23 return new SystemExec();
24 }
25 61 }
@@ -1,33 +1,37
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.io.File;
4 4 import java.io.FileInputStream;
5 5 import java.io.InputStream;
6 6 import java.io.OutputStream;
7 7 import java.util.concurrent.CompletableFuture;
8 8
9 /** Describes how to redirect input streams. This interface is used to configure
10 * lazy redirection. {@link #redirect(OutputStream)} is called when the process
11 * is started.
12 */
9 13 public interface RedirectFrom {
10 14 CompletableFuture<Void> redirect(OutputStream to);
11 15
12 16 public static RedirectFrom file(final File file) {
13 17 return to -> CompletableFuture.runAsync(() -> {
14 18 try (var from = new FileInputStream(file); to) {
15 19 from.transferTo(to);
16 20 } catch (Exception e) {
17 21 // silence!
18 22 }
19 23 });
20 24 }
21 25
22 26 public static RedirectFrom stream(final InputStream from) {
23 27 return to -> CompletableFuture.runAsync(() -> {
24 28 try (from; to) {
25 29 from.transferTo(to);
26 30 } catch (Exception e) {
27 31 // silence!
28 32 }
29 33 });
30 34 }
31 35
32 36
33 37 }
@@ -1,76 +1,98
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.io.File;
4 4 import java.io.IOException;
5 5 import java.lang.ProcessBuilder.Redirect;
6 6 import java.util.ArrayList;
7 7 import java.util.concurrent.CompletableFuture;
8 8
9 class SystemExec implements Executor {
9 class SystemExecBuilder implements ExecBuilder {
10 10
11 11 private final ProcessBuilder builder = new ProcessBuilder();
12 12
13 13 private RedirectFrom inputRedirect;
14 14
15 15 private RedirectTo outputRedirect;
16 16
17 17 private RedirectTo errorRedirect;
18 18
19 19 @Override
20 20 public CompletableFuture<Integer> start() throws IOException {
21 21 // TODO Auto-generated method stub
22 22 var tasks = new ArrayList<CompletableFuture<?>>();
23 23
24 24 // discard stdout if not redirected
25 25 if (outputRedirect == null)
26 26 builder.redirectOutput(Redirect.DISCARD);
27 27
28 28 // discard stderr if not redirected
29 29 if (errorRedirect == null)
30 30 builder.redirectError(Redirect.DISCARD);
31 31
32 32 // run process
33 33 var proc = builder.start();
34 34
35 35 tasks.add(proc.onExit());
36 36
37 37 if (inputRedirect != null)
38 38 tasks.add(inputRedirect.redirect(proc.getOutputStream()));
39 39
40 40 if (outputRedirect != null)
41 41 tasks.add(outputRedirect.redirect(proc.getInputStream()));
42 42
43 43 if (errorRedirect != null)
44 44 tasks.add(errorRedirect.redirect(proc.getErrorStream()));
45 45
46 46 return CompletableFuture
47 47 .allOf(tasks.toArray(new CompletableFuture<?>[0]))
48 48 .thenApply(t -> proc.exitValue());
49 49 }
50 50
51 public void command(String... args) {
52 builder.command(args);
53 }
54
51 @Override
55 52 public void directory(File workingDir) {
56 53 builder.directory(workingDir);
57 54 }
58 55
56 @Override
59 57 public void stderr(RedirectTo to) {
60 58 errorRedirect = to;
61 59 }
62 60
61 @Override
63 62 public void stdin(RedirectFrom from) {
64 63 inputRedirect = from;
65 64 }
66 65
66 @Override
67 67 public void stdout(RedirectTo to) {
68 68 outputRedirect = to;
69 69 }
70 70
71 71 @Override
72 72 public void argument(String arg) {
73 73 builder.command().add(arg);
74 74 }
75 75
76 @Override
77 public void setEnvironment(String envVar, String value) {
78 builder.environment().put(envVar, value);
79
80 }
81
82 @Override
83 public void unsetEnvironment(String envVar) {
84 builder.environment().remove(envVar);
85 }
86
87 @Override
88 public void command(String cmd, String... args) {
89 var commandLine = new ArrayList<String>();
90
91 commandLine.add(cmd);
92 for (var arg : args)
93 commandLine.add(arg);
94
95 builder.command(commandLine);
96 }
97
76 98 }
@@ -1,104 +1,104
1 1 package org.implab.gradle.common.tasks;
2 2
3 3 import java.io.IOException;
4 4 import java.util.Optional;
5 5 import java.util.concurrent.ExecutionException;
6 6
7 7 import org.gradle.api.DefaultTask;
8 8 import org.implab.gradle.common.dsl.ProcessSpec;
9 import org.implab.gradle.common.exec.Executor;
9 import org.implab.gradle.common.exec.ExecBuilder;
10 10 import org.implab.gradle.common.exec.RedirectFrom;
11 11 import org.implab.gradle.common.exec.RedirectTo;
12 12
13 13 public abstract class ExternalTask extends DefaultTask {
14 14
15 15 /**
16 16 * A default redirection to the build log when loglevel is set to INFO,
17 17 * otherwise returns an empty redirection.
18 18 */
19 19 protected Optional<RedirectTo> loggerInfoRedirect() {
20 20 return getLogger().isInfoEnabled()
21 21 ? Optional.of(RedirectTo.consumer(getLogger()::info))
22 22 : Optional.empty();
23 23 }
24 24
25 25 /**
26 26 * A default redirection to the build log when loglevel is set to ERROR,
27 27 * otherwise returns an empty redirection. Note that ERROR level is set
28 28 * by default for a build runs.
29 29 */
30 30 protected Optional<RedirectTo> loggerErrorRedirect() {
31 31 return getLogger().isErrorEnabled()
32 32 ? Optional.of(RedirectTo.consumer(getLogger()::error))
33 33 : Optional.empty();
34 34 }
35 35
36 36 /**
37 37 * Stdout redirection for {@link #exec(ProcessSpec)}, default implementation
38 38 * will forward stdout to the build log through {@link #loggerErrorRedirect()}
39 39 */
40 40 protected Optional<RedirectTo> stdoutRedirection() {
41 41 return loggerInfoRedirect();
42 42 }
43 43
44 44 /**
45 45 * Stderr redirection for {@link #exec(ProcessSpec)}, default implementation
46 46 * will forward stderr to build log through {@link #loggerErrorRedirect()}.
47 47 */
48 48 protected Optional<RedirectTo> stderrRedirection() {
49 49 return loggerErrorRedirect();
50 50 }
51 51
52 52 /**
53 53 * Stdin redirection for {@link #exec(ProcessSpec)}, empty by default.
54 54 */
55 55 protected Optional<RedirectFrom> stdinRedirection() {
56 56 return Optional.empty();
57 57 }
58 58
59 59 /**
60 60 * Executes the specified process specification
61 61 *
62 62 * @param spec
63 63 * @throws InterruptedException
64 64 * @throws ExecutionException
65 65 * @throws IOException
66 66 */
67 67 protected void exec(ProcessSpec spec) throws InterruptedException, ExecutionException, IOException {
68 68 var executor = exec();
69 69
70 70 stdoutRedirection().ifPresent(executor::stdout);
71 71 stderrRedirection().ifPresent(executor::stderr);
72 72 stdinRedirection().ifPresent(executor::stdin);
73 73
74 74 getLogger().info("Staring: {}", spec.command());
75 75
76 76 spec.accept(executor);
77 77
78 78 // runs the command and checks the error code
79 79 var code = executor.start().get();
80 80
81 81 // check success code
82 82 if (code != 0)
83 83 throw new IOException("The process exited with error code " + code);
84 84 }
85 85
86 86 protected boolean checkRetCode(ProcessSpec proc, int code)
87 87 throws InterruptedException, ExecutionException, IOException {
88 88
89 89 var executor = exec();
90 90
91 91 if (getLogger().isInfoEnabled()) {
92 92 loggerInfoRedirect().ifPresent(executor::stdout);
93 93 loggerInfoRedirect().ifPresent(executor::stderr);
94 94 }
95 95
96 96 getLogger().info("Starting: {}", proc.command());
97 97
98 98 proc.accept(executor);
99 99
100 100 return executor.start().get() == code;
101 101 }
102 102
103 protected abstract Executor exec();
103 protected abstract ExecBuilder exec();
104 104 }
General Comments 0
You need to be logged in to leave comments. Login now