io.maelstorm.server.ServerInit.java Source code

Java tutorial

Introduction

Here is the source code for io.maelstorm.server.ServerInit.java

Source

/*
 * 
 * This file is part of Http-Server
 * Copyright (C) 2013  Jatinder
 * 
 * This program 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.
 * 
 * This library 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 this library; If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package io.maelstorm.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.AdaptiveRecvByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.io.InputStream;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.maelstorm.netty.AppendableCharSequenceAddon;

/**
 * @author Jatinder
 *
 * Bootstrap class for server
 *
 */
public class ServerInit {
    private static final Logger LOG = LoggerFactory.getLogger(ServerInit.class);
    private ServerBootstrap controller;
    private volatile RequestHandler requestDistributor;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private static final int NUM_WORKER_THREADS = Math.max(Runtime.getRuntime().availableProcessors() - 1, 2);

    public void start(String configFile, RequestHandler handler) throws Exception {
        LOG.info("Starting.");
        AppendableCharSequenceAddon.configure();
        try {
            System.in.close(); // Release a FD we don't need.
        } catch (Exception e) {
            LOG.warn("Failed to close stdin", e);
        }
        Properties prop = getProperties(configFile);
        requestDistributor = handler;
        requestDistributor.setInitializer(this);
        String os = System.getProperty("os.name").toLowerCase(Locale.UK).trim();
        if (os.startsWith("linux")) {
            bossGroup = (null == bossGroup) ? new EpollEventLoopGroup(1) : bossGroup;
            workerGroup = (null == workerGroup) ? new EpollEventLoopGroup(NUM_WORKER_THREADS) : workerGroup;
        } else {
            bossGroup = (null == bossGroup) ? new NioEventLoopGroup(1) : bossGroup;
            workerGroup = (null == workerGroup) ? new NioEventLoopGroup(NUM_WORKER_THREADS) : workerGroup;
        }

        String[] servers = prop.getProperty("servers").split(",");
        for (String server : servers) {

            try {
                controller = new ServerBootstrap();
                controller.group(bossGroup, workerGroup);
                if (os.startsWith("linux")) {
                    controller.channel(EpollServerSocketChannel.class);
                    controller.option(EpollChannelOption.TCP_CORK, true);
                } else {
                    controller.channel(NioServerSocketChannel.class);
                }
                controller.childHandler(new PipelineFactory(handler, getPipelineConfig(prop, server)));
                controller.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
                controller.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT);
                controller.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
                        getInt(prop, server, "connectTimeoutMillis"));
                controller.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 64 * 1024);
                controller.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 1 * 1024);
                controller.option(ChannelOption.SO_KEEPALIVE, getBoolean(prop, server, "SOKeepalive"));
                controller.option(ChannelOption.SO_REUSEADDR, true);
                controller.option(ChannelOption.TCP_NODELAY, true);
                controller.option(ChannelOption.SO_LINGER, 0);
                controller.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
                controller.childOption(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 64 * 1024);
                controller.childOption(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 10);
                controller.childOption(ChannelOption.SO_KEEPALIVE, true);
                controller.childOption(ChannelOption.SO_REUSEADDR, true);
                controller.childOption(ChannelOption.TCP_NODELAY, true);
                controller.childOption(ChannelOption.SO_LINGER, 0);
                controller.childOption(ChannelOption.SO_RCVBUF, 6291456);

                final InetSocketAddress addr = new InetSocketAddress(getInt(prop, server, "port"));
                ChannelFuture future = controller.bind(addr).sync();
                if (future.isSuccess())
                    LOG.info("Server Ready to serve on " + addr);
                else
                    throw new Exception("Address already in use");
            } catch (Throwable t) {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
                throw new RuntimeException("Initialization failed", t);
            }
        }
    }

    private boolean getBoolean(Properties prop, String server, String type) throws Exception {
        String bool = prop.getProperty(server + "." + type);
        if (null != bool && !bool.isEmpty()) {
            return Boolean.parseBoolean(bool);
        } else
            throw new Exception(type + " Keepalive not defined for server " + server);
    }

    private int getInt(Properties prop, String server, String type) throws Exception {
        String port = prop.getProperty(server + "." + type);
        if (null != port && !port.isEmpty()) {
            return Integer.parseInt(port);
        } else
            throw new Exception(type + " not defined for server " + server);
    }

    private Map<String, String> getPipelineConfig(Properties prop, String server) throws Exception {
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("connectTimeoutMillis", Integer.toString(getInt(prop, server, "connectTimeoutMillis")));
        configs.put("idleTimeoutSeconds", Integer.toString(getInt(prop, server, "idleTimeoutSeconds")));
        configs.put("maxInitialLineLength", Integer.toString(getInt(prop, server, "maxInitialLineLength")));
        configs.put("maxHeaderSize", Integer.toString(getInt(prop, server, "maxHeaderSize")));
        configs.put("maxChunkSize", Integer.toString(getInt(prop, server, "maxChunkSize")));
        configs.put("maxContentLength", Integer.toString(getInt(prop, server, "maxContentLength")));
        configs.put("chunkedSupported", Boolean.toString(getBoolean(prop, server, "chunkedSupported")));
        boolean isSSL = getBoolean(prop, server, "isSSL");
        configs.put("isSSL", Boolean.toString(isSSL));
        if (isSSL) {
            String keystore = prop.getProperty(server + ".keystore");
            if (null != keystore && !keystore.isEmpty())
                configs.put("keystore", keystore);
            else
                throw new Exception("keyStore not defined for server " + server);
            String password = prop.getProperty(server + ".keystorePassword");
            if (null != password && !password.isEmpty())
                configs.put("password", password);
            else
                throw new Exception("keystorePasswords not defined for server " + server);
        }
        return configs;
    }

    private Properties getProperties(String configFile) throws Exception {
        Properties properties = new Properties();
        InputStream is = this.getClass().getResourceAsStream((configFile == null) ? "/config.properties"
                : configFile.isEmpty() ? "/config.properties" : configFile);
        properties.load(is);
        return properties;
    }

    protected EventLoopGroup getBossGroup() {
        return bossGroup;
    }

    protected EventLoopGroup getWorkerGroup() {
        return workerGroup;
    }

    public RequestDistributor getRequestDistributor() {
        return (RequestDistributor) requestDistributor;
    }

    public void shutDownGracefully() {
        LOG.info("Stop Requested");
        if (null != controller) {
            controller.group().shutdownGracefully().awaitUninterruptibly();
            controller.childGroup().shutdownGracefully().awaitUninterruptibly();
        }
    }
}