| 1 | package jagi.fs; |
| 2 | |
| 3 | import java.util.*; |
| 4 | import java.util.function.*; |
| 5 | import java.util.logging.*; |
| 6 | import java.lang.reflect.*; |
| 7 | import java.io.*; |
| 8 | import java.nio.file.*; |
| 9 | import jagi.*; |
| 10 | |
| 11 | public class Handler implements Function<Map<Object, Object>, Map<Object, Object>> { |
| 12 | private static final Logger log = Logger.getLogger("jagi-fs"); |
| 13 | private Map<String, Function<Map<Object, Object>, Map<Object, Object>>> handlers = new HashMap<>(); |
| 14 | private Map<String, Function<Map<Object, Object>, Map<Object, Object>>> exts = new HashMap<>(); |
| 15 | |
| 16 | @SuppressWarnings("unchecked") |
| 17 | private static Function<Map<Object, Object>, Map<Object, Object>> resolve(ClassLoader loader, String nm) { |
| 18 | Class<?> cl; |
| 19 | try { |
| 20 | cl = loader.loadClass(nm); |
| 21 | } catch(ClassNotFoundException e) { |
| 22 | try { |
| 23 | cl = loader.loadClass(nm + ".Bootstrap"); |
| 24 | } catch(ClassNotFoundException e2) { |
| 25 | throw(new RuntimeException("could not find handler class or package: " + nm, e2)); |
| 26 | } |
| 27 | } |
| 28 | Method wmain; |
| 29 | try { |
| 30 | wmain = cl.getDeclaredMethod("wmain", String[].class); |
| 31 | int mod = wmain.getModifiers(); |
| 32 | if(((mod & Modifier.STATIC) == 0) || ((mod & Modifier.PUBLIC) == 0)) |
| 33 | throw(new NoSuchMethodException()); |
| 34 | } catch(NoSuchMethodException e) { |
| 35 | throw(new RuntimeException("could not find wmain method in " + cl.getName(), e)); |
| 36 | } |
| 37 | Object handler; |
| 38 | try { |
| 39 | handler = wmain.invoke(null, new Object[] {new String[] {}}); |
| 40 | } catch(IllegalAccessException e) { |
| 41 | throw(new RuntimeException("could not call wmain in " + cl.getName(), e)); |
| 42 | } catch(InvocationTargetException e) { |
| 43 | throw(new RuntimeException("wmain in " + cl.getName() + " failed", e.getCause())); |
| 44 | } |
| 45 | if(!(handler instanceof Function)) |
| 46 | throw(new RuntimeException("wmain in " + cl.getName() + " returned " + ((handler == null) ? "null" : ("a " + handler.getClass())))); |
| 47 | return((Function<Map<Object, Object>, Map<Object, Object>>)handler); |
| 48 | } |
| 49 | |
| 50 | public Function<Map<Object, Object>, Map<Object, Object>> resolve(String nm) { |
| 51 | synchronized(handlers) { |
| 52 | Function<Map<Object, Object>, Map<Object, Object>> handler = handlers.get(nm); |
| 53 | if(handler == null) |
| 54 | handlers.put(nm, handler = resolve(Thread.currentThread().getContextClassLoader(), nm)); |
| 55 | return(handler); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | public void addext(String ext, String name) { |
| 60 | addext(ext, resolve(name)); |
| 61 | } |
| 62 | |
| 63 | public void addext(String ext, Function<Map<Object, Object>, Map<Object, Object>> handler) { |
| 64 | Map<String, Function<Map<Object, Object>, Map<Object, Object>>> exts = new HashMap<>(this.exts); |
| 65 | synchronized(exts) { |
| 66 | exts.put(ext, handler); |
| 67 | } |
| 68 | this.exts = exts; |
| 69 | } |
| 70 | |
| 71 | { |
| 72 | addext("jagi", new JavaHandler()); |
| 73 | } |
| 74 | |
| 75 | public Map<Object, Object> apply(Map<Object, Object> req) { |
| 76 | String filename = (String)req.get("SCRIPT_FILENAME"); |
| 77 | if(filename == null) { |
| 78 | log.warning("jagi-fs called without SCRIPT_FILENAME set"); |
| 79 | return(Utils.simpleerror(500, "Internal Error", "The server is erroneously configured")); |
| 80 | } |
| 81 | Path path = Paths.get(filename); |
| 82 | if(!Files.isReadable(path)) { |
| 83 | log.warning(path + ": not readable"); |
| 84 | return(Utils.simpleerror(500, "Internal Error", "The server is erroneously configured")); |
| 85 | } |
| 86 | String hname = (String)req.get("HTTP_X_ASH_JAVA_HANDLER"); |
| 87 | if(hname != null) { |
| 88 | Function<Map<Object, Object>, Map<Object, Object>> handler; |
| 89 | try { |
| 90 | handler = resolve(hname); |
| 91 | } catch(Exception e) { |
| 92 | log.log(Level.WARNING, "could not load handler " + hname, e); |
| 93 | return(Utils.simpleerror(500, "Internal Error", "The server is erroneously configured")); |
| 94 | } |
| 95 | return(handler.apply(req)); |
| 96 | } else { |
| 97 | String base = path.getFileName().toString(); |
| 98 | int p = base.lastIndexOf('.'); |
| 99 | if(p < 0) { |
| 100 | log.warning(path + ": no file extension"); |
| 101 | return(Utils.simpleerror(500, "Internal Error", "The server is erroneously configured")); |
| 102 | } |
| 103 | String ext = base.substring(p + 1); |
| 104 | Function<Map<Object, Object>, Map<Object, Object>> handler = exts.get(ext); |
| 105 | if(handler == null) { |
| 106 | log.warning("non-registered file extension: " + ext); |
| 107 | return(Utils.simpleerror(500, "Internal Error", "The server is erroneously configured")); |
| 108 | } |
| 109 | return(handler.apply(req)); |
| 110 | } |
| 111 | } |
| 112 | } |