Commit | Line | Data |
---|---|---|
49ccd711 FT |
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 | } | |
965619c0 | 162 | Runnable server = new EventServer(sk, handler); |
49ccd711 FT |
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 | } |