env = new HashMap<>();
52 | env.put("MY_VAR", "whatever");
53 | Process.create(vertx, "ls", new ProcessOptions().setEnv(env)).start();
54 | }
55 |
56 | public void ex04(Vertx vertx) {
57 | ProcessOptions options = new ProcessOptions().setEnv(Process.env());
58 | Process.create(vertx, "ls", options).start();
59 | }
60 |
61 | public void ex05(Vertx vertx) {
62 | ProcessOptions options = new ProcessOptions().setCwd("/some-dir");
63 | Process.create(vertx, "ls", options).start();
64 | }
65 |
66 | public void ex10(Vertx vertx) {
67 | ProcessBuilder processBuilder = Process.create(vertx, "cat");
68 |
69 | processBuilder.startHandler(process -> {
70 | process.stdout().handler(buff -> {
71 | System.out.println(buff.toString());
72 | });
73 |
74 | process.stdin().write(Buffer.buffer("Hello World"));
75 | });
76 |
77 | processBuilder.start();
78 | }
79 |
80 | public void ex20(Vertx vertx) {
81 | Process
82 | .create(vertx, "sleep", Arrays.asList("2"))
83 | .startHandler(process -> {
84 | process.exitHandler(code -> {
85 | System.out.println("Child process exited with code: " + code);
86 | });
87 | })
88 | .start();
89 | }
90 |
91 | public void ex30(Vertx vertx) {
92 | Process
93 | .create(vertx, "cat")
94 | .startHandler(process -> {
95 |
96 | process.stdout().handler(buff -> {
97 | System.out.println(buff.toString());
98 | });
99 |
100 | process.stdin().write(Buffer.buffer("Hello World"));
101 |
102 | // Kill the process
103 | process.kill();
104 | })
105 | .start();
106 | }
107 |
108 | public void ex31(Vertx vertx) {
109 | Process
110 | .create(vertx, "cat")
111 | .startHandler(process -> {
112 |
113 | process.stdout().handler(buff -> {
114 | System.out.println(buff.toString());
115 | });
116 |
117 | process.stdin().write(Buffer.buffer("Hello World"));
118 |
119 | // Kill the process forcibly
120 | process.kill(true);
121 |
122 | }).start();
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/Process.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package io.reactiverse.childprocess;
19 |
20 | import io.reactiverse.childprocess.impl.ProcessBuilderImpl;
21 | import io.vertx.codegen.annotations.CacheReturn;
22 | import io.vertx.codegen.annotations.Fluent;
23 | import io.vertx.codegen.annotations.VertxGen;
24 | import io.vertx.core.Handler;
25 | import io.vertx.core.Vertx;
26 | import io.vertx.core.internal.ContextInternal;
27 |
28 | import java.util.Collections;
29 | import java.util.HashMap;
30 | import java.util.List;
31 | import java.util.Map;
32 |
33 | /**
34 | * A process launched from this current process.
35 | *
36 | * Please see the user manual for more detailed usage information.
37 | *
38 | * @author Julien Viet
39 | */
40 | @VertxGen
41 | public interface Process {
42 |
43 | /**
44 | * @return the current process environment variables
45 | */
46 | static Map env() {
47 | return new HashMap<>(System.getenv());
48 | }
49 |
50 | /**
51 | * Create a child process (not running) from this process, call {@link ProcessBuilder#start()} to start the process.
52 | *
53 | * @param vertx the vertx instance
54 | * @param command the command to run
55 | * @return the created child process
56 | */
57 | static ProcessBuilder create(Vertx vertx, String command) {
58 | return create(vertx, command, Collections.emptyList(), new ProcessOptions());
59 | }
60 |
61 | /**
62 | * Create a child process (not running) from this process, call {@link ProcessBuilder#start()} to start the process.
63 | *
64 | * @param vertx the vertx instance
65 | * @param command the command to run
66 | * @param args list of string arguments
67 | * @return the created child process
68 | */
69 | static ProcessBuilder create(Vertx vertx, String command, List args) {
70 | return create(vertx, command, args, new ProcessOptions());
71 | }
72 |
73 | /**
74 | * Create a child process (not running) from this process, call {@link ProcessBuilder#start()} to start the process.
75 | *
76 | * @param vertx the vertx instance
77 | * @param command the command to run
78 | * @param options the options to run the command
79 | * @return the created child process
80 | */
81 | static ProcessBuilder create(Vertx vertx, String command, ProcessOptions options) {
82 | return create(vertx, command, Collections.emptyList(), options);
83 | }
84 |
85 | /**
86 | * Create a child process (not running) from this process, call {@link ProcessBuilder#start()} to start the process.
87 | *
88 | * @param vertx the vertx instance
89 | * @param command the command to run
90 | * @param args list of string arguments
91 | * @param options the options to run the command
92 | * @return the created child process
93 | */
94 | static ProcessBuilder create(Vertx vertx, String command, List args, ProcessOptions options) {
95 | return new ProcessBuilderImpl((ContextInternal) vertx.getOrCreateContext(), command, args, options);
96 | }
97 |
98 | /**
99 | * Set the handler to be called when the process exits, the handler will be called with the
100 | * process status code value.
101 | *
102 | * @param handler the handler
103 | * @return a reference to this, so the API can be used fluently
104 | */
105 | @Fluent
106 | Process exitHandler(Handler handler);
107 |
108 | /**
109 | * @return the process PID or null if the process is not running
110 | */
111 | Integer pid();
112 |
113 | /**
114 | * @return the process stdin stream
115 | */
116 | @CacheReturn
117 | StreamOutput stdin();
118 |
119 | /**
120 | * @return the process stdout stream
121 | */
122 | @CacheReturn
123 | StreamInput stdout();
124 |
125 | /**
126 | * @return the process stderr stream
127 | */
128 | @CacheReturn
129 | StreamInput stderr();
130 |
131 | /**
132 | * Terminates the process in a graceful manner.
133 | *
134 | * On a POSIX OS, it sends the {@code SIGTERM}.
135 | */
136 | default void kill() {
137 | kill(false);
138 | }
139 |
140 |
141 | /**
142 | * Terminates the process.
143 | *
144 | * If {@code force} is {@code false}, the process will be terminated gracefully (i.e. its shutdown logic will
145 | * be allowed to execute), assuming the OS supports such behavior. Note that the process may not actually
146 | * terminate, as its cleanup logic may fail or it may choose to ignore the termination request. If a guarantee
147 | * of termination is required, call this method with force equal to true instead.
148 | *
149 | * If {@code force} is {@code true}, the process is guaranteed to terminate, but whether it is terminated
150 | * gracefully or not is OS-dependent. Note that it may take the OS a moment to terminate the process, so
151 | * {@link #isRunning()} may return {@code true} for a brief period after calling this method.
152 | *
153 | * On a POSIX OS, it sends the {@code SIGTERM} or {@code SIGKILL} signals.
154 | *
155 | * @param force if true is passed, the process will be forcibly killed
156 | */
157 | void kill(boolean force);
158 |
159 | /**
160 | * Tests whether or not the process is still running or has exited.
161 | */
162 | boolean isRunning();
163 |
164 | }
165 |
166 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/ProcessBuilder.java:
--------------------------------------------------------------------------------
1 | package io.reactiverse.childprocess;
2 |
3 | import io.vertx.codegen.annotations.Fluent;
4 | import io.vertx.codegen.annotations.VertxGen;
5 | import io.vertx.core.Future;
6 | import io.vertx.core.Handler;
7 |
8 | @VertxGen
9 | public interface ProcessBuilder {
10 |
11 | /**
12 | * Start the process.
13 | */
14 | Future start();
15 |
16 | /**
17 | * Set the handler to be called when the process starts.
18 | *
19 | * @param handler the handler
20 | * @return a reference to this, so the API can be used fluently
21 | */
22 | @Fluent
23 | ProcessBuilder startHandler(Handler handler);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/ProcessOptions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package io.reactiverse.childprocess;
19 |
20 | import io.vertx.codegen.annotations.DataObject;
21 | import io.vertx.codegen.json.annotations.JsonGen;
22 | import io.vertx.core.json.JsonObject;
23 |
24 | import java.util.HashMap;
25 | import java.util.Map;
26 |
27 | /**
28 | * Options for spawning new processes.
29 | *
30 | * @author Julien Viet
31 | */
32 | @DataObject
33 | @JsonGen(publicConverter = false)
34 | public class ProcessOptions {
35 |
36 | /**
37 | * The default environment variables = the current process environment variables
38 | */
39 | public static final Map DEFAULT_ENV = System.getenv();
40 |
41 | private Map env;
42 | private String cwd;
43 |
44 | public ProcessOptions() {
45 | this.env = new HashMap<>(DEFAULT_ENV);
46 | }
47 |
48 | public ProcessOptions(ProcessOptions that) {
49 | env = that.env != null ? new HashMap<>(that.env) : null;
50 | cwd = that.cwd;
51 | }
52 |
53 | public ProcessOptions(JsonObject json) {
54 | ProcessOptionsConverter.fromJson(json, this);
55 | }
56 |
57 | /**
58 | * @return the current options environment variables as key-value pairs
59 | */
60 | public Map getEnv() {
61 | return env;
62 | }
63 |
64 | /**
65 | * Set the child process environment variables as key-value pairs.
66 | *
67 | * @param env the env variables
68 | * @return a reference to this, so the API can be used fluently
69 | */
70 | public ProcessOptions setEnv(Map env) {
71 | this.env = env;
72 | return this;
73 | }
74 |
75 | /**
76 | * @return the child process current working directory
77 | */
78 | public String getCwd() {
79 | return cwd;
80 | }
81 |
82 | /**
83 | * Set the child process current working directory.
84 | *
85 | * @param cwd the current working directory
86 | * @return a reference to this, so the API can be used fluently
87 | */
88 | public ProcessOptions setCwd(String cwd) {
89 | this.cwd = cwd;
90 | return this;
91 | }
92 |
93 | public JsonObject toJson() {
94 | JsonObject json = new JsonObject();
95 | ProcessOptionsConverter.toJson(this, json);
96 | return json;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/StartException.java:
--------------------------------------------------------------------------------
1 | package io.reactiverse.childprocess;
2 |
3 | import io.vertx.core.buffer.Buffer;
4 |
5 | public class StartException extends Exception {
6 |
7 | final int exitCode;
8 | final Buffer stdout;
9 | final Buffer stdin;
10 |
11 | public StartException(int exitCode, Buffer stdout, Buffer stdin) {
12 | this.exitCode = exitCode;
13 | this.stdout = stdout;
14 | this.stdin = stdin;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/StreamInput.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package io.reactiverse.childprocess;
19 |
20 | import io.vertx.codegen.annotations.Fluent;
21 | import io.vertx.codegen.annotations.VertxGen;
22 | import io.vertx.core.Handler;
23 | import io.vertx.core.buffer.Buffer;
24 | import io.vertx.core.streams.StreamBase;
25 |
26 | /**
27 | * The input of a process: a stream of {@link Buffer buffers}.
28 | *
29 | * @author Julien Viet
30 | */
31 | @VertxGen
32 | public interface StreamInput extends StreamBase {
33 |
34 | /**
35 | * Set an exception handler on the read stream.
36 | *
37 | * @param handler the exception handler
38 | * @return a reference to this, so the API can be used fluently
39 | */
40 | @Fluent
41 | StreamInput exceptionHandler(Handler handler);
42 |
43 | /**
44 | * Set a buffer handler. As bytes are read, the handler will be called with the data.
45 | *
46 | * @return a reference to this, so the API can be used fluently
47 | */
48 | @Fluent
49 | StreamInput handler(Handler handler);
50 |
51 | /**
52 | * Set an end handler. Once the stream has ended, and there is no more data to be read, this handler will be called.
53 | *
54 | * @return a reference to this, so the API can be used fluently
55 | */
56 | @Fluent
57 | StreamInput endHandler(Handler handler);
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/StreamOutput.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package io.reactiverse.childprocess;
19 |
20 | import io.vertx.codegen.annotations.VertxGen;
21 | import io.vertx.core.Future;
22 | import io.vertx.core.Handler;
23 | import io.vertx.core.buffer.Buffer;
24 | import io.vertx.core.streams.WriteStream;
25 |
26 | /**
27 | * The output of a process: a stream of {@link Buffer buffers}.
28 | *
29 | * @author Julien Viet
30 | */
31 | @VertxGen
32 | public interface StreamOutput extends WriteStream {
33 |
34 | @Override
35 | StreamOutput exceptionHandler(Handler handler);
36 |
37 | @Override
38 | Future write(Buffer buffer);
39 |
40 | @Override
41 | StreamOutput setWriteQueueMaxSize(int i);
42 |
43 | @Override
44 | StreamOutput drainHandler(Handler handler);
45 |
46 | /**
47 | * Calls {code close()}.
48 | */
49 | @Override
50 | default Future end() {
51 | return close().mapEmpty();
52 | }
53 |
54 | /**
55 | * Close the stream.
56 | */
57 | Future close();
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/impl/ProcessBuilderImpl.java:
--------------------------------------------------------------------------------
1 | package io.reactiverse.childprocess.impl;
2 |
3 | import io.reactiverse.childprocess.Process;
4 | import io.reactiverse.childprocess.ProcessBuilder;
5 | import io.reactiverse.childprocess.ProcessOptions;
6 | import io.reactiverse.childprocess.StartException;
7 | import com.zaxxer.nuprocess.NuProcess;
8 | import com.zaxxer.nuprocess.NuProcessBuilder;
9 | import com.zaxxer.nuprocess.NuProcessHandler;
10 | import io.vertx.core.Future;
11 | import io.vertx.core.Handler;
12 | import io.vertx.core.Promise;
13 | import io.vertx.core.buffer.Buffer;
14 | import io.vertx.core.internal.ContextInternal;
15 |
16 | import java.io.File;
17 | import java.nio.ByteBuffer;
18 | import java.util.ArrayList;
19 | import java.util.HashMap;
20 | import java.util.List;
21 | import java.util.Map;
22 |
23 | public class ProcessBuilderImpl implements ProcessBuilder {
24 |
25 | private final ContextInternal context;
26 | private final String command;
27 | private final List args;
28 | private final ProcessOptions options;
29 | private Handler processHandler;
30 |
31 | public ProcessBuilderImpl(ContextInternal context, String command, List args, ProcessOptions options) {
32 | this.context = context;
33 | this.command = command;
34 | this.args = args;
35 | this.options = options;
36 | }
37 |
38 | @Override
39 | public Future start() {
40 | Promise promise = context.promise();
41 | Map env = new HashMap<>();
42 | if (options.getEnv() != null) {
43 | options.getEnv().entrySet().forEach(entry -> {
44 | if (entry.getValue() != null) {
45 | env.put(entry.getKey(), entry.getValue());
46 | }
47 | });
48 | }
49 | ArrayList commands = new ArrayList<>();
50 | commands.add(command);
51 | commands.addAll(args);
52 | NuProcessBuilder builder = new NuProcessBuilder(commands, env);
53 | if (options.getCwd() != null) {
54 | builder.setCwd(new File(options.getCwd()).toPath());
55 | }
56 | Buffer bufferedStdout = Buffer.buffer();
57 | Buffer bufferedStderr = Buffer.buffer();
58 | Handler handler = processHandler; // Capture
59 | builder.setProcessListener(new NuProcessHandler() {
60 | volatile ProcessImpl process;
61 | @Override
62 | public void onPreStart(NuProcess nuProcess) {
63 | }
64 | @Override
65 | public void onStart(NuProcess nuProcess) {
66 | ProcessImpl p = new ProcessImpl(context, nuProcess);
67 | process = p;
68 | if (handler != null) {
69 | context.emit(p, handler);
70 | }
71 | promise.complete();
72 | }
73 | @Override
74 | public void onExit(int exitCode) {
75 | ProcessImpl p = process;
76 | if (p == null) {
77 | promise.fail(new StartException(exitCode, bufferedStdout, bufferedStderr));
78 | } else {
79 | p.onExit(exitCode);
80 | }
81 | }
82 | private byte[] getBytes(ByteBuffer byteBuffer) {
83 | if (byteBuffer != null && byteBuffer.remaining() > 0) {
84 | byte[] bytes = new byte[byteBuffer.remaining()];
85 | byteBuffer.get(bytes);
86 | return bytes;
87 | } else {
88 | return null;
89 | }
90 | }
91 | @Override
92 | public void onStdout(ByteBuffer byteBuffer, boolean closed) {
93 | byte[] bytes = getBytes(byteBuffer);
94 | ProcessImpl p = process;
95 | if (process != null) {
96 | p.onStdout(bytes != null ? Buffer.buffer(bytes) : null, closed);
97 | } else {
98 | if (bytes != null) {
99 | bufferedStdout.appendBytes(bytes);
100 | }
101 | }
102 | }
103 | @Override
104 | public void onStderr(ByteBuffer byteBuffer, boolean closed) {
105 | byte[] bytes = getBytes(byteBuffer);
106 | ProcessImpl p = process;
107 | if (p != null) {
108 | p.onStderr(bytes != null ? Buffer.buffer(bytes) : null, closed);
109 | } else {
110 | if (bytes != null) {
111 | bufferedStdout.appendBytes(bytes);
112 | }
113 | }
114 | }
115 | @Override
116 | public boolean onStdinReady(ByteBuffer buffer) {
117 | ProcessImpl p = process;
118 | return p.onStdinReady(buffer);
119 | }
120 | });
121 | builder.start();
122 | return promise.future();
123 | }
124 |
125 | @Override
126 | public synchronized ProcessBuilder startHandler(Handler handler) {
127 | processHandler = handler;
128 | return this;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/impl/ProcessImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package io.reactiverse.childprocess.impl;
19 |
20 | import io.reactiverse.childprocess.StreamInput;
21 | import io.reactiverse.childprocess.StreamOutput;
22 | import com.zaxxer.nuprocess.NuProcess;
23 | import io.vertx.core.*;
24 | import io.vertx.core.buffer.Buffer;
25 | import io.reactiverse.childprocess.Process;
26 | import io.vertx.core.internal.ContextInternal;
27 |
28 | import java.nio.ByteBuffer;
29 | import java.util.ArrayDeque;
30 |
31 | /**
32 | * @author Julien Viet
33 | */
34 | public class ProcessImpl implements Process, StreamOutput {
35 |
36 | private static final int OPEN = 0, CLOSING = 1, CLOSED = 2;
37 |
38 | private int stdinStatus = OPEN;
39 | private Promise stdninEnd;
40 | private final ArrayDeque stdinPending = new ArrayDeque<>();
41 | private int stdinPendingSize;
42 | private int stdinMaxSize = 1024;
43 | private Handler drainHandler;
44 | private final ContextInternal context;
45 | private final ProcessStreamInput stdout;
46 | private final ProcessStreamInput stderr;
47 | private Handler exitHandler;
48 | private final NuProcess process;
49 | private Promise exitFuture;
50 | private boolean wantWrite;
51 |
52 | public ProcessImpl(ContextInternal context, NuProcess process) {
53 | this.context = context;
54 | this.process = process;
55 | this.stdout = new ProcessStreamInput(context);
56 | this.stderr = new ProcessStreamInput(context);
57 | this.exitFuture = context.promise();
58 | this.stdninEnd = context.promise();
59 | }
60 |
61 | //
62 |
63 | @Override
64 | public synchronized Process exitHandler(Handler handler) {
65 | exitHandler = handler;
66 | return this;
67 | }
68 |
69 | @Override
70 | public synchronized Integer pid() {
71 | return process.getPID();
72 | }
73 |
74 | @Override
75 | public StreamOutput stdin() {
76 | return this;
77 | }
78 |
79 | @Override
80 | public StreamInput stdout() {
81 | return stdout;
82 | }
83 |
84 | @Override
85 | public StreamInput stderr() {
86 | return stderr;
87 | }
88 |
89 | //
90 |
91 | @Override
92 | public StreamOutput exceptionHandler(Handler handler) {
93 | return this;
94 | }
95 |
96 | @Override
97 | public Future write(Buffer buffer) {
98 | Promise promise = context.promise();
99 | synchronized (this) {
100 | if (stdinStatus == CLOSING || stdinStatus == CLOSED) {
101 | throw new IllegalStateException();
102 | }
103 | stdinPending.add(new Write(buffer, promise));
104 | stdinPendingSize += buffer.length();
105 | if (process != null && !wantWrite) {
106 | wantWrite = true;
107 | process.wantWrite();
108 | }
109 | }
110 | return promise.future();
111 | }
112 |
113 | @Override
114 | public synchronized StreamOutput setWriteQueueMaxSize(int i) {
115 | stdinMaxSize = i;
116 | return this;
117 | }
118 |
119 | @Override
120 | public StreamOutput drainHandler(Handler handler) {
121 | synchronized (this) {
122 | drainHandler = handler;
123 | }
124 | checkDrained();
125 | return this;
126 | }
127 |
128 | @Override
129 | public synchronized boolean writeQueueFull() {
130 | return stdinPendingSize > stdinMaxSize;
131 | }
132 |
133 | @Override
134 | public Future close() {
135 | synchronized (this) {
136 | if (stdinStatus != OPEN) {
137 | return stdninEnd.future();
138 | }
139 | if (stdinPendingSize == 0) {
140 | stdinStatus = CLOSED;
141 | } else {
142 | stdinStatus = CLOSING;
143 | return stdninEnd.future();
144 | }
145 | }
146 | process.closeStdin(false);
147 | stdninEnd.complete();
148 | return stdninEnd.future();
149 | }
150 |
151 | //
152 |
153 | public synchronized void onExit(int exitCode) {
154 | synchronized (this) {
155 | stdinStatus = CLOSED;
156 | }
157 | handleExit(exitCode);
158 | }
159 |
160 | private void handleExit(int exitCode) {
161 | exitFuture.complete(exitCode);
162 | Handler handler = exitHandler;
163 | if (handler != null) {
164 | context.emit(exitCode, handler);
165 | }
166 | }
167 |
168 | public void onStdout(Buffer buffer, boolean closed) {
169 | if (buffer != null) {
170 | stdout.write(buffer);
171 | }
172 | if (closed) {
173 | stdout.close();
174 | }
175 | }
176 |
177 | public void onStderr(Buffer buffer, boolean closed) {
178 | if (buffer != null) {
179 | stderr.write(buffer);
180 | }
181 | if (closed) {
182 | stderr.close();
183 | }
184 | }
185 |
186 | public boolean onStdinReady(ByteBuffer byteBuffer) {
187 | synchronized (this) {
188 | Write write;
189 | while (byteBuffer.remaining() > 0 && (write = stdinPending.poll()) != null) {
190 | byte[] bytes;
191 | if (write.buffer.length() <= byteBuffer.remaining()) {
192 | bytes = write.buffer.getBytes();
193 | write.promise.complete();
194 | } else {
195 | bytes = write.buffer.getBytes(0, byteBuffer.remaining());
196 | stdinPending.addFirst(new Write(write.buffer.slice(byteBuffer.remaining(), write.buffer.length()), write.promise));
197 | }
198 | byteBuffer.put(bytes); // See to do directly with Netty ByteBuf
199 | stdinPendingSize -= bytes.length;
200 | }
201 | byteBuffer.flip();
202 | context.execute(v -> checkDrained());
203 | if (stdinPendingSize > 0) {
204 | return true;
205 | } else {
206 | wantWrite = false;
207 | if (stdinStatus == CLOSING) {
208 | stdinStatus = CLOSED;
209 | } else {
210 | return false;
211 | }
212 | }
213 | }
214 | process.closeStdin(false);
215 | stdninEnd.complete();
216 | return false;
217 | }
218 |
219 | private void checkDrained() {
220 | Handler handler;
221 | synchronized (this) {
222 | if (stdinPendingSize >= stdinMaxSize / 2) {
223 | return;
224 | }
225 | handler = drainHandler;
226 | drainHandler = null;
227 | }
228 | if (handler != null) {
229 | context.emit(handler);
230 | }
231 | }
232 |
233 | @Override
234 | public void kill(boolean force) {
235 | if (process != null) {
236 | process.destroy(force);
237 | }
238 | }
239 |
240 | @Override
241 | public boolean isRunning() {
242 | return process.isRunning();
243 | }
244 |
245 | private static class Write {
246 | final Buffer buffer;
247 | final Promise promise;
248 | Write(Buffer buffer, Promise promise) {
249 | this.buffer = buffer;
250 | this.promise = promise;
251 | }
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/impl/ProcessStreamInput.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package io.reactiverse.childprocess.impl;
19 |
20 | import io.vertx.core.Handler;
21 | import io.vertx.core.buffer.Buffer;
22 | import io.reactiverse.childprocess.StreamInput;
23 | import io.vertx.core.internal.ContextInternal;
24 |
25 | /**
26 | * @author Julien Viet
27 | */
28 | public class ProcessStreamInput implements StreamInput {
29 |
30 | private final ContextInternal context;
31 | private Handler dataHandler;
32 | private Handler endHandler;
33 |
34 | ProcessStreamInput(ContextInternal context) {
35 | this.context = context;
36 | }
37 |
38 | synchronized void write(Buffer buffer) {
39 | sendBuffer(buffer);
40 | }
41 |
42 | void close() {
43 | Handler handler = endHandler;
44 | if (handler != null) {
45 | context.emit(handler);
46 | }
47 | }
48 |
49 | private void sendBuffer(Buffer buffer) {
50 | Handler handler = dataHandler;
51 | if (handler != null) {
52 | context.emit(buffer, handler);
53 | }
54 | }
55 |
56 | @Override
57 | public StreamInput exceptionHandler(Handler handler) {
58 | return this;
59 | }
60 |
61 | @Override
62 | public StreamInput handler(Handler handler) {
63 | this.dataHandler = handler;
64 | return this;
65 | }
66 |
67 | @Override
68 | public StreamInput endHandler(Handler handler) {
69 | this.endHandler = handler;
70 | return this;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/io/reactiverse/childprocess/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | @ModuleGen(name = "childprocess", groupPackage = "io.reactiverse")
19 | package io.reactiverse.childprocess;
20 |
21 | import io.vertx.codegen.annotations.ModuleGen;
22 |
--------------------------------------------------------------------------------
/src/test/java/EchoStderr.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | /**
19 | * @author Julien Viet
20 | */
21 | public class EchoStderr {
22 |
23 | public static void main(String[] args) {
24 | for (String arg : args) {
25 | System.err.print(arg);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/EchoStdout.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | /**
19 | * @author Julien Viet
20 | */
21 | public class EchoStdout {
22 |
23 | public static void main(String[] args) {
24 | for (String arg : args) {
25 | System.out.print(arg);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/ExitCode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | /**
19 | * @author Julien Viet
20 | */
21 | public class ExitCode {
22 |
23 | public static void main(String[] args) {
24 | System.exit(25);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/java/PrintCwd.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | import java.io.File;
19 |
20 | /**
21 | * @author Julien Viet
22 | */
23 | public class PrintCwd {
24 |
25 | public static void main(String[] args) {
26 | System.out.print(new File("").getAbsolutePath());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/PrintEnv.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | /**
19 | * @author Julien Viet
20 | */
21 | public class PrintEnv {
22 |
23 | public static void main(String[] args) {
24 | String val = System.getenv(args[0]);
25 | if (val != null) {
26 | System.out.print(val);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/java/Shutdown.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | /**
19 | * @author Julien Viet
20 | */
21 | public class Shutdown {
22 |
23 | public static void main(String[] args) throws Exception {
24 | Runtime.getRuntime().addShutdownHook(new Thread() {
25 | @Override
26 | public void run() {
27 | System.out.print("exited");
28 | }
29 | });
30 | System.out.print("ok");
31 | Thread.sleep(10000);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/java/StdoutLongSequence.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | /**
19 | * @author Julien Viet
20 | */
21 | public class StdoutLongSequence {
22 |
23 | public static void main(String[] args) {
24 | for (int i = 0;i < 100000;i++) {
25 | System.out.print("" + i);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/io/reactiverse/childprocess/Main.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Julien Viet
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package io.reactiverse.childprocess;
19 |
20 | import java.io.File;
21 |
22 | /**
23 | * @author Julien Viet
24 | */
25 | public class Main {
26 |
27 | public static void main(String[] args) throws Exception {
28 |
29 | File f = new File(args[0]);
30 | if (!f.exists()) {
31 | System.exit(-1);
32 | }
33 |
34 | while (f.length() == 0) {
35 | Thread.sleep(1);
36 | }
37 |
38 | while (true) {
39 | int c = System.in.read();
40 | if (c == 'h' || c == 'e' || c == 'l' || c == 'o') {
41 | int a = 0;
42 | } else {
43 | if (c == -1) {
44 | System.exit(-1);
45 | } else if (c == 4) {
46 | break;
47 | }
48 | }
49 | }
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------