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