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

cool.scx.core.Scx Maven / Gradle / Ivy

package cool.scx.core;

import cool.scx.config.ScxConfig;
import cool.scx.config.ScxConfigSource;
import cool.scx.config.ScxEnvironment;
import cool.scx.config.ScxFeatureConfig;
import cool.scx.core.enumeration.ScxCoreFeature;
import cool.scx.core.eventbus.ZeroCopyMessageCodec;
import cool.scx.core.scheduler.ScxScheduler;
import cool.scx.data.jdbc.AnnotationConfigTable;
import cool.scx.data.jdbc.JDBCContext;
import cool.scx.data.jdbc.meta_data.SchemaHelper;
import cool.scx.data.jdbc.sql.SQLRunner;
import cool.scx.mvc.ScxMvc;
import cool.scx.mvc.ScxMvcOptions;
import cool.scx.mvc.websocket.WebSocketRouter;
import cool.scx.util.NetUtils;
import cool.scx.util.StopWatch;
import cool.scx.util.ansi.Ansi;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.net.JksOptions;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

import javax.sql.DataSource;
import java.lang.System.Logger;
import java.net.BindException;
import java.util.Arrays;
import java.util.List;

import static cool.scx.core.ScxHelper.*;
import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.WARNING;

/**
 * 启动类
 *
 * @author scx567888
 * @version 0.3.6
 */
public final class Scx {

    private static final Logger logger = System.getLogger(Scx.class.getName());

    private final ScxEnvironment scxEnvironment;

    private final String appKey;

    private final ScxFeatureConfig scxFeatureConfig;

    private final ScxConfig scxConfig;

    private final ScxModule[] scxModules;

    private final ScxOptions scxOptions;

    private final Vertx vertx;

    private final DefaultListableBeanFactory beanFactory;

    private final ScxMvc scxMvc;

    private final ScxScheduler scxScheduler;

    private JDBCContext jdbcContext = null;

    private ScxHttpRouter scxHttpRouter = null;

    private WebSocketRouter webSocketRouter = null;

    private HttpServer vertxHttpServer = null;

    Scx(ScxEnvironment scxEnvironment, String appKey, ScxFeatureConfig scxFeatureConfig, ScxConfigSource[] scxConfigSources, ScxModule[] scxModules) {
        //0, 赋值到全局
        ScxContext.scx(this);
        //1, 初始化基本参数
        this.scxEnvironment = scxEnvironment;
        this.appKey = appKey;
        this.scxFeatureConfig = scxFeatureConfig;
        this.scxConfig = new ScxConfig(scxConfigSources);
        this.scxModules = initScxModuleMetadataList(scxModules);
        this.scxOptions = new ScxOptions(this.scxConfig, this.scxEnvironment, this.appKey);
        //2, 初始化 ScxLog 日志框架
        initScxLoggerFactory(this.scxConfig, this.scxEnvironment);
        //3, 初始化 Vertx 这里在 log4j2 之后初始化是因为 vertx 需要使用 log4j2 打印日志
        this.vertx = initVertx();
        //4, 初始化事件总线
        ZeroCopyMessageCodec.registerCodec(this.vertx.eventBus());
        //5, 初始化 BeanFactory
        this.beanFactory = initBeanFactory(this.scxModules, this.vertx.nettyEventLoopGroup(), this.scxFeatureConfig);
        //7, 初始化 MVC
        this.scxMvc = new ScxMvc(new ScxMvcOptions().templateRoot(scxOptions.templateRoot()).useDevelopmentErrorPage(scxFeatureConfig.get(ScxCoreFeature.USE_DEVELOPMENT_ERROR_PAGE)));
        //8, 初始化任务调度器
        this.scxScheduler = new ScxScheduler(this.vertx.nettyEventLoopGroup());
    }

    public static ScxBuilder builder() {
        return new ScxBuilder();
    }

    /**
     * 执行模块启动的生命周期
     */
    private void startAllScxModules() {
        if (this.scxFeatureConfig.get(ScxCoreFeature.SHOW_MODULE_LIFE_CYCLE_INFO)) {
            for (var m : scxModules) {
                Ansi.out().brightWhite("[").brightGreen("Starting").brightWhite("] " + m.name()).println();
                m.start(this);
                Ansi.out().brightWhite("[").brightGreen("Start OK").brightWhite("] " + m.name()).println();
            }
        } else {
            for (var m : scxModules) {
                m.start(this);
            }
        }
    }

    /**
     * 执行模块结束的生命周期
     */
    private void stopAllScxModules() {
        if (this.scxFeatureConfig.get(ScxCoreFeature.SHOW_MODULE_LIFE_CYCLE_INFO)) {
            for (var m : scxModules) {
                Ansi.out().brightWhite("[").brightRed("Stopping").brightWhite("] " + m.name()).println();
                m.stop(this);
                Ansi.out().brightWhite("[").brightRed("Stop  OK").brightWhite("] " + m.name()).println();
            }
        } else {
            for (var m : scxModules) {
                m.stop(this);
            }
        }
    }

    /**
     * 运行项目
     */
    public void run() {
        //0, 启动 核心计时器
        StopWatch.start("ScxRun");
        //1, 根据配置打印一下 banner 或者配置文件信息之类
        if (this.scxFeatureConfig.get(ScxCoreFeature.SHOW_BANNER)) {
            ScxVersion.printBanner();
        }
        if (this.scxFeatureConfig.get(ScxCoreFeature.SHOW_OPTIONS_INFO)) {
            this.scxOptions.printInfo();
        }
        //2, 初始化路由器 (Http 和 WebSocket)
        this.scxHttpRouter = new ScxHttpRouter(this);
        this.webSocketRouter = new WebSocketRouter();
        //3, 注册 路由
        var classList = Arrays.stream(this.scxModules()).flatMap(c -> c.classList().stream()).toList();
        this.scxMvc.bindErrorHandler(this.scxHttpRouter).registerHttpRoutes(scxHttpRouter, beanFactory, classList).registerWebSocketRoutes(webSocketRouter, beanFactory, classList);
        //4, 依次执行 模块的 start 生命周期 , 在这里我们可以操作 scxRouteRegistry, vertxRouter 等对象 "手动注册新路由" 或其他任何操作
        this.startAllScxModules();
        //5, 打印基本信息
        if (this.scxFeatureConfig.get(ScxCoreFeature.SHOW_START_UP_INFO)) {
            Ansi.out()
                    .brightYellow("已加载 " + this.beanFactory.getBeanDefinitionNames().length + " 个 Bean !!!").ln()
                    .brightGreen("已加载 " + this.scxHttpRouter.getRoutes().size() + " 个 Http 路由 !!!").ln()
                    .brightBlue("已加载 " + this.webSocketRouter.getRoutes().size() + " 个 WebSocket 路由 !!!").println();
        }
        //6, 初始化服务器
        var httpServerOptions = new HttpServerOptions();
        if (this.scxOptions.isHttpsEnabled()) {
            httpServerOptions.setSsl(true)
                    .setKeyStoreOptions(new JksOptions()
                            .setPath(this.scxOptions.sslPath().toString())
                            .setPassword(this.scxOptions.sslPassword()));
        }
        this.vertxHttpServer = vertx.createHttpServer(httpServerOptions);
        this.vertxHttpServer.requestHandler(this.scxHttpRouter).webSocketHandler(this.webSocketRouter);
        //7, 添加程序停止时的钩子函数
        this.addShutdownHook();
        //8, 使用初始端口号 启动服务器
        this.startServer(this.scxOptions.port());
        //9, 此处刷新 scxBeanFactory 使其实例化所有符合条件的 Bean
        this.beanFactory.preInstantiateSingletons();
    }

    /**
     * 启动服务器
     *
     * @param port a int
     */
    private void startServer(int port) {
        var listenFuture = this.vertxHttpServer.listen(port);
        listenFuture.onSuccess(server -> {
            var httpOrHttps = this.scxOptions.isHttpsEnabled() ? "https" : "http";
            var o = Ansi.out().green("服务器启动成功... 用时 " + StopWatch.stopToMillis("ScxRun") + " ms").ln();
            var normalIP = NetUtils.getLocalIPAddress().getNormalIP();
            for (var ip : normalIP) {
                o.green("> 网络: " + httpOrHttps + "://" + ip + ":" + server.actualPort() + "/").ln();
            }
            o.green("> 本地: " + httpOrHttps + "://localhost:" + server.actualPort() + "/").println();
        }).onFailure(cause -> {
            if (cause instanceof BindException) {
                //获取新的端口号然后 重新启动服务器
                if (isUseNewPort(port)) {
                    startServer(0);
                }
            } else {
                cause.printStackTrace();
            }
        });
    }

    private void addShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            this.stopAllScxModules();
            Ansi.out().red("项目正在停止!!!").println();
        }));
    }

    /**
     * 检查数据源是否可用
     *
     * @return b
     */
    public boolean checkDataSource() {
        try (var conn = dataSource().getConnection()) {
            var dm = conn.getMetaData();
            logger.log(DEBUG, "数据源连接成功 : 类型 [{0}]  版本 [{1}]", dm.getDatabaseProductName(), dm.getDatabaseProductVersion());
            return true;
        } catch (Exception e) {
            dataSourceExceptionHandler(e);
            return false;
        }
    }

    /**
     * 

fixTable.

*/ public void fixTable() { logger.log(DEBUG, "修复数据表结构中..."); //修复成功的表 var fixSuccess = 0; //修复失败的表 var fixFail = 0; //不需要修复的表 var noNeedToFix = 0; for (var v : getAllScxBaseModelClassList()) { //根据 class 获取 tableInfo var tableInfo = new AnnotationConfigTable(v); try { if (SchemaHelper.checkNeedFixTable(tableInfo, dataSource())) { SchemaHelper.fixTable(tableInfo, jdbcContext); fixSuccess = fixSuccess + 1; } else { noNeedToFix = noNeedToFix + 1; } } catch (Exception e) { e.printStackTrace(); fixFail = fixFail + 1; } } if (fixSuccess != 0) { logger.log(DEBUG, "修复成功 {0} 张表...", fixSuccess); } if (fixFail != 0) { logger.log(WARNING, "修复失败 {0} 张表...", fixFail); } if (fixSuccess + fixFail == 0) { logger.log(DEBUG, "没有表需要修复..."); } } /** * 获取所有 class * * @return s */ private List> getAllScxBaseModelClassList() { return Arrays.stream(scxModules) .flatMap(c -> c.classList().stream()) .filter(ScxHelper::isScxBaseModelClass)// 继承自 BaseModel .toList(); } /** * 检查是否有任何 (BaseModel) 类需要修复表 * * @return 是否有 */ public boolean checkNeedFixTable() { logger.log(DEBUG, "检查数据表结构中..."); for (var v : getAllScxBaseModelClassList()) { //根据 class 获取 tableInfo var tableInfo = new AnnotationConfigTable(v); try { //有任何需要修复的直接 返回 true if (SchemaHelper.checkNeedFixTable(tableInfo, dataSource())) { return true; } } catch (Exception e) { e.printStackTrace(); } } return false; } @SuppressWarnings("unchecked") public T findScxModule(Class clazz) { for (var m : this.scxModules) { if (m.getClass() == clazz) { return (T) m; } } return null; } public ScxModule[] scxModules() { return Arrays.copyOf(scxModules, scxModules.length); } public ScxEnvironment scxEnvironment() { return scxEnvironment; } public Vertx vertx() { return vertx; } public String appKey() { return appKey; } public ScxOptions scxOptions() { return scxOptions; } public DefaultListableBeanFactory beanFactory() { return beanFactory; } public ScxHttpRouter scxHttpRouter() { return this.scxHttpRouter; } public ScxConfig scxConfig() { return scxConfig; } public ScxFeatureConfig scxFeatureConfig() { return scxFeatureConfig; } public DataSource dataSource() { return jdbcContext().dataSource(); } public SQLRunner sqlRunner() { return jdbcContext().sqlRunner(); } public JDBCContext jdbcContext() { if (jdbcContext == null) { //1, 初始化数据源及 sqlRunner var dataSource = initDataSource(this.scxOptions, this.scxFeatureConfig); this.jdbcContext = new JDBCContext(dataSource); } return jdbcContext; } public HttpServer vertxHttpServer() { return vertxHttpServer; } public EventBus eventBus() { return vertx.eventBus(); } public WebSocketRouter webSocketRouter() { return webSocketRouter; } public ScxMvc scxMvc() { return scxMvc; } public ScxScheduler scxScheduler() { return scxScheduler; } public T getBean(Class requiredType) { return beanFactory.getBean(requiredType); } }




© 2015 - 2026 Weber Informatics LLC | Privacy Policy