View Javadoc

1   /*
2    * Copyright  2004-2005 Stefan Reuter
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
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                 // for backward compatibility only
168                 portString = resourceBundle.getString("bindPort");
169             }
170             port = Integer.parseInt(portString);
171         }
172         catch(Exception e)
173         {
174             //swallow
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             //swallow
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             // swallow only if shutdown
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                     // swallow
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             // nothing we can do about that...
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 }