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