Reorganize compiler for more flexibility.
[jagi.git] / src / jagi / fs / JavaHandler.java
... / ...
CommitLineData
1package jagi.fs;
2
3import jagi.*;
4import java.util.*;
5import java.util.function.*;
6import java.util.logging.*;
7import java.lang.reflect.*;
8import java.nio.file.*;
9
10public class JavaHandler implements Function<Map<Object, Object>, Map<Object, Object>> {
11 private static final Logger log = Logger.getLogger("jagi-fs");
12 private final Map<Compiler.Module, Function<Map<Object, Object>, Map<Object, Object>>> handlers = new WeakHashMap<>();
13
14 public static class HandlerException extends RuntimeException {
15 public final Path file;
16
17 public HandlerException(Path file, String msg, Throwable cause) {
18 super(msg, cause);
19 this.file = file;
20 }
21 public HandlerException(Path file, String msg) {
22 this(file, msg, null);
23 }
24
25 public String getMessage() {
26 return(file + ": " + super.getMessage());
27 }
28 }
29
30 @SuppressWarnings("unchecked")
31 private static Function<Map<Object, Object>, Map<Object, Object>> makehandler(Compiler.Module mod) {
32 Class<?> main;
33 try {
34 main = mod.code.loadClass("Main");
35 } catch(ClassNotFoundException e) {
36 throw(new HandlerException(mod.file, "no Main class"));
37 }
38 try {
39 Method wmain = main.getDeclaredMethod("wmain", String[].class);
40 int attr = wmain.getModifiers();
41 if(((attr & Modifier.STATIC) == 0) || ((attr & Modifier.PUBLIC) == 0))
42 throw(new NoSuchMethodException());
43 Object handler = wmain.invoke(null, new Object[] {new String[] {}});
44 if(!(handler instanceof Function))
45 throw(new HandlerException(mod.file, "wmain in " + main.getName() + " returned " + ((handler == null) ? "null" : ("a " + handler.getClass()))));
46 return((Function<Map<Object, Object>, Map<Object, Object>>)handler);
47 } catch(IllegalAccessException e) {
48 throw(new HandlerException(mod.file, "could not call wmain", e));
49 } catch(InvocationTargetException e) {
50 throw(new HandlerException(mod.file, "wmain failed", e.getCause()));
51 } catch(NoSuchMethodException e) {
52 }
53 if(Function.class.isAssignableFrom(main)) {
54 try {
55 Constructor<? extends Function> cons = main.asSubclass(Function.class).getConstructor();
56 Function handler = cons.newInstance();
57 return((Function<Map<Object, Object>, Map<Object, Object>>)handler);
58 } catch(NoSuchMethodException e) {
59 } catch(InvocationTargetException e) {
60 throw(new HandlerException(mod.file, "constructor failed", e.getCause()));
61 } catch(ReflectiveOperationException e) {
62 throw(new HandlerException(mod.file, "could not construct Main", e));
63 }
64 }
65 throw(new HandlerException(mod.file, "no wmain and not directly applicable"));
66 }
67
68 private Function<Map<Object, Object>, Map<Object, Object>> gethandler(Compiler.File file) {
69 Compiler.Module mod = file.mod();
70 synchronized(handlers) {
71 Function<Map<Object, Object>, Map<Object, Object>> ret = handlers.get(mod);
72 if(ret == null)
73 handlers.put(mod, ret = makehandler(mod));
74 return(ret);
75 }
76 }
77
78 public Map<Object, Object> apply(Map<Object, Object> req) {
79 Compiler.File file = Compiler.get().file(Paths.get((String)req.get("SCRIPT_FILENAME")));
80 try {
81 file.update();
82 } catch(Compiler.CompilationException e) {
83 log.warning(String.format("Could not compile %s:\n%s", file.name, e.messages()));
84 return(Utils.simpleerror(500, "Internal Error", "Could not load JAGI handler"));
85 } catch(Exception e) {
86 log.log(Level.WARNING, String.format("Error occurred when loading %s", file.name), e);
87 return(Utils.simpleerror(500, "Internal Error", "Could not load JAGI handler"));
88 }
89 Function<Map<Object, Object>, Map<Object, Object>> handler;
90 try {
91 handler = gethandler(file);
92 } catch(HandlerException e) {
93 Throwable cause = e.getCause();
94 if(cause != null)
95 log.log(Level.WARNING, cause, e::getMessage);
96 else
97 log.log(Level.WARNING, e::getMessage);
98 return(Utils.simpleerror(500, "Internal Error", "Invalid JAGI handler"));
99 }
100 return(handler.apply(req));
101 }
102}