FileDocCategorySizeDatePackage
AbstractJamesService.javaAPI DocApache James 2.3.118838Fri Jan 12 12:56:22 GMT 2007org.apache.james.core

AbstractJamesService

public abstract class AbstractJamesService extends org.apache.avalon.cornerstone.services.connection.AbstractHandlerFactory implements org.apache.avalon.framework.activity.Disposable, org.apache.avalon.framework.service.Serviceable, org.apache.avalon.framework.activity.Initializable, org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory, org.apache.avalon.framework.configuration.Configurable
Server which creates connection handlers. All new James service must inherit from this abstract implementation.

Fields Summary
protected static final int
DEFAULT_TIMEOUT
The default value for the connection timeout.
protected static final String
TIMEOUT_NAME
The name of the parameter defining the connection timeout.
protected static final int
DEFAULT_BACKLOG
The default value for the connection backlog.
protected static final String
BACKLOG_NAME
The name of the parameter defining the connection backlog.
public static final String
HELLO_NAME
The name of the parameter defining the service hello name.
private org.apache.james.services.JamesConnectionManager
connectionManager
The ConnectionManager that spawns and manages service connections.
protected String
threadGroup
The name of the thread group to be used by this service for generating connections
protected org.apache.excalibur.thread.ThreadPool
threadPool
The thread pool used by this service that holds the threads that service the client connections.
protected String
serverSocketType
The server socket type used to generate connections for this server.
protected int
port
The port on which this service will be made available.
protected InetAddress
bindTo
Network interface to which the service will bind. If not set, the server binds to all available interfaces.
protected ServerSocket
serverSocket
protected String
connectionName
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.
protected Integer
connectionLimit
The maximum number of connections allowed for this service.
protected int
timeout
The connection idle timeout. Used primarily to prevent server problems from hanging a connection.
protected int
backlog
The connection backlog.
protected String
helloName
The hello name for the service.
private org.apache.avalon.framework.service.ServiceManager
compMgr
The component manager used by this service.
private volatile boolean
enabled
Whether this service is enabled.
private boolean
m_disposed
Flag holding the disposed state of the component.
Constructors Summary
Methods Summary
public voidconfigure(org.apache.avalon.framework.configuration.Configuration conf)

see
org.apache.avalon.framework.configuration.Configurable#configure(Configuration)

        enabled = conf.getAttributeAsBoolean("enabled", true);
        if (!enabled) {
          getLogger().info(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);

        port = conf.getChild("port").getValueAsInteger(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";
            }
        } else {
            serverSocketType = confSocketType;
        }

        StringBuffer infoBuffer;
        threadGroup = conf.getChild("threadGroup").getValue(null);
        if (threadGroup != null) {
            infoBuffer =
                new StringBuffer(64)
                        .append(getServiceType())
                        .append(" uses thread group: ")
                        .append(threadGroup);
            getLogger().info(infoBuffer.toString());
        }
        else {
            getLogger().info(getServiceType() + " uses default thread group.");
        }

        try {
            final String bindAddress = conf.getChild("bind").getValue(null);
            if( null != bindAddress ) {
                bindTo = InetAddress.getByName(bindAddress);
                infoBuffer =
                    new StringBuffer(64)
                            .append(getServiceType())
                            .append(" bound to: ")
                            .append(bindTo);
                getLogger().info(infoBuffer.toString());
            }
        }
        catch( final UnknownHostException unhe ) {
            throw new ConfigurationException( "Malformed bind parameter in configuration of service " + getServiceType(), unhe );
        }

        String hostName = null;
        try {
            hostName = InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException ue) {
            hostName = "localhost";
        }

        infoBuffer =
            new StringBuffer(64)
                    .append(getServiceType())
                    .append(" is running on: ")
                    .append(hostName);
        getLogger().info(infoBuffer.toString());

        Configuration helloConf = handlerConfiguration.getChild(HELLO_NAME);
        boolean autodetect = helloConf.getAttributeAsBoolean("autodetect", true);
        if (autodetect) {
            helloName = hostName;
        } else {
            helloName = helloConf.getValue("localhost");
        }
        infoBuffer =
            new StringBuffer(64)
                    .append(getServiceType())
                    .append(" handler hello name is: ")
                    .append(helloName);
        getLogger().info(infoBuffer.toString());

        timeout = handlerConfiguration.getChild(TIMEOUT_NAME).getValueAsInteger(DEFAULT_TIMEOUT);

        infoBuffer =
            new StringBuffer(64)
                    .append(getServiceType())
                    .append(" handler connection timeout is: ")
                    .append(timeout);
        getLogger().info(infoBuffer.toString());

        backlog = conf.getChild(BACKLOG_NAME).getValueAsInteger(DEFAULT_BACKLOG);

        infoBuffer =
                    new StringBuffer(64)
                    .append(getServiceType())
                    .append(" connection backlog is: ")
                    .append(backlog);
        getLogger().info(infoBuffer.toString());

        if (connectionManager instanceof JamesConnectionManager) {
            String connectionLimitString = conf.getChild("connectionLimit").getValue(null);
            if (connectionLimitString != null) {
                try {
                    connectionLimit = new Integer(connectionLimitString);
                } catch (NumberFormatException nfe) {
                    getLogger().error("Connection limit value is not properly formatted.", nfe);
                }
                if (connectionLimit.intValue() < 0) {
                    getLogger().error("Connection limit value cannot be less than zero.");
                    throw new ConfigurationException("Connection limit value cannot be less than zero.");
                }
            } else {
                connectionLimit = new Integer(((JamesConnectionManager)connectionManager).getMaximumNumberOfOpenConnections());
            }
            infoBuffer = new StringBuffer(128)
                .append(getServiceType())
                .append(" will allow a maximum of ")
                .append(connectionLimit.intValue())
                .append(" connections.");
            getLogger().info(infoBuffer.toString());
        }
    
public voiddispose()

see
org.apache.avalon.framework.activity.Disposable#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() )
        {
            StringBuffer infoBuffer =
               new StringBuffer(64).append(getServiceType()).append(
                   " dispose... ").append(connectionName);
            getLogger().debug(infoBuffer.toString());
        }

        try {
            connectionManager.disconnect(connectionName, true);
        } catch (final Exception e) {
            StringBuffer warnBuffer =
                new StringBuffer(64)
                        .append("Error disconnecting ")
                        .append(getServiceType())
                        .append(": ");
            getLogger().warn(warnBuffer.toString(), e);
        }

        compMgr = 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(getServiceType() + " ...dispose end");
    
protected intgetDefaultPort()
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

        return 0;
     
public java.lang.StringgetNetworkInterface()
Returns the address if the network interface the socket is bound to

return
String The network interface name

        if (bindTo == null) {
            return "All";
        } else {
            return bindTo.getHostAddress();
        }
    
public intgetPort()
Returns the port that the service is bound to

return
int The port number

        return port;
    
public java.lang.StringgetServiceType()
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 name = getClass().getName();
        int p = name.lastIndexOf(".");
        if (p > 0 && p < name.length() - 2) {
            name = name.substring(p + 1);
        }
        return name;
    
public java.lang.StringgetSocketType()
Returns the server socket type, plain or SSL

return
String The scoekt type, plain or SSL

        return serverSocketType;
    
protected org.apache.james.util.watchdog.WatchdogFactorygetWatchdogFactory()
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.

        WatchdogFactory theWatchdogFactory = null;
        theWatchdogFactory = new ThreadPerWatchdogFactory(threadPool, timeout);
        if (theWatchdogFactory instanceof LogEnabled) {
            ((LogEnabled)theWatchdogFactory).enableLogging(getLogger());
        }
        return theWatchdogFactory;
     
public voidinitialize()

see
org.apache.avalon.framework.activity.Initializable#initialize()

        if (!isEnabled()) {
            getLogger().info(getServiceType() + " Disabled");
            System.out.println(getServiceType() + " Disabled");
            return;
        }
        getLogger().debug(getServiceType() + " init...");

        SocketManager socketManager = (SocketManager) compMgr.lookup(SocketManager.ROLE);

        ThreadManager threadManager = (ThreadManager) compMgr.lookup(ThreadManager.ROLE);

        if (threadGroup != null) {
            threadPool = threadManager.getThreadPool(threadGroup);
        } else {
            threadPool = threadManager.getDefaultThreadPool();
        }

        ServerSocketFactory factory = socketManager.getServerSocketFactory(serverSocketType);
        ServerSocket serverSocket = factory.createServerSocket(port, backlog, bindTo);
    
        if (null == connectionName) {
            final StringBuffer sb = new StringBuffer();
            sb.append(serverSocketType);
            sb.append(':");
            sb.append(port);
    
            if (null != bindTo) {
                sb.append('/");
                sb.append(bindTo);
            }
            connectionName = sb.toString();
        }

        if ((connectionLimit != null) &&
            (connectionManager instanceof JamesConnectionManager)) {
            if (null != threadPool) {
                ((JamesConnectionManager)connectionManager).connect(connectionName, serverSocket, this, threadPool, connectionLimit.intValue());
            }
            else {
                ((JamesConnectionManager)connectionManager).connect(connectionName, serverSocket, this, connectionLimit.intValue()); // default pool
            }
        } else {
            if (null != threadPool) {
                connectionManager.connect(connectionName, serverSocket, this, threadPool);
            }
            else {
                connectionManager.connect(connectionName, serverSocket, this); // default pool
            }
        }

        getLogger().debug(getServiceType() + " ...init end");

        StringBuffer logBuffer =
            new StringBuffer(64)
                .append(getServiceType())
                .append(" started ")
                .append(connectionName);
        String logString = logBuffer.toString();
        System.out.println(logString);
        getLogger().info(logString);
    
protected booleanisDefaultTLSEnabled()
Get whether TLS is enabled for this server's socket by default.

return
the default port

        return false;
     
public final booleanisEnabled()
Describes whether this service is enabled by configuration.

return
is the service enabled.

        return enabled;
    
protected abstract org.apache.avalon.cornerstone.services.connection.ConnectionHandlernewHandler()
Overide this method to create actual instance of connection handler.

return
the new ConnectionHandler
exception
Exception if an error occurs

public voidservice(org.apache.avalon.framework.service.ServiceManager comp)

see
org.apache.avalon.framework.service.Serviceable#service(ServiceManager)


           
          
        super.service( comp );
        compMgr               = comp;
        connectionManager =
            (JamesConnectionManager)compMgr.lookup(JamesConnectionManager.ROLE);