FileDocCategorySizeDatePackage
SimpleConnectionManager.javaAPI DocApache James 2.3.111309Fri Jan 12 12:56:34 GMT 2007org.apache.james.util.connection

SimpleConnectionManager.java

/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you 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.james.util.connection;
import java.net.ServerSocket;
import java.util.HashMap;
import org.apache.excalibur.thread.ThreadPool;
import org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory;
import org.apache.james.services.JamesConnectionManager;
import org.apache.avalon.cornerstone.services.threads.ThreadManager;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
/**
 * An implementation of ConnectionManager that supports configurable
 * idle timeouts and a configurable value for the maximum number of
 * client connections to a particular port.
 *
 */
public class SimpleConnectionManager
    extends AbstractLogEnabled
    implements JamesConnectionManager, Serviceable, Configurable, Disposable {
    /**
     * The default value for client socket idle timeouts.  The
     * Java default is 0, meaning no timeout.  That's dangerous
     * for a connection handler like this one, because it can
     * easily lead to consumption of network resources.  So we
     * allow users to configure the system to allow no timeout,
     * but if no timeout is specified in the configuration, we
     * use a timeout of 5 minutes.
     */
    private static final int DEFAULT_SOCKET_TIMEOUT = 5 * 60 * 1000;
    /**
     * The default value for the maximum number of allowed client
     * connections.
     */
    private static final int DEFAULT_MAX_CONNECTIONS = 30;
    /**
     * The map of connection name / server connections managed by this connection
     * manager.
     */
    private final HashMap connectionMap = new HashMap();
    /**
     * The idle timeout for the individual sockets spawed from the server socket.
     */
    protected int timeout = 0;
    /**
     * The maximum number of client connections allowed per server connection.
     */
    protected int maxOpenConn = 0;
    /**
     * The ThreadManager component that is used to provide a default thread pool.
     */
    private ThreadManager threadManager;
    /**
     * Whether the SimpleConnectionManager has been disposed.
     */
    private volatile boolean disposed = false;
    /**
     * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
     */
    public void configure(final Configuration configuration) throws ConfigurationException {
        timeout = configuration.getChild("idle-timeout").getValueAsInteger(DEFAULT_SOCKET_TIMEOUT);
        maxOpenConn = configuration.getChild("max-connections").getValueAsInteger(DEFAULT_MAX_CONNECTIONS);
        if (timeout < 0) {
            StringBuffer exceptionBuffer =
                new StringBuffer(128).append("Specified socket timeout value of ").append(timeout).append(
                    " is not a legal value.");
            throw new ConfigurationException(exceptionBuffer.toString());
        }
        if (maxOpenConn < 0) {
            StringBuffer exceptionBuffer =
                new StringBuffer(128).append("Specified maximum number of open connections of ").append(
                    maxOpenConn).append(
                    " is not a legal value.");
            throw new ConfigurationException(exceptionBuffer.toString());
        }
        if (getLogger().isDebugEnabled()) {
            getLogger().debug(
                "Connection timeout is " + (timeout == 0 ? "unlimited" : Long.toString(timeout)));
            getLogger().debug(
                "The maximum number of simultaneously open connections is "
                    + (maxOpenConn == 0 ? "unlimited" : Integer.toString(maxOpenConn)));
        }
    }
    /**
     * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
     */
    public void service(ServiceManager componentManager) throws ServiceException {
        threadManager = (ThreadManager)componentManager.lookup(ThreadManager.ROLE);
    }
    /**
     * Disconnects all the underlying ServerConnections
     */
    public void dispose() {
        disposed = true;
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Starting SimpleConnectionManager dispose...");
        }
        final String[] names = (String[])connectionMap.keySet().toArray(new String[0]);
        for (int i = 0; i < names.length; i++) {
            try {
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Disconnecting ServerConnection " + names[i]);
                }
                disconnect(names[i], true);
            } catch (final Exception e) {
                getLogger().warn("Error disconnecting " + names[i], e);
            }
        }
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Finishing SimpleConnectionManager dispose...");
        }
    }
    /**
     * Start managing a connection.
     * Management involves accepting connections and farming them out to threads
     * from pool to be handled.
     *
     * @param name the name of connection
     * @param socket the ServerSocket from which to
     * @param handlerFactory the factory from which to acquire handlers
     * @param threadPool the thread pool to use
     * @param maxOpenConnections the maximum number of open connections allowed for this server socket.
     * @exception Exception if an error occurs
     */
    public void connect(
        String name,
        ServerSocket socket,
        ConnectionHandlerFactory handlerFactory,
        ThreadPool threadPool,
        int maxOpenConnections)
        throws Exception {
        if (disposed) {
            throw new IllegalStateException("Connection manager has already been shutdown.");
        }
        if (null != connectionMap.get(name)) {
            throw new IllegalArgumentException("Connection already exists with name " + name);
        }
        if (maxOpenConnections < 0) {
            throw new IllegalArgumentException("The maximum number of client connections per server socket cannot be less that zero.");
        }
        ServerConnection runner =
            new ServerConnection(socket, handlerFactory, threadPool, timeout, maxOpenConnections);
        setupLogger(runner);
        ContainerUtil.initialize(runner);
        connectionMap.put(name, runner);
        threadPool.execute(runner);
    }
    /**
     * Start managing a connection.
     * Management involves accepting connections and farming them out to threads
     * from pool to be handled.
     *
     * @param name the name of connection
     * @param socket the ServerSocket from which to
     * @param handlerFactory the factory from which to acquire handlers
     * @param threadPool the thread pool to use
     * @exception Exception if an error occurs
     */
    public void connect(
        String name,
        ServerSocket socket,
        ConnectionHandlerFactory handlerFactory,
        ThreadPool threadPool)
        throws Exception {
        connect(name, socket, handlerFactory, threadPool, maxOpenConn);
    }
    /**
     * Start managing a connection.
     * This is similar to other connect method except that it uses default thread pool.
     *
     * @param name the name of connection
     * @param socket the ServerSocket from which to
     * @param handlerFactory the factory from which to acquire handlers
     * @exception Exception if an error occurs
     */
    public void connect(String name, ServerSocket socket, ConnectionHandlerFactory handlerFactory)
        throws Exception {
        connect(name, socket, handlerFactory, threadManager.getDefaultThreadPool());
    }
    /**
     * Start managing a connection.
     * This is similar to other connect method except that it uses default thread pool.
     *
     * @param name the name of connection
     * @param socket the ServerSocket from which to
     * @param handlerFactory the factory from which to acquire handlers
     * @param maxOpenConnections the maximum number of open connections allowed for this server socket.
     * @exception Exception if an error occurs
     */
    public void connect(
        String name,
        ServerSocket socket,
        ConnectionHandlerFactory handlerFactory,
        int maxOpenConnections)
        throws Exception {
        connect(name, socket, handlerFactory, threadManager.getDefaultThreadPool(), maxOpenConnections);
    }
    /**
     * This shuts down all handlers and socket, waiting for each to gracefully shutdown.
     *
     * @param name the name of connection
     * @exception Exception if an error occurs
     */
    public void disconnect(final String name) throws Exception {
        disconnect(name, false);
    }
    /**
     * This shuts down a connection.
     * If tearDown is true then it will forcefully the connection and try
     * to return as soon as possible. Otherwise it will behave the same as
     * void disconnect( String name );
     *
     * @param name the name of connection
     * @param tearDown if true will forcefully tear down all handlers
     * @exception Exception if an error occurs
     */
    public void disconnect(final String name, final boolean tearDown) throws Exception {
        ServerConnection connection = (ServerConnection)connectionMap.remove(name);
        if (null == connection) {
            throw new IllegalArgumentException("No such connection with name " + name);
        }
        // TODO: deal with tear down parameter
        connection.dispose();
    }
    /**
     * Returns the default maximum number of open connections supported by this
     * SimpleConnectionManager
     *
     * @return the maximum number of connections
     */
    public int getMaximumNumberOfOpenConnections() {
        return maxOpenConn;
    }

}