Added a way to properly shut down the SCGI server.
[jsvc.git] / src / dolda / jsvc / scgi / DirServer.java
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     
16     public DirServer(ServerSocket sk, Environment env) {
17         super(sk);
18         this.env = env;
19     }
20
21     private DSContext context(File file) throws ThreadContext.CreateException {
22         synchronized(contexts) {
23             DSContext ctx = contexts.get(file);
24             String act = "loaded %s as %s";
25             if(ctx != null) {
26                 if(ctx.mtime < file.lastModified()) {
27                     ctx.tg.shutdown();
28                     contexts.remove(file);
29                     ctx = null;
30                     act = "reloaded %s as %s";
31                 }
32             }
33             if(ctx == null) {
34                 ctx = new DSContext(file, env);
35                 contexts.put(file, ctx);
36                 logger.config(String.format(act, file, ctx.name()));
37             }
38             return(ctx);
39         }
40     }
41
42     public void handle(Map<String, String> head, Socket sk) throws Exception {
43         String filename = head.get("SCRIPT_FILENAME");
44         if(filename == null)
45             throw(new Exception("Request for DirServer must contain SCRIPT_FILENAME"));
46         File file = new File(filename);
47         if(!file.exists() || !file.canRead())
48             throw(new Exception("Cannot access the requested JSvc file " + file.toString()));
49         DSContext ctx = context(file);
50         Request req = new ScgiRequest(sk, head);
51         RequestThread w = ctx.tg.respond(req);
52         w.start();
53     }
54     
55     protected void shutdown() {
56         synchronized(contexts) {
57             for(Iterator<Map.Entry<File, DSContext>> i = contexts.entrySet().iterator(); i.hasNext();) {
58                 Map.Entry<File, DSContext> e = i.next();
59                 DSContext ctx = e.getValue();
60                 i.remove();
61                 ctx.tg.shutdown();
62             }
63         }
64     }
65
66     private static void usage(PrintStream out) {
67         out.println("usage: dolda.jsvc.scgi.DirServer [-h] [-e CHARSET] [-d DATADIR] PORT");
68     }
69     
70     public static void main(String[] args) {
71         PosixArgs opt = PosixArgs.getopt(args, "h");
72         if(opt == null) {
73             usage(System.err);
74             System.exit(1);
75         }
76         String charset = null;
77         File datroot = null;
78         for(char c : opt.parsed()) {
79             switch(c) {
80             case 'e':
81                 charset = opt.arg;
82                 break;
83             case 'd':
84                 datroot = new File(opt.arg);
85                 if(!datroot.exists() || !datroot.isDirectory()) {
86                     System.err.println(opt.arg + ": no such directory");
87                     System.exit(1);
88                 }
89                 break;
90             case 'h':
91                 usage(System.out);
92                 return;
93             }
94         }
95         if(opt.rest.length < 1) {
96             usage(System.err);
97             System.exit(1);
98         }
99         Environment env = (datroot == null)?new Environment():new Environment(datroot);
100         env.initvm();
101         int port = Integer.parseInt(opt.rest[0]);
102         ServerSocket sk;
103         try {
104             sk = new ServerSocket(port);
105         } catch(IOException e) {
106             System.err.println("could not bind to port " + port + ": " + e.getMessage());
107             System.exit(1);
108             return; /* Because javac is stupid. :-/ */
109         }
110         DirServer s = new DirServer(sk, env);
111         if(charset != null)
112             s.headcs = charset;
113         
114         new Thread(s, "SCGI server thread").start();
115     }
116 }