You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ba...@apache.org on 2009/10/14 13:38:15 UTC
svn commit: r825089 - in /james/server/trunk:
avalon-socket-library/src/main/java/org/apache/james/socket/
socket-shared-library/src/main/java/org/apache/james/socket/shared/
Author: bago
Date: Wed Oct 14 11:38:14 2009
New Revision: 825089
URL: http://svn.apache.org/viewvc?rev=825089&view=rev
Log:
Defined ProtocolHandlerFactory and ProtocolServer as the interfaces for the composition and extracted the socket service code to a non abstract service class named AvalonProtocolServer (JAMES-930)
Added:
james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AvalonProtocolServer.java (with props)
james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolHandlerFactory.java (with props)
james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolServer.java (with props)
Added: james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AvalonProtocolServer.java
URL: http://svn.apache.org/viewvc/james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AvalonProtocolServer.java?rev=825089&view=auto
==============================================================================
--- james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AvalonProtocolServer.java (added)
+++ james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AvalonProtocolServer.java Wed Oct 14 11:38:14 2009
@@ -0,0 +1,893 @@
+/****************************************************************
+ * 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.socket;
+
+import java.net.BindException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.PostConstruct;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.apache.avalon.cornerstone.services.connection.AbstractHandlerFactory;
+import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
+import org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory;
+import org.apache.avalon.cornerstone.services.sockets.ServerSocketFactory;
+import org.apache.avalon.cornerstone.services.sockets.SocketManager;
+import org.apache.avalon.cornerstone.services.threads.ThreadManager;
+import org.apache.avalon.excalibur.pool.DefaultPool;
+import org.apache.avalon.excalibur.pool.HardResourceLimitingPool;
+import org.apache.avalon.excalibur.pool.ObjectFactory;
+import org.apache.avalon.excalibur.pool.Pool;
+import org.apache.avalon.excalibur.pool.Poolable;
+import org.apache.avalon.framework.activity.Disposable;
+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.logger.Logger;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.excalibur.thread.ThreadPool;
+import org.apache.james.api.dnsservice.DNSService;
+import org.apache.james.services.FileSystem;
+import org.apache.james.socket.shared.ProtocolHandlerFactory;
+import org.apache.james.socket.shared.ProtocolServer;
+import org.apache.james.socket.shared.Watchdog;
+
+/**
+ * Server which creates connection handlers.
+ * The protocol handling will be delegated to a ProtocolHandler
+ * create using the ProtocolHandlerFactory.
+ */
+public class AvalonProtocolServer extends AbstractHandlerFactory
+ implements Serviceable, Configurable, Disposable, ConnectionHandlerFactory, ObjectFactory, ProtocolServer {
+
+ /**
+ * The default value for the connection timeout.
+ */
+ private static final int DEFAULT_TIMEOUT = 5* 60 * 1000;
+
+ /**
+ * The name of the parameter defining the connection timeout.
+ */
+ private static final String TIMEOUT_NAME = "connectiontimeout";
+
+ /**
+ * The default value for the connection backlog.
+ */
+ private static final int DEFAULT_BACKLOG = 5;
+
+ /**
+ * The name of the parameter defining the connection backlog.
+ */
+ private static final String BACKLOG_NAME = "connectionBacklog";
+
+ /**
+ * The name of the parameter defining the service hello name.
+ */
+ private static final String HELLO_NAME = "helloName";
+
+ /**
+ * The ConnectionManager that spawns and manages service connections.
+ */
+ private JamesConnectionManager connectionManager;
+
+ /**
+ * The factory used to generate protocol handlers for this server.
+ */
+ private ProtocolHandlerFactory protocolHandlerFactory;
+
+ /**
+ * The name of the thread group to be used by this service for
+ * generating connections
+ */
+ private String threadGroup;
+
+ /**
+ * The thread pool used by this service that holds the threads
+ * that service the client connections.
+ */
+ private ThreadPool threadPool = null;
+
+ /**
+ * The server socket type used to generate connections for this server.
+ */
+ private String serverSocketType = "plain";
+
+ /**
+ * The port on which this service will be made available.
+ */
+ private int port = -1;
+
+ /**
+ * Network interface to which the service will bind. If not set,
+ * the server binds to all available interfaces.
+ */
+ private InetAddress bindTo = null;
+
+ /**
+ * The name of the connection used by this service. We need to
+ * track this so we can tell the ConnectionManager which service
+ * to disconnect upon shutdown.
+ */
+ private String connectionName;
+
+ /**
+ * The maximum number of connections allowed for this service.
+ */
+ private Integer connectionLimit;
+
+ /**
+ * The connection idle timeout. Used primarily to prevent server
+ * problems from hanging a connection.
+ */
+ private int timeout;
+
+ /**
+ * The connection backlog.
+ */
+ private int backlog;
+
+ /**
+ * The hello name for the service.
+ */
+ private String helloName;
+
+ /**
+ * The component manager used by this service.
+ */
+ private ServiceManager componentManager;
+
+ /**
+ * Whether this service is enabled.
+ */
+ private volatile boolean enabled;
+
+ /**
+ * Flag holding the disposed state of the component.
+ */
+ private boolean m_disposed = false;
+
+
+ /**
+ * The pool used to provide Protocol Handler objects
+ */
+ private Pool theHandlerPool = null;
+
+ /**
+ * The factory used to generate Watchdog objects
+ */
+ private WatchdogFactory theWatchdogFactory = null;
+
+ /**
+ * The DNSService
+ */
+ private DNSService dnsService = null;
+
+ /**
+ * Counts the number of handler instances created.
+ * This allows a unique identity to be assigned to each for
+ * context sensitive logging.
+ */
+ private AtomicLong handlerCount = new AtomicLong(0);
+
+ private boolean connPerIPConfigured = false;
+ private int connPerIP = 0;
+
+ /**
+ * If not null, it will be used to dump the tcp commands for debugging purpose
+ */
+ private String streamDumpDir = null;
+
+ private FileSystem fSystem;
+ private SSLSocketFactory factory;
+
+ private String keystore;
+
+ private String secret;
+
+ private boolean useStartTLS;
+ /**
+ * Gets the DNS Service.
+ * @return the dnsServer
+ */
+ public final DNSService getDnsServer() {
+ return dnsService;
+ }
+
+ /**
+ * Sets the DNS service.
+ * @param dnsServer the dnsServer to set
+ */
+ public final void setDnsServer(DNSService dnsServer) {
+ this.dnsService = dnsServer;
+ }
+
+ public void setConnectionManager(JamesConnectionManager connectionManager) {
+ this.connectionManager = connectionManager;
+ }
+
+ public void setFileSystem(FileSystem fSystem) {
+ this.fSystem = fSystem;
+ }
+
+ /**
+ * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
+ */
+ public void service(ServiceManager comp) throws ServiceException {
+ super.service( comp );
+ componentManager = comp;
+ JamesConnectionManager connectionManager =
+ (JamesConnectionManager)componentManager.lookup(JamesConnectionManager.ROLE);
+ setConnectionManager(connectionManager);
+ dnsService = (DNSService) comp.lookup(DNSService.ROLE);
+ fSystem= (FileSystem) comp.lookup(FileSystem.ROLE);
+ setProtocolHandlerFactory((ProtocolHandlerFactory) comp.lookup(ProtocolHandlerFactory.ROLE));
+ }
+
+ /**
+ * Setter for the ProtocolHandlerFactory factory
+ * @param protocolHandlerFactory the factory
+ */
+ public void setProtocolHandlerFactory(ProtocolHandlerFactory protocolHandlerFactory) {
+ this.protocolHandlerFactory = protocolHandlerFactory;
+ }
+
+ /**
+ * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
+ */
+ public void configure(Configuration conf) throws ConfigurationException {
+ enabled = conf.getAttributeAsBoolean("enabled", true);
+ final Logger logger = getLogger();
+ if (!enabled) {
+ logger.info(protocolHandlerFactory.getServiceType() + " disabled by configuration");
+ return;
+ }
+
+ Configuration handlerConfiguration = conf.getChild("handler");
+
+ // Send the handler subconfiguration to the super class. This
+ // ensures that the handler config is passed to the handlers.
+ //
+ // TODO: This should be rationalized. The handler element of the
+ // server configuration doesn't really make a whole lot of
+ // sense. We should modify the config to get rid of it.
+ // Keeping it for now to maintain backwards compatibility.
+ super.configure(handlerConfiguration);
+
+
+ boolean streamdump=handlerConfiguration.getChild("streamdump").getAttributeAsBoolean("enabled", false);
+ String streamdumpDir=streamdump ? handlerConfiguration.getChild("streamdump").getAttribute("directory", null) : null;
+ setStreamDumpDir(streamdumpDir);
+
+
+ port = conf.getChild("port").getValueAsInteger(protocolHandlerFactory.getDefaultPort());
+
+ Configuration serverSocketTypeConf = conf.getChild("serverSocketType", false);
+ String confSocketType = null;
+ if (serverSocketTypeConf != null ) {
+ confSocketType = serverSocketTypeConf.getValue();
+ }
+
+ if (confSocketType == null) {
+ // Only load the useTLS parameter if a specific socket type has not
+ // been specified. This maintains backwards compatibility while
+ // allowing us to have more complex (i.e. multiple SSL configuration)
+ // deployments
+ final boolean useTLS = conf.getChild("useTLS").getValueAsBoolean(isDefaultTLSEnabled());
+ if (useTLS) {
+ serverSocketType = "ssl";
+ loadJCEProviders(conf, logger);
+ }
+ } else {
+ serverSocketType = confSocketType;
+ }
+
+
+ StringBuilder infoBuffer;
+ threadGroup = conf.getChild("threadGroup").getValue(null);
+ if (threadGroup != null) {
+ infoBuffer =
+ new StringBuilder(64)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" uses thread group: ")
+ .append(threadGroup);
+ logger.info(infoBuffer.toString());
+ }
+ else {
+ logger.info(protocolHandlerFactory.getServiceType() + " uses default thread group.");
+ }
+
+ try {
+ final String bindAddress = conf.getChild("bind").getValue(null);
+ if( null != bindAddress ) {
+ bindTo = InetAddress.getByName(bindAddress);
+ infoBuffer =
+ new StringBuilder(64)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" bound to: ")
+ .append(bindTo);
+ logger.info(infoBuffer.toString());
+ }
+ }
+ catch( final UnknownHostException unhe ) {
+ throw new ConfigurationException( "Malformed bind parameter in configuration of service " + protocolHandlerFactory.getServiceType(), unhe );
+ }
+
+ configureHelloName(handlerConfiguration);
+
+ timeout = handlerConfiguration.getChild(TIMEOUT_NAME).getValueAsInteger(DEFAULT_TIMEOUT);
+
+ infoBuffer =
+ new StringBuilder(64)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" handler connection timeout is: ")
+ .append(timeout);
+ logger.info(infoBuffer.toString());
+
+ backlog = conf.getChild(BACKLOG_NAME).getValueAsInteger(DEFAULT_BACKLOG);
+
+ infoBuffer =
+ new StringBuilder(64)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" connection backlog is: ")
+ .append(backlog);
+ logger.info(infoBuffer.toString());
+
+ String connectionLimitString = conf.getChild("connectionLimit").getValue(null);
+ if (connectionLimitString != null) {
+ try {
+ connectionLimit = new Integer(connectionLimitString);
+ } catch (NumberFormatException nfe) {
+ logger.error("Connection limit value is not properly formatted.", nfe);
+ }
+ if (connectionLimit.intValue() < 0) {
+ logger.error("Connection limit value cannot be less than zero.");
+ throw new ConfigurationException("Connection limit value cannot be less than zero.");
+ }
+ } else {
+ connectionLimit = new Integer(connectionManager.getMaximumNumberOfOpenConnections());
+ }
+ infoBuffer = new StringBuilder(128)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" will allow a maximum of ")
+ .append(connectionLimit.intValue())
+ .append(" connections.");
+ logger.info(infoBuffer.toString());
+
+ String connectionLimitPerIP = conf.getChild("connectionLimitPerIP").getValue(null);
+ if (connectionLimitPerIP != null) {
+ try {
+ connPerIP = new Integer(connectionLimitPerIP).intValue();
+ connPerIPConfigured = true;
+ } catch (NumberFormatException nfe) {
+ logger.error("Connection limit per IP value is not properly formatted.", nfe);
+ }
+ if (connPerIP < 0) {
+ logger.error("Connection limit per IP value cannot be less than zero.");
+ throw new ConfigurationException("Connection limit value cannot be less than zero.");
+ }
+ } else {
+ connPerIP = connectionManager.getMaximumNumberOfOpenConnectionsPerIP();
+ }
+ infoBuffer = new StringBuilder(128)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" will allow a maximum of ")
+ .append(connPerIP)
+ .append(" per IP connections for " +protocolHandlerFactory.getServiceType());
+ logger.info(infoBuffer.toString());
+
+ Configuration tlsConfig = conf.getChild("startTLS");
+ if (tlsConfig != null) {
+ useStartTLS = tlsConfig.getAttributeAsBoolean("enable", false);
+
+ if (useStartTLS) {
+ keystore = tlsConfig.getChild("keystore").getValue(null);
+ if (keystore == null) {
+ throw new ConfigurationException("keystore needs to get configured");
+ }
+ secret = tlsConfig.getChild("secret").getValue("");
+ loadJCEProviders(tlsConfig, getLogger());
+ }
+ }
+ }
+
+ private void loadJCEProviders(Configuration conf, final Logger logger) throws ConfigurationException {
+ final Configuration [] providerConfiguration = conf.getChildren("provider");
+ for (int i = 0; i < providerConfiguration.length; i++) {
+ final String providerName = providerConfiguration[i].getValue();
+ loadProvider(logger, providerName);
+ }
+ }
+
+ private void loadProvider(final Logger logger, final String providerName) {
+ if (providerName == null) {
+ logger.warn("Failed to specify provider. Continuing but JCE provider will not be loaded");
+ } else {
+ try {
+ logger.debug("Trying to load JCE provider '" + providerName + "'");
+ Security.addProvider((Provider) Class.forName(providerName).newInstance());
+ logger.info("Load JCE provider '" + providerName + "'");
+ } catch (IllegalAccessException e) {
+ logJCELoadFailure(logger, providerName, e);
+ } catch (InstantiationException e) {
+ logJCELoadFailure(logger, providerName, e);
+ } catch (ClassNotFoundException e) {
+ logJCELoadFailure(logger, providerName, e);
+ } catch (RuntimeException e) {
+ logJCELoadFailure(logger, providerName, e);
+ }
+ }
+ }
+
+ private void logJCELoadFailure(final Logger logger, final String providerName, Exception e) {
+ logger.warn("Cannot load JCE provider" + providerName);
+ logger.debug(e.getMessage(), e);
+ }
+
+ private void setStreamDumpDir(String streamdumpDir) {
+ this.streamDumpDir = streamdumpDir;
+ }
+
+ private void configureHelloName(Configuration handlerConfiguration) {
+ StringBuilder infoBuffer;
+ String hostName = null;
+ try {
+ hostName = dnsService.getHostName(dnsService.getLocalHost());
+ } catch (UnknownHostException ue) {
+ hostName = "localhost";
+ }
+
+ infoBuffer =
+ new StringBuilder(64)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" is running on: ")
+ .append(hostName);
+ getLogger().info(infoBuffer.toString());
+
+ Configuration helloConf = handlerConfiguration.getChild(HELLO_NAME);
+
+ if (helloConf != null) {
+ boolean autodetect = helloConf.getAttributeAsBoolean("autodetect", true);
+ if (autodetect) {
+ helloName = hostName;
+ } else {
+ // Should we use the defaultdomain here ?
+ helloName = helloConf.getValue("localhost");
+ }
+ } else {
+ helloName = null;
+ }
+ infoBuffer =
+ new StringBuilder(64)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" handler hello name is: ")
+ .append(helloName);
+ getLogger().info(infoBuffer.toString());
+ }
+
+ /**
+ * @see org.apache.avalon.framework.activity.Initializable#initialize()
+ */
+ @PostConstruct
+ public final void initialize() throws Exception {
+ if (!isEnabled()) {
+ getLogger().info(protocolHandlerFactory.getServiceType() + " Disabled");
+ System.out.println(protocolHandlerFactory.getServiceType() + " Disabled");
+ return;
+ }
+
+ getLogger().debug(protocolHandlerFactory.getServiceType() + " init...");
+
+ protocolHandlerFactory.prepare(this);
+
+ if (useStartTLS) {
+ initStartTLS();
+ }
+
+ // keeping these looked up services locally, because they are only needed beyond initialization
+ ThreadManager threadManager = (ThreadManager) componentManager.lookup(ThreadManager.ROLE);
+ SocketManager socketManager = (SocketManager) componentManager.lookup(SocketManager.ROLE);
+
+ initializeThreadPool(threadManager);
+
+ initializeServerSocket(socketManager);
+
+ getLogger().debug(protocolHandlerFactory.getServiceType() + " ...init end");
+
+ initializeHandlerPool();
+
+ // do avalon specific preparations
+ ContainerUtil.enableLogging(theHandlerPool, getLogger());
+ ContainerUtil.initialize(theHandlerPool);
+
+ theWatchdogFactory = getWatchdogFactory();
+
+ // Allow subclasses to perform initialisation
+ protocolHandlerFactory.initialize();
+ }
+
+ private void initStartTLS() throws Exception {
+ KeyStore ks = null;
+ KeyManagerFactory kmf = null;
+ SSLContext sslcontext = null;
+
+ // This loads the key material, and initialises the
+ // SSLSocketFactory
+ // This should be done once!!
+ // Note: in order to load SunJCE provider the jre/lib/ext should be
+ // added
+ // to the java.ext.dirs see the note in run.sh script
+ try {
+ // just to see SunJCE is loaded
+ Provider[] provs = Security.getProviders();
+ for (int i = 0; i < provs.length; i++)
+ getLogger().debug("Provider[" + i + "]=" + provs[i].getName());
+
+ char[] passphrase = secret.toCharArray();
+ ks = KeyStore.getInstance("JKS","SUN");
+ ks.load(fSystem.getResource(keystore), passphrase);
+ kmf = KeyManagerFactory.getInstance("SunX509", "SunJSSE");
+ kmf.init(ks, passphrase);
+ sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
+ sslcontext.init(kmf.getKeyManagers(), null, null);
+ } catch (Exception e) {
+ getLogger().error("Exception accessing keystore: " + e);
+ throw e;
+ }
+ factory = sslcontext.getSocketFactory();
+ // just to see the list of supported ciphers
+ String[] ss = factory.getSupportedCipherSuites();
+ getLogger().debug("list of supported ciphers");
+ for (int i = 0; i < ss.length; i++)
+ getLogger().debug(ss[i]);
+ }
+
+ private void initializeThreadPool(ThreadManager threadManager) {
+ if (threadGroup != null) {
+ threadPool = threadManager.getThreadPool(threadGroup);
+ } else {
+ threadPool = threadManager.getDefaultThreadPool();
+ }
+ }
+
+ private void initializeServerSocket(SocketManager socketManager) throws Exception {
+ try {
+ initializeServerSocketWorker(socketManager);
+ } catch (BindException e) {
+ // handle a common exception and give detailed error message
+ String errorMessage = getBindingErrorMessage(e);
+ System.out.println("------------------------------");
+ System.out.println(errorMessage);
+ System.out.println("------------------------------");
+ getLogger().fatalError(errorMessage);
+ throw e;
+ }
+ }
+
+ private String getBindingErrorMessage(BindException e) {
+ // general info about binding error
+ StringBuilder errorMessage = new StringBuilder();
+ errorMessage.append("FATAL ERROR when starting service '").append(protocolHandlerFactory.getServiceType()).append("'! ");
+ errorMessage.append("could not bind to ");
+ errorMessage.append(bindTo == null ? "0.0.0.0" : bindTo.toString());
+ errorMessage.append(":").append(port).append(". ");
+
+ // try to deliver more specific information
+ if (e.getMessage().indexOf("Address already in use") != -1) {
+ errorMessage.append("Port is already exclusively in use by another application.");
+ } else if (e.getMessage().indexOf("Permission denied") != -1) {
+ errorMessage.append("The user account James is running under has not enough privileges to bind to this ");
+ if (port < 1024) errorMessage.append("privileged ");
+ errorMessage.append("port.");
+ } else {
+ errorMessage.append(e.getMessage());
+ }
+ return errorMessage.toString();
+ }
+
+ private void initializeServerSocketWorker(SocketManager socketManager) throws Exception {
+ ServerSocketFactory factory = socketManager.getServerSocketFactory(serverSocketType);
+ ServerSocket serverSocket = factory.createServerSocket(port, backlog, bindTo);
+
+ if (null == connectionName) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(serverSocketType);
+ sb.append(':');
+ sb.append(port);
+
+ if (null != bindTo) {
+ sb.append('/');
+ sb.append(bindTo);
+ }
+ connectionName = sb.toString();
+ }
+
+ if ((connectionLimit != null)) {
+ if (null != threadPool) {
+ if (connPerIPConfigured) {
+ connectionManager.connect(connectionName, serverSocket, this, threadPool, connectionLimit.intValue(),connPerIP);
+ } else {
+ connectionManager.connect(connectionName, serverSocket, this, threadPool, connectionLimit.intValue());
+ }
+ } else {
+ if (connPerIPConfigured) {
+ connectionManager.connect(connectionName, serverSocket, this, connectionLimit.intValue(),connPerIP); // default pool
+ } else {
+ connectionManager.connect(connectionName, serverSocket, this, connectionLimit.intValue());
+ }
+ }
+ } else {
+ if (null != threadPool) {
+ if (connPerIPConfigured) {
+ connectionManager.connect(connectionName, serverSocket, this, threadPool);
+ } else {
+ connectionManager.connect(connectionName, serverSocket, this, threadPool, 0, connPerIP);
+ }
+ } else {
+ if (connPerIPConfigured) {
+ connectionManager.connect(connectionName, serverSocket, this); // default pool
+ } else {
+ connectionManager.connect(connectionName, serverSocket, this, 0, connPerIP);
+ }
+ }
+ }
+ }
+
+ private void initializeHandlerPool() throws Exception {
+ StringBuilder logBuffer =
+ new StringBuilder(64)
+ .append(protocolHandlerFactory.getServiceType())
+ .append(" started ")
+ .append(connectionName);
+ String logString = logBuffer.toString();
+ System.out.println(logString);
+ getLogger().info(logString);
+
+ if (connectionLimit != null) {
+ theHandlerPool = new HardResourceLimitingPool(this, 5, connectionLimit.intValue());
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Using a bounded pool for "+protocolHandlerFactory.getServiceType()+" handlers with upper limit " + connectionLimit.intValue());
+ }
+ } else {
+ // NOTE: The maximum here is not a real maximum. The handler pool will continue to
+ // provide handlers beyond this value.
+ theHandlerPool = new DefaultPool(this, null, 5, 30);
+ getLogger().debug("Using an unbounded pool for "+protocolHandlerFactory.getServiceType()+" handlers.");
+ }
+ }
+
+ /**
+ * @see org.apache.avalon.framework.activity.Disposable#dispose()
+ */
+ public void dispose() {
+
+ if (!isEnabled()) {
+ return;
+ }
+
+ if( m_disposed )
+ {
+ if( getLogger().isWarnEnabled() )
+ {
+ getLogger().warn( "ignoring disposal request - already disposed" );
+ }
+ return;
+ }
+
+ if( getLogger().isDebugEnabled() )
+ {
+ getLogger().debug( "disposal" );
+ }
+
+ m_disposed = true;
+ if( getLogger().isDebugEnabled() )
+ {
+ StringBuilder infoBuffer =
+ new StringBuilder(64).append(protocolHandlerFactory.getServiceType()).append(
+ " dispose... ").append(connectionName);
+ getLogger().debug(infoBuffer.toString());
+ }
+
+ try {
+ connectionManager.disconnect(connectionName, true);
+ } catch (final Exception e) {
+ StringBuilder warnBuffer =
+ new StringBuilder(64)
+ .append("Error disconnecting ")
+ .append(protocolHandlerFactory.getServiceType())
+ .append(": ");
+ getLogger().warn(warnBuffer.toString(), e);
+ }
+
+ componentManager = null;
+
+ connectionManager = null;
+ threadPool = null;
+
+ // This is needed to make sure sockets are promptly closed on Windows 2000
+ // TODO: Check this - shouldn't need to explicitly gc to force socket closure
+ System.gc();
+
+ getLogger().debug(protocolHandlerFactory.getServiceType() + " ...dispose end");
+ }
+
+ /**
+ * This constructs the WatchdogFactory that will be used to guard
+ * against runaway or stuck behavior. Should only be called once
+ * by a subclass in its initialize() method.
+ *
+ * @return the WatchdogFactory to be employed by subclasses.
+ */
+ private WatchdogFactory getWatchdogFactory() {
+ WatchdogFactory theWatchdogFactory = null;
+ theWatchdogFactory = new ThreadPerWatchdogFactory(threadPool, timeout);
+ ContainerUtil.enableLogging(theWatchdogFactory,getLogger());
+ return theWatchdogFactory;
+ }
+
+
+ /**
+ * Describes whether this service is enabled by configuration.
+ *
+ * @return is the service enabled.
+ */
+ public final boolean isEnabled() {
+ return enabled;
+ }
+ /**
+ * Override this method to create actual instance of connection handler.
+ *
+ * @return the new ConnectionHandler
+ * @exception Exception if an error occurs
+ */
+ protected ConnectionHandler newHandler()
+ throws Exception {
+ JamesConnectionBridge theHandler = (JamesConnectionBridge)theHandlerPool.get();
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Handler [" + theHandler + "] obtained from pool.");
+ }
+
+ Watchdog theWatchdog = theWatchdogFactory.getWatchdog(theHandler);
+
+ theHandler.setStreamDumpDir(streamDumpDir);
+ theHandler.setWatchdog(theWatchdog);
+ return theHandler;
+ }
+
+ /**
+ * @see org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory#releaseConnectionHandler(ConnectionHandler)
+ */
+ public void releaseConnectionHandler( ConnectionHandler connectionHandler ) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Returning Handler [" + connectionHandler + "] to pool.");
+ }
+ theHandlerPool.put((Poolable)connectionHandler);
+ }
+
+ /**
+ * Get whether TLS is enabled for this server's socket by default.
+ *
+ * @return the default port
+ */
+ protected boolean isDefaultTLSEnabled() {
+ return false;
+ }
+
+ /**
+ * Returns the port that the service is bound to
+ *
+ * @return int The port number
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Returns the address if the network interface the socket is bound to
+ *
+ * @return String The network interface name
+ */
+ public String getNetworkInterface() {
+ if (bindTo == null) {
+ return "All";
+ } else {
+ return bindTo.getHostAddress();
+ }
+ }
+
+ /**
+ * Returns the server socket type, plain or SSL
+ *
+ * @return String The socket type, plain or SSL
+ */
+ public String getSocketType() {
+ return serverSocketType;
+ }
+
+ /**
+ * @see org.apache.avalon.excalibur.pool.ObjectFactory#decommission(Object)
+ */
+ public void decommission( Object object ) throws Exception {
+ return;
+ }
+
+ /**
+ * @see org.apache.avalon.cornerstone.services.connection.AbstractHandlerFactory#createConnectionHandler()
+ */
+ public ConnectionHandler createConnectionHandler() throws Exception {
+ ConnectionHandler conn = super.createConnectionHandler();
+ ContainerUtil.service(conn, componentManager);
+ return conn;
+ }
+
+ /**
+ * @see org.apache.avalon.excalibur.pool.ObjectFactory#newInstance()
+ */
+ public Object newInstance() throws Exception {
+ final String serviceShortNameString;
+ final String serviceType = protocolHandlerFactory.getServiceType();
+ final int firstSpace = serviceType.indexOf(' ');
+ if (firstSpace > 0) {
+ serviceShortNameString = serviceType.substring(0, firstSpace);
+ } else {
+ serviceShortNameString = serviceType;
+ }
+ final String name = serviceShortNameString + "Handler-" + handlerCount.getAndAdd(1);
+ final JamesConnectionBridge delegatingJamesHandler;
+
+ if (useStartTLS) {
+ delegatingJamesHandler = new JamesConnectionBridge(protocolHandlerFactory.newProtocolHandlerInstance(), dnsService, name, getLogger(), factory);
+ } else {
+ delegatingJamesHandler = new JamesConnectionBridge(protocolHandlerFactory.newProtocolHandlerInstance(), dnsService, name, getLogger());
+ }
+ return delegatingJamesHandler;
+
+ }
+
+ /**
+ * @see org.apache.avalon.excalibur.pool.ObjectFactory#getCreatedClass()
+ */
+ @SuppressWarnings("unchecked")
+ public Class getCreatedClass() {
+ return JamesConnectionBridge.class;
+ }
+
+
+ public boolean useStartTLS() {
+ return useStartTLS;
+ }
+
+ public String getHelloName() {
+ return helloName;
+ }
+
+}
+
Propchange: james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AvalonProtocolServer.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: james/server/trunk/avalon-socket-library/src/main/java/org/apache/james/socket/AvalonProtocolServer.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolHandlerFactory.java
URL: http://svn.apache.org/viewvc/james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolHandlerFactory.java?rev=825089&view=auto
==============================================================================
--- james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolHandlerFactory.java (added)
+++ james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolHandlerFactory.java Wed Oct 14 11:38:14 2009
@@ -0,0 +1,74 @@
+/****************************************************************
+ * 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.socket.shared;
+
+/**
+ * An interface each protocol factory will expose to socket services.
+ */
+public interface ProtocolHandlerFactory {
+
+ public final static String ROLE = "org.apache.james.socket.shared.ProtocolHandlerFactory";
+
+ /**
+ * Get the default port for this server type.
+ *
+ * It is strongly recommended that subclasses of this class override this
+ * method to specify the default port for their specific server type.
+ *
+ * @return the default port
+ */
+ int getDefaultPort();
+
+ /**
+ * This method returns the type of service provided by this server. This
+ * should be invariant over the life of the class.
+ *
+ * Subclasses may override this implementation. This implementation parses
+ * the complete class name and returns the undecorated class name.
+ *
+ * @return description of this server
+ */
+ String getServiceType();
+
+ /**
+ * This is the factory method to obtain a new instance of a ProtocolHandler.
+ *
+ * @return a new ProtocolHandler instance
+ */
+ ProtocolHandler newProtocolHandlerInstance();
+
+ /**
+ * Hook for protocol factories to perform an required initialisation before
+ * the socket handler has been initialised. Called before the socket handler
+ * has completed it's initialisation.
+ *
+ * @throws Exception
+ */
+ void prepare(ProtocolServer server) throws Exception;
+
+ /**
+ * Hook for protocol factories to perform the initialisation after the
+ * socket handler has been initialized TODO maybe this is not required
+ *
+ * @throws Exception
+ */
+ void initialize() throws Exception;
+
+}
\ No newline at end of file
Propchange: james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolHandlerFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolHandlerFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolServer.java
URL: http://svn.apache.org/viewvc/james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolServer.java?rev=825089&view=auto
==============================================================================
--- james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolServer.java (added)
+++ james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolServer.java Wed Oct 14 11:38:14 2009
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.socket.shared;
+
+/**
+ * Each socket provider implements this interface.
+ * A ProtocolHandlerFactory can retrieve transport informations
+ * using this interface
+ */
+public interface ProtocolServer {
+
+ boolean isEnabled();
+
+ String getHelloName();
+
+ boolean useStartTLS();
+
+}
Propchange: james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolServer.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: james/server/trunk/socket-shared-library/src/main/java/org/apache/james/socket/shared/ProtocolServer.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org