source: javavenv/src/main/java/geniusweb/javavenv/Call.java@ 12

Last change on this file since 12 was 12, checked in by bart, 3 years ago

Refactor to help reusing partiesserver.

File size: 4.1 KB
Line 
1package geniusweb.javavenv;
2
3import java.io.IOException;
4import java.io.InputStream;
5import java.util.LinkedList;
6import java.util.List;
7import java.util.concurrent.ArrayBlockingQueue;
8import java.util.concurrent.BlockingQueue;
9import java.util.concurrent.TimeUnit;
10
11/**
12 * A straight python call. Handles all the connection stuff: connecting to the
13 * input and output streams so that we can try to follow what python is doing.
14 * NOTE generally this is for internal use, instead use {@link PythonVenv}
15 */
16public class Call {
17 private final List<String> allargs = new LinkedList<>();
18 // size 2, we expect both stdin and stderr to always post something here.
19 private final BlockingQueue<Object> result = new ArrayBlockingQueue<Object>(
20 2);
21 private final int deadlinems;
22
23 /**
24 * Call python with given arguments. call {@link #run()} to run. We
25 * recommend thatn you use {@link PythonVenv} to run commands instead of
26 * this direct call.
27 *
28 * @param pythonexe a working python3 exe, typically
29 * "/usr/local/bin/python3" or something in a venv on temp
30 * disk.
31 * @param args the other args for the call
32 * @param deadlinems the deadline (ms) after which the call should be
33 * cancelled.
34 */
35
36 public Call(String pythonexe, List<String> args, int deadlinems) {
37 if (pythonexe == null || args == null)
38 throw new IllegalArgumentException("args must be not null");
39 allargs.add(pythonexe);
40 allargs.addAll(args);
41 this.deadlinems = deadlinems;
42 }
43
44 /**
45 *
46 * @return the result of the python call (that is, what the program printed
47 * out)
48 * @throws InterruptedException if the call does not terminate in time.
49 * @throws IOException if we can not connect properly to the python
50 * process
51 * @throws PythonError if the python program printed to stderr, eg
52 * when an error occured. We try to capture all
53 * the error lines as text in the pythonerror
54 * object.
55 */
56 public String run() throws InterruptedException, IOException, PythonError {
57 ProcessBuilder processBuilder = new ProcessBuilder(allargs);
58 // System.out.println("all args " + allargs);
59 Process p;
60 p = processBuilder.start();
61
62 Runnable stdinreader = new Runnable() {
63 @Override
64 public void run() {
65 String totalreply = "";
66 byte[] cbuf = new byte[500];
67 InputStream is = p.getInputStream();
68 try {
69 while (true) {
70 int n = is.read(cbuf);
71 if (n == -1)
72 break;
73 totalreply += new String(cbuf, 0, n, "UTF8");
74 }
75 result.add(totalreply);
76 } catch (IOException e) {
77 result.add(e);
78 }
79 }
80 };
81
82 Runnable errorlogger = new Runnable() {
83 @Override
84 public void run() {
85 byte[] cbuf = new byte[500];
86 InputStream is = p.getErrorStream();
87 String totalerror = "";
88 try {
89 while (true) {
90 int n = is.read(cbuf);
91 if (n == -1)
92 break;
93 totalerror += new String(cbuf, 0, n, "UTF8");
94 }
95 if (!totalerror.isEmpty())
96 result.add(new PythonError(totalerror));
97 else
98 result.add("");
99 } catch (IOException e) {
100 result.add(e);
101 }
102 }
103
104 };
105 new Thread(stdinreader).start();
106 new Thread(errorlogger).start();
107
108 // now we wait till both are finished.
109 // wait till both are finished. Order is irrelevant.
110 Object res1 = result.poll(deadlinems, TimeUnit.MILLISECONDS);
111 if (res1 == null)
112 throw new InterruptedException("Python code failed to return");
113 Object res2 = result.poll(100, TimeUnit.MILLISECONDS);
114 if (res2 == null)
115 throw new InterruptedException(
116 "Second python channel failed to close");
117 // If there is a non-empty error, return it, otherwise return res
118 rethrowIfException(res1);
119 rethrowIfException(res2);
120 if (!((String) res1).isEmpty())
121 return (String) res1;
122 return (String) res2;
123
124 }
125
126 @Override
127 public String toString() {
128 return "python " + allargs;
129 }
130
131 private void rethrowIfException(Object obj)
132 throws IOException, PythonError {
133 if (obj instanceof Exception) {
134 throw new PythonError("Failed to execute " + this, (Exception) obj);
135 }
136 }
137
138}
Note: See TracBrowser for help on using the repository browser.