org.waarp.ftp.core.config.FtpInternalConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.waarp.ftp.core.config.FtpInternalConfiguration.java

Source

/**
 * This file is part of Waarp Project.
 * 
 * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
 * COPYRIGHT.txt in the distribution for a full listing of individual contributors.
 * 
 * All Waarp Project is free software: you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 * 
 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with Waarp . If not, see
 * <http://www.gnu.org/licenses/>.
 */
package org.waarp.ftp.core.config;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.traffic.ChannelTrafficShapingHandler;
import io.netty.handler.traffic.GlobalChannelTrafficShapingHandler;
import io.netty.util.concurrent.EventExecutorGroup;

import org.waarp.common.command.exception.Reply425Exception;
import org.waarp.common.crypto.ssl.WaarpSslUtility;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.DetectionUtils;
import org.waarp.common.utility.WaarpNettyUtil;
import org.waarp.common.utility.WaarpThreadFactory;
import org.waarp.ftp.core.control.FtpInitializer;
import org.waarp.ftp.core.control.ftps.FtpsInitializer;
import org.waarp.ftp.core.data.handler.FtpDataInitializer;
import org.waarp.ftp.core.data.handler.ftps.FtpsDataInitializer;
import org.waarp.ftp.core.exception.FtpNoConnectionException;
import org.waarp.ftp.core.session.FtpSession;
import org.waarp.ftp.core.session.FtpSessionReference;
import org.waarp.ftp.core.utils.FtpChannelUtils;
import org.waarp.ftp.core.utils.FtpShutdownHook;

/**
 * Internal configuration of the FTP server, related to Netty
 * 
 * @author Frederic Bregier
 * 
 */
public class FtpInternalConfiguration {
    // Static values
    /**
     * Internal Logger
     */
    private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(FtpInternalConfiguration.class);

    // Network Internals
    /**
     * Time elapse for retry in ms
     */
    public static final long RETRYINMS = 10;

    /**
     * Number of retry before error
     */
    public static final int RETRYNB = 3;

    /**
     * Time elapse for WRITE OR CLOSE WAIT elaps in ms
     */
    public static final long WAITFORNETOP = 1000;
    /**
     * Hack to say Windows or Unix (USR1 not OK on Windows)
     */
    public static Boolean ISUNIX = null;

    /**
     * Default size for buffers (NIO)
     */
    public static final int BUFFERSIZEDEFAULT = 0x10000; // 64K

    // Dynamic values
    /**
     * List of all Command Channels to enable the close call on them using Netty ChannelGroup
     */
    private ChannelGroup commandChannelGroup = null;

    /**
     * ExecutorService Boss
     */
    private final EventLoopGroup execBoss;

    /**
     * ExecutorService Worker
     */
    private final EventLoopGroup execWorker;

    /**
     * Bootstrap for Command server
     */
    private ServerBootstrap serverBootstrap = null;

    /**
     * List of all Data Channels to enable the close call on them using Netty ChannelGroup
     */
    private ChannelGroup dataChannelGroup = null;

    /**
     * ExecutorService Data Passive Boss
     */
    private final EventLoopGroup execPassiveDataBoss;

    /**
     * ExecutorService Command Event Loop
     */
    private final EventLoopGroup execCommandEvent;

    /**
     * ExecutorService Data Active Worker
     */
    private final EventLoopGroup execDataWorker;

    /**
     * FtpSession references used by Data Connection process
     */
    private final FtpSessionReference ftpSessionReference = new FtpSessionReference();

    /**
     * Bootstrap for Active connections
     */
    private Bootstrap activeBootstrap = null;

    /**
     * ServerBootStrap for Passive connections
     */
    private ServerBootstrap passiveBootstrap = null;

    /**
     * Scheduler for TrafficCounter
     */
    private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2,
            new WaarpThreadFactory("TimerTrafficFtp"));

    /**
     * Global TrafficCounter (set from global configuration)
     */
    private FtpGlobalTrafficShapingHandler globalTrafficShapingHandler = null;

    /**
     * Does the FTP will be SSL native based (990 989 port)
     */
    private boolean usingNativeSsl = false;

    /**
     * Does the FTP accept AUTH and PROT
     */
    private boolean acceptAuthProt = false;
    /**
     * Bootstrap for Active Ssl connections
     */
    private Bootstrap activeSslBootstrap = null;

    /**
     * ServerBootStrap for Passive Ssl connections
     */
    private ServerBootstrap passiveSslBootstrap = null;

    /**
     * 
     * @author Frederic Bregier org.waarp.ftp.core.config BindAddress
     * 
     */
    public static class BindAddress {
        /**
         * Parent passive channel
         */
        public final Channel parent;

        /**
         * Number of binded Data connections
         */
        volatile public int nbBind = 0;

        /**
         * Constructor
         * 
         * @param channel
         */
        public BindAddress(Channel channel) {
            parent = channel;
            nbBind = 0;
        }
    }

    /**
     * List of already bind local addresses for Passive connections
     */
    private final ConcurrentHashMap<InetSocketAddress, BindAddress> hashBindPassiveDataConn = new ConcurrentHashMap<InetSocketAddress, BindAddress>();

    /**
     * Global Configuration
     */
    private final FtpConfiguration configuration;

    /**
     * Constructor
     * 
     * @param configuration
     */
    public FtpInternalConfiguration(FtpConfiguration configuration) {
        this.configuration = configuration;
        ISUNIX = !DetectionUtils.isWindows();
        configuration.shutdownConfiguration.timeout = configuration.TIMEOUTCON;
        new FtpShutdownHook(configuration.shutdownConfiguration, configuration);
        execCommandEvent = new NioEventLoopGroup(configuration.CLIENT_THREAD * 2,
                new WaarpThreadFactory("Command"));
        execBoss = new NioEventLoopGroup(configuration.SERVER_THREAD, new WaarpThreadFactory("CommandBoss", false));
        execWorker = new NioEventLoopGroup(configuration.CLIENT_THREAD, new WaarpThreadFactory("CommandWorker"));
        execPassiveDataBoss = new NioEventLoopGroup(configuration.SERVER_THREAD * 2,
                new WaarpThreadFactory("PassiveDataBoss"));
        execDataWorker = new NioEventLoopGroup(configuration.CLIENT_THREAD * 2,
                new WaarpThreadFactory("DataWorker"));
    }

    /**
     * Startup the server
     * 
     * @throws FtpNoConnectionException
     * 
     */
    public void serverStartup() throws FtpNoConnectionException {
        WaarpLoggerFactory.setDefaultFactory(WaarpLoggerFactory.getDefaultFactory());
        // Command
        commandChannelGroup = new DefaultChannelGroup(configuration.fromClass.getName(), execWorker.next());
        // Data
        dataChannelGroup = new DefaultChannelGroup(configuration.fromClass.getName() + ".data", execWorker.next());

        // Passive Data Connections
        passiveBootstrap = new ServerBootstrap();
        WaarpNettyUtil.setServerBootstrap(passiveBootstrap, execPassiveDataBoss, execDataWorker,
                (int) configuration.TIMEOUTCON);
        if (usingNativeSsl) {
            passiveBootstrap
                    .childHandler(new FtpsDataInitializer(configuration.dataBusinessHandler, configuration, false));
        } else {
            passiveBootstrap
                    .childHandler(new FtpDataInitializer(configuration.dataBusinessHandler, configuration, false));
        }
        if (acceptAuthProt) {
            passiveSslBootstrap = new ServerBootstrap();
            WaarpNettyUtil.setServerBootstrap(passiveSslBootstrap, execPassiveDataBoss, execDataWorker,
                    (int) configuration.TIMEOUTCON);
            passiveSslBootstrap
                    .childHandler(new FtpsDataInitializer(configuration.dataBusinessHandler, configuration, false));
        } else {
            passiveSslBootstrap = passiveBootstrap;
        }

        // Active Data Connections
        activeBootstrap = new Bootstrap();
        WaarpNettyUtil.setBootstrap(activeBootstrap, execDataWorker, (int) configuration.TIMEOUTCON);
        if (usingNativeSsl) {
            activeBootstrap
                    .handler(new FtpsDataInitializer(configuration.dataBusinessHandler, configuration, true));
        } else {
            activeBootstrap.handler(new FtpDataInitializer(configuration.dataBusinessHandler, configuration, true));
        }
        if (acceptAuthProt) {
            activeSslBootstrap = new Bootstrap();
            WaarpNettyUtil.setBootstrap(activeSslBootstrap, execDataWorker, (int) configuration.TIMEOUTCON);
            activeSslBootstrap
                    .handler(new FtpsDataInitializer(configuration.dataBusinessHandler, configuration, true));
        } else {
            activeSslBootstrap = activeBootstrap;
        }

        // Main Command server
        serverBootstrap = new ServerBootstrap();
        WaarpNettyUtil.setServerBootstrap(serverBootstrap, execBoss, execWorker, (int) configuration.TIMEOUTCON);
        if (usingNativeSsl) {
            serverBootstrap.childHandler(new FtpsInitializer(configuration.businessHandler, configuration));
        } else {
            serverBootstrap.childHandler(new FtpInitializer(configuration.businessHandler, configuration));
        }

        try {
            FtpChannelUtils.addCommandChannel(
                    serverBootstrap.bind(new InetSocketAddress(configuration.getServerPort())).sync().channel(),
                    configuration);
        } catch (InterruptedException e) {
            throw new FtpNoConnectionException("Can't initiate the FTP server", e);
        }

        // Init Shutdown Hook handler
        configuration.shutdownConfiguration.timeout = configuration.TIMEOUTCON;
        FtpShutdownHook.addShutdownHook();
        // Factory for TrafficShapingHandler
        globalTrafficShapingHandler = new FtpGlobalTrafficShapingHandler(executorService,
                configuration.getServerGlobalWriteLimit(), configuration.getServerGlobalReadLimit(),
                configuration.getServerChannelWriteLimit(), configuration.getServerChannelReadLimit(),
                configuration.getDelayLimit());
    }

    /**
     * 
     * @return an ExecutorService
     */
    public ExecutorService getWorker() {
        return execWorker;
    }

    /**
     * Add a session from a couple of addresses
     * 
     * @param ipOnly
     * @param fullIp
     * @param session
     */
    public void setNewFtpSession(InetAddress ipOnly, InetSocketAddress fullIp, FtpSession session) {
        ftpSessionReference.setNewFtpSession(ipOnly, fullIp, session);
    }

    /**
     * Return and remove the FtpSession
     * 
     * @param channel
     * @param active
     * @return the FtpSession if it exists associated to this channel
     */
    public FtpSession getFtpSession(Channel channel, boolean active, boolean remove) {
        if (active) {
            return ftpSessionReference.getActiveFtpSession(channel, remove);
        } else {
            return ftpSessionReference.getPassiveFtpSession(channel, remove);
        }
    }

    /**
     * Remove the FtpSession
     * 
     * @param ipOnly
     * @param fullIp
     */
    public void delFtpSession(InetAddress ipOnly, InetSocketAddress fullIp) {
        ftpSessionReference.delFtpSession(ipOnly, fullIp);
    }

    /**
     * Test if the couple of addresses is already in the context
     * 
     * @param ipOnly
     * @param fullIp
     * @return True if the couple is present
     */
    public boolean hasFtpSession(InetAddress ipOnly, InetSocketAddress fullIp) {
        return ftpSessionReference.contains(ipOnly, fullIp);
    }

    /**
     * 
     * @return the number of Active Sessions
     */
    public int getNumberSessions() {
        return ftpSessionReference.sessionsNumber();
    }

    /**
     * Try to add a Passive Channel listening to the specified local address
     * 
     * @param address
     * @param ssl
     * @throws Reply425Exception
     *             in case the channel cannot be opened
     */
    public void bindPassive(InetSocketAddress address, boolean ssl) throws Reply425Exception {
        configuration.bindLock();
        try {
            BindAddress bindAddress = hashBindPassiveDataConn.get(address);
            if (bindAddress == null) {
                logger.debug("Bind really to {}", address);
                Channel parentChannel = null;
                try {
                    ChannelFuture future = null;
                    if (ssl) {
                        future = passiveSslBootstrap.bind(address);
                    } else {
                        future = passiveBootstrap.bind(address);
                    }
                    if (future.await(configuration.TIMEOUTCON)) {
                        parentChannel = future.sync().channel();
                    } else {
                        logger.warn("Cannot open passive connection due to Timeout");
                        throw new Reply425Exception("Cannot open a Passive Connection due to Timeout");
                    }
                } catch (ChannelException e) {
                    logger.warn("Cannot open passive connection {}", e.getMessage());
                    throw new Reply425Exception("Cannot open a Passive Connection");
                } catch (InterruptedException e) {
                    logger.warn("Cannot open passive connection {}", e.getMessage());
                    throw new Reply425Exception("Cannot open a Passive Connection");
                }
                bindAddress = new BindAddress(parentChannel);
                FtpChannelUtils.addDataChannel(parentChannel, configuration);
                hashBindPassiveDataConn.put(address, bindAddress);
            }
            bindAddress.nbBind++;
            logger.debug("Bind number to {} is {}", address, bindAddress.nbBind);
        } finally {
            configuration.bindUnlock();
        }
    }

    /**
     * Try to unbind (closing the parent channel) the Passive Channel listening to the specified
     * local address if the last one. It returns only when the underlying parent channel is closed
     * if this was the last session that wants to open on this local address.
     * 
     * @param address
     */
    public void unbindPassive(InetSocketAddress address) {
        configuration.bindLock();
        try {
            BindAddress bindAddress = hashBindPassiveDataConn.get(address);
            if (bindAddress != null) {
                bindAddress.nbBind--;
                logger.debug("Bind number to {} left is {}", address, bindAddress.nbBind);
                if (bindAddress.nbBind == 0) {
                    WaarpSslUtility.closingSslChannel(bindAddress.parent);
                    hashBindPassiveDataConn.remove(address);
                }
            } else {
                logger.warn("No Bind to {}", address);
            }
        } finally {
            configuration.bindUnlock();
        }
    }

    /**
     * 
     * @return the number of Binded Passive Connections
     */
    public int getNbBindedPassive() {
        return hashBindPassiveDataConn.size();
    }

    /**
     * Return the associated Executor for Command
     * 
     * @return the Command Executor
     */
    public EventExecutorGroup getExecutor() {
        return execCommandEvent;
    }

    /**
     * @param ssl
     * @return the ActiveBootstrap
     */
    public Bootstrap getActiveBootstrap(boolean ssl) {
        if (ssl) {
            return activeSslBootstrap;
        } else {
            return activeBootstrap;
        }
    }

    /**
     * @return the commandChannelGroup
     */
    public ChannelGroup getCommandChannelGroup() {
        return commandChannelGroup;
    }

    /**
     * @return the dataChannelGroup
     */
    public ChannelGroup getDataChannelGroup() {
        return dataChannelGroup;
    }

    /**
     * 
     * @return The TrafficCounterFactory
     */
    public FtpGlobalTrafficShapingHandler getGlobalTrafficShapingHandler() {
        return globalTrafficShapingHandler;
    }

    /**
     * 
     * @return a new ChannelTrafficShapingHandler
     */
    public ChannelTrafficShapingHandler newChannelTrafficShapingHandler() {
        if (configuration.getServerChannelWriteLimit() == 0 && configuration.getServerChannelReadLimit() == 0) {
            return null;
        }
        if (globalTrafficShapingHandler instanceof GlobalChannelTrafficShapingHandler) {
            return null;
        }
        return new FtpChannelTrafficShapingHandler(configuration.getServerChannelWriteLimit(),
                configuration.getServerChannelReadLimit(), configuration.getDelayLimit());
    }

    public void releaseResources() {
        WaarpSslUtility.forceCloseAllSslChannels();
        execBoss.shutdownGracefully();
        execWorker.shutdownGracefully();
        execPassiveDataBoss.shutdownGracefully();
        execDataWorker.shutdownGracefully();
        //execCommandEvent.shutdownGracefully();
        globalTrafficShapingHandler.release();
        executorService.shutdown();
    }

    public boolean isAcceptAuthProt() {
        return acceptAuthProt;
    }

    /**
     * @return the usingNativeSsl
     */
    public boolean isUsingNativeSsl() {
        return usingNativeSsl;
    }

    /**
     * @param usingNativeSsl
     *            the usingNativeSsl to set
     */
    public void setUsingNativeSsl(boolean usingNativeSsl) {
        this.usingNativeSsl = usingNativeSsl;
    }

    /**
     * @param acceptAuthProt
     *            the acceptAuthProt to set
     */
    public void setAcceptAuthProt(boolean acceptAuthProt) {
        this.acceptAuthProt = acceptAuthProt;
    }

}