All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.blade.server.netty.NettyServer Maven / Gradle / Ivy

package com.blade.server.netty;

import com.blade.Blade;
import com.blade.Environment;
import com.blade.event.BeanProcessor;
import com.blade.event.EventType;
import com.blade.exception.ExceptionResolve;
import com.blade.ioc.BeanDefine;
import com.blade.ioc.DynamicContext;
import com.blade.ioc.Ioc;
import com.blade.ioc.OrderComparator;
import com.blade.ioc.annotation.Bean;
import com.blade.ioc.reader.ClassInfo;
import com.blade.kit.BladeKit;
import com.blade.kit.NamedThreadFactory;
import com.blade.kit.ReflectKit;
import com.blade.kit.StringKit;
import com.blade.mvc.WebContext;
import com.blade.mvc.annotation.Path;
import com.blade.mvc.hook.WebHook;
import com.blade.mvc.route.RouteBuilder;
import com.blade.mvc.route.RouteMatcher;
import com.blade.mvc.ui.DefaultUI;
import com.blade.mvc.ui.template.DefaultEngine;
import com.blade.server.Server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.blade.mvc.Const.*;

/**
 * @author biezhi
 *         2017/5/31
 */
@Slf4j
public class NettyServer implements Server {

    private Blade            blade;
    private Environment      environment;
    private EventLoopGroup   bossGroup;
    private EventLoopGroup   workerGroup;
    private ExecutorService  bossExecutors;
    private ExecutorService  workerExecutors;
    private int              threadCount;
    private int              workers;
    private int              backlog;
    private Channel          channel;
    private RouteBuilder     routeBuilder;
    private ExceptionResolve exceptionResolve;

    @Override
    public void start(Blade blade, String[] args) throws Exception {
        this.blade = blade;
        this.environment = blade.environment();

        long initStart = System.currentTimeMillis();
        log.info("Environment: jdk.version\t\t\t=> {}", System.getProperty("java.version"));
        log.info("Environment: user.dir\t\t\t=> {}", System.getProperty("user.dir"));
        log.info("Environment: java.io.tmpdir\t\t=> {}", System.getProperty("java.io.tmpdir"));
        log.info("Environment: user.timezone\t\t=> {}", System.getProperty("user.timezone"));
        log.info("Environment: file.encoding\t\t=> {}", System.getProperty("file.encoding"));
        log.info("Environment: classpath\t\t\t=> {}", CLASSPATH);

        this.loadConfig(args);

        this.initConfig();

        WebContext.init(blade, "/", false);

        this.initIoc();

        this.startServer(initStart);

    }

    private void initIoc() {
        RouteMatcher routeMatcher = blade.routeMatcher();
        routeMatcher.initMiddleware(blade.middleware());

        routeBuilder = new RouteBuilder(routeMatcher);

        blade.scanPackages().stream()
                .flatMap(DynamicContext::recursionFindClasses)
                .map(ClassInfo::getClazz)
                .filter(ReflectKit::isNormalClass)
                .forEach(this::parseCls);

        routeMatcher.register();

        beanProcessors.stream().sorted(new OrderComparator<>()).forEach(b -> b.preHandle(blade));

        Ioc ioc = blade.ioc();
        if (BladeKit.isNotEmpty(ioc.getBeans())) {
            log.info("⬢ Register bean: {}", ioc.getBeans());
        }

        List beanDefines = ioc.getBeanDefines();
        if (BladeKit.isNotEmpty(beanDefines)) {
            beanDefines.forEach(b -> BladeKit.injection(ioc, b));
        }

        beanProcessors.stream().sorted(new OrderComparator<>()).forEach(b -> b.processor(blade));

    }

    private void startServer(long startTime) throws Exception {
        // Configure SSL.
        SslContext sslCtx = null;
        boolean    SSL    = false;

        // Configure the server.
        this.bossGroup = new NioEventLoopGroup(threadCount, bossExecutors);
        this.workerGroup = new NioEventLoopGroup(workers, workerExecutors);

        ServerBootstrap b = new ServerBootstrap();
        b.option(ChannelOption.SO_BACKLOG, backlog);
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .childHandler(new HttpServerInitializer(blade, exceptionResolve, sslCtx));

        String address = environment.get(ENV_KEY_SERVER_ADDRESS, DEFAULT_SERVER_ADDRESS);
        int    port    = environment.getInt(ENV_KEY_SERVER_PORT, DEFAULT_SERVER_PORT);

        channel = b.bind(address, port).sync().channel();
        String appName = environment.get(ENV_KEY_APP_NAME, "Blade");

        log.info("⬢ {} initialize successfully, Time elapsed: {} ms", appName, System.currentTimeMillis() - startTime);
        log.info("⬢ Blade start with {}:{}", address, port);
        log.info("⬢ Open your web browser and navigate to {}://{}:{} ⚡", (SSL ? "https" : "http"), address.replace("0.0.0.0", "127.0.0.1"), port);

        blade.eventManager().fireEvent(EventType.SERVER_STARTED, blade);
    }

    private List beanProcessors = new ArrayList<>();

    private void parseCls(Class clazz) {
        if (null != clazz.getAnnotation(Bean.class)) blade.register(clazz);
        if (null != clazz.getAnnotation(Path.class)) {
            if (null == clazz.getAnnotation(Bean.class)) {
                blade.register(clazz);
            }
            Object controller = blade.ioc().getBean(clazz);
            routeBuilder.addRouter(clazz, controller);
        }
        if (ReflectKit.hasInterface(clazz, WebHook.class) && null != clazz.getAnnotation(Bean.class)) {
            Object hook = blade.ioc().getBean(clazz);
            routeBuilder.addWebHook(clazz, hook);
        }
        if (ReflectKit.hasInterface(clazz, BeanProcessor.class) && null != clazz.getAnnotation(Bean.class)) {
            beanProcessors.add((BeanProcessor) blade.ioc().getBean(clazz));
        }
        if (ReflectKit.hasInterface(clazz, ExceptionResolve.class) && null != clazz.getAnnotation(Bean.class)) {
            this.exceptionResolve = (ExceptionResolve) blade.ioc().getBean(clazz);
        }
    }

    private void loadConfig(String[] args) {

        String bootConf = blade.environment().get(ENV_KEY_BOOT_CONF, "classpath:app.properties");

        Environment bootEnv = Environment.of(bootConf);

        if (bootEnv != null) {
            bootEnv.props().forEach((key, value) -> environment.set(key.toString(), value));
        }

        blade.register(environment);

        // load terminal param
        if (!BladeKit.isEmpty(args)) {
            for (int i = 0; i < args.length; i++) {
                if (args[i].startsWith(TERMINAL_SERVER_ADDRESS)) {
                    int    pos     = args[i].indexOf(TERMINAL_SERVER_ADDRESS) + TERMINAL_SERVER_ADDRESS.length();
                    String address = args[i].substring(pos);
                    environment.set(ENV_KEY_SERVER_ADDRESS, address);
                } else if (args[i].startsWith(TERMINAL_SERVER_PORT)) {
                    int    pos  = args[i].indexOf(TERMINAL_SERVER_PORT) + TERMINAL_SERVER_PORT.length();
                    String port = args[i].substring(pos);
                    environment.set(ENV_KEY_SERVER_PORT, port);
                }
            }
        }
    }

    private void initConfig() {

        if (null != blade.bootClass()) {
            blade.scanPackages(blade.bootClass().getPackage().getName());
        }

        DefaultUI.printBanner();

        String statics = environment.get(ENV_KEY_STATIC_DIRS, "");
        if (StringKit.isNotBlank(statics)) {
            blade.addStatics(statics.split(","));
        }

        String templatePath = environment.get(ENV_KEY_TEMPLATE_PATH, "templates");
        if (templatePath.charAt(0) == '/') {
            templatePath = templatePath.substring(1);
        }
        if (templatePath.endsWith("/")) {
            templatePath = templatePath.substring(0, templatePath.length() - 1);
        }
        DefaultEngine.TEMPLATE_PATH = templatePath;

        String boosGroupName   = environment.get(ENV_KEY_NETTY_BOOS_GROUP_NAME, "pool");
        String workerGroupName = environment.get(ENV_KEY_NETTY_WORKER_GROUP_NAME, "pool");

        bossExecutors = Executors.newCachedThreadPool(new NamedThreadFactory("boss@" + boosGroupName));
        workerExecutors = Executors.newCachedThreadPool(new NamedThreadFactory("worker@" + workerGroupName));

        threadCount = environment.getInt(ENV_KEY_NETTY_THREAD_COUNT, 1);
        workers = environment.getInt(ENV_KEY_NETTY_WORKERS, 0);
        backlog = environment.getInt(ENV_KEY_NETTY_SO_BACKLOG, 1024);
    }

    @Override
    public void stop() {
        if (this.bossGroup != null) {
            this.bossGroup.shutdownGracefully();
        }
        if (this.workerGroup != null) {
            this.workerGroup.shutdownGracefully();
        }
        if (bossExecutors != null) {
            bossExecutors.shutdown();
        }
        if (workerExecutors != null) {
            workerExecutors.shutdown();
        }
    }

    @Override
    public void join() throws InterruptedException {
        try {
            channel.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
            this.stop();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy