com.chiorichan.net.NetworkManager.java Source code

Java tutorial

Introduction

Here is the source code for com.chiorichan.net.NetworkManager.java

Source

/**
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2016 Chiori Greene a.k.a. Chiori-chan <me@chiorichan.com>
 * All Right Reserved.
 */
package com.chiorichan.net;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
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 java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.Security;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.chiorichan.AppConfig;
import com.chiorichan.AppController;
import com.chiorichan.http.HttpInitializer;
import com.chiorichan.http.ssl.SslInitializer;
import com.chiorichan.http.ssl.SslManager;
import com.chiorichan.lang.StartupException;
import com.chiorichan.logger.Log;
import com.chiorichan.logger.LogSource;
import com.chiorichan.net.query.QueryServerInitializer;
import com.chiorichan.services.AppManager;
import com.chiorichan.tasks.TaskManager;
import com.chiorichan.tasks.TaskRegistrar;
import com.chiorichan.tasks.Ticks;
import com.chiorichan.util.Application;
import com.google.common.collect.Lists;

/**
 * Works as the main network managing class for netty implementations, e.g., Http, Https, and Query
 */
public class NetworkManager implements TaskRegistrar, LogSource {
    private static final NetworkManager SELF = new NetworkManager();

    private static EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private static EventLoopGroup workerGroup = new NioEventLoopGroup(100);

    private static Channel httpChannel = null;
    private static Channel httpsChannel = null;
    private static Channel queryChannel = null;
    private static Channel tcpChannel = null;

    private static void close(Channel channel) {
        try {
            if (channel != null && channel.isOpen())
                channel.close();
        } catch (Throwable t) {
            // Ignore
        }
    }

    public static List<String> getListeningIps() {
        List<String> ips = Lists.newArrayList();
        try {
            Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
            while (e.hasMoreElements()) {
                NetworkInterface n = e.nextElement();
                Enumeration<InetAddress> ee = n.getInetAddresses();
                while (ee.hasMoreElements()) {
                    InetAddress i = ee.nextElement();
                    ips.add(i.getHostAddress());
                }
            }
        } catch (SocketException e1) {
            getLogger().severe("Failed to retrieve all active server ips.", e1);
        }

        String ip = ((InetSocketAddress) (httpChannel == null ? httpsChannel : httpChannel).localAddress())
                .getAddress().getHostAddress();

        // Assert that both unsecure and secure servers are listening on the same address
        if (httpsChannel != null && httpsChannel.isOpen())
            assert ip.equals(((InetSocketAddress) httpsChannel.localAddress()).getAddress().getHostAddress());

        if (ip.contains("%"))
            ip = ip.split("\\%")[0];

        if ("0.0.0.0".equals(ip) || "0:0:0:0:0:0:0:0".equals(ip))
            return ips;
        else
            return Arrays.asList(ip);
    }

    public static Log getLogger() {
        return Log.get(SELF);
    }

    public static boolean isHttpRunning() {
        return httpChannel != null && httpChannel.isOpen();
    }

    public static boolean isHttpsRunning() {
        return httpsChannel != null && httpsChannel.isOpen();
    }

    public static boolean isQueryRunning() {
        return queryChannel != null && queryChannel.isOpen();
    }

    public static boolean isTcpRunning() {
        return false;
    }

    public static void shutdown() {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();

        close(httpChannel);
        close(httpsChannel);
        close(tcpChannel);
        close(queryChannel);
    }

    public static void shutdownHttpServer() {
        if (httpChannel != null && httpChannel.isOpen())
            httpChannel.close();
    }

    public static void shutdownHttpsServer() {
        if (httpsChannel != null && httpsChannel.isOpen())
            httpsChannel.close();
    }

    public static void shutdownQueryServer() {
        if (queryChannel != null && queryChannel.isOpen())
            queryChannel.close();
    }

    public static void shutdownTcpServer() {
        if (tcpChannel != null && tcpChannel.isOpen())
            tcpChannel.close();
    }

    public static void startHttpServer() throws StartupException {
        if (httpChannel != null && httpChannel.isOpen())
            throw new StartupException("The HTTP Server is already running");

        try {
            InetSocketAddress socket;
            String httpIp = AppConfig.get().getString("server.httpHost", "");
            int httpPort = AppConfig.get().getInt("server.httpPort", 8080);

            if (httpPort > 0) {
                if (Application.isPrivilegedPort(httpPort)) {
                    getLogger().warning(
                            "It would seem that you are trying to start ChioriWebServer's Web Server on a privileged port without root access.");
                    getLogger().warning(
                            "Most likely you will see an exception thrown below this. http://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html");
                    getLogger().warning(
                            "It's recommended that you either run CWS on a port like 8080 then use the firewall to redirect from 80 or run as root if you must use port: "
                                    + httpPort);
                }

                if (httpIp.isEmpty())
                    socket = new InetSocketAddress(httpPort);
                else
                    socket = new InetSocketAddress(httpIp, httpPort);

                // TODO Allow the server to bind to more than one IP

                getLogger().info("Starting Web Server on " + (httpIp.isEmpty() ? "*" : httpIp) + ":" + httpPort);

                try {
                    ServerBootstrap b = new ServerBootstrap();
                    b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                            .childHandler(new HttpInitializer());

                    httpChannel = b.bind(socket).sync().channel();

                    // HTTP Server Thread
                    AppController.registerRunnable(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                httpChannel.closeFuture().sync();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                            getLogger().info("The HTTP Server has been shutdown!");
                        }
                    });
                } catch (NullPointerException e) {
                    throw new StartupException(
                            "There was a problem starting the Web Server. Check logs and try again.", e);
                } catch (Throwable e) {
                    getLogger().warning("**** FAILED TO BIND HTTP SERVER TO PORT!");
                    // getLogger().warning( "The exception was: {0}", new Object[] {e.toString()} );
                    getLogger().warning("Perhaps a server is already running on that port?");

                    throw new StartupException(e);
                }
            } else
                getLogger().warning("The HTTP server is disabled per configs.");
        } catch (Throwable e) {
            if (e instanceof StartupException)
                throw e;
            else
                throw new StartupException(e);
        }
    }

    public static void startHttpsServer() throws StartupException {
        if (httpsChannel != null && httpsChannel.isOpen())
            throw new StartupException("The HTTPS Server is already running");

        try {
            InetSocketAddress socket;
            String httpIp = AppConfig.get().getString("server.httpHost", "");
            int httpsPort = AppConfig.get().getInt("server.httpsPort", 8443);

            Security.addProvider(new BouncyCastleProvider());

            if (httpsPort >= 1) {
                if (Application.isPrivilegedPort(httpsPort)) {
                    getLogger().warning(
                            "It would seem that you are trying to start ChioriWebServer's Web Server (SSL) on a privileged port without root access.");
                    getLogger().warning(
                            "Most likely you will see an exception thrown below this. http://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html");
                    getLogger().warning(
                            "It's recommended that you either run CWS (SSL) on a port like 4443 then use the firewall to redirect from 443 or run as root if you must use port: "
                                    + httpsPort);
                }

                if (httpIp.isEmpty())
                    socket = new InetSocketAddress(httpsPort);
                else
                    socket = new InetSocketAddress(httpIp, httpsPort);

                AppManager.manager(SslManager.class).init();

                getLogger().info(
                        "Starting Secure Web Server on " + (httpIp.isEmpty() ? "*" : httpIp) + ":" + httpsPort);

                try {
                    ServerBootstrap b = new ServerBootstrap();
                    b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                            .childHandler(new SslInitializer());

                    httpsChannel = b.bind(socket).sync().channel();

                    // HTTPS Server Thread
                    AppController.registerRunnable(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                httpsChannel.closeFuture().sync();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                            getLogger().info("The HTTPS Server has been shutdown!");
                        }
                    });
                } catch (NullPointerException e) {
                    throw new StartupException(
                            "There was a problem starting the Web Server. Check logs and try again.", e);
                } catch (Throwable e) {
                    getLogger().warning("**** FAILED TO BIND HTTPS SERVER TO PORT!");
                    getLogger().warning("Perhaps a server is already running on that port?");

                    throw new StartupException(e);
                }
            } else
                getLogger().warning("The HTTPS server is disabled per configs.");
        } catch (Throwable e) {
            throw new StartupException(e);
        }
    }

    public static void startQueryServer() throws StartupException {
        if (queryChannel != null && queryChannel.isOpen())
            throw new StartupException("The Query Server is already running");

        try {
            InetSocketAddress socket;
            String queryHost = AppConfig.get().getString("server.queryHost", "");
            int queryPort = AppConfig.get().getInt("server.queryPort", 8992);

            if (queryPort >= 1 && AppConfig.get().getBoolean("server.queryEnabled")) {
                if (Application.isPrivilegedPort(queryPort)) {
                    getLogger().warning(
                            "It would seem that you are trying to start the Query Server on a privileged port without root access.");
                    getLogger().warning(
                            "Most likely you will see an exception thrown below this. http://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html");
                    getLogger().warning(
                            "It's recommended that you either run CWS on a port like 8080 then use the firewall to redirect or run as root if you must use port: "
                                    + queryPort);
                }

                if (queryHost.isEmpty())
                    socket = new InetSocketAddress(queryPort);
                else
                    socket = new InetSocketAddress(queryHost, queryPort);

                getLogger().info(
                        "Starting Query Server on " + (queryHost.isEmpty() ? "*" : queryHost) + ":" + queryPort);

                try {
                    ServerBootstrap b = new ServerBootstrap();
                    b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                            .childHandler(new QueryServerInitializer());

                    queryChannel = b.bind(socket).sync().channel();

                    // Query Server Thread
                    AppController.registerRunnable(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                queryChannel.closeFuture().sync();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                            getLogger().info("The Query Server has been shutdown!");
                        }
                    });
                } catch (NullPointerException e) {
                    throw new StartupException(
                            "There was a problem starting the Web Server. Check logs and try again.", e);
                } catch (Throwable e) {
                    getLogger().warning("**** FAILED TO BIND QUERY SERVER TO PORT!");
                    getLogger().warning("Perhaps a server is already running on that port?");

                    throw new StartupException(e);
                }
            }
        } catch (Throwable e) {
            throw new StartupException(e);
        }
    }

    public static void startTcpServer() throws StartupException {
        // XXX TCP IS TEMPORARY REMOVED UNTIL IT CAN BE PORTED TO NETTY.
    }

    private NetworkManager() {
        TaskManager.instance().scheduleAsyncRepeatingTask(this, Ticks.SECOND_15, Ticks.SECOND_15, new Runnable() {
            @Override
            public void run() {
                for (WeakReference<SocketChannel> ref : HttpInitializer.activeChannels)
                    if (ref.get() == null)
                        HttpInitializer.activeChannels.remove(ref);
                for (WeakReference<SocketChannel> ref : SslInitializer.activeChannels)
                    if (ref.get() == null)
                        SslInitializer.activeChannels.remove(ref);
            }
        });
    }

    @Override
    public String getLoggerId() {
        return "NetMgr";
    }

    @Override
    public String getName() {
        return "NetMgr";
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}