Allow updated jagidir modules to be formally disposed.
[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 } catch(NoClassDefFoundError e) {
38 throw(new HandlerException(mod.file, "class not found: " + e.getMessage()));
39 }
40 try {
41 Method wmain = main.getDeclaredMethod("wmain", String[].class);
42 int attr = wmain.getModifiers();
43 if(((attr & Modifier.STATIC) == 0) || ((attr & Modifier.PUBLIC) == 0))
44 throw(new NoSuchMethodException());
45 Object handler = wmain.invoke(null, new Object[] {new String[] {}});
46 if(!(handler instanceof Function))
47 throw(new HandlerException(mod.file, "wmain in " + main.getName() + " returned " + ((handler == null) ? "null" : ("a " + handler.getClass()))));
48 return((Function<Map<Object, Object>, Map<Object, Object>>)handler);
49 } catch(IllegalAccessException e) {
50 throw(new HandlerException(mod.file, "could not call wmain", e));
51 } catch(InvocationTargetException e) {
52 throw(new HandlerException(mod.file, "wmain failed", e.getCause()));
53 } catch(NoSuchMethodException e) {
54 }
55 if(Function.class.isAssignableFrom(main)) {
56 try {
57 Constructor<? extends Function> cons = main.asSubclass(Function.class).getConstructor();
58 Function handler = cons.newInstance();
59 return((Function<Map<Object, Object>, Map<Object, Object>>)handler);
60 } catch(NoSuchMethodException e) {
61 } catch(InvocationTargetException e) {
62 throw(new HandlerException(mod.file, "constructor failed", e.getCause()));
63 } catch(ReflectiveOperationException e) {
64 throw(new HandlerException(mod.file, "could not construct Main", e));
65 }
66 }
67 throw(new HandlerException(mod.file, "no wmain and not directly applicable"));
68 }
69
70 private Function<Map<Object, Object>, Map<Object, Object>> gethandler(Compiler.File file) {
71 Compiler.Module mod = file.mod();
72 synchronized(handlers) {
73 Function<Map<Object, Object>, Map<Object, Object>> ret = handlers.get(mod);
74 if(ret == null)
75 handlers.put(mod, ret = makehandler(mod));
76 return(ret);
77 }
78 }
79
80 public Map<Object, Object> apply(Map<Object, Object> req) {
81 Compiler.File file = Compiler.get().file(Paths.get((String)req.get("SCRIPT_FILENAME")));
82 try {
83 file.update();
84 } catch(Compiler.CompilationException e) {
85 log.warning(String.format("Could not compile %s:\n%s", file.name, e.messages()));
86 return(Utils.simpleerror(500, "Internal Error", "Could not load JAGI handler"));
87 } catch(Exception e) {
88 log.log(Level.WARNING, String.format("Error occurred when loading %s", file.name), e);
89 return(Utils.simpleerror(500, "Internal Error", "Could not load JAGI handler"));
90 }
91 Function<Map<Object, Object>, Map<Object, Object>> handler;
92 try {
93 handler = gethandler(file);
94 } catch(HandlerException e) {
95 Throwable cause = e.getCause();
96 if(cause != null)
97 log.log(Level.WARNING, cause, e::getMessage);
98 else
99 log.log(Level.WARNING, e::getMessage);
100 return(Utils.simpleerror(500, "Internal Error", "Invalid JAGI handler"));
101 }
102 return(handler.apply(req));
103 }
104}