Allow jagidir $use directives to specify absolute paths.
[jagi.git] / src / jagi / scgi / Bootstrap.java
1 package jagi.scgi;
2
3 import jagi.*;
4 import java.lang.reflect.*;
5 import java.util.*;
6 import java.util.function.*;
7 import java.io.*;
8 import java.net.*;
9 import java.nio.channels.*;
10
11 public class Bootstrap {
12     private static InetSocketAddress resolveinaddr(String spec) {
13         int p = spec.indexOf(':');
14         SocketAddress bind;
15         if(p >= 0) {
16             InetAddress host;
17             try {
18                 if(spec.charAt(0) == '[') {
19                     p = spec.indexOf(']');
20                     if((p < 0) || (spec.charAt(p + 1) != ':'))
21                         throw(new IllegalArgumentException("invalid address syntax: " + spec));
22                     host = InetAddress.getByName(spec.substring(1, p));
23                     p++;
24                 } else {
25                     host = InetAddress.getByName(spec.substring(0, p));
26                 }
27             } catch(UnknownHostException e) {
28                 throw(new IllegalArgumentException("could not resolve inet host: " + spec, e));
29             }
30             try {
31                 return(new InetSocketAddress(host, Integer.parseInt(spec.substring(p + 1))));
32             } catch(NumberFormatException e) {
33                 throw(new IllegalArgumentException("not a valid port number: " + spec.substring(p + 1), e));
34             }
35         } else {
36             try {
37                 return(new InetSocketAddress(Integer.parseInt(spec)));
38             } catch(NumberFormatException e) {
39                 throw(new IllegalArgumentException("not a valid port number: " + spec, e));
40             }
41         }
42     }
43
44     private static ServerSocketChannel tcplisten(String spec) {
45         SocketAddress bind;
46         try {
47             bind = resolveinaddr(spec);
48         } catch(IllegalArgumentException e) {
49             System.err.println("scgi-jagi: " + e.getMessage());
50             System.exit(1);
51             return(null);
52         }
53         try {
54             ServerSocketChannel sk = ServerSocketChannel.open();
55             sk.bind(bind);
56             return(sk);
57         } catch(IOException e) {
58             System.err.println("scgi-jagi: could not create TCP socket: " + e.getMessage());
59             System.exit(1);
60             return(null);
61         }
62     }
63
64     private static ServerSocketChannel getstdin() {
65         Channel stdin;
66         try {
67             stdin = System.inheritedChannel();
68         } catch(IOException e) {
69             System.err.println("scgi-jagi: could not get stdin channel: " + e.getMessage());
70             System.exit(1);
71             return(null);
72         }
73         if(!(stdin instanceof ServerSocketChannel)) {
74             System.err.println("scgi-jagi: stdin is not a listening socket");
75             System.exit(1);
76             return(null);
77         }
78         return((ServerSocketChannel)stdin);
79     }
80
81     private static Function gethandler(ClassLoader loader, String nm, String... args) {
82         Class<?> cl;
83         try {
84             cl = loader.loadClass(nm);
85         } catch(ClassNotFoundException e) {
86             try {
87                 cl = loader.loadClass(nm + ".Bootstrap");
88             } catch(ClassNotFoundException e2) {
89                 System.err.println("scgi-jagi: could not find handler class or package: " + nm);
90                 System.exit(1);
91                 return(null);
92             }
93         }
94         Method wmain;
95         try {
96             wmain = cl.getDeclaredMethod("wmain", String[].class);
97             int mod = wmain.getModifiers();
98             if(((mod & Modifier.STATIC) == 0) || ((mod & Modifier.PUBLIC) == 0))
99                 throw(new NoSuchMethodException());
100         } catch(NoSuchMethodException e) {
101             System.err.println("scgi-jagi: could not find wmain method in " + cl.getName());
102             System.exit(1);
103             return(null);
104         }
105         Object handler;
106         try {
107             handler = wmain.invoke(null, new Object[] {args});
108         } catch(IllegalAccessException e) {
109             System.err.println("scgi-jagi: could not call wmain in " + cl.getName());
110             System.exit(1);
111             return(null);
112         } catch(InvocationTargetException e) {
113             System.err.println("scgi-jagi: wmain in " + cl.getName() + " failed");
114             e.printStackTrace(System.err);
115             System.exit(1);
116             return(null);
117         }
118         if(!(handler instanceof Function)) {
119             System.err.println("scgi-jagi: wmain in " + cl.getName() + " returned " + ((handler == null) ? "null" : ("a " + handler.getClass())));
120             System.exit(1);
121             return(null);
122         }
123         return((Function)handler);
124     }
125
126     private static void usage(PrintStream out) {
127         out.println("usage: jagi.jar [-h] [-T [HOST:]PORT] HANDLER-CLASS [ARGS...]");
128     }
129
130     public static void main(String[] args) {
131         PosixArgs opt = PosixArgs.getopt(args, "hT:");
132         if(opt == null) {
133             usage(System.err);
134             System.exit(1);
135             return;
136         }
137         String tcpspec = null;
138         ClassLoader loader = Bootstrap.class.getClassLoader();
139         for(char c : opt.parsed()) {
140             switch(c) {
141             case 'h':
142                 usage(System.out);
143                 System.exit(0);
144                 break;
145             case 'T':
146                 tcpspec = opt.arg;
147                 break;
148             }
149         }
150         if(opt.rest.length < 1) {
151             usage(System.err);
152             System.exit(1);
153             return;
154         }
155         Function handler = gethandler(loader, opt.rest[0], Arrays.copyOfRange(opt.rest, 1, opt.rest.length));
156         ServerSocketChannel sk;
157         if(tcpspec != null) {
158             sk = tcplisten(tcpspec);
159         } else {
160             sk = getstdin();
161         }
162         Runnable server = new EventServer(sk, handler);
163         try {
164             server.run();
165         } catch(Throwable e) {
166             System.err.println("scgi-jagi: server exited abnormally");
167             e.printStackTrace();
168             System.exit(1);
169         }
170         System.exit(0);
171     }
172 }