org.asterisque.netty.Netty.java Source code

Java tutorial

Introduction

Here is the source code for org.asterisque.netty.Netty.java

Source

/*
 * Copyright (c) 2013 koiroha.org.
 * All sources and related resources are available under Apache License 2.0.
 * http://www.apache.org/licenses/LICENSE-2.0.html
*/
package org.asterisque.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;
import org.asterisque.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLEngine;
import java.net.SocketAddress;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Netty
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
 * ?? I/O ? Netty ??????? SSL ??????
 *
 * @author Takami Torao
 */
public class Netty implements Bridge {
    private static final Logger logger = LoggerFactory.getLogger(Netty.class);

    private final AtomicReference<NioEventLoopGroup> worker = new AtomicReference<>();
    private final AtomicBoolean closing = new AtomicBoolean(false);

    // ==============================================================================================
    // 
    // ==============================================================================================
    /**
     *
     */
    public Netty() {
    }

    // ==============================================================================================
    // 
    // ==============================================================================================
    /**
     *
     */
    private NioEventLoopGroup worker() {
        if (closing.get()) {
            throw new IllegalStateException("netty bridge already closed");
        }
        if (worker.get() == null) {
            NioEventLoopGroup w = new NioEventLoopGroup();
            if (!worker.compareAndSet(null, w)) {
                w.shutdownGracefully();
            } else {
                logger.debug("worker NioEventLoop created");
            }
        }
        return worker.get();
    }

    // ==============================================================================================
    // ?
    // ==============================================================================================
    /**
     * ???????
     * @param options 
     * @return Wire ? Future
     */
    public CompletableFuture<Wire> newWire(Node node, SocketAddress address, Options options) {
        Bootstrap client = new Bootstrap();
        CompletableFuture<Wire> future = new CompletableFuture<>();

        Initializer factory = new Initializer(node, false, options, wire -> {
            logger.debug(Asterisque.logPrefix(false) + ": onConnect(" + wire + ")");
            future.complete(wire);
        });

        client.group(worker()).channel(NioSocketChannel.class).remoteAddress(address)
                .option(ChannelOption.TCP_NODELAY, java.lang.Boolean.TRUE).handler(factory);

        client.connect(address).addListener(f -> {
            if (f.isSuccess()) {
                logger.debug(Asterisque.logPrefix(false) + ": connection success");
            } else {
                logger.debug(Asterisque.logPrefix(false) + ": connection failure");
                future.completeExceptionally(f.cause());
            }
        });
        return future;
    }

    // ==============================================================================================
    // ??
    // ==============================================================================================
    /**
     * ??????????
     *
     * @return Server ? Future
     */
    public CompletableFuture<Server> newServer(Node node, SocketAddress address, Options options,
            Consumer<Wire> onAccept) {
        NioEventLoopGroup master = new NioEventLoopGroup(); // ??????
        ServerBootstrap server = new ServerBootstrap();
        CompletableFuture<Server> future = new CompletableFuture<>();

        Initializer factory = new Initializer(node, true, options, wire -> {
            logger.debug(Asterisque.logPrefix(true) + ": onAccept(" + wire + ")");
            onAccept.accept(wire);
        });

        server.group(master, worker()).channel(NioServerSocketChannel.class).localAddress(address)
                .option(ChannelOption.SO_BACKLOG, options.get(Options.KEY_SERVER_BACKLOG).get())
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE).childHandler(factory);

        server.bind().addListener(f -> {
            if (f.isSuccess()) {
                logger.info(Asterisque.logPrefix(true) + ": startup server: " + Debug.toString(address));
                future.complete(new Server(node, address, options) {
                    @Override
                    public void close() {
                        master.shutdownGracefully();
                    }
                });
            } else {
                logger.error(Asterisque.logPrefix(true) + ": server bind failure: " + Debug.toString(address),
                        f.cause());
                future.completeExceptionally(f.cause());
                master.shutdownGracefully();
            }
        });
        return future;
    }

    // ==============================================================================================
    // 
    // ==============================================================================================
    /**
     * {@inheritDoc}
     */
    public void close() {
        logger.trace("close()");
        if (closing.compareAndSet(false, true)) {
            NioEventLoopGroup w = worker.getAndSet(null);
            if (w != null) {
                w.shutdownGracefully();
            }
        }
    }

    private static class Initializer extends ChannelInitializer {
        private final Node node;
        private final boolean isServer;
        private final Options options;
        private final Consumer<NettyWire> onWireCreate;
        private final String sym;

        // ============================================================================================
        // 
        // ============================================================================================
        /**
         */
        public Initializer(Node node, boolean isServer, Options options, Consumer<NettyWire> onWireCreate) {
            this.node = node;
            this.isServer = isServer;
            this.options = options;
            this.onWireCreate = onWireCreate;
            this.sym = Asterisque.logPrefix(isServer);
        }

        // ============================================================================================
        // ????
        // ============================================================================================
        /**
         * ???
         */
        @Override
        public void initChannel(Channel ch) {
            logger.trace(sym + ": initChannel(" + ch + ")");
            ChannelPipeline pipeline = ch.pipeline();

            Optional<SslHandler> sslHandler;
            if (options.get(Options.KEY_SSL_CONTEXT).isPresent()) {
                // SSL ?
                SSLEngine engine = options.get(Options.KEY_SSL_CONTEXT).get().createSSLEngine();
                engine.setUseClientMode(!isServer);
                engine.setNeedClientAuth(true); // TODO ???????
                if (logger.isTraceEnabled()) {
                    logger.trace(sym + ": CipherSuites: ${engine.getEnabledCipherSuites.mkString(", ")}");
                    logger.trace(sym + ": Protocols: ${engine.getEnabledProtocols.mkString(", ")}");
                }
                SslHandler handler = new SslHandler(engine);
                pipeline.addLast("tls", handler);
                sslHandler = Optional.of(handler);
            } else {
                // SSL ??
                logger.trace(sym + ": insecure connection");
                sslHandler = Optional.empty();
            }

            pipeline.addLast("io.asterisque.frame.encoder",
                    new MessageEncoder(options.get(Options.KEY_CODEC).get()));
            pipeline.addLast("io.asterisque.frame.decoder",
                    new MessageDecoder(options.get(Options.KEY_CODEC).get()));
            pipeline.addLast("io.asterisque.service", new WireConnect(node, ch.localAddress(), ch.remoteAddress(),
                    isServer, sslHandler, onWireCreate, options));
        }
    }

}