| 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.getByName(null); |
| 115 | } catch(UnknownHostException e) { |
| 116 | throw(new Error(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 | } |