Bind SCGI server to localhost by default, and allow overriding.
[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] [-B BINDADDR] [-e CHARSET] [-d DATADIR] PORT");
102     }
103     
104     public static void main(String[] args) {
105         PosixArgs opt = PosixArgs.getopt(args, "he:d:B:");
106         if(opt == null) {
107             usage(System.err);
108             System.exit(1);
109         }
110         String charset = null;
111         File datroot = null;
112         InetAddress bindaddr;
113         try {
114             bindaddr = InetAddress.getLocalHost();
115         } catch(UnknownHostException e) {
116             throw(new Error("WTF?!", e));
117         }
118         for(char c : opt.parsed()) {
119             switch(c) {
120             case 'e':
121                 charset = opt.arg;
122                 break;
123             case 'd':
124                 datroot = new File(opt.arg);
125                 if(!datroot.exists() || !datroot.isDirectory()) {
126                     System.err.println(opt.arg + ": no such directory");
127                     System.exit(1);
128                 }
129                 break;
130             case 'B':
131                 try {
132                     bindaddr = InetAddress.getByName(opt.arg);
133                 } catch(UnknownHostException e) {
134                     System.err.println(opt.arg  + ": no such host");
135                     System.exit(1);
136                 }
137                 break;
138             case 'h':
139                 usage(System.out);
140                 return;
141             }
142         }
143         if(opt.rest.length < 1) {
144             usage(System.err);
145             System.exit(1);
146         }
147         Environment env = (datroot == null)?new Environment():new Environment(datroot);
148         env.initvm();
149         int port = Integer.parseInt(opt.rest[0]);
150         ServerSocket sk;
151         SocketAddress saddr = new InetSocketAddress(bindaddr, port);
152         try {
153             sk = new ServerSocket();
154             sk.bind(saddr);
155         } catch(IOException e) {
156             System.err.println("could not bind to " + saddr + ": " + e.getMessage());
157             System.exit(1);
158             return; /* Because javac is stupid. :-/ */
159         }
160         DirServer s = new DirServer(sk, env);
161         try {
162             ManagementFactory.getPlatformMBeanServer().registerMBean(new dolda.jsvc.scgi.jmx.Server(s), dolda.jsvc.scgi.jmx.Server.name);
163         } catch(InstanceAlreadyExistsException e) {
164         } catch(MBeanRegistrationException e) {
165         } catch(NotCompliantMBeanException e) {
166         }
167         if(charset != null)
168             s.headcs = charset;
169         
170         Runtime.getRuntime().addShutdownHook(s.sdhook = s.new ShutdownHandler());
171         s.main = new Thread(s, "SCGI server thread");
172         s.main.start();
173     }
174 }