| /* |
| * Copyright 1999-2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.ajp.tomcat4; |
| |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| import java.security.AccessControlException; |
| import java.util.Stack; |
| import java.util.Vector; |
| |
| import org.apache.catalina.Connector; |
| import org.apache.catalina.Container; |
| import org.apache.catalina.Lifecycle; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.LifecycleListener; |
| import org.apache.catalina.Request; |
| import org.apache.catalina.Response; |
| import org.apache.catalina.Service; |
| import org.apache.catalina.net.DefaultServerSocketFactory; |
| import org.apache.catalina.net.ServerSocketFactory; |
| import org.apache.catalina.util.LifecycleSupport; |
| import org.apache.catalina.util.StringManager; |
| |
| /** |
| * Implementation of an Ajp13 connector. |
| * |
| * @author Kevin Seguin |
| * @version $Revision$ $Date$ |
| */ |
| |
| |
| public final class Ajp13Connector |
| implements Connector, Lifecycle, Runnable { |
| |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| |
| /** |
| * The accept count for this Connector. |
| */ |
| private int acceptCount = 10; |
| |
| |
| /** |
| * The IP address on which to bind, if any. If <code>null</code>, all |
| * addresses on the server will be bound. |
| */ |
| private String address = null; |
| |
| |
| /** |
| * The input buffer size we should create on input streams. |
| */ |
| private int bufferSize = 2048; |
| |
| |
| /** |
| * The Container used for processing requests received by this Connector. |
| */ |
| protected Container container = null; |
| |
| |
| /** |
| * The set of processors that have ever been created. |
| */ |
| private Vector created = new Vector(); |
| |
| |
| /** |
| * The current number of processors that have been created. |
| */ |
| private int curProcessors = 0; |
| |
| |
| /** |
| * The debugging detail level for this component. |
| */ |
| private int debug = 0; |
| |
| |
| /** |
| * The server socket factory for this component. |
| */ |
| private ServerSocketFactory factory = null; |
| |
| |
| /** |
| * Descriptive information about this Connector implementation. |
| */ |
| private static final String info = |
| "org.apache.catalina.connector.ajp.Ajp13Connector/1.0"; |
| |
| |
| /** |
| * redirect port. |
| */ |
| private int redirectPort = -1; |
| |
| /** |
| * enable DNS lookups. |
| */ |
| private boolean enableLookups = false; |
| |
| /** |
| * The lifecycle event support for this component. |
| */ |
| protected LifecycleSupport lifecycle = new LifecycleSupport(this); |
| |
| |
| /** |
| * The minimum number of processors to start at initialization time. |
| */ |
| protected int minProcessors = 5; |
| |
| |
| /** |
| * The maximum number of processors allowed, or <0 for unlimited. |
| */ |
| private int maxProcessors = 20; |
| |
| |
| /** |
| * Timeout value on the incoming connection. |
| * Note : a value of 0 means no timeout. |
| */ |
| private int connectionTimeout = -1; |
| |
| |
| /** |
| * Linger value to be used on socket close. |
| * Note : a value of -1 means no linger used on close. |
| */ |
| private int connectionLinger = -1; |
| |
| |
| /** |
| * The port number on which we listen for ajp13 requests. |
| */ |
| private int port = 8009; |
| |
| |
| /** |
| * The set of processors that have been created but are not currently |
| * being used to process a request. |
| */ |
| private Stack processors = new Stack(); |
| |
| |
| /** |
| * The request scheme that will be set on all requests received |
| * through this connector. |
| */ |
| private String scheme = "http"; |
| |
| |
| /** |
| * The secure connection flag that will be set on all requests received |
| * through this connector. |
| */ |
| private boolean secure = false; |
| |
| |
| /** |
| * 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; |
| |
| |
| /** |
| * This connector's thread group. |
| */ |
| private ThreadGroup threadGroup = null; |
| |
| |
| /** |
| * The name to register for the background thread. |
| */ |
| private String threadName = null; |
| |
| |
| /** |
| * A thread that periodically logs debug info if debug > 0. |
| */ |
| private DebugThread debugThread = null; |
| |
| |
| /** |
| * The thread synchronization object. |
| */ |
| private Object threadSync = new Object(); |
| |
| private Ajp13Logger logger = new Ajp13Logger(); |
| |
| /** |
| * The service which which the connector is associated |
| */ |
| private Service service = null; |
| |
| private String secret = null; |
| |
| |
| /** |
| * Tomcat authentication flag. If true, the authnetication is done by |
| * Tomcat, otherwise, it is done by the native webserver. |
| */ |
| private boolean tomcatAuthentication = true; |
| |
| |
| // ------------------------------------------------------------- Properties |
| |
| |
| /** |
| * Return the connection timeout for this Connector. |
| */ |
| public int getConnectionTimeout() { |
| |
| return (connectionTimeout); |
| |
| } |
| |
| |
| /** |
| * Set the connection timeout for this Connector. |
| * |
| * @param connectionTimeout The new connection timeout |
| */ |
| public void setConnectionTimeout(int connectionTimeout) { |
| |
| this.connectionTimeout = connectionTimeout; |
| |
| } |
| |
| /** |
| * Return the connection linger settings for this Connector. |
| */ |
| public int getConnectionLinger() { |
| |
| return (connectionLinger); |
| |
| } |
| |
| |
| /** |
| * Set the connection linger for this Connector. |
| * |
| * @param connectionLinger The new connection linger |
| */ |
| public void setConnectionLinger(int connectionLinger) { |
| |
| this.connectionLinger = connectionLinger; |
| |
| } |
| |
| public void setSecret( String s ) { |
| secret=s; |
| } |
| |
| public String getSecret() { |
| return secret; |
| } |
| |
| |
| /** |
| * 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; |
| |
| } |
| |
| |
| |
| /** |
| * Return the bind IP address for this Connector. |
| */ |
| public String getAddress() { |
| |
| return (this.address); |
| |
| } |
| |
| |
| /** |
| * Set the bind IP address for this Connector. |
| * |
| * @param address The bind IP address |
| */ |
| public void setAddress(String address) { |
| |
| this.address = address; |
| |
| } |
| |
| |
| /** |
| * Is this connector available for processing requests? |
| */ |
| public boolean isAvailable() { |
| |
| return (started); |
| |
| } |
| |
| |
| /** |
| * Return the input buffer size for this Connector. |
| */ |
| public int getBufferSize() { |
| |
| return (this.bufferSize); |
| |
| } |
| |
| |
| /** |
| * Set the input buffer size for this Connector. |
| * |
| * @param bufferSize The new input buffer size. |
| */ |
| public void setBufferSize(int bufferSize) { |
| |
| this.bufferSize = bufferSize; |
| |
| } |
| |
| |
| /** |
| * Return the Container used for processing requests received by this |
| * Connector. |
| */ |
| public Container getContainer() { |
| |
| return (container); |
| |
| } |
| |
| |
| /** |
| * Set the Container used for processing requests received by this |
| * Connector. |
| * |
| * @param container The new Container to use |
| */ |
| public void setContainer(Container container) { |
| |
| this.container = container; |
| |
| } |
| |
| |
| /** |
| * Return the current number of processors that have been created. |
| */ |
| public int getCurProcessors() { |
| |
| return (curProcessors); |
| |
| } |
| |
| |
| /** |
| * Return the debugging detail level for this component. |
| */ |
| public int getDebug() { |
| |
| return (debug); |
| |
| } |
| |
| |
| /** |
| * Set the debugging detail level for this component. |
| * |
| * @param debug The new debugging detail level |
| */ |
| public void setDebug(int debug) { |
| |
| this.debug = debug; |
| |
| } |
| |
| /** |
| * Return the "enable DNS lookups" flag. |
| */ |
| public boolean getEnableLookups() { |
| return this.enableLookups; |
| } |
| |
| /** |
| * Set the "enable DNS lookups" flag. |
| * |
| * @param enableLookups The new "enable DNS lookups" flag value |
| */ |
| public void setEnableLookups(boolean enableLookups) { |
| this.enableLookups = enableLookups; |
| } |
| |
| /** |
| * Return the port number to which a request should be redirected if |
| * it comes in on a non-SSL port and is subject to a security constraint |
| * with a transport guarantee that requires SSL. |
| */ |
| public int getRedirectPort() { |
| return this.redirectPort; |
| } |
| |
| |
| /** |
| * Set the redirect port number. |
| * |
| * @param redirectPort The redirect port number (non-SSL to SSL) |
| */ |
| public void setRedirectPort(int redirectPort) { |
| this.redirectPort = redirectPort; |
| } |
| |
| /** |
| * Return the server socket factory used by this Container. |
| */ |
| public ServerSocketFactory getFactory() { |
| |
| if (this.factory == null) { |
| synchronized (this) { |
| this.factory = new DefaultServerSocketFactory(); |
| } |
| } |
| return (this.factory); |
| |
| } |
| |
| |
| /** |
| * Set the server socket factory used by this Container. |
| * |
| * @param factory The new server socket factory |
| */ |
| public void setFactory(ServerSocketFactory factory) { |
| |
| this.factory = factory; |
| |
| } |
| |
| |
| /** |
| * Return descriptive information about this Connector implementation. |
| */ |
| public String getInfo() { |
| |
| return (info); |
| |
| } |
| |
| |
| /** |
| * Return the minimum number of processors to start at initialization. |
| */ |
| public int getMinProcessors() { |
| |
| return (minProcessors); |
| |
| } |
| |
| |
| /** |
| * Set the minimum number of processors to start at initialization. |
| * |
| * @param minProcessors The new minimum processors |
| */ |
| public void setMinProcessors(int minProcessors) { |
| |
| this.minProcessors = minProcessors; |
| |
| } |
| |
| |
| /** |
| * 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 AJP13 requests. |
| */ |
| public int getPort() { |
| |
| return (this.port); |
| |
| } |
| |
| |
| /** |
| * Set the port number on which we listen for AJP13 requests. |
| * |
| * @param port The new port number |
| */ |
| public void setPort(int port) { |
| |
| this.port = port; |
| |
| } |
| |
| |
| /** |
| * Return the scheme that will be assigned to requests received |
| * through this connector. Default value is "http". |
| */ |
| public String getScheme() { |
| |
| return (this.scheme); |
| |
| } |
| |
| |
| /** |
| * Set the scheme that will be assigned to requests received through |
| * this connector. |
| * |
| * @param scheme The new scheme |
| */ |
| public void setScheme(String scheme) { |
| |
| this.scheme = scheme; |
| |
| } |
| |
| |
| /** |
| * Return the secure connection flag that will be assigned to requests |
| * received through this connector. Default value is "false". |
| */ |
| public boolean getSecure() { |
| |
| return (this.secure); |
| |
| } |
| |
| |
| /** |
| * Set the secure connection flag that will be assigned to requests |
| * received through this connector. |
| * |
| * @param secure The new secure connection flag |
| */ |
| public void setSecure(boolean secure) { |
| |
| this.secure = secure; |
| |
| } |
| |
| |
| /** |
| * Returns the <code>Service</code> with which we are associated. |
| */ |
| public Service getService() { |
| return service; |
| } |
| |
| |
| /** |
| * Set the <code>Service</code> with which we are associated. |
| */ |
| public void setService(Service service) { |
| this.service = service; |
| } |
| |
| |
| /** |
| * Get the value of the tomcatAuthentication flag. |
| */ |
| public boolean getTomcatAuthentication() { |
| return tomcatAuthentication; |
| } |
| |
| |
| /** |
| * Set the value of the tomcatAuthentication flag. |
| */ |
| public void setTomcatAuthentication(boolean tomcatAuthentication) { |
| this.tomcatAuthentication = tomcatAuthentication; |
| } |
| |
| |
| // --------------------------------------------------------- Public Methods |
| |
| |
| /** |
| * Create (or allocate) and return a Request object suitable for |
| * specifying the contents of a Request to the responsible Container. |
| */ |
| public Request createRequest() { |
| |
| Ajp13Request request = new Ajp13Request(this); |
| request.setConnector(this); |
| return (request); |
| |
| } |
| |
| |
| /** |
| * Create (or allocate) and return a Response object suitable for |
| * receiving the contents of a Response from the responsible Container. |
| */ |
| public Response createResponse() { |
| |
| Ajp13Response response = new Ajp13Response(); |
| response.setConnector(this); |
| return (response); |
| |
| } |
| |
| /** |
| * Invoke a pre-startup initialization. This is used to allow connectors |
| * to bind to restricted ports under Unix operating environments. |
| * ServerSocket (we start as root and change user? or I miss something?). |
| */ |
| public void initialize() throws LifecycleException { |
| } |
| |
| |
| // -------------------------------------------------------- Package Methods |
| |
| |
| /** |
| * Recycle the specified Processor so that it can be used again. |
| * |
| * @param processor The processor to be recycled |
| */ |
| void recycle(Ajp13Processor processor) { |
| |
| synchronized(processors) { |
| if (debug > 0) { |
| logger.log("added processor to available processors, available=" |
| + processors.size()); |
| } |
| processors.push(processor); |
| } |
| |
| } |
| |
| |
| // -------------------------------------------------------- Private Methods |
| |
| |
| /** |
| * Create (or allocate) and return an available processor for use in |
| * processing a specific AJP13 request, if possible. If the maximum |
| * allowed processors have already been created and are in use, return |
| * <code>null</code> instead. |
| */ |
| private Ajp13Processor createProcessor() { |
| |
| synchronized (processors) { |
| if (processors.size() > 0) |
| return ((Ajp13Processor) processors.pop()); |
| if ((maxProcessors > 0) && (curProcessors < maxProcessors)) |
| return (newProcessor()); |
| else |
| return (null); |
| } |
| |
| } |
| |
| |
| /** |
| * Create and return a new processor suitable for processing AJP13 |
| * requests and returning the corresponding responses. |
| */ |
| private Ajp13Processor newProcessor() { |
| |
| Ajp13Processor processor = new Ajp13Processor(this, curProcessors++, threadGroup); |
| if (processor instanceof Lifecycle) { |
| try { |
| ((Lifecycle) processor).start(); |
| } catch (LifecycleException e) { |
| logger.log("newProcessor", e); |
| curProcessors--; |
| return (null); |
| } |
| } |
| created.addElement(processor); |
| return (processor); |
| |
| } |
| |
| |
| /** |
| * Open and return the server socket for this Connector. If an IP |
| * address has been specified, the socket will be opened only on that |
| * address; otherwise it will be opened on all addresses. |
| * |
| * @exception IOException if an input/output error occurs |
| */ |
| private ServerSocket open() throws IOException { |
| |
| // Acquire the server socket factory for this Connector |
| ServerSocketFactory factory = getFactory(); |
| |
| // If no address is specified, open a connection on all addresses |
| if (address == null) { |
| logger.log(sm.getString("ajp13Connector.allAddresses")); |
| try { |
| return (factory.createSocket(port, acceptCount)); |
| } catch(Exception ex ) { |
| ex.printStackTrace(); |
| return null; |
| } |
| } |
| |
| // Open a server socket on the specified address |
| try { |
| InetAddress is = InetAddress.getByName(address); |
| logger.log(sm.getString("ajp13Connector.anAddress", address)); |
| return (factory.createSocket(port, acceptCount, is)); |
| } catch (Exception e) { |
| try { |
| logger.log(sm.getString("ajp13Connector.noAddress", address)); |
| return (factory.createSocket(port, acceptCount)); |
| } catch( Exception e1 ) { |
| e1.printStackTrace(); |
| return null; |
| } |
| } |
| |
| } |
| |
| |
| // ---------------------------------------------- Background Thread Methods |
| |
| |
| /** |
| * The background thread that listens for incoming TCP/IP connections and |
| * hands them off to an appropriate processor. |
| */ |
| public void run() { |
| |
| // Loop until we receive a shutdown command |
| while (!stopped) { |
| |
| // Accept the next incoming connection from the server socket |
| Socket socket = null; |
| try { |
| if (debug > 0) { |
| logger.log("accepting socket..."); |
| } |
| |
| socket = serverSocket.accept(); |
| |
| if (debug > 0) { |
| logger.log("accepted socket, assigning to processor."); |
| } |
| |
| /* Warning : |
| * |
| * To be able to close more quickly a connection, it's recommanded |
| * to set linger to a small value. |
| * |
| * AJP13 connection SHOULD be closed under webserver responsability and |
| * in such case it's safe to close socket on Tomcat side without delay, |
| * which may be also the case for HTTP connectors. |
| * |
| * I (henri) recommand to set Linger to 0, making socket closed immediatly |
| * so the OS will free faster the underlying io descriptor and resources. |
| * It's very important under heavy load ! |
| */ |
| |
| if (connectionLinger < 0) |
| socket.setSoLinger(false, 0); |
| else |
| socket.setSoLinger(true, connectionLinger); |
| |
| /* We don't need it since it's the native side which |
| * will set the connection with keep alive |
| * if specified in workers.properties. |
| * |
| * socket.setKeepAlive(true); |
| */ |
| |
| /* Warning : |
| * |
| * AJP13 shouldn't use socket timeout on tomcat site since |
| * when Tomcat close a connection after a timeout is reached |
| * the socket stay in half-closed state until the webserver |
| * try to send a request to tomcat and detect the socket close |
| * when it will try to read the reply. |
| * |
| * On many Unix platforms the write() call didn't told |
| * webserver that the socket is closed. |
| */ |
| |
| if (connectionTimeout >= 0) { |
| socket.setSoTimeout(connectionTimeout); |
| } |
| } catch (AccessControlException ace) { |
| logger.log("socket accept security exception: " |
| + ace.getMessage()); |
| continue; |
| } catch (IOException e) { |
| if (started && !stopped) |
| logger.log("accept: ", e); |
| try { |
| if (serverSocket != null) { |
| serverSocket.close(); |
| } |
| if (stopped) { |
| if (debug > 0) { |
| logger.log("run(): stopped, so breaking"); |
| } |
| break; |
| } else { |
| if (debug > 0) { |
| logger.log("run(): not stopped, " + |
| "so reopening server socket"); |
| } |
| serverSocket = open(); |
| } |
| } catch (IOException ex) { |
| // If reopening fails, exit |
| logger.log("socket reopen: ", ex); |
| break; |
| } |
| continue; |
| } |
| |
| // Hand this socket off to an appropriate processor |
| if (debug > 0) { |
| synchronized(processors) { |
| logger.log("about to create a processor, available=" |
| + processors.size() + ", created=" + created.size() |
| + ", maxProcessors=" + maxProcessors); |
| } |
| } |
| Ajp13Processor processor = createProcessor(); |
| if (processor == null) { |
| try { |
| logger.log(sm.getString("ajp13Connector.noProcessor")); |
| socket.close(); |
| } catch (IOException e) { |
| ; |
| } |
| continue; |
| } |
| processor.assign(socket); |
| |
| // The processor will recycle itself when it finishes |
| |
| } |
| |
| // Notify the threadStop() method that we have shut ourselves down |
| synchronized (threadSync) { |
| threadSync.notifyAll(); |
| } |
| |
| } |
| |
| |
| /** |
| * Start the background processing thread. |
| */ |
| private void threadStart() { |
| |
| logger.log(sm.getString("ajp13Connector.starting")); |
| |
| thread = new Thread(threadGroup, this, threadName); |
| thread.setDaemon(true); |
| thread.start(); |
| |
| } |
| |
| |
| /** |
| * Stop the background processing thread. |
| */ |
| private void threadStop() { |
| |
| logger.log(sm.getString("ajp13Connector.stopping")); |
| |
| stopped = true; |
| synchronized (threadSync) { |
| try { |
| threadSync.wait(5000); |
| } catch (InterruptedException e) { |
| ; |
| } |
| } |
| thread = null; |
| |
| } |
| |
| |
| // ------------------------------------------------------ Lifecycle Methods |
| |
| |
| /** |
| * Add a lifecycle event listener to this component. |
| * |
| * @param listener The listener to add |
| */ |
| public void addLifecycleListener(LifecycleListener listener) { |
| |
| lifecycle.addLifecycleListener(listener); |
| |
| } |
| |
| /** |
| * Get the lifecycle listeners associated with this lifecycle. If this |
| * Lifecycle has no listeners registered, a zero-length array is returned. |
| */ |
| public LifecycleListener[] findLifecycleListeners() { |
| return null; // FIXME: lifecycle.findLifecycleListeners(); |
| } |
| |
| |
| /** |
| * Remove a lifecycle event listener from this component. |
| * |
| * @param listener The listener to add |
| */ |
| public void removeLifecycleListener(LifecycleListener listener) { |
| |
| lifecycle.removeLifecycleListener(listener); |
| |
| } |
| |
| |
| /** |
| * 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("ajp13Connector.alreadyStarted")); |
| |
| if (debug > 0) { |
| debugThread = new DebugThread(); |
| debugThread.setDaemon(true); |
| debugThread.start(); |
| } |
| |
| threadName = "Ajp13Connector[" + port + "]"; |
| threadGroup = new ThreadGroup(threadName); |
| threadGroup.setDaemon(true); |
| logger.setConnector(this); |
| logger.setName(threadName); |
| lifecycle.fireLifecycleEvent(START_EVENT, null); |
| started = true; |
| |
| // Establish a server socket on the specified port |
| try { |
| serverSocket = open(); |
| } catch (IOException e) { |
| throw new LifecycleException(threadName + ".open", e); |
| } |
| |
| // Start our background thread |
| threadStart(); |
| |
| // Create the specified minimum number of processors |
| while (curProcessors < minProcessors) { |
| if ((maxProcessors > 0) && (curProcessors >= maxProcessors)) |
| break; |
| Ajp13Processor processor = newProcessor(); |
| recycle(processor); |
| } |
| |
| } |
| |
| |
| /** |
| * 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("ajp13Connector.notStarted")); |
| lifecycle.fireLifecycleEvent(STOP_EVENT, null); |
| started = false; |
| |
| // Gracefully shut down all processors we have created |
| for (int i = created.size() - 1; i >= 0; i--) { |
| Ajp13Processor processor = (Ajp13Processor) created.elementAt(i); |
| if (processor instanceof Lifecycle) { |
| try { |
| ((Lifecycle) processor).stop(); |
| } catch (LifecycleException e) { |
| logger.log("Ajp13Connector.stop", e); |
| } |
| } |
| } |
| |
| // Stop our background thread |
| threadStop(); |
| |
| // Close the server socket we were using |
| if (serverSocket != null) { |
| try { |
| serverSocket.close(); |
| } catch (IOException e) { |
| ; |
| } |
| serverSocket = null; |
| } |
| |
| } |
| |
| /** |
| * Debugging thread used to debug thread activity in this |
| * connector. |
| */ |
| private class DebugThread extends Thread |
| { |
| public void run() { |
| while (true) { |
| try { |
| sleep(60 * 1000); |
| } catch (InterruptedException e) { |
| break; |
| } |
| logger.log("active threads=" + threadGroup.activeCount()); |
| System.out.println("==================================="); |
| System.out.println("Ajp13Connector active threads=" |
| + threadGroup.activeCount()); |
| threadGroup.list(); |
| System.out.println("==================================="); |
| } |
| } |
| } |
| |
| } |