1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sf.asterisk.fastagi;
18
19 import java.io.IOException;
20 import java.util.MissingResourceException;
21 import java.util.ResourceBundle;
22
23 import net.sf.asterisk.util.Log;
24 import net.sf.asterisk.util.LogFactory;
25
26 import net.sf.asterisk.io.ServerSocketFacade;
27 import net.sf.asterisk.io.SocketConnectionFacade;
28 import net.sf.asterisk.io.impl.ServerSocketFacadeImpl;
29 import net.sf.asterisk.util.ThreadPool;
30
31 public class DefaultAGIServer implements AGIServer
32 {
33 /***
34 * The default name of the resource bundle that contains the config.
35 */
36 private static final String DEFAULT_CONFIG_RESOURCE_BUNDLE_NAME = "fastagi";
37
38 /***
39 * The default bind port.
40 */
41 private static final int DEFAULT_BIND_PORT = 4573;
42
43 /***
44 * The default thread pool size.
45 */
46 private static final int DEFAULT_POOL_SIZE = 10;
47
48 /***
49 * Instance logger.
50 */
51 private final Log logger = LogFactory.getLog(DefaultAGIServer.class);
52
53 private ServerSocketFacade serverSocket;
54
55 /***
56 * The port to listen on.
57 */
58 private int port;
59
60 /***
61 * The thread pool that contains the worker threads to process incoming
62 * requests.
63 */
64 private ThreadPool pool;
65
66 /***
67 * The number of worker threads in the thread pool. This equals the maximum
68 * number of concurrent requests this AGIServer can serve.
69 */
70 private int poolSize;
71
72 /***
73 * True while this server is shut down.
74 */
75 private boolean die;
76
77 /***
78 * The strategy to use for mapping AGIRequests to AGIScripts that serve
79 * them.
80 */
81 private MappingStrategy mappingStrategy;
82
83 /***
84 * Creates a new DefaultAGIServer.
85 *
86 */
87 public DefaultAGIServer()
88 {
89 this.port = DEFAULT_BIND_PORT;
90 this.poolSize = DEFAULT_POOL_SIZE;
91 this.mappingStrategy = new ResourceBundleMappingStrategy();
92
93 loadConfig();
94 }
95
96 /***
97 * Sets the number of worker threads in the thread pool.<br>
98 * This equals the maximum number of concurrent requests this AGIServer can
99 * serve.<br>
100 * The default pool size is 10.
101 *
102 * @param poolSize the size of the worker thread pool.
103 */
104 public void setPoolSize(int poolSize)
105 {
106 this.poolSize = poolSize;
107 }
108
109 /***
110 * Sets the TCP port to listen on for new connections.<br>
111 * The default port is 4573.
112 *
113 * @param bindPort the port to bind to.
114 * @deprecated use {@see #setPort(int)} instead
115 */
116 public void setBindPort(int bindPort)
117 {
118 this.port = bindPort;
119 }
120
121 /***
122 * Sets the TCP port to listen on for new connections.<br>
123 * The default port is 4573.
124 *
125 * @param port the port to bind to.
126 * @since 0.2
127 */
128 public void setPort(int port)
129 {
130 this.port = port;
131 }
132
133 /***
134 * Sets the strategy to use for mapping AGIRequests to AGIScripts that serve
135 * them.<br>
136 * The default mapping strategy is a ResourceBundleMappingStrategy.
137 *
138 * @param mappingStrategy the mapping strategy to use.
139 * @see ResourceBundleMappingStrategy
140 */
141 public void setMappingStrategy(MappingStrategy mappingStrategy)
142 {
143 this.mappingStrategy = mappingStrategy;
144 }
145
146 private void loadConfig()
147 {
148 ResourceBundle resourceBundle;
149
150 try
151 {
152 resourceBundle = ResourceBundle
153 .getBundle(DEFAULT_CONFIG_RESOURCE_BUNDLE_NAME);
154 }
155 catch (MissingResourceException e)
156 {
157 return;
158 }
159
160 try
161 {
162 String portString;
163
164 portString = resourceBundle.getString("port");
165 if (portString == null)
166 {
167
168 portString = resourceBundle.getString("bindPort");
169 }
170 port = Integer.parseInt(portString);
171 }
172 catch(Exception e)
173 {
174
175 }
176
177 try
178 {
179 String poolSizeString;
180
181 poolSizeString = resourceBundle.getString("poolSize");
182 poolSize = Integer.parseInt(poolSizeString);
183 }
184 catch(Exception e)
185 {
186
187 }
188 }
189
190 protected ServerSocketFacade createServerSocket() throws IOException
191 {
192 return new ServerSocketFacadeImpl(port, 0, null);
193 }
194
195 public void startup() throws IOException, IllegalStateException
196 {
197 SocketConnectionFacade socket;
198 AGIConnectionHandler connectionHandler;
199
200 die = false;
201 pool = new ThreadPool("AGIServer", poolSize);
202 logger.info("Thread pool started.");
203
204 try
205 {
206 serverSocket = createServerSocket();
207 }
208 catch (IOException e)
209 {
210 logger.error("Unable start AGI Server: cannot to bind to *:" + port + ".", e);
211 throw e;
212 }
213
214 logger.info("Listening on *:" + port + ".");
215
216 try
217 {
218 while ((socket = serverSocket.accept()) != null)
219 {
220 logger.info("Received connection.");
221 connectionHandler = new AGIConnectionHandler(socket,
222 mappingStrategy);
223 pool.addJob(connectionHandler);
224 }
225 }
226 catch (IOException e)
227 {
228
229 if (!die)
230 {
231 logger.error("IOException while waiting for connections.", e);
232 throw e;
233 }
234 }
235 finally
236 {
237 if (serverSocket != null)
238 {
239 try
240 {
241 serverSocket.close();
242 }
243 catch (IOException e)
244 {
245
246 }
247 }
248 serverSocket = null;
249 pool.shutdown();
250 logger.info("AGIServer shut down.");
251 }
252 }
253
254 public void run()
255 {
256 try
257 {
258 startup();
259 }
260 catch (IOException e)
261 {
262
263 }
264 }
265
266 public void die() throws IOException
267 {
268 die = true;
269
270 if (serverSocket != null)
271 {
272 serverSocket.close();
273 }
274 }
275
276 public void shutdown() throws IOException, IllegalStateException
277 {
278 die();
279 }
280
281 public static void main(String[] args) throws Exception
282 {
283 AGIServer server;
284
285 server = new DefaultAGIServer();
286 server.startup();
287 }
288 }