| 1 | package dolda.jsvc.scgi; |
| 2 | |
| 3 | import java.io.*; |
| 4 | import java.net.*; |
| 5 | import java.util.*; |
| 6 | import java.util.logging.*; |
| 7 | import dolda.jsvc.*; |
| 8 | import dolda.jsvc.util.*; |
| 9 | import dolda.jsvc.j2ee.PosixArgs; |
| 10 | |
| 11 | public class DirServer extends Server { |
| 12 | private final Map<File, DSContext> contexts = new HashMap<File, DSContext>(); |
| 13 | private final Environment env; |
| 14 | private final Logger logger = Logger.getLogger("dolda.jsvc.scgi.dirserver"); |
| 15 | private Thread sdhook = null, main = null; |
| 16 | |
| 17 | public DirServer(ServerSocket sk, Environment env) { |
| 18 | super(sk); |
| 19 | this.env = env; |
| 20 | } |
| 21 | |
| 22 | private DSContext context(File file) throws ThreadContext.CreateException { |
| 23 | synchronized(contexts) { |
| 24 | DSContext ctx = contexts.get(file); |
| 25 | String act = "loaded %s as %s"; |
| 26 | if(ctx != null) { |
| 27 | if(ctx.mtime < file.lastModified()) { |
| 28 | ctx.tg.shutdown(); |
| 29 | contexts.remove(file); |
| 30 | ctx = null; |
| 31 | act = "reloaded %s as %s"; |
| 32 | } |
| 33 | } |
| 34 | if(ctx == null) { |
| 35 | ctx = new DSContext(file, env); |
| 36 | contexts.put(file, ctx); |
| 37 | logger.config(String.format(act, file, ctx.name())); |
| 38 | } |
| 39 | return(ctx); |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | public void handle(Map<String, String> head, Socket sk) throws Exception { |
| 44 | String filename = head.get("SCRIPT_FILENAME"); |
| 45 | if(filename == null) |
| 46 | throw(new Exception("Request for DirServer must contain SCRIPT_FILENAME")); |
| 47 | File file = new File(filename); |
| 48 | if(!file.exists() || !file.canRead()) |
| 49 | throw(new Exception("Cannot access the requested JSvc file " + file.toString())); |
| 50 | DSContext ctx = context(file); |
| 51 | Request req = new ScgiRequest(sk, head); |
| 52 | RequestThread w = ctx.tg.respond(req); |
| 53 | w.start(); |
| 54 | } |
| 55 | |
| 56 | private class ShutdownHandler extends Thread { |
| 57 | public void run() { |
| 58 | sdhook = null; |
| 59 | DirServer.this.stop(); |
| 60 | try { |
| 61 | main.join(); |
| 62 | } catch(InterruptedException e) {} |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | protected void shutdown() { |
| 67 | try { |
| 68 | if(sdhook != null) |
| 69 | Runtime.getRuntime().removeShutdownHook(sdhook); |
| 70 | } catch(Exception e) {} |
| 71 | synchronized(contexts) { |
| 72 | for(Iterator<Map.Entry<File, DSContext>> i = contexts.entrySet().iterator(); i.hasNext();) { |
| 73 | Map.Entry<File, DSContext> e = i.next(); |
| 74 | DSContext ctx = e.getValue(); |
| 75 | i.remove(); |
| 76 | ctx.tg.shutdown(); |
| 77 | } |
| 78 | } |
| 79 | super.shutdown(); |
| 80 | } |
| 81 | |
| 82 | private static void usage(PrintStream out) { |
| 83 | out.println("usage: dolda.jsvc.scgi.DirServer [-h] [-e CHARSET] [-d DATADIR] PORT"); |
| 84 | } |
| 85 | |
| 86 | public static void main(String[] args) { |
| 87 | PosixArgs opt = PosixArgs.getopt(args, "h"); |
| 88 | if(opt == null) { |
| 89 | usage(System.err); |
| 90 | System.exit(1); |
| 91 | } |
| 92 | String charset = null; |
| 93 | File datroot = null; |
| 94 | for(char c : opt.parsed()) { |
| 95 | switch(c) { |
| 96 | case 'e': |
| 97 | charset = opt.arg; |
| 98 | break; |
| 99 | case 'd': |
| 100 | datroot = new File(opt.arg); |
| 101 | if(!datroot.exists() || !datroot.isDirectory()) { |
| 102 | System.err.println(opt.arg + ": no such directory"); |
| 103 | System.exit(1); |
| 104 | } |
| 105 | break; |
| 106 | case 'h': |
| 107 | usage(System.out); |
| 108 | return; |
| 109 | } |
| 110 | } |
| 111 | if(opt.rest.length < 1) { |
| 112 | usage(System.err); |
| 113 | System.exit(1); |
| 114 | } |
| 115 | Environment env = (datroot == null)?new Environment():new Environment(datroot); |
| 116 | env.initvm(); |
| 117 | int port = Integer.parseInt(opt.rest[0]); |
| 118 | ServerSocket sk; |
| 119 | try { |
| 120 | sk = new ServerSocket(port); |
| 121 | } catch(IOException e) { |
| 122 | System.err.println("could not bind to port " + port + ": " + e.getMessage()); |
| 123 | System.exit(1); |
| 124 | return; /* Because javac is stupid. :-/ */ |
| 125 | } |
| 126 | DirServer s = new DirServer(sk, env); |
| 127 | if(charset != null) |
| 128 | s.headcs = charset; |
| 129 | |
| 130 | Runtime.getRuntime().addShutdownHook(s.sdhook = s.new ShutdownHandler()); |
| 131 | s.main = new Thread(s, "SCGI server thread"); |
| 132 | s.main.start(); |
| 133 | } |
| 134 | } |