/*
* $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/connector/http/HttpConnector.java,v 1.1 2000/02/20 02:57:10 craigmcc Exp $
* $Revision: 1.1 $
* $Date: 2000/02/20 02:57:10 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.connector.http;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Stack;
import org.apache.tomcat.Connector;
import org.apache.tomcat.Container;
import org.apache.tomcat.Lifecycle;
import org.apache.tomcat.LifecycleException;
import org.apache.tomcat.Logger;
import org.apache.tomcat.Request;
import org.apache.tomcat.Response;
import org.apache.tomcat.connector.ConnectorBase;
import org.apache.tomcat.util.StringManager;
public final class HttpConnector
extends ConnectorBase
implements Lifecycle, Runnable {
// ----------------------------------------------------- Instance Variables
/**
* The accept count for this Connector.
*/
private int acceptCount = 10;
/**
* The current number of processors that have been created.
*/
private int curProcessors = 0;
/**
* Descriptive information about this Connector implementation.
*/
private static final String info =
"org.apache.tomcat.connector.http.HttpConnector/1.0";
/**
* The maximum number of processors allowed, or <0 for unlimited.
*/
private int maxProcessors = -1;
/**
* The port number on which we listen for HTTP requests.
*/
private int port = 8080;
/**
* The set of processors that have been created but are not currently
* being used to process a request.
*/
private Stack processors = new Stack();
/**
* The server socket through which we listen for incoming TCP connections.
*/
private ServerSocket serverSocket = null;
/**
* The string manager for this package.
*/
private StringManager sm =
StringManager.getManager(Constants.Package);
/**
* Has this component been started yet?
*/
private boolean started = false;
/**
* The shutdown signal to our background thread
*/
private boolean stopped = false;
/**
* The background thread.
*/
private Thread thread = null;
/**
* The name to register for the background thread.
*/
private String threadName = null;
/**
* The thread synchronization object.
*/
private String threadSync = "";
// ------------------------------------------------------------- Properties
/**
* Return the accept count for this Connector.
*/
public int getAcceptCount() {
return (acceptCount);
}
/**
* Set the accept count for this Connector.
*
* @param count The new accept count
*/
public void setAcceptCount(int count) {
this.acceptCount = count;
}
/**
* Is this connector available for processing requests?
*/
public boolean isAvailable() {
return (started);
}
/**
* Return the current number of processors that have been created.
*/
public int getCurProcessors() {
return (curProcessors);
}
/**
* Return descriptive information about this Connector implementation.
*/
public String getInfo() {
return (info);
}
/**
* Return the maximum number of processors allowed, or <0 for unlimited.
*/
public int getMaxProcessors() {
return (maxProcessors);
}
/**
* Set the maximum number of processors allowed, or <0 for unlimited.
*
* @param maxProcessors The new maximum processors
*/
public void setMaxProcessors(int maxProcessors) {
this.maxProcessors = maxProcessors;
}
/**
* Return the port number on which we listen for HTTP requests.
*/
public int getPort() {
return (this.port);
}
/**
* Set the port number on which we listen for HTTP requests.
*
* @param port The new port number
*/
public void setPort(int port) {
this.port = port;
}
/**
* Return the thread name of our background thread.
*/
public String getThreadName() {
return (threadName);
}
/**
* Set the thread name of our background thread.
*
* @param name The new thread name
*/
public void setThreadName(String name) {
this.threadName = name;
}
// --------------------------------------------------------- Public Methods
/**
* Instantiate and return a new Request object suitable for specifying
* the contents of a Request to the responsible Container. This method
* must be implemented by a subclass.
*/
public Request newRequest() {
HttpRequest request = new HttpRequest();
request.setConnector(this);
return (request);
}
/**
* Instantiate and return a new Response object suitable for receiving
* the contents of a Response from the responsible Container. This method
* must be implemented by a subclass.
*/
public Response newResponse() {
HttpResponse response = new HttpResponse();
response.setConnector(this);
return (response);
}
// -------------------------------------------------------- Package Methods
/**
* Recycle the specified Request.
*
* @param request The request to be recycled
*/
void recycle(Request request) {
requests.push(request);
}
/**
* Recycle the specified Response.
*
* @param response The response to be recycled
*/
void recycle(Response response) {
responses.push(response);
}
// -------------------------------------------------------- Private Methods
/**
* Create (or allocate) and return an available processor for use in
* processing a specific HTTP request, if possible. If the maximum
* allowed processors have already been created and are in use, return
* <code>null</code> instead.
*/
private HttpProcessor createProcessor() {
synchronized (processors) {
if (processors.size() > 0)
return ((HttpProcessor) processors.pop());
if ((maxProcessors > 0) && (curProcessors < maxProcessors))
return (newProcessor());
return (null);
}
}
/**
* Instantiate and return a new processor suitable for receiving
* and processing HTTP requests.
*/
private HttpProcessor newProcessor() {
curProcessors++;
HttpProcessor processor = new HttpProcessor(this);
if (processor instanceof Lifecycle) {
try {
((Lifecycle) processor).start();
} catch (LifecycleException e) {
Logger logger = container.getLogger();
if (logger != null)
logger.log(threadName + ".newProcessor", e);
}
}
return (processor);
}
// ---------------------------------------------- Background Thread Methods
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (IOException e) {
if (!stopped) {
Logger logger = container.getLogger();
if (logger != null)
logger.log(threadName + ".accept", e);
}
break;
}
// Hand this socket off to an appropriate processor
HttpProcessor processor = createProcessor();
if (processor == null) {
try {
Logger logger = container.getLogger();
if (logger != null)
logger.log(threadName +
"select: No processor available");
socket.close();
} catch (IOException e) {
;
}
continue;
}
processor.process(socket);
}
threadSync.notifyAll();
}
/**
* Start the background processing thread.
*/
private void threadStart() {
if (threadName == null)
setThreadName("HttpConnector[" + port + "]");
Logger logger = container.getLogger();
if (logger != null)
logger.log(threadName + ": Starting");
thread = new Thread(this, threadName);
thread.setDaemon(true);
thread.start();
}
/**
* Stop the background processing thread.
*/
private void threadStop() {
Logger logger = container.getLogger();
if (logger != null)
logger.log(threadName + ": Stopping");
stopped = true;
try {
threadSync.wait(5000);
} catch (InterruptedException e) {
;
}
thread = null;
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Begin processing requests via this Connector.
*
* @exception LifecycleException if a fatal startup error occurs
*/
public void start() throws LifecycleException {
// Validate and update our current state
if (started)
throw new LifecycleException
(sm.getString("httpConnector.alreadyStarted"));
started = true;
// Establish a server socket on the specified port
try {
serverSocket = new ServerSocket(port, acceptCount);
} catch (IOException e) {
throw new LifecycleException("HttpConnector[" + port + "]", e);
}
// Start our background thread
threadStart();
}
/**
* Terminate processing requests via this Connector.
*
* @exception LifecycleException if a fatal shutdown error occurs
*/
public void stop() throws LifecycleException {
// Validate and update our current state
if (!started)
throw new LifecycleException
(sm.getString("httpConnector.notStarted"));
started = false;
// Gracefully shut down all processors we have created
; // FIXME
// Close the server socket we were using
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
;
}
serverSocket = null;
}
// Stop our background thread
threadStop();
}
}