001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http10/HttpProcessor.java,v 1.9 2002/04/04 17:50:34 remm Exp $
003: * $Revision: 1.9 $
004: * $Date: 2002/04/04 17:50:34 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.connector.http10;
065:
066: import java.io.BufferedInputStream;
067: import java.io.InputStream;
068: import java.io.IOException;
069: import java.io.OutputStream;
070: import java.net.InetAddress;
071: import java.net.Socket;
072: import java.util.NoSuchElementException;
073: import java.util.StringTokenizer;
074: import java.util.Locale;
075: import java.util.Hashtable;
076: import java.util.Vector;
077: import java.util.Enumeration;
078: import javax.servlet.ServletException;
079: import javax.servlet.http.Cookie;
080: import javax.servlet.http.HttpServletRequest;
081: import javax.servlet.http.HttpServletResponse;
082: import org.apache.catalina.Connector;
083: import org.apache.catalina.Container;
084: import org.apache.catalina.Globals;
085: import org.apache.catalina.HttpRequest;
086: import org.apache.catalina.HttpResponse;
087: import org.apache.catalina.Lifecycle;
088: import org.apache.catalina.LifecycleEvent;
089: import org.apache.catalina.LifecycleException;
090: import org.apache.catalina.LifecycleListener;
091: import org.apache.catalina.Logger;
092: import org.apache.catalina.util.RequestUtil;
093: import org.apache.catalina.util.LifecycleSupport;
094: import org.apache.catalina.util.ServerInfo;
095: import org.apache.catalina.util.StringManager;
096:
097: /**
098: * Implementation of a request processor (and its associated thread) that may
099: * be used by an HttpConnector to process individual requests. The connector
100: * will allocate a processor from its pool, assign a particular socket to it,
101: * and the processor will then execute the processing required to complete
102: * the request. When the processor is completed, it will recycle itself.
103: *
104: * @author Craig R. McClanahan
105: * @version $Revision: 1.9 $ $Date: 2002/04/04 17:50:34 $
106: * @deprecated
107: */
108:
109: final class HttpProcessor implements Lifecycle, Runnable {
110:
111: // ----------------------------------------------------- Manifest Constants
112:
113: /**
114: * Server information string for this server.
115: */
116: private static final String SERVER_INFO = ServerInfo
117: .getServerInfo()
118: + " (HTTP/1.0 Connector)";
119:
120: // ----------------------------------------------------------- Constructors
121:
122: /**
123: * Construct a new HttpProcessor associated with the specified connector.
124: *
125: * @param connector HttpConnector that owns this processor
126: * @param id Identifier of this HttpProcessor (unique per connector)
127: */
128: public HttpProcessor(HttpConnector connector, int id) {
129:
130: super ();
131: this .connector = connector;
132: this .debug = connector.getDebug();
133: this .id = id;
134: this .proxyName = connector.getProxyName();
135: this .proxyPort = connector.getProxyPort();
136: this .request = (HttpRequestImpl) connector.createRequest();
137: this .response = (HttpResponseImpl) connector.createResponse();
138: this .serverPort = connector.getPort();
139: this .threadName = "HttpProcessor[" + connector.getPort() + "]["
140: + id + "]";
141:
142: }
143:
144: // ----------------------------------------------------- Instance Variables
145:
146: /**
147: * Is there a new socket available?
148: */
149: private boolean available = false;
150:
151: /**
152: * The HttpConnector with which this processor is associated.
153: */
154: private HttpConnector connector = null;
155:
156: /**
157: * The debugging detail level for this component.
158: */
159: private int debug = 0;
160:
161: /**
162: * The identifier of this processor, unique per connector.
163: */
164: private int id = 0;
165:
166: /**
167: * The lifecycle event support for this component.
168: */
169: private LifecycleSupport lifecycle = new LifecycleSupport(this );
170:
171: /**
172: * The match string for identifying a session ID parameter.
173: */
174: private static final String match = ";"
175: + Globals.SESSION_PARAMETER_NAME + "=";
176:
177: /**
178: * The proxy server name for our Connector.
179: */
180: private String proxyName = null;
181:
182: /**
183: * The proxy server port for our Connector.
184: */
185: private int proxyPort = 0;
186:
187: /**
188: * The HTTP request object we will pass to our associated container.
189: */
190: private HttpRequestImpl request = null;
191:
192: /**
193: * The HTTP response object we will pass to our associated container.
194: */
195: private HttpResponseImpl response = null;
196:
197: /**
198: * The actual server port for our Connector.
199: */
200: private int serverPort = 0;
201:
202: /**
203: * The string manager for this package.
204: */
205: protected StringManager sm = StringManager
206: .getManager(Constants.Package);
207:
208: /**
209: * The socket we are currently processing a request for. This object
210: * is used for inter-thread communication only.
211: */
212: private Socket socket = null;
213:
214: /**
215: * Has this component been started yet?
216: */
217: private boolean started = false;
218:
219: /**
220: * The shutdown signal to our background thread
221: */
222: private boolean stopped = false;
223:
224: /**
225: * The background thread.
226: */
227: private Thread thread = null;
228:
229: /**
230: * The name to register for the background thread.
231: */
232: private String threadName = null;
233:
234: /**
235: * The thread synchronization object.
236: */
237: private Object threadSync = new Object();
238:
239: // -------------------------------------------------------- Package Methods
240:
241: /**
242: * Process an incoming TCP/IP connection on the specified socket. Any
243: * exception that occurs during processing must be logged and swallowed.
244: * <b>NOTE</b>: This method is called from our Connector's thread. We
245: * must assign it to our own thread so that multiple simultaneous
246: * requests can be handled.
247: *
248: * @param socket TCP socket to process
249: */
250: synchronized void assign(Socket socket) {
251:
252: // Wait for the Processor to get the previous Socket
253: while (available) {
254: try {
255: wait();
256: } catch (InterruptedException e) {
257: }
258: }
259:
260: // Store the newly available Socket and notify our thread
261: this .socket = socket;
262: available = true;
263: notifyAll();
264:
265: if ((debug >= 1) && (socket != null))
266: log(" An incoming request is being assigned");
267:
268: }
269:
270: // -------------------------------------------------------- Private Methods
271:
272: /**
273: * Await a newly assigned Socket from our Connector, or <code>null</code>
274: * if we are supposed to shut down.
275: */
276: private synchronized Socket await() {
277:
278: // Wait for the Connector to provide a new Socket
279: while (!available) {
280: try {
281: wait();
282: } catch (InterruptedException e) {
283: }
284: }
285:
286: // Notify the Connector that we have received this Socket
287: Socket socket = this .socket;
288: available = false;
289: notifyAll();
290:
291: if ((debug >= 1) && (socket != null))
292: log(" The incoming request has been awaited");
293:
294: return (socket);
295:
296: }
297:
298: /**
299: * Log a message on the Logger associated with our Container (if any)
300: *
301: * @param message Message to be logged
302: */
303: private void log(String message) {
304:
305: Logger logger = connector.getContainer().getLogger();
306: if (logger != null)
307: logger.log(threadName + " " + message);
308:
309: }
310:
311: /**
312: * Log a message on the Logger associated with our Container (if any)
313: *
314: * @param message Message to be logged
315: * @param throwable Associated exception
316: */
317: private void log(String message, Throwable throwable) {
318:
319: Logger logger = connector.getContainer().getLogger();
320: if (logger != null)
321: logger.log(threadName + " " + message, throwable);
322:
323: }
324:
325: /**
326: * Parse and record the connection parameters related to this request.
327: *
328: * @param socket The socket on which we are connected
329: *
330: * @exception IOException if an input/output error occurs
331: * @exception ServletException if a parsing error occurs
332: */
333: private void parseConnection(Socket socket) throws IOException,
334: ServletException {
335:
336: if (debug >= 2)
337: log(" parseConnection: address=" + socket.getInetAddress()
338: + ", port=" + connector.getPort());
339: ((HttpRequestImpl) request).setInet(socket.getInetAddress());
340: if (proxyPort != 0)
341: request.setServerPort(proxyPort);
342: else
343: request.setServerPort(serverPort);
344: request.setSocket(socket);
345:
346: }
347:
348: /**
349: * Parse the incoming HTTP request headers, and set the appropriate
350: * request headers.
351: *
352: * @param input The input stream connected to our socket
353: *
354: * @exception IOException if an input/output error occurs
355: * @exception ServletException if a parsing error occurs
356: */
357: private void parseHeaders(InputStream input) throws IOException,
358: ServletException {
359:
360: while (true) {
361:
362: // Read the next header line
363: String line = read(input);
364: if ((line == null) || (line.length() < 1))
365: break;
366:
367: // Parse the header name and value
368: int colon = line.indexOf(':');
369: if (colon < 0)
370: throw new ServletException(sm
371: .getString("httpProcessor.parseHeaders.colon"));
372: String name = line.substring(0, colon).trim();
373: String match = name.toLowerCase();
374: String value = line.substring(colon + 1).trim();
375: if (debug >= 1)
376: log(" Header " + name + " = " + value);
377:
378: // Set the corresponding request headers
379: if (match.equals("authorization")) {
380: request.setAuthorization(value);
381: request.addHeader(name, value);
382: } else if (match.equals("accept-language")) {
383: request.addHeader(name, value);
384: //
385: // Adapted from old code perhaps maybe optimized
386: //
387: //
388: Hashtable languages = new Hashtable();
389: StringTokenizer languageTokenizer = new StringTokenizer(
390: value, ",");
391:
392: while (languageTokenizer.hasMoreTokens()) {
393: String language = languageTokenizer.nextToken()
394: .trim();
395: int qValueIndex = language.indexOf(';');
396: int qIndex = language.indexOf('q');
397: int equalIndex = language.indexOf('=');
398: Double qValue = new Double(1);
399:
400: if (qValueIndex > -1 && qValueIndex < qIndex
401: && qIndex < equalIndex) {
402: String qValueStr = language
403: .substring(qValueIndex + 1);
404: language = language.substring(0, qValueIndex);
405: qValueStr = qValueStr.trim().toLowerCase();
406: qValueIndex = qValueStr.indexOf('=');
407: qValue = new Double(0);
408: if (qValueStr.startsWith("q")
409: && qValueIndex > -1) {
410: qValueStr = qValueStr
411: .substring(qValueIndex + 1);
412: try {
413: qValue = new Double(qValueStr.trim());
414: } catch (NumberFormatException nfe) {
415: }
416: }
417: }
418: // XXX
419: // may need to handle "*" at some point in time
420: if (!language.equals("*")) {
421: String key = qValue.toString();
422: Vector v = (Vector) ((languages
423: .containsKey(key)) ? languages.get(key)
424: : new Vector());
425: v.addElement(language);
426: languages.put(key, v);
427: }
428: }
429: Vector l = new Vector();
430: Enumeration e = languages.keys();
431: while (e.hasMoreElements()) {
432: String key = (String) e.nextElement();
433: Vector v = (Vector) languages.get(key);
434: Enumeration le = v.elements();
435: while (le.hasMoreElements()) {
436: String language = (String) le.nextElement();
437: String country = "";
438: String variant = "";
439: int countryIndex = language.indexOf('-');
440: if (countryIndex > -1) {
441: country = language.substring(
442: countryIndex + 1).trim();
443: language = language.substring(0,
444: countryIndex).trim();
445: int vDash = country.indexOf("-");
446: if (vDash > 0) {
447: String cTemp = country.substring(0,
448: vDash);
449: variant = country.substring(vDash + 1);
450: country = cTemp;
451: }
452: }
453: request.addLocale(new Locale(language, country,
454: variant));
455: }
456: }
457: } else if (match.equals("cookie")) {
458: Cookie cookies[] = RequestUtil.parseCookieHeader(value);
459: for (int i = 0; i < cookies.length; i++) {
460: if (cookies[i].getName().equals(
461: Globals.SESSION_COOKIE_NAME)) {
462: // Override anything requested in the URL
463: if (!request.isRequestedSessionIdFromCookie()) {
464: // Accept only the first session id cookie
465: request.setRequestedSessionId(cookies[i]
466: .getValue());
467: request.setRequestedSessionCookie(true);
468: request.setRequestedSessionURL(false);
469: if (debug >= 1)
470: log(" Requested cookie session id is "
471: + ((HttpServletRequest) request
472: .getRequest())
473: .getRequestedSessionId());
474: }
475: }
476: request.addCookie(cookies[i]);
477: }
478: // Keep Watchdog from whining by adding the header as well
479: // (GetHeaderTest, GetIntHeader_1Test)
480: request.addHeader(name, value);
481: } else if (match.equals("content-length")) {
482: int n = -1;
483: try {
484: n = Integer.parseInt(value);
485: } catch (Exception e) {
486: throw new ServletException(
487: sm
488: .getString("httpProcessor.parseHeaders.contentLength"));
489: }
490: request.setContentLength(n);
491: request.addHeader(name, value);
492: } else if (match.equals("content-type")) {
493: request.setContentType(value);
494: request.addHeader(name, value);
495: } else if (match.equals("host")) {
496: int n = value.indexOf(':');
497: if (n < 0)
498: request.setServerName(value);
499: else {
500: request.setServerName(value.substring(0, n).trim());
501: int port = 80;
502: try {
503: port = Integer.parseInt(value.substring(n + 1)
504: .trim());
505: } catch (Exception e) {
506: throw new ServletException(
507: sm
508: .getString("httpProcessor.parseHeaders.portNumber"));
509: }
510: request.setServerPort(port);
511: }
512: request.addHeader(name, value);
513: } else {
514: request.addHeader(name, value);
515: }
516: }
517:
518: }
519:
520: /**
521: * Parse the incoming HTTP request and set the corresponding HTTP request
522: * properties.
523: *
524: * @param input The input stream attached to our socket
525: *
526: * @exception IOException if an input/output error occurs
527: * @exception ServletException if a parsing error occurs
528: */
529: private void parseRequest(InputStream input) throws IOException,
530: ServletException {
531:
532: // Parse the incoming request line
533: String line = read(input);
534: if (line == null)
535: throw new ServletException(sm
536: .getString("httpProcessor.parseRequest.read"));
537: StringTokenizer st = new StringTokenizer(line);
538:
539: String method = null;
540: try {
541: method = st.nextToken();
542: } catch (NoSuchElementException e) {
543: method = null;
544: }
545:
546: String uri = null;
547: try {
548: uri = st.nextToken();
549: ; // FIXME - URL decode the URI?
550: } catch (NoSuchElementException e) {
551: uri = null;
552: }
553:
554: String protocol = null;
555: try {
556: protocol = st.nextToken();
557: } catch (NoSuchElementException e) {
558: protocol = "HTTP/0.9";
559: }
560:
561: // Validate the incoming request line
562: if (method == null) {
563: throw new ServletException(sm
564: .getString("httpProcessor.parseRequest.method"));
565: } else if (uri == null) {
566: throw new ServletException(sm
567: .getString("httpProcessor.parseRequest.uri"));
568: }
569:
570: // Parse any query parameters out of the request URI
571: int question = uri.indexOf('?');
572: if (question >= 0) {
573: request.setQueryString(uri.substring(question + 1));
574: if (debug >= 1)
575: log(" Query string is "
576: + ((HttpServletRequest) request.getRequest())
577: .getQueryString());
578: uri = uri.substring(0, question);
579: } else
580: request.setQueryString(null);
581:
582: // Parse any requested session ID out of the request URI
583: int semicolon = uri.indexOf(match);
584: if (semicolon >= 0) {
585: String rest = uri.substring(semicolon + match.length());
586: int semicolon2 = rest.indexOf(';');
587: if (semicolon2 >= 0) {
588: request.setRequestedSessionId(rest.substring(0,
589: semicolon2));
590: rest = rest.substring(semicolon2);
591: } else {
592: request.setRequestedSessionId(rest);
593: rest = "";
594: }
595: request.setRequestedSessionURL(true);
596: uri = uri.substring(0, semicolon) + rest;
597: if (debug >= 1)
598: log(" Requested URL session id is "
599: + ((HttpServletRequest) request.getRequest())
600: .getRequestedSessionId());
601: } else {
602: request.setRequestedSessionId(null);
603: request.setRequestedSessionURL(false);
604: }
605:
606: // Set the corresponding request properties
607: ((HttpRequest) request).setMethod(method);
608: request.setProtocol(protocol);
609: ((HttpRequest) request).setRequestURI(uri);
610: request.setSecure(false); // No SSL support
611: request.setScheme("http"); // No SSL support
612:
613: if (debug >= 1)
614: log(" Request is " + method + " for " + uri);
615:
616: }
617:
618: /**
619: * Process an incoming HTTP request on the Socket that has been assigned
620: * to this Processor. Any exceptions that occur during processing must be
621: * swallowed and dealt with.
622: *
623: * @param socket The socket on which we are connected to the client
624: */
625: private void process(Socket socket) {
626:
627: boolean ok = true;
628: InputStream input = null;
629: OutputStream output = null;
630:
631: // Construct and initialize the objects we will need
632: try {
633: input = new BufferedInputStream(socket.getInputStream(),
634: connector.getBufferSize());
635: request.setStream(input);
636: request.setResponse(response);
637: output = socket.getOutputStream();
638: response.setStream(output);
639: response.setRequest(request);
640: ((HttpServletResponse) response.getResponse()).setHeader(
641: "Server", SERVER_INFO);
642: } catch (Exception e) {
643: log("process.create", e);
644: ok = false;
645: }
646:
647: // Parse the incoming request
648: try {
649: if (ok) {
650: parseConnection(socket);
651: parseRequest(input);
652: if (!request.getRequest().getProtocol().startsWith(
653: "HTTP/0"))
654: parseHeaders(input);
655: }
656: } catch (Exception e) {
657: try {
658: log("process.parse", e);
659: ((HttpServletResponse) response.getResponse())
660: .sendError(HttpServletResponse.SC_BAD_REQUEST);
661: } catch (Exception f) {
662: ;
663: }
664: }
665:
666: // Ask our Container to process this request
667: try {
668: if (ok) {
669: connector.getContainer().invoke(request, response);
670: }
671: } catch (ServletException e) {
672: log("process.invoke", e);
673: try {
674: ((HttpServletResponse) response.getResponse())
675: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
676: } catch (Exception f) {
677: ;
678: }
679: ok = false;
680: } catch (Throwable e) {
681: log("process.invoke", e);
682: try {
683: ((HttpServletResponse) response.getResponse())
684: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
685: } catch (Exception f) {
686: ;
687: }
688: ok = false;
689: }
690:
691: // Finish up the handling of the response
692: try {
693: if (ok)
694: response.finishResponse();
695: } catch (IOException e) {
696: log("FIXME-Exception from finishResponse", e);
697: }
698: try {
699: if (output != null)
700: output.flush();
701: } catch (IOException e) {
702: log("FIXME-Exception flushing output", e);
703: }
704: try {
705: if (output != null)
706: output.close();
707: } catch (IOException e) {
708: log("FIXME-Exception closing output", e);
709: }
710:
711: // Finish up the handling of the request
712: try {
713: if (ok)
714: request.finishRequest();
715: } catch (IOException e) {
716: log("FIXME-Exception from finishRequest", e);
717: }
718: try {
719: if (input != null)
720: input.close();
721: } catch (IOException e) {
722: log("FIXME-Exception closing input", e);
723: }
724:
725: // Finish up the handling of the socket connection itself
726: try {
727: socket.close();
728: } catch (IOException e) {
729: log("FIXME-Exception closing socket", e);
730: }
731: socket = null;
732:
733: }
734:
735: /**
736: * Read a line from the specified input stream, and strip off the
737: * trailing carriage return and newline (if any). Return the remaining
738: * characters that were read as a String.
739: *
740: * @param input The input stream connected to our socket
741: *
742: * @returns The line that was read, or <code>null</code> if end-of-file
743: * was encountered
744: *
745: * @exception IOException if an input/output error occurs
746: */
747: private String read(InputStream input) throws IOException {
748:
749: StringBuffer sb = new StringBuffer();
750: while (true) {
751: int ch = input.read();
752: if (ch < 0) {
753: if (sb.length() == 0) {
754: return (null);
755: } else {
756: break;
757: }
758: } else if (ch == '\r') {
759: continue;
760: } else if (ch == '\n') {
761: break;
762: }
763: sb.append((char) ch);
764: }
765: if (debug >= 2)
766: log(" Read: " + sb.toString());
767: return (sb.toString());
768:
769: }
770:
771: // ---------------------------------------------- Background Thread Methods
772:
773: /**
774: * The background thread that listens for incoming TCP/IP connections and
775: * hands them off to an appropriate processor.
776: */
777: public void run() {
778:
779: // Process requests until we receive a shutdown signal
780: while (!stopped) {
781:
782: // Wait for the next socket to be assigned
783: Socket socket = await();
784: if (socket == null)
785: continue;
786:
787: // Process the request from this socket
788: process(socket);
789:
790: // Finish up this request
791: request.recycle();
792: response.recycle();
793: connector.recycle(this );
794:
795: }
796:
797: // Tell threadStop() we have shut ourselves down successfully
798: synchronized (threadSync) {
799: threadSync.notifyAll();
800: }
801:
802: }
803:
804: /**
805: * Start the background processing thread.
806: */
807: private void threadStart() {
808:
809: log(sm.getString("httpProcessor.starting"));
810:
811: thread = new Thread(this , threadName);
812: thread.setDaemon(true);
813: thread.start();
814:
815: if (debug >= 1)
816: log(" Background thread has been started");
817:
818: }
819:
820: /**
821: * Stop the background processing thread.
822: */
823: private void threadStop() {
824:
825: log(sm.getString("httpProcessor.stopping"));
826:
827: stopped = true;
828: assign(null);
829: synchronized (threadSync) {
830: try {
831: threadSync.wait(5000);
832: } catch (InterruptedException e) {
833: ;
834: }
835: }
836: thread = null;
837:
838: }
839:
840: // ------------------------------------------------------ Lifecycle Methods
841:
842: /**
843: * Add a lifecycle event listener to this component.
844: *
845: * @param listener The listener to add
846: */
847: public void addLifecycleListener(LifecycleListener listener) {
848:
849: lifecycle.addLifecycleListener(listener);
850:
851: }
852:
853: /**
854: * Get the lifecycle listeners associated with this lifecycle. If this
855: * Lifecycle has no listeners registered, a zero-length array is returned.
856: */
857: public LifecycleListener[] findLifecycleListeners() {
858:
859: return lifecycle.findLifecycleListeners();
860:
861: }
862:
863: /**
864: * Remove a lifecycle event listener from this component.
865: *
866: * @param listener The listener to add
867: */
868: public void removeLifecycleListener(LifecycleListener listener) {
869:
870: lifecycle.removeLifecycleListener(listener);
871:
872: }
873:
874: /**
875: * Start the background thread we will use for request processing.
876: *
877: * @exception LifecycleException if a fatal startup error occurs
878: */
879: public void start() throws LifecycleException {
880:
881: if (started)
882: throw new LifecycleException(sm
883: .getString("httpProcessor.alreadyStarted"));
884: lifecycle.fireLifecycleEvent(START_EVENT, null);
885: started = true;
886:
887: threadStart();
888:
889: }
890:
891: /**
892: * Stop the background thread we will use for request processing.
893: *
894: * @exception LifecycleException if a fatal shutdown error occurs
895: */
896: public void stop() throws LifecycleException {
897:
898: if (!started)
899: throw new LifecycleException(sm
900: .getString("httpProcessor.notStarted"));
901: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
902: started = false;
903:
904: threadStop();
905:
906: }
907:
908: }
|