cn.wantedonline.puppy.Bootstrap.java Source code

Java tutorial

Introduction

Here is the source code for cn.wantedonline.puppy.Bootstrap.java

Source

/*
 * Copyright [2016-2026] wangcheng(wantedonline@outlook.com)
 *
 *  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 cn.wantedonline.puppy;

import cn.wantedonline.puppy.httpserver.component.CmdPageDispatcher;
import cn.wantedonline.puppy.httpserver.stat.NioWorkerStat;
import cn.wantedonline.puppy.httpserver.stat.StatisticManager;
import cn.wantedonline.puppy.spring.BeanUtil;
import cn.wantedonline.puppy.spring.SpringBootstrap;
import cn.wantedonline.puppy.spring.annotation.Config;
import cn.wantedonline.puppy.util.*;
import cn.wantedonline.puppy.httpserver.common.HttpServerConfig;
import cn.wantedonline.puppy.util.concurrent.ConcurrentUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.logging.Slf4JLoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.lang.management.ManagementFactory;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Puppy?
 *
 * @author wangcheng
 * @since V0.1.0 on 2016/10/27.
 */
@Component
public class Bootstrap {
    private Logger log = Log.getLogger(Bootstrap.class);

    private static ApplicationContext context;

    @Autowired
    private HttpServerConfig httpServerConfig;
    @Autowired
    private CmdPageDispatcher dispatcher;
    @Autowired
    private NioWorkerStat nioWorkerStat;
    @Autowired
    private StatisticManager statisticManager;

    private Runnable shutdownRunnable;

    private ChannelFuture serverChannelFuture;

    private String serverStartTime = "";
    private String serverStatus = "";

    private volatile boolean stopping = false;

    @Config
    private long bindRetryTimeout = 60000; //?60s

    public static boolean isArgWhat(String[] args, String... what) {
        if (AssertUtil.isEmptyArray(args)) {
            return false;
        }
        String arg = args[0].toLowerCase();
        for (String w : what) {
            if (arg.indexOf(w) >= 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * ?HttpServerBootstrap,??
     */
    private ServerBootstrap initHttpServerBootstrap() {
        EventLoopGroup bossGroup = httpServerConfig.getBossEventLoopGroup();
        NioEventLoopGroup workerGroup = httpServerConfig.getWorkerEventLoopGroup();
        nioWorkerStat.registerWorkers(workerGroup);
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                .childHandler(httpServerConfig.getHttpServerHandler());
        return serverBootstrap;
    }

    private void startHttpServer(Runnable initRunnable) {
        initEnv();
        dispatcher.init();
        initOutter(initRunnable);
        start();
    }

    private void initEnv() {
        long begin = System.currentTimeMillis();
        //?Netty
        InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory());
        //????
        try {
            statisticManager.readStatisticData();
        } catch (Throwable t) {//???????
            log.error("read statistic data error, error info{}", t);
        }
        System.out.println("------------------------------> "
                + (System.currentTimeMillis() - begin) + "MS");
    }

    private void start() {
        ServerBootstrap b = initHttpServerBootstrap();
        int port = httpServerConfig.getListenPort(); //??????
        Exception ex = null;
        try {
            final AtomicLong retryBind = new AtomicLong(0);
            final AtomicBoolean sended = new AtomicBoolean(false);
            boolean binded = false;
            long beginBind = System.currentTimeMillis();
            long lastPrintTime = 0;

            while (!binded) {
                try {
                    long thisPrintTime = System.currentTimeMillis();
                    if (thisPrintTime - lastPrintTime > 1000) {
                        System.out.println("Ready bind Http port [" + port + "]");
                        lastPrintTime = thisPrintTime;
                    }
                    serverChannelFuture = b.bind(new InetSocketAddress(port)).sync();
                    long endBind = System.currentTimeMillis();
                    long rb = retryBind.get();

                    if (rb > 0) {
                        System.out.println(
                                "------------------------------> ????"
                                        + (endBind - rb) + "MS");
                    }
                    System.out.println("------------------------------> ??[port:" + port
                            + "] " + (endBind - beginBind) + "MS");
                    binded = true;
                } catch (final Exception e) {
                    if (System.currentTimeMillis() - beginBind > bindRetryTimeout) {
                        throw e;
                    }
                    if (!sended.get()) {
                        ConcurrentUtil.getDaemonExecutor().execute(new Runnable() {

                            @Override
                            public void run() {
                                System.out.println("------------------------------> sendShutdownCmd  ["
                                        + httpServerConfig.getListenPort() + "]");
                                log.error("{}", e.toString());
                                long oriSvrBeginExitTime = sendShutdownCmd();
                                boolean result = oriSvrBeginExitTime > 0;
                                if (result) {
                                    if (retryBind.get() == 0) {
                                        retryBind.set(oriSvrBeginExitTime);
                                    }
                                }
                                sended.set(result);
                            }
                        });
                        sended.set(true);
                    }
                    ConcurrentUtil.threadSleep(10);
                }
            }
        } catch (Exception e) {
            ex = e;
        }

        long span = System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime();
        String msg = ex == null ? "OK" : "ERROR";
        String chnmsg = ex == null ? "??.(port[" + port + "])"
                : "??.(port[" + port + "])";
        String spanStr = "[" + span + "MS]";
        String errStr = ex == null ? "" : ex.getMessage();
        log.error("HTTPServer(port[{}],workerCount[{}]) Start {}.{}",
                new Object[] { port, httpServerConfig.getWorkerCount(), msg, spanStr, ex });

        serverStatus = chnmsg;
        serverStartTime = DateStringUtil.DEFAULT.now();
        if (ex != null) {
            System.exit(1);
        }

    }

    private void initOutter(Runnable runnable) {
        if (AssertUtil.isNotNull(runnable)) {
            long begin = System.currentTimeMillis();
            try {
                runnable.run(); //?
            } catch (Throwable t) {
                t.printStackTrace();
                System.exit(1); //??
            }
            System.out.println("------------------------------> initRunable: "
                    + (System.currentTimeMillis() - begin) + "MS");
        }
    }

    public static ApplicationContext main(String[] args, String... springConfigLocations) {
        return main(args, null, null, springConfigLocations);
    }

    public static ApplicationContext main(String[] args, Runnable initRunnable, String... springConfigLocations) {
        return main(args, initRunnable, null, springConfigLocations);
    }

    /**
     * Puppy??
     * @param args ??
     * @param initRunnable Puppy??
     * @param shutdownRunnable Puppy
     * @param springConfigLocations
     * @return
     */
    public static ApplicationContext main(String[] args, Runnable initRunnable, Runnable shutdownRunnable,
            String... springConfigLocations) {
        long begin = System.currentTimeMillis();
        try {
            context = SpringBootstrap.load(springConfigLocations);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.exit(1); //Spring??
        }

        System.out.println("------------------------------> Spring?,: "
                + (System.currentTimeMillis() - begin) + "MS");

        Bootstrap bootstrap = BeanUtil.getTypedBean("bootstrap");
        bootstrap.shutdownRunnable = shutdownRunnable;

        if (isArgWhat(args, "stop", "shutdown")) {
            bootstrap.sendShutdownCmd();
            System.exit(0);
        } else {
            if (!isArgWhat(args, "compelled", "force")) {
                bootstrap.bindRetryTimeout = 0;
            }
            bootstrap.startHttpServer(initRunnable);
        }

        // http://www.kammerl.de/ascii/AsciiSignature.php
        // choose font: doom
        String ascii_doom = "______                            \n" + "| ___ \\                           \n"
                + "| |_/ /_   _  _ __   _ __   _   _ \n" + "|  __/| | | || '_ \\ | '_ \\ | | | |\n"
                + "| |   | |_| || |_) || |_) || |_| |\n" + "\\_|    \\__,_|| .__/ | .__/  \\__, |\n"
                + "             | |    | |      __/ |\n" + "             |_|    |_|     |___/ ";

        System.out.println(ascii_doom + " [" + bootstrap.serverStartTime + "]??...");
        return context;
    }

    public long sendShutdownCmd() {
        HttpURLConnection urlConnection = null;
        LineNumberReader lineReader = null;
        try {
            URL url = new URL("http://localhost:" + httpServerConfig.getListenPort() + "/shutdown");
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setReadTimeout(10000);
            lineReader = new LineNumberReader(new InputStreamReader(urlConnection.getInputStream()));
            StringBuilder sb = new StringBuilder();
            while (true) {
                String tmp = lineReader.readLine();
                if (tmp == null) {
                    break;
                }
                sb.append(tmp);
            }
            String returnStr = sb.toString();
            log.error("shutdown last result:{}", returnStr);
            return Long.valueOf(returnStr);
        } catch (Exception e) {
            log.error("", e);
        } finally {
            if (AssertUtil.isNotNull(urlConnection)) {
                urlConnection.disconnect();
                urlConnection = null;
            }
            CloseableHelper.closeSilently(lineReader);
        }
        return 0;
    }

    public void stopServer() {
        if (AssertUtil.isNull(serverChannelFuture) || !serverChannelFuture.isSuccess()) {
            System.out.println(
                    "------------------------------> ?????...");
        }
        long begin = System.currentTimeMillis();
        stopping = true;
        if (AssertUtil.isNotNull(shutdownRunnable)) {
            shutdownRunnable.run();
            System.out
                    .println("------------------------------> ???: "
                            + (System.currentTimeMillis() - begin) + "MS");
        }
        try {
            serverChannelFuture.channel().closeFuture();
            httpServerConfig.stopEventLoopGroup();
            //?
            statisticManager.writeStatisticData();
            Thread.currentThread().sleep(1000); //???"ByeBye"
        } catch (Throwable e) {
            e.printStackTrace();
        }
        String ascii_doom = "______              ______              \n"
                + "| ___ \\             | ___ \\             \n" + "| |_/ / _   _   ___ | |_/ / _   _   ___ \n"
                + "| ___ \\| | | | / _ \\| ___ \\| | | | / _ \\\n" + "| |_/ /| |_| ||  __/| |_/ /| |_| ||  __/\n"
                + "\\____/  \\__, | \\___|\\____/  \\__, | \\___|\n" + "         __/ |               __/ |      \n"
                + "        |___/               |___/       ";
        System.out.println(ascii_doom);
    }

    public boolean isStopping() {
        return stopping;
    }

    public String getServerStartTime() {
        return serverStartTime;
    }
}