Initial commit.
[jagi.git] / src / jagi / fs / Handler.java
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 }