org.restnext.server.Server.java Source code

Java tutorial

Introduction

Here is the source code for org.restnext.server.Server.java

Source

/*
 * Copyright (C) 2016 Thiago Gutenberg Carvalho da Costa
 *
 * Licensed 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.restnext.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
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 io.netty.util.concurrent.Future;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Stream;

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

/**
 * Created by thiago on 04/08/16.
 */
public final class Server {

    private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);

    private final ServerInitializer serverInitializer;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    /**
     * Constructor with auto start flag and the server initializer.
     *
     * @param serverInitializer the server initializer
     */
    public Server(final ServerInitializer serverInitializer) {
        this.serverInitializer = Objects.requireNonNull(serverInitializer, "serverInitializer");
        Runtime.getRuntime().addShutdownHook(new Thread("server-hook") {
            @Override
            public void run() {
                LOGGER.info("Running JVM shutdown hook to stop the server.");
                Server.this.stop();
            }
        });
    }

    /**
     * Starts the server.
     */
    public void start() {
        loadAndPrintBanner();
        try {
            InetSocketAddress bindAddress = serverInitializer.getBindAddress();

            ServerBootstrap serverBootstrap = Epoll.isAvailable() ? newEpoolServerBootstrap()
                    : newNioServerBootstrap();

            ChannelFuture channelFuture = serverBootstrap
                    //.handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(serverInitializer)
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT).bind(bindAddress).sync();

            LOGGER.info("Application is running at - {}://{}",
                    serverInitializer.isSslConfigured() ? "https" : "http", bindAddress);

            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
            throw new ServerException("Could not start the server", e);
        } finally {
            stop();
        }
    }

    public void stop() {
        stop(false);
    }

    private void stop(final boolean await) {
        if (bossGroup != null && workerGroup != null) {
            Future<?> futureWorkerShutdown = workerGroup.shutdownGracefully();
            Future<?> futureBossShutdown = bossGroup.shutdownGracefully();
            if (await) {
                futureWorkerShutdown.awaitUninterruptibly();
                futureBossShutdown.awaitUninterruptibly();
            }
        }
    }

    private void loadAndPrintBanner() {
        String target = "banner-logo.txt";
        URL url = Server.class.getClassLoader().getResource(target);
        URI uri = url != null ? URI.create(url.toString()) : null;
        if (uri != null) {
            String scheme = uri.getScheme();
            switch (scheme) {
            case "file":
                printBanner(Paths.get(uri));
                break;
            case "jar":
                try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) {
                    printBanner(fs.getPath(target));
                } catch (IOException ignore) {
                    LOGGER.warn("An I/O error occurs creating the file system");
                }
                break;
            default: //nop
            }
        }
    }

    private ServerBootstrap newEpoolServerBootstrap() {
        bossGroup = new EpollEventLoopGroup();
        workerGroup = new EpollEventLoopGroup();
        return new ServerBootstrap().group(bossGroup, workerGroup).channel(EpollServerSocketChannel.class);
    }

    private ServerBootstrap newNioServerBootstrap() {
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        return new ServerBootstrap().group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
    }

    private void printBanner(Path path) {
        if (path != null && Files.exists(path)) {
            try (Stream<String> stream = Files.lines(path, StandardCharsets.UTF_8)) {
                final Properties props = new Properties();
                ClassLoader classLoader = Server.class.getClassLoader();
                try (InputStream mavenProps = classLoader
                        .getResourceAsStream("META-INF/maven/org.restnext/restnext-server/pom.properties")) {
                    if (mavenProps != null) {
                        props.load(mavenProps);
                    }
                }
                stream.map(s -> String.format(s, props.getProperty("version", ""))).forEach(LOGGER::info);
            } catch (IOException ignore) {
                LOGGER.warn("An error occurred when reading from the input stream");
            }
        }
    }
}