-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path47.DesignACodeCompilerService.java
More file actions
267 lines (228 loc) · 11.4 KB
/
47.DesignACodeCompilerService.java
File metadata and controls
267 lines (228 loc) · 11.4 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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/* -------------------------------------------------------- */
/* ( The Authentic JS/JAVA CodeBuff )
___ _ _ _
| _ ) |_ __ _ _ _ __ _ __| |_ __ ____ _ (_)
| _ \ ' \/ _` | '_/ _` / _` \ V V / _` || |
|___/_||_\__,_|_| \__,_\__,_|\_/\_/\__,_|/ |
|__/
*/
/* --------------------------------------------------------- */
/* Youtube: https://youtube.com/@code-with-Bharadwaj */
/* Github : https://github.com/Manu577228 */
/* ----------------------------------------------------------- */
import javax.tools.JavaCompiler; // JavaCompiler for compiling source files
import javax.tools.ToolProvider; // To obtain system Java compiler
import java.io.*; // IO utilities: File, PrintWriter, BufferedReader
import java.nio.file.*; // Files and Paths utilities
import java.util.*; // Collections and utilities
import java.util.concurrent.TimeUnit; // For process timeout
// Single-file demo: CompilerServiceDemo
public class CompilerServiceDemo {
// ---------------- Data Model: CodeSnippet ----------------
public static class CodeSnippet {
public final int id; // snippet id
public final String code; // full java source text
public CodeSnippet(int id, String code) {
this.id = id;
this.code = code;
}
}
// ---------------- Data Model: ExecutionResult -------------
public static class ExecutionResult {
public final boolean success; // true if run succeeded (no compile/runtime error)
public final String output; // captured stdout
public final String error; // compile or runtime error text
public ExecutionResult(boolean success, String output, String error) {
this.success = success;
this.output = output;
this.error = error;
}
}
// ---------------- Repository ------------------------------
public static class Repository {
private final List<CodeSnippet> snippets = new ArrayList<>(); // in-memory store
// synchronized add to be thread-safe for concurrent submissions
public synchronized int add(String code) {
int id = snippets.size();
snippets.add(new CodeSnippet(id, code));
return id;
}
// synchronized get to read safely
public synchronized CodeSnippet get(int id) {
if (id >= 0 && id < snippets.size()) return snippets.get(id);
return null;
}
// history snapshot (id and first-line preview)
public synchronized List<String> history() {
List<String> h = new ArrayList<>();
for (CodeSnippet s : snippets) {
String preview = s.code.split("\\R", 2)[0]; // first line
h.add("id=" + s.id + " preview=\"" + preview + "\"");
}
return h;
}
}
// ---------------- Execution Engine ------------------------
public static class ExecutionEngine {
// compile sourceFile and run with timeoutSeconds, return ExecutionResult
public ExecutionResult compileAndRun(Path srcFile, String className, int timeoutSeconds) {
// compile using system JavaCompiler
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) { // no compiler available (likely running on JRE)
return new ExecutionResult(false, "", "JavaCompiler not available. Run with a JDK, not JRE.");
}
// prepare compilation output directory (same dir as source)
Path workDir = srcFile.getParent();
StringWriter compileErrors = new StringWriter();
int compileResult = compiler.run(null, null, new PrintWriter(compileErrors), srcFile.toString());
if (compileResult != 0) {
// compilation failed -> return errors
return new ExecutionResult(false, "", compileErrors.toString());
}
// if compilation succeeded, run the class using 'java' process
ProcessBuilder pb = new ProcessBuilder("java", "-cp", workDir.toString(), className);
pb.directory(workDir.toFile());
pb.redirectErrorStream(true); // merge stderr with stdout
try {
Process proc = pb.start();
// read process output asynchronously (but simple buffered read is fine here)
StringBuilder out = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
// wait with timeout
boolean finished = proc.waitFor(timeoutSeconds, TimeUnit.SECONDS);
// drain output (non-blocking if process finished)
while (reader.ready()) {
out.append(reader.readLine()).append(System.lineSeparator());
}
if (!finished) {
proc.destroyForcibly(); // kill long-running process
return new ExecutionResult(false, out.toString(),
"Timeout / Possible infinite loop (killed after " + timeoutSeconds + "s).");
}
}
int exit = proc.exitValue();
if (exit != 0) {
// non-zero exit but output captured in out
return new ExecutionResult(false, out.toString(), "Process exited with code " + exit);
}
return new ExecutionResult(true, out.toString(), "");
} catch (IOException | InterruptedException e) {
Thread.currentThread().interrupt();
return new ExecutionResult(false, "", "Execution failed: " + e.getMessage());
} finally {
// cleanup: class and source cleanup is caller's responsibility (we do not delete here)
}
}
}
// ---------------- Facade: CompilerService -----------------
public static class CompilerService {
private final Repository repo = new Repository();
private final ExecutionEngine engine = new ExecutionEngine();
// Submit code -> returns snippet id
public int submit(String code) {
return repo.add(code);
}
// Run snippet by id with timeoutSeconds -> returns ExecutionResult
public ExecutionResult run(int id, int timeoutSeconds) {
CodeSnippet s = repo.get(id);
if (s == null) return new ExecutionResult(false, "", "Snippet not found");
// Determine class name: require user to provide a public class with same name
// For demo convenience, we attempt to extract "public class X" name,
// if absent, we wrap user code into public class AutoClass{ public static void main... }
String source = s.code;
String className = extractPublicClassName(source);
boolean wrapped = false;
if (className == null) {
// wrap the code into a public class named AutoClass<ID>
className = "AutoClass" + id;
String wrappedSource = "public class " + className + " {\n"
+ " public static void main(String[] args) throws Exception {\n"
+ indent(source, " ") + "\n"
+ " }\n"
+ "}\n";
source = wrappedSource;
wrapped = true;
}
// create temp directory for this run
try {
Path tempDir = Files.createTempDirectory("cs_demo_" + id + "_");
Path srcFile = tempDir.resolve(className + ".java");
// write source file using PrintWriter / BufferedWriter
try (PrintWriter pw = new PrintWriter(Files.newBufferedWriter(srcFile))) {
pw.write(source);
}
// compile and run
ExecutionResult result = engine.compileAndRun(srcFile, className, timeoutSeconds);
// cleanup: delete class files and source
try {
// delete recursively
Files.walk(tempDir)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (IOException ignored) {
}
return result;
} catch (IOException e) {
return new ExecutionResult(false, "", "IO error preparing run: " + e.getMessage());
}
}
// Return history preview list
public List<String> history() {
return repo.history();
}
// Helper to extract public class name from source (very simple regex-like scan)
private static String extractPublicClassName(String src) {
String[] lines = src.split("\\R");
for (String ln : lines) {
ln = ln.trim();
if (ln.startsWith("public class ")) {
String remainder = ln.substring("public class ".length()).trim();
String[] parts = remainder.split("\\s|\\{");
if (parts.length > 0) return parts[0];
}
}
return null;
}
// Helper to indent multiple lines
private static String indent(String text, String prefix) {
String[] lines = text.split("\\R");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
sb.append(prefix).append(lines[i]);
if (i + 1 < lines.length) sb.append("\n");
}
return sb.toString();
}
}
// ---------------- Demo main ------------------------------
public static void main(String[] args) {
// create service instance
CompilerService service = new CompilerService();
// Example 1: simple print (no public class required)
String code1 = "System.out.println(\"Hello Compiler Service!\");";
int id1 = service.submit(code1);
System.out.println("Submitted id=" + id1);
// Run snippet with 3-second timeout
ExecutionResult res1 = service.run(id1, 3);
System.out.println("Success: " + res1.success);
System.out.println("Output:\n" + res1.output);
System.out.println("Error:\n" + res1.error);
// Example 2: user provides full public class
String code2 =
"public class MyApp {\n" +
" public static void main(String[] args) {\n" +
" System.out.println(\"Hello from MyApp\");\n" +
" }\n" +
"}\n";
int id2 = service.submit(code2);
System.out.println("Submitted id=" + id2);
ExecutionResult res2 = service.run(id2, 3);
System.out.println("Success: " + res2.success);
System.out.println("Output:\n" + res2.output);
System.out.println("Error:\n" + res2.error);
// Show history
System.out.println("History:");
for (String h : service.history()) System.out.println(h);
}
}