Commit | Line | Data |
---|---|---|
49ccd711 FT |
1 | package jagi.fs; |
2 | ||
3 | import jagi.*; | |
4 | import java.util.*; | |
5 | import java.util.function.*; | |
6 | import java.util.logging.*; | |
7 | import java.lang.reflect.*; | |
8 | import java.nio.file.*; | |
9 | ||
10 | public class JavaHandler implements Function<Map<Object, Object>, Map<Object, Object>> { | |
11 | private static final Logger log = Logger.getLogger("jagi-fs"); | |
12 | private final Map<ClassLoader, 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.Module mod) { | |
69 | ClassLoader code = mod.code(); | |
70 | synchronized(handlers) { | |
71 | Function<Map<Object, Object>, Map<Object, Object>> ret = handlers.get(code); | |
72 | if(ret == null) | |
73 | handlers.put(code, ret = makehandler(mod)); | |
74 | return(ret); | |
75 | } | |
76 | } | |
77 | ||
78 | public Map<Object, Object> apply(Map<Object, Object> req) { | |
79 | Compiler.Module mod = Compiler.get().module(Paths.get((String)req.get("SCRIPT_FILENAME"))); | |
80 | try { | |
81 | mod.update(); | |
82 | } catch(Compiler.CompilationException e) { | |
83 | log.warning(String.format("Could not compile %s:\n%s", mod.file, 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", mod.file), 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(mod); | |
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 | } |