holon.internal.http.netty.NettyEngine.java Source code

Java tutorial

Introduction

Here is the source code for holon.internal.http.netty.NettyEngine.java

Source

/**
 * Copyright (c) 2002-2014 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j 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 program 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 program.  If not, see <http://www.gnu.org/licenses/>.
 */
package holon.internal.http.netty;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.dsl.Disruptor;
import holon.Holon;
import holon.api.config.Config;
import holon.api.exception.HolonException;
import holon.api.logging.Logging;
import holon.internal.http.netty.work.NettyWorkEvent;
import holon.internal.http.netty.work.NettyWorkHandler;
import holon.spi.HolonEngine;
import holon.spi.Route;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

public class NettyEngine implements HolonEngine {
    private final Logging.Logger logger;
    private final int httpPort;
    private final int workers;

    private volatile boolean running = false;
    private ExecutorService executor;
    private Disruptor<NettyWorkEvent> disruptor;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    public NettyEngine(Config config, Logging.Logger logger) {
        this.logger = logger;
        this.workers = config.get(Holon.Configuration.workers);
        this.httpPort = config.get(Holon.Configuration.http_port);
    }

    @Override
    public void serve(Supplier<Iterable<Route>> routes) {
        running = true;
        executor = Executors.newCachedThreadPool();

        disruptor = new Disruptor<>(NettyWorkEvent::new, 1024, executor);
        disruptor.handleEventsWithWorkerPool(createWorkers(workers, routes));
        disruptor.start();

        RingBuffer<NettyWorkEvent> ringBuffer = disruptor.getRingBuffer();

        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup();

        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup);
        b.channel(NioServerSocketChannel.class);
        b.childHandler(new Initializer(ringBuffer));

        try {
            b.bind(httpPort).sync();
        } catch (InterruptedException e) {
            throw new HolonException("Failed to bind to '" + httpPort + "', " + e.getMessage());
        }

    }

    private WorkHandler<NettyWorkEvent>[] createWorkers(int count, Supplier<Iterable<Route>> routes) {
        WorkHandler[] handlers = new WorkHandler[count];
        for (int i = 0; i < count; i++) {
            handlers[i] = new NettyWorkHandler(routes);
        }
        return handlers;
    }

    @Override
    public void shutdown() {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        try {
            bossGroup.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // swallow
        }
        if (disruptor != null) {
            disruptor.shutdown();
            disruptor = null;
        }
        if (executor != null) {
            executor.shutdown();
            executor = null;
        }
        running = false;
    }

    @Override
    public void join() {
        while (running) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                break;
            }
        }
    }

    private final static class Initializer extends ChannelInitializer<SocketChannel> {
        private final RingBuffer<NettyWorkEvent> ringBuffer;

        public Initializer(RingBuffer<NettyWorkEvent> ringBuffer) {
            this.ringBuffer = ringBuffer;
        }

        @Override
        public void initChannel(SocketChannel ch) {
            ChannelPipeline pipeline = ch.pipeline();

            pipeline.addLast(new HttpRequestDecoder());
            pipeline.addLast(new HttpResponseEncoder());

            pipeline.addLast(new HttpContentCompressor());

            pipeline.addLast(new NettyServerHandler(ringBuffer));
        }
    }
}