AbstractAsyncServer.java :  » Net » james-3.0.0 » org » apache » james » socket » mina » Java Open Source

Java Open Source » Net » james 3.0.0 
james 3.0.0 » org » apache » james » socket » mina » AbstractAsyncServer.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.socket.mina;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;

import javax.annotation.Resource;

import org.apache.avalon.framework.activity.Initializable;
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.logger.LogEnabled;
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.commons.logging.Log;
import org.apache.commons.logging.impl.AvalonLogger;
import org.apache.james.api.dnsservice.DNSService;
import org.apache.james.api.kernel.LoaderService;
import org.apache.james.services.FileSystem;
import org.apache.james.services.MailServer;
import org.apache.james.socket.mina.filter.ConnectionFilter;
import org.apache.mailet.MailetContext;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.ssl.BogusTrustManagerFactory;
import org.apache.mina.filter.ssl.KeyStoreFactory;
import org.apache.mina.filter.ssl.SslContextFactory;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

/**
 * Abstract base class for Servers which want to use async io
 *
 */
public abstract class AbstractAsyncServer implements LogEnabled, Initializable, Serviceable, Configurable{
    /**
     * The default value for the connection backlog.
     */
    private static final int DEFAULT_BACKLOG = 5;
    
    /**
     * 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 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.
     */
    public static final String HELLO_NAME = "helloName";
    
    
    /**
     * The mailet context - we access it here to set the hello name for the Mailet API
     */
    private MailetContext mailetcontext;

    private FileSystem fileSystem;
    
    /**
     * The internal mail server service.
     */
    private MailServer mailServer;

    /** Loads instances */
    private LoaderService loader;

    private Logger logger;

    private DNSService dns;

    private boolean enabled;

    private int connPerIP;

    private boolean useStartTLS;

    private int connectionLimit;

    private String helloName;
    
    private String keystore;

    private String secret;
    
    private int backlog;
    
    private InetAddress bindTo;

    private int port;

    private int timeout;

    private SslContextFactory contextFactory;;

    /**
     * Gets the current instance loader.
     * @return the loader
     */
    public final LoaderService getLoader() {
        return loader;
    }

    /**
     * Sets the loader to be used for instances.
     * @param loader the loader to set, not null
     */
    @Resource(name="org.apache.james.LoaderService")
    public final void setLoader(LoaderService loader) {
        this.loader = loader;
    }
    
    public void setDNSService(DNSService dns) {
        this.dns = dns;
    }
    
    public void setFileSystem(FileSystem filesystem) {
        this.fileSystem = filesystem;
    }
    
    public void setMailServer(MailServer mailServer) {
        this.mailServer = mailServer;
    }
    
    public void setMailetContext(MailetContext mailetcontext) {
        this.mailetcontext = mailetcontext;
    }

    
    /**
     * @see org.apache.avalon.framework.logger.LogEnabled#enableLogging(org.apache.avalon.framework.logger.Logger)
     */
    public void enableLogging(Logger logger) {
       this.logger = logger;
    }

    /**
     * @see org.apache.avalon.framework.activity.Initializable#initialize()
     */
    public void initialize() throws Exception {
        preInit();
        buildSSLContextFactory();
        SocketAcceptor acceptor = new NioSocketAcceptor();  
        acceptor.setFilterChainBuilder(createIoFilterChainBuilder());
        acceptor.setBacklog(backlog);
        acceptor.setReuseAddress(true);
        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, timeout );
        acceptor.setHandler(createIoHandler());
        acceptor.bind(new InetSocketAddress(bindTo,port));
    }

    /**
     * This method is called on init of the Server. Subclasses should override this method to init stuff
     *
     * @throws Exception 
     */
    protected void preInit() throws Exception {
        // override me
    }

    /**
     * Return the DNSService
     * 
     * @return dns
     */
    protected DNSService getDNSService() {
        return dns;
    }
    
    /**
     * Return the MailServer
     * 
     * @return mailServer
     */
    protected MailServer getMailServer() {
        return mailServer;
    }
    
    /**
     * Return the MailetContext
     * 
     * @return mailetContext
     */
    protected MailetContext getMailetContext() {
        return mailetcontext;
    }
    
    /**
     * Return the FileSystem
     * 
     * @return fileSystem
     */
    protected FileSystem getFileSystem() {
        return fileSystem;
    }
    
    /**
     * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
     */
    public void service( final ServiceManager manager ) throws ServiceException {
        setMailetContext((MailetContext) manager.lookup("org.apache.mailet.MailetContext"));
        setMailServer((MailServer) manager.lookup(MailServer.ROLE));
        setDNSService((DNSService) manager.lookup(DNSService.ROLE));
        setFileSystem((FileSystem) manager.lookup(FileSystem.ROLE));
    }

    /**
     * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
     */
    public void configure(Configuration configuration) throws ConfigurationException {
        enabled = configuration.getAttributeAsBoolean("enabled", true);
        final Logger logger = getLogger();
        if (!enabled) {
          logger.info(getServiceType() + " disabled by configuration");
          return;
        }

        Configuration handlerConfiguration = configuration.getChild("handler");

        
        /*
        boolean streamdump=handlerConfiguration.getChild("streamdump").getAttributeAsBoolean("enabled", false);
        String streamdumpDir=streamdump ? handlerConfiguration.getChild("streamdump").getAttribute("directory", null) : null;
        setStreamDumpDir(streamdumpDir);
        */

        port = configuration.getChild("port").getValueAsInteger(getDefaultPort());

     

        StringBuilder infoBuffer;
        

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

        configureHelloName(handlerConfiguration);

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

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

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

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

        
        String connectionLimitString = configuration.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 < 0) {
                logger.error("Connection limit value cannot be less than zero.");
                throw new ConfigurationException("Connection limit value cannot be less than zero.");
            } else if (connectionLimit > 0){
                infoBuffer = new StringBuilder(128)
                .append(getServiceType())
                .append(" will allow a maximum of ")
                .append(connectionLimitString)
                .append(" connections.");
                logger.info(infoBuffer.toString());
            }
        } 
       
        String connectionLimitPerIP = handlerConfiguration.getChild("connectionLimitPerIP").getValue(null);
        if (connectionLimitPerIP != null) {
            try {
            connPerIP = new Integer(connectionLimitPerIP).intValue();
            } 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 if (connPerIP > 0){
                infoBuffer = new StringBuilder(128)
                .append(getServiceType())
                .append(" will allow a maximum of ")
                .append(connPerIP)
                .append(" per IP connections for " +getServiceType());
                logger.info(infoBuffer.toString());
            }
        }
       
        
        Configuration tlsConfig = configuration.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("");
            }
        }        
    }

    
    /**
     * Configure the helloName for the given Configuration
     * 
     * @param handlerConfiguration
     */
    private void configureHelloName(Configuration handlerConfiguration) {
        StringBuilder infoBuffer;
        String hostName = null;
        try {
            hostName = dns.getHostName(dns.getLocalHost());
        } catch (UnknownHostException ue) {
            hostName = "localhost";
        }

        infoBuffer =
            new StringBuilder(64)
                    .append(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(getServiceType())
                    .append(" handler hello name is: ")
                    .append(helloName);
        getLogger().info(infoBuffer.toString());
    }
    
    /**
     * Return the port this server will listen on
     * 
     * @return port
     */
    public int getPort() {
        return port;
    }
    
    /**
     * Return the logger
     * 
     * @return logger
     */
    protected Logger getLogger() {
        return logger;
    }
    
    /**
     * Return if the server is enabled by the configuration
     * 
     * @return enabled
     */
    public boolean isEnabled() {
        return enabled;
    }
    
    /**
     * Return helloName for this server
     * 
     * @return helloName
     */
    public String getHelloName() {
        return helloName;
    }
    
    
    /**
     * Return if startTLS is supported by this server
     * 
     * @return startTlsSupported
     */
    protected boolean isStartTLSSupported() {
        return useStartTLS;
    }
    
    /**
     * Build the SslContextFactory
     * 
     * @throws Exception
     */
    private void buildSSLContextFactory() throws Exception{
        if (useStartTLS) {
            KeyStoreFactory kfactory = new KeyStoreFactory();
            kfactory.setDataFile(fileSystem.getFile(keystore));
            kfactory.setPassword(secret);
            
            contextFactory = new SslContextFactory();
            contextFactory.setKeyManagerFactoryKeyStore(kfactory.newInstance());
            contextFactory.setKeyManagerFactoryAlgorithm("SunX509");
            contextFactory.setTrustManagerFactory(new BogusTrustManagerFactory());
            contextFactory.setKeyManagerFactoryKeyStorePassword(secret);
        }
    }
    
    /**
     * Createh IoHandler to use by this Server implementation
     * 
     * @return ioHandler
     */
    protected abstract IoHandler createIoHandler();
    
    /**
     * Return the SslContextFactory which was created for this service. 
     * 
     * @return contextFactory
     */
    protected SslContextFactory getSslContextFactory() {
        return contextFactory;
    }
    
    
    /**
     * Create IoFilterChainBuilder which will get used for the Acceptor. 
     * The builder will contain a ProtocalCodecFilter which handles Line based Protocols and
     * a ConnectionFilter which limit the connection count / connection count per ip.
     * 
     * Developers should override this to add more filters to the chain.
     * 
     * @return ioFilterChainBuilder
     */
    protected DefaultIoFilterChainBuilder createIoFilterChainBuilder() {
        ProtocolCodecFilter codecFactory = new ProtocolCodecFilter(new TextLineCodecFactory());
        Log cLogger = new AvalonLogger(getLogger());

        DefaultIoFilterChainBuilder builder = new DefaultIoFilterChainBuilder();
        builder.addLast("protocolCodecFactory", codecFactory);
        builder.addLast("connectionFilter", new ConnectionFilter(cLogger, connectionLimit, connPerIP));
        return builder;
    }
    
    /**
     * Return the default port which will get used for this server if non is specify in the configuration
     * 
     * @return port
     */
    protected abstract int getDefaultPort();
    
    /**
     * Return textual representation of the service this server provide
     * 
     * @return serviceType
     */
    protected abstract String getServiceType();
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.