Repository navigation
Expand file tree
/
Copy pathController.java
More file actions
203 lines (180 loc) · 6.03 KB
/
Copy pathController.java
File metadata and controls
203 lines (180 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
final class Controller {
private final int port;
private final String boxBinaryPath;
private final Executor executor;
private ServerSocket socket;
private Controller(int port, String boxBinaryPath, Executor executor) {
this.port = port;
this.boxBinaryPath = boxBinaryPath;
this.executor = executor;
}
public void start() throws IOException {
socket = new ServerSocket();
socket.bind(new InetSocketAddress(port));
try {
listen();
} finally {
socket.close();
}
}
private void listen() throws IOException {
while (!Thread.currentThread().isInterrupted() && socket.isBound()) {
var client = socket.accept();
executor.execute(() -> {
try {
accept(client);
} catch (IOException failure) {
System.err.println(
"encountered error while serving " + client.getRemoteSocketAddress()
);
failure.printStackTrace();
}
});
}
}
private void accept(Socket client) throws IOException {
var input = client.getInputStream();
byte[] initialPacket = input.readNBytes(Long.SIZE * 2);
var id = readId(initialPacket);
System.err.println("accepted client with id " + id);
useBox(client);
client.close();
}
private static final String[] firstNames = {
"Leonard", "Lionel", "Elton", "Nina", "Art", "Tracy", "Freddy",
};
private static final String[] lastNames = {
"Cohen", "Richie", "John", "Simone", "Garfunkel", "Chapman", "Mercury"
};
private void useBox(Socket client) throws IOException {
var payload = createPayload();
client.getOutputStream().write(payload);
}
private byte[] createPayload() {
var random = ThreadLocalRandom.current();
var firstName = firstNames[random.nextInt(firstNames.length)];
var lastName = lastNames[random.nextInt(lastNames.length)];
var fullName = firstName + " " + lastName;
return fullName.getBytes(StandardCharsets.UTF_8);
}
private UUID readId(byte[] bytes) {
var buffer = ByteBuffer.wrap(bytes);
return new UUID(
buffer.getLong(),
buffer.getLong()
);
}
public void startBox(UUID id) throws IOException {
System.err.println("starting box " + id);
var process = startBoxProcess();
executor.execute(() -> {
redirectBoxOutput(id, process);
redirectBoxErrors(id, process);
});
}
private void redirectBoxOutput(UUID id, Process process) {
var prefix = "%s: ".formatted(id).getBytes(StandardCharsets.UTF_8);
var suffix = "\r\n".getBytes(StandardCharsets.UTF_8);
try {
redirect(prefix, suffix, process.getInputStream(), System.out);
} catch (IOException failedRedirect) {
System.err.println("encountered error while redirecting " + id);
failedRedirect.printStackTrace();
}
}
private void redirectBoxErrors(UUID id, Process process) {
var prefix = "Error in %s: ".formatted(id).getBytes(StandardCharsets.UTF_8);
var suffix = "\r\n".getBytes(StandardCharsets.UTF_8);
try {
redirect(prefix, suffix, process.getErrorStream(), System.out);
} catch (IOException failedRedirect) {
System.err.println("encountered error while redirecting " + id);
failedRedirect.printStackTrace();
}
}
private static final String boxMemory = System.getProperty("box.memory", "2M");
private Process startBoxProcess() throws IOException {
var ownAddress = ":" + port;
return new ProcessBuilder()
.command(
"java",
"-Xmx%s".formatted(boxMemory),
"-Xms%s".formatted(boxMemory),
"-XX:+UseSerialGC",
/* program */ "-jar", boxBinaryPath,
/* arguments */ UUID.randomUUID().toString(), ownAddress
).start();
}
private static void redirect(
byte[] prefix,
byte[] suffix,
InputStream input,
OutputStream output
) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = input.read(buffer)) != -1) {
synchronized (output) {
output.write(prefix);
output.write(buffer, 0, read);
output.write(suffix);
}
}
}
private static final int internalPort = 3_1337 ;
// All logging is done to System.err since output is redirected to System.out
public static void main(String[] arguments) throws InterruptedException {
validateArguments(arguments);
var executor = Executors.newCachedThreadPool();
var binaryPath = arguments[0];
var controller = new Controller(internalPort, binaryPath, executor);
runController(executor, controller);
startSomeBoxes(controller, readBoxCount(arguments));
Thread.sleep(10_000);
executor.shutdownNow();
}
private static final int boxCountIndex = 1;
private static final int defaultBoxCount = 3;
private static int readBoxCount(String[] arguments) {
return arguments.length > boxCountIndex
? Integer.parseInt(arguments[boxCountIndex])
: defaultBoxCount;
}
private static void runController(Executor executor, Controller controller) {
executor.execute(() -> {
try {
controller.start();
} catch (IOException failure) {
System.err.println("encountered error in controller");
failure.printStackTrace();
}
});
}
private static void startSomeBoxes(Controller controller, int count) {
try {
for (int index = 0; index < count; index++) {
controller.startBox(UUID.randomUUID());
}
} catch (IOException failure) {
System.err.println("failed to start boxes");
}
}
private static void validateArguments(String[] arguments) {
if (arguments.length < 1) {
System.err.println("usage: ./run <path-to-box-binary> [box-count]");
System.exit(-1);
}
}
}