Source code: org/mortbay/http/SocketListener.java
1 // ========================================================================
2 // Copyright (c) 1999-2002 Mort Bay Consulting (Australia) Pty. Ltd.
3 // $Id: SocketListener.java,v 1.33 2003/11/22 16:06:02 gregwilkins Exp $
4 // ========================================================================
5
6 package org.mortbay.http;
7 import java.io.IOException;
8 import java.net.Socket;
9
10 import org.apache.commons.logging.Log;
11 import org.apache.commons.logging.LogFactory;
12 import org.mortbay.util.InetAddrPort;
13 import org.mortbay.util.LogSupport;
14 import org.mortbay.util.ThreadedServer;
15
16
17 /* ------------------------------------------------------------ */
18 /** Socket HTTP Listener.
19 * The behaviour of the listener can be controlled with the
20 * attributues of the ThreadedServer and ThreadPool from which it is
21 * derived. Specifically: <PRE>
22 * MinThreads - Minumum threads waiting to service requests.
23 * MaxThread - Maximum thread that will service requests.
24 * MaxIdleTimeMs - Time for an idle thread to wait for a request or read.
25 * LowResourcePersistTimeMs - time in ms that connections will persist if listener is
26 * low on resources.
27 * </PRE>
28 * @version $Id: SocketListener.java,v 1.33 2003/11/22 16:06:02 gregwilkins Exp $
29 * @author Greg Wilkins (gregw)
30 */
31 public class SocketListener
32 extends ThreadedServer
33 implements HttpListener
34 {
35 private static Log log = LogFactory.getLog(SocketListener.class);
36
37 /* ------------------------------------------------------------------- */
38 private int _lowResourcePersistTimeMs=2000;
39 private String _scheme=HttpMessage.__SCHEME;
40 private String _integralScheme=HttpMessage.__SSL_SCHEME;
41 private String _confidentialScheme=HttpMessage.__SSL_SCHEME;
42 private int _integralPort=0;
43 private int _confidentialPort=0;
44 private boolean _identifyListener=false;
45 private int _bufferSize=8192;
46 private int _bufferReserve=512;
47 private HttpHandler _handler;
48
49 private transient HttpServer _server;
50 private transient boolean _isLow=false;
51 private transient boolean _isOut=false;
52 private transient long _warned=0;
53
54 /* ------------------------------------------------------------------- */
55 public SocketListener()
56 {}
57
58 /* ------------------------------------------------------------------- */
59 public SocketListener(InetAddrPort address)
60 {
61 super(address);
62 }
63
64 /* ------------------------------------------------------------ */
65 public HttpServer getHttpServer()
66 {
67 return _server;
68 }
69
70 /* ------------------------------------------------------------ */
71 public void setHttpServer(HttpServer server)
72 {
73 if (server!=null && _server!=null && _server!=server)
74 throw new IllegalStateException("Cannot share listeners");
75 _server=server;
76 }
77
78 /* ------------------------------------------------------------ */
79 public HttpHandler getHttpHandler()
80 {
81 return _handler;
82 }
83
84 /* ------------------------------------------------------------ */
85 public void setHttpHandler(HttpHandler handler)
86 {
87 _handler=handler;
88 }
89
90 /* ------------------------------------------------------------ */
91 public int getBufferSize()
92 {
93 return _bufferSize;
94 }
95
96 /* ------------------------------------------------------------ */
97 public void setBufferSize(int size)
98 {
99 _bufferSize=size;
100 }
101
102 /* ------------------------------------------------------------ */
103 public int getBufferReserve()
104 {
105 return _bufferReserve;
106 }
107
108 /* ------------------------------------------------------------ */
109 public void setBufferReserve(int size)
110 {
111 _bufferReserve=size;
112 }
113
114 /* ------------------------------------------------------------ */
115 public boolean getIdentifyListener()
116 {
117 return _identifyListener;
118 }
119
120 /* ------------------------------------------------------------ */
121 /**
122 * @param identifyListener If true, the listener name is added to all
123 * requests as the org.mortbay.http.HttListener attribute
124 */
125 public void setIdentifyListener(boolean identifyListener)
126 {
127 _identifyListener = identifyListener;
128 }
129
130 /* --------------------------------------------------------------- */
131 public void setDefaultScheme(String scheme)
132 {
133 _scheme=scheme;
134 }
135
136 /* --------------------------------------------------------------- */
137 public String getDefaultScheme()
138 {
139 return _scheme;
140 }
141
142 /* ------------------------------------------------------------ */
143 /**
144 * @return time in ms that connections will persist if listener is
145 * low on resources.
146 */
147 public int getLowResourcePersistTimeMs()
148 {
149 return _lowResourcePersistTimeMs;
150 }
151
152 /* ------------------------------------------------------------ */
153 /** Set the low resource persistace time.
154 * When the listener is low on resources, this timeout is used for idle
155 * persistent connections. It is desirable to have this set to a short
156 * period of time so that idle persistent connections do not consume
157 * resources on a busy server.
158 * @param ms time in ms that connections will persist if listener is
159 * low on resources.
160 */
161 public void setLowResourcePersistTimeMs(int ms)
162 {
163 _lowResourcePersistTimeMs=ms;
164 }
165
166
167 /* --------------------------------------------------------------- */
168 public void start()
169 throws Exception
170 {
171 super.start();
172 log.info("Started SocketListener on "+getInetAddrPort());
173 }
174
175 /* --------------------------------------------------------------- */
176 public void stop()
177 throws InterruptedException
178 {
179 super.stop();
180 log.info("Stopped SocketListener on "+getInetAddrPort());
181 }
182
183 /* ------------------------------------------------------------ */
184 /** Handle Job.
185 * Implementation of ThreadPool.handle(), calls handleConnection.
186 * @param socket A Connection.
187 */
188 public void handleConnection(Socket socket)
189 throws IOException
190 {
191 HttpConnection connection = createConnection(socket);
192
193 try
194 {
195 if (_lowResourcePersistTimeMs>0 && isLowOnResources())
196 {
197 socket.setSoTimeout(_lowResourcePersistTimeMs);
198 connection.setThrottled(true);
199 }
200 else
201 {
202 socket.setSoTimeout(getMaxIdleTimeMs());
203 connection.setThrottled(false);
204 }
205
206 }
207 catch(Exception e)
208 {
209 log.warn(LogSupport.EXCEPTION,e);
210 }
211
212 connection.handle();
213 }
214
215 /* ------------------------------------------------------------ */
216 /** Create an HttpConnection instance. This method can be used to
217 * override the connection instance.
218 * @param socket The underlying socket.
219 */
220 protected HttpConnection createConnection(Socket socket)
221 throws IOException
222 {
223 return new HttpConnection(this,
224 socket.getInetAddress(),
225 socket.getInputStream(),
226 socket.getOutputStream(),
227 socket);
228 }
229
230 /* ------------------------------------------------------------ */
231 /** Customize the request from connection.
232 * This method extracts the socket from the connection and calls
233 * the customizeRequest(Socket,HttpRequest) method.
234 * @param request
235 */
236 public void customizeRequest(HttpConnection connection,
237 HttpRequest request)
238 {
239 if (_identifyListener)
240 request.setAttribute(HttpListener.ATTRIBUTE,getName());
241
242 Socket socket=(Socket)(connection.getConnection());
243 customizeRequest(socket,request);
244 }
245
246 /* ------------------------------------------------------------ */
247 /** Customize request from socket.
248 * Derived versions of SocketListener may specialize this method
249 * to customize the request with attributes of the socket used (eg
250 * SSL session ids).
251 * This version resets the SoTimeout if it has been reduced due to
252 * low resources. Derived implementations should call
253 * super.customizeRequest(socket,request) unless persistConnection
254 * has also been overridden and not called.
255 * @param request
256 */
257 protected void customizeRequest(Socket socket,
258 HttpRequest request)
259 {
260 try
261 {
262 if (request.getHttpConnection().isThrottled())
263 {
264 socket.setSoTimeout(getMaxIdleTimeMs());
265 request.getHttpConnection().setThrottled(false);
266 }
267 }
268 catch(Exception e)
269 {
270 LogSupport.ignore(log,e);
271 }
272 }
273
274 /* ------------------------------------------------------------ */
275 /** Persist the connection.
276 * This method is called by the HttpConnection in order to prepare a
277 * connection to be persisted. For this implementation,
278 * if the listener is low on resources, the connection read
279 * timeout is set to lowResourcePersistTimeMs. The
280 * customizeRequest method is used to reset this to the normal
281 * value after a request has been read.
282 * @param connection The HttpConnection to use.
283 */
284 public void persistConnection(HttpConnection connection)
285 {
286 try
287 {
288 Socket socket=(Socket)(connection.getConnection());
289
290 if (_lowResourcePersistTimeMs>0 && isLowOnResources())
291 {
292 socket.setSoTimeout(_lowResourcePersistTimeMs);
293 connection.setThrottled(true);
294 }
295 else
296 connection.setThrottled(false);
297 }
298 catch(Exception e)
299 {
300 LogSupport.ignore(log,e);
301 }
302 }
303
304 /* ------------------------------------------------------------ */
305 /** Get the lowOnResource state of the listener.
306 * A SocketListener is considered low on resources if the total number of
307 * threads is maxThreads and the number of idle threads is less than minThreads.
308 * @return True if low on idle threads.
309 */
310 public boolean isLowOnResources()
311 {
312 boolean low =
313 getThreads()==getMaxThreads() &&
314 getIdleThreads()<getMinThreads();
315
316 if (low && !_isLow)
317 {
318 log.info("LOW ON THREADS: "+this);
319 _warned=System.currentTimeMillis();
320 _isLow=true;
321 }
322 else if (!low && _isLow)
323 {
324 if (System.currentTimeMillis()-_warned > 1000)
325 {
326 _isOut=false;
327 _isLow=false;
328 }
329 }
330 return low;
331 }
332
333 /* ------------------------------------------------------------ */
334 /** Get the outOfResource state of the listener.
335 * A SocketListener is considered out of resources if the total number of
336 * threads is maxThreads and the number of idle threads is zero.
337 * @return True if out of resources.
338 */
339 public boolean isOutOfResources()
340 {
341 boolean out =
342 getThreads()==getMaxThreads() &&
343 getIdleThreads()==0;
344
345 if (out && !_isOut)
346 {
347 log.warn("OUT OF THREADS: "+this);
348 _warned=System.currentTimeMillis();
349 _isOut=true;
350 }
351
352 return out;
353 }
354
355 /* ------------------------------------------------------------ */
356 public boolean isIntegral(HttpConnection connection)
357 {
358 return false;
359 }
360
361 /* ------------------------------------------------------------ */
362 public boolean isConfidential(HttpConnection connection)
363 {
364 return false;
365 }
366
367 /* ------------------------------------------------------------ */
368 public String getIntegralScheme()
369 {
370 return _integralScheme;
371 }
372
373 /* ------------------------------------------------------------ */
374 public void setIntegralScheme(String integralScheme)
375 {
376 _integralScheme = integralScheme;
377 }
378
379 /* ------------------------------------------------------------ */
380 public int getIntegralPort()
381 {
382 return _integralPort;
383 }
384
385 /* ------------------------------------------------------------ */
386 public void setIntegralPort(int integralPort)
387 {
388 _integralPort = integralPort;
389 }
390
391 /* ------------------------------------------------------------ */
392 public String getConfidentialScheme()
393 {
394 return _confidentialScheme;
395 }
396
397 /* ------------------------------------------------------------ */
398 public void setConfidentialScheme(String confidentialScheme)
399 {
400 _confidentialScheme = confidentialScheme;
401 }
402
403 /* ------------------------------------------------------------ */
404 public int getConfidentialPort()
405 {
406 return _confidentialPort;
407 }
408
409 /* ------------------------------------------------------------ */
410 public void setConfidentialPort(int confidentialPort)
411 {
412 _confidentialPort = confidentialPort;
413 }
414
415 }