From 28cb764c81b7c0c59cabfb4bfb630757d40dd2a3 Mon Sep 17 00:00:00 2001 From: QAIU <736226400@qq.com> Date: Thu, 20 Apr 2023 17:42:39 +0800 Subject: [PATCH] first commit ZZZzzzzzz --- .gitignore | 36 ++ bin/run.bat | 5 + bin/run.sh | 5 + core/README.md | 9 + core/pom.xml | 127 ++++++ .../main/java/cn/com/yhinfo/core/Deploy.java | 159 ++++++++ .../yhinfo/core/annotaions/DateFormat.java | 18 + .../yhinfo/core/annotaions/RouteHandler.java | 25 ++ .../yhinfo/core/annotaions/RouteMapping.java | 45 ++ .../com/yhinfo/core/annotaions/Service.java | 18 + .../core/annotaions/SockRouteMapper.java | 17 + .../yhinfo/core/base/BaseAsyncService.java | 39 ++ .../cn/com/yhinfo/core/base/BaseHttpApi.java | 34 ++ .../cn/com/yhinfo/core/enums/MIMEType.java | 39 ++ .../cn/com/yhinfo/core/enums/RouteMethod.java | 11 + .../handlerfactory/RouterHandlerFactory.java | 383 ++++++++++++++++++ .../yhinfo/core/interceptor/Interceptor.java | 19 + .../cn/com/yhinfo/core/model/JsonResult.java | 160 ++++++++ .../yhinfo/core/util/AsyncServiceUtil.java | 20 + .../cn/com/yhinfo/core/util/CastUtil.java | 18 + .../cn/com/yhinfo/core/util/CommonUtil.java | 129 ++++++ .../cn/com/yhinfo/core/util/ConfigUtil.java | 60 +++ .../com/yhinfo/core/util/LocalConstant.java | 43 ++ .../cn/com/yhinfo/core/util/ParamUtil.java | 45 ++ .../com/yhinfo/core/util/ReflectionUtil.java | 270 ++++++++++++ .../com/yhinfo/core/util/SharedDataUtil.java | 67 +++ .../yhinfo/core/util/SnowflakeIdWorker.java | 243 +++++++++++ .../cn/com/yhinfo/core/util/StringCase.java | 110 +++++ .../cn/com/yhinfo/core/util/VertxHolder.java | 26 ++ .../core/verticle/ReverseProxyVerticle.java | 263 ++++++++++++ .../yhinfo/core/verticle/RouterVerticle.java | 74 ++++ .../yhinfo/core/verticle/ServiceVerticle.java | 51 +++ core/src/main/resources/logback.xml | 59 +++ logs/20230420/run.log | 0 pom.xml | 120 ++++++ web/assembly.xml | 13 + web/logs/20230420/run.log | 0 web/pom.xml | 198 +++++++++ .../common/model/MyDataParametersMapper.java | 28 ++ .../real/common/model/MyDataRowMapper.java | 29 ++ .../model/UserInfoParametersMapper.java | 30 ++ .../real/common/model/UserInfoRowMapper.java | 35 ++ .../web/service/DbServiceVertxEBProxy.java | 89 ++++ .../service/DbServiceVertxProxyHandler.java | 140 +++++++ .../web/service/UserServiceVertxEBProxy.java | 76 ++++ .../service/UserServiceVertxProxyHandler.java | 135 ++++++ .../main/java/cn/com/yhinfo/real/AppMain.java | 29 ++ .../cn/com/yhinfo/real/common/ToJson.java | 22 + .../interceptorImpl/DefaultInterceptor.java | 45 ++ .../com/yhinfo/real/common/model/MyData.java | 34 ++ .../yhinfo/real/common/model/UserInfo.java | 40 ++ .../yhinfo/real/common/util/ArrayUtil.java | 20 + .../yhinfo/real/common/util/ConnectUtil.java | 19 + .../java/cn/com/yhinfo/real/package-info.java | 10 + .../com/yhinfo/real/web/http/ServerApi.java | 76 ++++ .../com/yhinfo/real/web/model/RealUser.java | 22 + .../yhinfo/real/web/service/DbService.java | 19 + .../real/web/service/JdkProxyFactory.java | 18 + .../real/web/service/ServiceJdkProxy.java | 29 ++ .../yhinfo/real/web/service/UserService.java | 17 + .../real/web/service/impl/DbServiceImpl.java | 38 ++ .../web/service/impl/UserServiceImpl.java | 22 + web/src/main/resources/1.http | 163 ++++++++ web/src/main/resources/2.http | 47 +++ web/src/main/resources/app-dev.yml | 33 ++ web/src/main/resources/app.yml | 7 + web/src/main/resources/conf/dictionaries.json | 3 + web/src/main/resources/curl/curl.sh | 3 + web/src/main/resources/server-proxy.yml | 30 ++ .../java/cn/com/yhinfo/test/StompTest.java | 42 ++ .../test/java/cn/com/yhinfo/test/Test01.java | 223 ++++++++++ .../test/java/cn/com/yhinfo/test/Test02.java | 85 ++++ .../test/java/cn/com/yhinfo/test/TestOS.java | 161 ++++++++ .../cn/com/yhinfo/test/WebProxyExamples.java | 169 ++++++++ webroot/err/404.html | 15 + webroot/test/sockTest.html | 52 +++ webroot/test/sockjs-min.js | 27 ++ webroot/test2/index.html | 15 + 78 files changed, 5055 insertions(+) create mode 100644 .gitignore create mode 100644 bin/run.bat create mode 100644 bin/run.sh create mode 100644 core/README.md create mode 100644 core/pom.xml create mode 100644 core/src/main/java/cn/com/yhinfo/core/Deploy.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/annotaions/DateFormat.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/annotaions/RouteHandler.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/annotaions/RouteMapping.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/annotaions/Service.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/annotaions/SockRouteMapper.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/base/BaseAsyncService.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/base/BaseHttpApi.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/enums/MIMEType.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/enums/RouteMethod.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/handlerfactory/RouterHandlerFactory.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/interceptor/Interceptor.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/model/JsonResult.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/AsyncServiceUtil.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/CastUtil.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/CommonUtil.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/ConfigUtil.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/LocalConstant.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/ParamUtil.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/ReflectionUtil.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/SharedDataUtil.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/SnowflakeIdWorker.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/StringCase.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/util/VertxHolder.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/verticle/ReverseProxyVerticle.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/verticle/RouterVerticle.java create mode 100644 core/src/main/java/cn/com/yhinfo/core/verticle/ServiceVerticle.java create mode 100644 core/src/main/resources/logback.xml create mode 100644 logs/20230420/run.log create mode 100644 pom.xml create mode 100644 web/assembly.xml create mode 100644 web/logs/20230420/run.log create mode 100644 web/pom.xml create mode 100644 web/src/main/generated/cn/com/yhinfo/real/common/model/MyDataParametersMapper.java create mode 100644 web/src/main/generated/cn/com/yhinfo/real/common/model/MyDataRowMapper.java create mode 100644 web/src/main/generated/cn/com/yhinfo/real/common/model/UserInfoParametersMapper.java create mode 100644 web/src/main/generated/cn/com/yhinfo/real/common/model/UserInfoRowMapper.java create mode 100644 web/src/main/generated/cn/com/yhinfo/real/web/service/DbServiceVertxEBProxy.java create mode 100644 web/src/main/generated/cn/com/yhinfo/real/web/service/DbServiceVertxProxyHandler.java create mode 100644 web/src/main/generated/cn/com/yhinfo/real/web/service/UserServiceVertxEBProxy.java create mode 100644 web/src/main/generated/cn/com/yhinfo/real/web/service/UserServiceVertxProxyHandler.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/AppMain.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/common/ToJson.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/common/interceptorImpl/DefaultInterceptor.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/common/model/MyData.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/common/model/UserInfo.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/common/util/ArrayUtil.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/common/util/ConnectUtil.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/package-info.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/web/http/ServerApi.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/web/model/RealUser.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/web/service/DbService.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/web/service/JdkProxyFactory.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/web/service/ServiceJdkProxy.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/web/service/UserService.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/web/service/impl/DbServiceImpl.java create mode 100644 web/src/main/java/cn/com/yhinfo/real/web/service/impl/UserServiceImpl.java create mode 100644 web/src/main/resources/1.http create mode 100644 web/src/main/resources/2.http create mode 100644 web/src/main/resources/app-dev.yml create mode 100644 web/src/main/resources/app.yml create mode 100644 web/src/main/resources/conf/dictionaries.json create mode 100644 web/src/main/resources/curl/curl.sh create mode 100644 web/src/main/resources/server-proxy.yml create mode 100644 web/src/test/java/cn/com/yhinfo/test/StompTest.java create mode 100644 web/src/test/java/cn/com/yhinfo/test/Test01.java create mode 100644 web/src/test/java/cn/com/yhinfo/test/Test02.java create mode 100644 web/src/test/java/cn/com/yhinfo/test/TestOS.java create mode 100644 web/src/test/java/cn/com/yhinfo/test/WebProxyExamples.java create mode 100644 webroot/err/404.html create mode 100644 webroot/test/sockTest.html create mode 100644 webroot/test/sockjs-min.js create mode 100644 webroot/test2/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e80c373 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# IntelliJ IDEA # +.idea/ +/.idea/ +*/.idea/ +*.iws +*.iml +*.ipr + +# Eclipse Project files +.classpath +.project +/.settings/ +.settings/ + +# Java class files +*.class + +# Generated files +*/bin/ +*/gen/ +*/out/ + +### user ### +target/ +/target/ +/src/logs/ +*.zip +sdkTest.log + + +#some local files +*/.DS_Store +.DS_Store +gradlew +gradlew.bat +unused.txt diff --git a/bin/run.bat b/bin/run.bat new file mode 100644 index 0000000..bf1e123 --- /dev/null +++ b/bin/run.bat @@ -0,0 +1,5 @@ +@echo off && @chcp 65001 > nul +pushd %~dp0 +set LIB_DIR=%~dp0 +for /f "delims=X" %%i in ('dir /b %LIB_DIR%\web-*.jar') do set LAUNCH_JAR=%LIB_DIR%\%%i +"%JAVA_HOME%\bin\java.exe" -Xmx512M -Dfile.encoding=utf8 -jar %LAUNCH_JAR% %* \ No newline at end of file diff --git a/bin/run.sh b/bin/run.sh new file mode 100644 index 0000000..3d3f13c --- /dev/null +++ b/bin/run.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# set -x +LAUNCH_JAR="web-*.jar" +nohup java -Xmx512M -jar "$LAUNCH_JAR" "$@" >startup.log 2>&1 & +tail -f startup.log \ No newline at end of file diff --git a/core/README.md b/core/README.md new file mode 100644 index 0000000..7979452 --- /dev/null +++ b/core/README.md @@ -0,0 +1,9 @@ +TODO + +- Interceptor重构 -> (0%) +- 可配置的反向代理服务器 -> (70%) +- SQL-gen/ORM/JSON-Model -> (1%) +- 注解式APO -> (0%) +- 注解式eventbus,sockjs-bridge -> (10%) +- Code-gen TemplateEngine -> (1%) +- HTML TemplateEngine -> (0%) \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 0000000..0a01d68 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,127 @@ + + + + lz-cow-api + cn.qaiu + 0.0.1 + + 4.0.0 + 1.0.8 + core + + + 17 + UTF-8 + 4.1.3 + 0.9.12 + 1.18.12 + 2.0.5 + 3.8.1 + 2.11.3 + + + + + + io.vertx + vertx-dependencies + ${vertx.version} + pom + import + + + + + + + + ch.qos.logback + logback-classic + 1.4.6 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + io.vertx + vertx-web + + + io.vertx + vertx-codegen + + + io.vertx + vertx-config + + + io.vertx + vertx-config-yaml + + + io.vertx + vertx-service-proxy + + + io.vertx + vertx-jdbc-client + + + + io.vertx + vertx-sql-client-templates + + + + io.vertx + vertx-auth-jwt + + + io.vertx + vertx-web-proxy + + + io.vertx + vertx-stomp + + + org.reflections + reflections + ${org.reflections.version} + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + commons-beanutils + commons-beanutils + 1.9.4 + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + + diff --git a/core/src/main/java/cn/com/yhinfo/core/Deploy.java b/core/src/main/java/cn/com/yhinfo/core/Deploy.java new file mode 100644 index 0000000..eb0ce52 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/Deploy.java @@ -0,0 +1,159 @@ +package cn.com.yhinfo.core; + +import cn.com.yhinfo.core.util.ConfigUtil; +import cn.com.yhinfo.core.util.VertxHolder; +import cn.com.yhinfo.core.verticle.ReverseProxyVerticle; +import cn.com.yhinfo.core.verticle.ServiceVerticle; +import cn.com.yhinfo.core.verticle.RouterVerticle; +import io.vertx.core.*; +import io.vertx.core.json.JsonObject; +import io.vertx.core.shareddata.LocalMap; +import io.vertx.core.shareddata.SharedData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.management.ManagementFactory; +import java.util.Calendar; +import java.util.Date; + +/** + * vertx启动类 需要在主启动类完成回调 + *
Create date 2021-05-07 10:26:54 + * + * @author QAIU + */ +public final class Deploy { + + private static final Deploy INSTANCE = new Deploy(); + private static final Logger LOGGER = LoggerFactory.getLogger(Deploy.class); + private static final long startTime = System.currentTimeMillis(); + + private final Vertx tempVertx = Vertx.vertx(); + StringBuilder path = new StringBuilder("app"); + + private JsonObject customConfig; + private Handler handle; + + public static Deploy instance() { + return INSTANCE; + } + + public void start(String[] args, Handler handle) { + this.handle = handle; + if (args.length > 0) { + // 启动参数dev或者prod + path.append("-").append(args[0]); + } + + // 读取yml配置 + ConfigUtil.readYamlConfig(path.toString(), tempVertx) + .onSuccess(this::readConf) + .onFailure(Throwable::printStackTrace); + } + + private void readConf(JsonObject conf) { + outLogo(conf); + String activeMode = conf.getString("active"); + if ("dev".equals(activeMode)) { + LOGGER.info("---------------> development environment <--------------\n"); + System.setProperty("vertxweb.environment","dev"); + } else { + LOGGER.info("---------------> Production environment <--------------\n"); + } + ConfigUtil.readYamlConfig(path + "-" + activeMode, tempVertx).onSuccess(this::deployVerticle); + } + + /** + * 打印logo + */ + private void outLogo(JsonObject conf) { + Date date = new Date(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int year = calendar.get(Calendar.YEAR); + String logoTemplete = "\nWeb Server powered by: \n" + + " ____ ____ _ _ _ \n" + + "|_^^_| |_^^_| / |_ | | | | \n" + + " \\ \\ / /.---. _ .--.`| |-' _ __ | |__| |_ \n" + + " \\ \\ / // /__\\\\[ `/'`\\]| | [ \\ [ ]|____ _| \n" + + " \\ V / | \\__., | | | |, _ > ' < _| |_ \n" + + " \\_/ '.__.'[___] \\__/(_)[__]`\\_] |_____| \n" + + " Version: %s; Framework version: %s; %s©%d.\n\n"; + + System.out.printf(logoTemplete, + conf.getString("version_app"), + conf.getString("version_vertx"), + conf.getString("copyright"), + year + ); + } + + /** + * 部署Verticle + * + * @param globalConfig 配置 + */ + private void deployVerticle(JsonObject globalConfig) { + tempVertx.close(); + LOGGER.info("配置读取成功"); + customConfig = globalConfig.getJsonObject("custom"); + + VertxOptions vertxOptions = new VertxOptions(globalConfig.getJsonObject("vertx")); + Vertx vertx = Vertx.vertx(vertxOptions); + VertxHolder.init(vertx); + //配置保存在共享数据中 + SharedData sharedData = vertx.sharedData(); + LocalMap localMap = sharedData.getLocalMap("local"); + localMap.put("globalConfig", globalConfig); + localMap.put("customConfig", customConfig); + localMap.put("server", globalConfig.getJsonObject("server")); + handle.handle(globalConfig); + + Future future1 = vertx.deployVerticle(RouterVerticle.class, getWorkDeploymentOptions("Router")); + Future future2 = vertx.deployVerticle(ServiceVerticle.class, getWorkDeploymentOptions("Service")); + Future future3 = vertx.deployVerticle(ReverseProxyVerticle.class, getWorkDeploymentOptions("proxy")); + + CompositeFuture.all(future1, future2, future3) + .onSuccess(this::deployWorkVerticalSuccess) + .onFailure(this::deployVerticalFailed); + } + + /** + * 部署失败 + * + * @param throwable Exception信息 + */ + private void deployVerticalFailed(Throwable throwable) { + LOGGER.error(throwable.getClass().getName() + ": " + throwable.getMessage()); + System.exit(-1); + } + + /** + * 启动时间信息 + * + * @param compositeFuture future wraps a list + */ + private void deployWorkVerticalSuccess(CompositeFuture compositeFuture) { + double t1 = ((double) (System.currentTimeMillis() - startTime)) / 1000; + double t2 = ((double) System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime()) / 1000; + LOGGER.info("web服务启动成功 -> 用时: {}s, jvm启动用时: {}s", t1, t2); + } + + /** + * deploy Verticle Options + * + * @param name the worker pool name + * @return Deployment Options + */ + private DeploymentOptions getWorkDeploymentOptions(String name) { + return getWorkDeploymentOptions(name, customConfig.getInteger("asyncServiceInstances")); + } + + private DeploymentOptions getWorkDeploymentOptions(String name, int ins) { + return new DeploymentOptions() + .setWorkerPoolName(name) + .setWorker(true) + .setInstances(ins); + } + +} diff --git a/core/src/main/java/cn/com/yhinfo/core/annotaions/DateFormat.java b/core/src/main/java/cn/com/yhinfo/core/annotaions/DateFormat.java new file mode 100644 index 0000000..b7e67f1 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/annotaions/DateFormat.java @@ -0,0 +1,18 @@ +package cn.com.yhinfo.core.annotaions; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 日期参数格式化注解 + *
Create date 2021-05-06 09:20:37 + * + * @author QAIU + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface DateFormat { + String value() default "yyyy-MM-dd HH:mm:ss"; +} diff --git a/core/src/main/java/cn/com/yhinfo/core/annotaions/RouteHandler.java b/core/src/main/java/cn/com/yhinfo/core/annotaions/RouteHandler.java new file mode 100644 index 0000000..e1e555a --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/annotaions/RouteHandler.java @@ -0,0 +1,25 @@ +package cn.com.yhinfo.core.annotaions; + +import java.lang.annotation.*; + +/** + * Web Router API类 标识注解 + *
Create date 2021-04-30 09:22:18 + * + * @author QAIU + */ +@Documented +@Inherited +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface RouteHandler { + + String value() default ""; + + boolean isOpen() default false; + + /** + * 注册顺序,数字越大越先注册 + */ + int order() default 0; +} diff --git a/core/src/main/java/cn/com/yhinfo/core/annotaions/RouteMapping.java b/core/src/main/java/cn/com/yhinfo/core/annotaions/RouteMapping.java new file mode 100644 index 0000000..97bb762 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/annotaions/RouteMapping.java @@ -0,0 +1,45 @@ +package cn.com.yhinfo.core.annotaions; + +import cn.com.yhinfo.core.enums.MIMEType; +import cn.com.yhinfo.core.enums.RouteMethod; + +import java.lang.annotation.*; + +/** + * Router API Method 标识注解 + *
Create date 2021-05-06 09:20:37 + * + * @author QAIU + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RouteMapping { + + String value() default ""; + + /** + * 使用http method + */ + RouteMethod method() default RouteMethod.GET; + + /** + * 接口description + */ + String description() default ""; + + /** + * 注册顺序,数字越大越先注册 + */ + int order() default 0; + + /** + * 响应类型 + */ + MIMEType responseMimeType() default MIMEType.APPLICATION_JSON; + + /** + * 请求类型 + */ + MIMEType requestMIMEType() default MIMEType.ALL; +} diff --git a/core/src/main/java/cn/com/yhinfo/core/annotaions/Service.java b/core/src/main/java/cn/com/yhinfo/core/annotaions/Service.java new file mode 100644 index 0000000..4b2e396 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/annotaions/Service.java @@ -0,0 +1,18 @@ +package cn.com.yhinfo.core.annotaions; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 服务实现层注解(XXServiceImpl) + *
Create date 2021/8/25 15:57 + * + * @author QAIU + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Service { + String name() default ""; +} diff --git a/core/src/main/java/cn/com/yhinfo/core/annotaions/SockRouteMapper.java b/core/src/main/java/cn/com/yhinfo/core/annotaions/SockRouteMapper.java new file mode 100644 index 0000000..7665c2e --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/annotaions/SockRouteMapper.java @@ -0,0 +1,17 @@ +package cn.com.yhinfo.core.annotaions; + +import java.lang.annotation.*; + +/** + * WebSocket api 注解类 + *
Create date 2021/8/25 15:57 + * + * @author QAIU + */ +@Documented +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface SockRouteMapper { + String value() default ""; +} diff --git a/core/src/main/java/cn/com/yhinfo/core/base/BaseAsyncService.java b/core/src/main/java/cn/com/yhinfo/core/base/BaseAsyncService.java new file mode 100644 index 0000000..1376398 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/base/BaseAsyncService.java @@ -0,0 +1,39 @@ +package cn.com.yhinfo.core.base; + +import cn.com.yhinfo.core.util.CastUtil; + +import java.util.Arrays; + +/** + * 反射获取父接口类名辅助类, 异步服务需要实现此接口 + * + * @author QAIU + */ +public interface BaseAsyncService { + + /** + * 获取异步服务接口地址 + * @see BaseAsyncService#getAsyncInterfaceClass + * @return 服务接口类名 + * @throws ClassNotFoundException 接口不存在 + */ + default String getAddress() throws ClassNotFoundException { + return getAsyncInterfaceClass().getName(); + } + + /** + * 获取异步服务接口地址 + * @return 服务接口类对象 + * @throws ClassNotFoundException 接口不存在 + */ + default Class getAsyncInterfaceClass() throws ClassNotFoundException { + // 获取实现接口 作为地址注册到EventBus + Class clazz = CastUtil.cast(Arrays.stream(this.getClass().getInterfaces()).filter( + clz-> Arrays.asList(clz.getInterfaces()).contains(BaseAsyncService.class) + ).findFirst().orElse(null)); + if (clazz == null) { + throw new ClassNotFoundException("No interface found: \""+this.getClass().getName()+"\" need to implement interface"); + } + return clazz; + } +} \ No newline at end of file diff --git a/core/src/main/java/cn/com/yhinfo/core/base/BaseHttpApi.java b/core/src/main/java/cn/com/yhinfo/core/base/BaseHttpApi.java new file mode 100644 index 0000000..b3f47a5 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/base/BaseHttpApi.java @@ -0,0 +1,34 @@ +package cn.com.yhinfo.core.base; + +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; + +import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; + +/** + * 统一响应处理 + *
Create date 2021-05-06 09:20:37 + * + * @author QAIU + */ +public interface BaseHttpApi { + + default void fireJsonResponse(RoutingContext ctx, JsonObject jsonResult) { + ctx.response().putHeader(CONTENT_TYPE, "application/json; charset=utf-8") + .setStatusCode(200) + .end(jsonResult.encode()); + } + + default void fireJsonResponse(RoutingContext ctx, T jsonResult) { + JsonObject jsonObject = JsonObject.mapFrom(jsonResult); + fireJsonResponse(ctx, jsonObject); + } + + default void fireTextResponse(RoutingContext ctx, String text) { + ctx.response().putHeader("content-type", "text/html; charset=utf-8").end(text); + } + + default void sendError(int statusCode, RoutingContext ctx) { + ctx.response().setStatusCode(statusCode).end(); + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/enums/MIMEType.java b/core/src/main/java/cn/com/yhinfo/core/enums/MIMEType.java new file mode 100644 index 0000000..b480ac9 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/enums/MIMEType.java @@ -0,0 +1,39 @@ +package cn.com.yhinfo.core.enums; + +/** + * MIMEType: request or response head + *
Create date 2021/8/30 4:35 + * + * @author QAIU + */ +public enum MIMEType { + + NULL(""), + ALL("*/*"), + TEXT_HTML("text/html"), + APPLICATION_POSTSCRIPT("application/postscript"), + TEXT_PLAIN("text/plain"), + APPLICATION_X_WWW_FORM_URLENCODED("application/x-www-form-urlencoded"), + APPLICATION_OCTET_STREAM("application/octet-stream"), + MULTIPART_FORM_DATA("multipart/form-data"), + APPLICATION_X_JAVA_AGENT("application/x-java-agent"), + MESSAGE_HTTP("message/http"), + TEXT_CSS("text/css"), + TEXT_XML("text/xml"), + TEXT("text/*"), + APPLICATION_RDF_XML("application/rdf+xml"), + APPLICATION_XHTML_XML("application/xhtml+xml"), + APPLICATION_XML("application/xml"), + APPLICATION_JSON("application/json"); + + public String getValue() { + return type; + } + + private final String type; + + MIMEType(String type) { + this.type = type; + } + +} diff --git a/core/src/main/java/cn/com/yhinfo/core/enums/RouteMethod.java b/core/src/main/java/cn/com/yhinfo/core/enums/RouteMethod.java new file mode 100644 index 0000000..fc34d62 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/enums/RouteMethod.java @@ -0,0 +1,11 @@ +package cn.com.yhinfo.core.enums; + +/** + * Router API 请求处理方式枚举 + *
Create date 2021-04-30 09:22:18 + * + * @author QAIU + */ +public enum RouteMethod { + OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, PATCH, ROUTE +} diff --git a/core/src/main/java/cn/com/yhinfo/core/handlerfactory/RouterHandlerFactory.java b/core/src/main/java/cn/com/yhinfo/core/handlerfactory/RouterHandlerFactory.java new file mode 100644 index 0000000..8b888a9 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/handlerfactory/RouterHandlerFactory.java @@ -0,0 +1,383 @@ +package cn.com.yhinfo.core.handlerfactory; + +import cn.com.yhinfo.core.annotaions.DateFormat; +import cn.com.yhinfo.core.annotaions.RouteHandler; +import cn.com.yhinfo.core.annotaions.RouteMapping; +import cn.com.yhinfo.core.annotaions.SockRouteMapper; +import cn.com.yhinfo.core.base.BaseHttpApi; +import cn.com.yhinfo.core.enums.MIMEType; +import cn.com.yhinfo.core.model.JsonResult; +import cn.com.yhinfo.core.util.*; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.ext.web.handler.CorsHandler; +import io.vertx.ext.web.handler.sockjs.SockJSHandler; +import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions; +import javassist.CtClass; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.reflections.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static io.vertx.core.http.HttpHeaders.*; + +/** + * 路由映射, 参数绑定 + *
Create date 2021-05-07 10:26:54 + * + * @author QAIU + */ +public class RouterHandlerFactory implements BaseHttpApi { + private static final Logger LOGGER = LoggerFactory.getLogger(RouterHandlerFactory.class); + + private static final Set httpMethods = new HashSet() {{ + add(HttpMethod.GET); + add(HttpMethod.POST); + add(HttpMethod.OPTIONS); + add(HttpMethod.PUT); + add(HttpMethod.DELETE); + add(HttpMethod.HEAD); + }}; + // 需要扫描注册的Router路径 + private static volatile Reflections reflections; + + private final String gatewayPrefix; + + public RouterHandlerFactory(String routerScanAddress, String gatewayPrefix) { + Objects.requireNonNull(routerScanAddress, "The router package address scan is empty."); + Objects.requireNonNull(gatewayPrefix, "The gateway prefix is empty."); + reflections = ReflectionUtil.getReflections(routerScanAddress); + this.gatewayPrefix = gatewayPrefix; + } + + /** + * 开始扫描并注册handler + */ + public Router createRouter() { + Router router = Router.router(VertxHolder.getVertxInstance()); + router.route().handler(ctx -> { + LOGGER.debug("The HTTP service request address information ===>path:{}, uri:{}, method:{}", + ctx.request().path(), ctx.request().absoluteURI(), ctx.request().method()); + ctx.response().headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*"); + ctx.response().headers().add(ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, OPTIONS, PUT, DELETE, HEAD"); + ctx.response().headers().add(ACCESS_CONTROL_ALLOW_HEADERS, "X-PINGOTHER, Origin,Content-Type, Accept, X-Requested-With, Dev, Authorization, Version, Token"); + ctx.response().headers().add(ACCESS_CONTROL_MAX_AGE, "1728000"); + ctx.next(); + }); + // 添加跨域的方法 + router.route().handler(CorsHandler.create("*").allowCredentials(true).allowedMethods(httpMethods)); + + // 配置文件上传路径 + router.route().handler(BodyHandler.create().setUploadsDirectory("uploads")); + + + try { + Set> handlers = reflections.getTypesAnnotatedWith(RouteHandler.class); + Comparator> comparator = (c1, c2) -> { + RouteHandler routeHandler1 = c1.getAnnotation(RouteHandler.class); + RouteHandler routeHandler2 = c2.getAnnotation(RouteHandler.class); + return Integer.compare(routeHandler2.order(), routeHandler1.order()); + }; + // 获取处理器类列表 + List> sortedHandlers = handlers.stream().sorted(comparator).collect(Collectors.toList()); + for (Class handler : sortedHandlers) { + try { + // 注册请求处理方法 + registerNewHandler(router, handler); + } catch (Throwable e) { + LOGGER.error("Error register {}, Error details:", handler, e.getCause()); + } + } + } catch (Exception e) { + LOGGER.error("Manually Register Handler Fail, Error details:" + e.getMessage()); + } + // 错误请求处理 + router.errorHandler(405, ctx -> fireJsonResponse(ctx, JsonResult + .error("Method Not Allowed", 405))); + router.errorHandler(404, ctx -> ctx.response().setStatusCode(404).setChunked(true) + .end("Internal server error: 404 not found")); + return router; + } + + /** + * 注册handler + */ + private void registerNewHandler(Router router, Class handler) throws Throwable { + String root = getRootPath(handler); + Object instance = ReflectionUtil.newWithNoParam(handler); + Method[] methods = handler.getMethods(); + // 注册处理方法排序 + Comparator comparator = (m1, m2) -> { + RouteMapping mapping1 = m1.getAnnotation(RouteMapping.class); + RouteMapping mapping2 = m2.getAnnotation(RouteMapping.class); + return Integer.compare(mapping2.order(), mapping1.order()); + }; + List methodList = Stream.of(methods).filter( + method -> method.isAnnotationPresent(RouteMapping.class) + ).sorted(comparator).collect(Collectors.toList()); + + methodList.addAll(Stream.of(methods).filter( + method -> method.isAnnotationPresent(SockRouteMapper.class) + ).toList()); + + // 拦截器 + Handler interceptor = getInterceptor(); + // 依次注册处理方法 + for (Method method : methodList) { + if (method.isAnnotationPresent(RouteMapping.class)) { + // 普通路由 + RouteMapping mapping = method.getAnnotation(RouteMapping.class); + HttpMethod routeMethod = HttpMethod.valueOf(mapping.method().name()); + String routeUrl = getRouteUrl(method.getName(), mapping.value()); + String url = root.concat(routeUrl); + // 匹配方法 + Route route = router.route(routeMethod, url); + String mineType = mapping.requestMIMEType().getValue(); + LOGGER.info("route -> {}:{} -> {}", routeMethod.name(), url, mineType); + if (StringUtils.isNotEmpty(mineType)) { + route.consumes(mineType); + } + + // 先执行拦截方法, 再进入业务请求 + route.handler(interceptor); + route.handler(ctx -> handlerMethod(instance, method, ctx)).failureHandler(ctx -> { + if (ctx.response().ended()) return; + ctx.failure().printStackTrace(); + fireJsonResponse(ctx, JsonResult.error(ctx.failure().getMessage(), 500)); + }); + } else if (method.isAnnotationPresent(SockRouteMapper.class)) { + // websocket 基于sockJs + SockRouteMapper mapping = method.getAnnotation(SockRouteMapper.class); + String routeUrl = getRouteUrl(method.getName(), mapping.value()); + String url = root.concat(routeUrl); + LOGGER.info("Register New Websocket Handler -> {}", url); + SockJSHandlerOptions options = new SockJSHandlerOptions() + .setHeartbeatInterval(2000) + .setRegisterWriteHandler(true); + + SockJSHandler sockJSHandler = SockJSHandler.create(VertxHolder.getVertxInstance(), options); + Router route = sockJSHandler.socketHandler(sock -> { + try { + ReflectionUtil.invokeWithArguments(method, instance, sock); + } catch (Throwable e) { + e.printStackTrace(); + } + }); + router.mountSubRouter(url, route); + } + } + } + + + /** + * 获取并处理路由URL分隔符 + * + * @param methodName 路由method + * @return String + */ + private String getRouteUrl(String methodName, String mapperValue) { + String routeUrl; + if (mapperValue.startsWith("/:") || "/".equals(mapperValue)) { + routeUrl = (methodName + mapperValue); + } else if (mapperValue.startsWith("/")) { + routeUrl = mapperValue.substring(1); + } else { + routeUrl = mapperValue; + } + return routeUrl; + } + + /** + * 配置拦截 + * + * @return Handler + * @throws Throwable Throwable + */ + private Handler getInterceptor() throws Throwable { + // 配置拦截 + Class interceptorClass = Class.forName(SharedDataUtil.getValueForCustomConfig("interceptorClassPath")); + Object handleInstance = ReflectionUtil.newWithNoParam(interceptorClass); + Method doHandle = interceptorClass.getMethod("doHandle"); + // 反射调用 + return CastUtil.cast(ReflectionUtil.invoke(doHandle, handleInstance)); + } + + /** + * 获取请求根路径 + * + * @param handler handler + * @return 根路径 + */ + private String getRootPath(Class handler) { + // 处理请求路径前缀和后缀 + String root = gatewayPrefix; + if (!root.startsWith("/")) { + root = "/" + root; + } + if (!root.endsWith("/")) { + root = root + "/"; + } + // 子路径 + if (handler.isAnnotationPresent(RouteHandler.class)) { + RouteHandler routeHandler = handler.getAnnotation(RouteHandler.class); + String value = routeHandler.value(); + root += ("/".equals(value) ? "" : value); + } + if (!root.endsWith("/")) { + root = root + "/"; + } + + return root; + } + + /** + * 处理请求-参数绑定 + * + * @param instance 类实例 + * @param method 处理方法 + * @param ctx 路由上下文 + */ + private void handlerMethod(Object instance, Method method, RoutingContext ctx) { + // 方法参数名列表 + Map> methodParameters = ReflectionUtil.getMethodParameter(method); + Map> methodParametersTemp = new LinkedHashMap<>(methodParameters); + Map pathParamValues = ctx.pathParams(); + + // 参数名-参数值 + Map parameterValueList = new LinkedHashMap<>(); + methodParameters.forEach((k, v) -> parameterValueList.put(k, null)); + + //绑定rest路径变量 + if (!pathParamValues.isEmpty()) { + methodParameters.forEach((k, v) -> { + if (pathParamValues.containsKey(k)) { + methodParametersTemp.remove(k); + if (ReflectionUtil.isBasicType(v.getRight())) { + String fmt = getFmt(v.getLeft(), v.getRight()); + parameterValueList.put(k, ReflectionUtil.conversion(v.getRight(), pathParamValues.get(k), fmt)); + } else if (ReflectionUtil.isBasicTypeArray(v.getRight())) { + parameterValueList.put(k, ReflectionUtil.conversionArray(v.getRight(), pathParamValues.get(k))); + } else { + throw new RuntimeException("参数绑定异常: 类型不匹配"); + } + } + }); + } + + final MultiMap queryParams = ctx.queryParams(); + if ("POST".equals(ctx.request().method().name())) { + queryParams.addAll(ctx.request().params()); + } + + JsonArray entityPackagesReg = SharedDataUtil.getJsonArrayForCustomConfig("entityPackagesReg"); + // 绑定get或post请求头的请求参数 + methodParametersTemp.forEach((k, v) -> { + if (ReflectionUtil.isBasicType(v.getRight())) { + String fmt = getFmt(v.getLeft(), v.getRight()); + String value = queryParams.get(k); + parameterValueList.put(k, ReflectionUtil.conversion(v.getRight(), value, fmt)); + } else if ("io.vertx.ext.web.RoutingContext".equals(v.getRight().getName())) { + parameterValueList.put(k, ctx); + } else if ("io.vertx.core.http.HttpServerRequest".equals(v.getRight().getName())) { + parameterValueList.put(k, ctx.request()); + } else if ("io.vertx.core.http.HttpServerResponse".equals(v.getRight().getName())) { + parameterValueList.put(k, ctx.response()); + } else if (CommonUtil.matchRegList(entityPackagesReg.getList(), v.getRight().getName())) { + // 绑定实体类 + try { + Class aClass = Class.forName(v.getRight().getName()); + Object entity = ParamUtil.multiMapToEntity(queryParams, aClass); + parameterValueList.put(k, entity); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + // 解析body-json参数 + if ("application/json".equals(ctx.parsedHeaders().contentType().value()) && ctx.getBodyAsJson() != null) { + JsonObject body = ctx.getBodyAsJson(); + if (body != null) { + methodParametersTemp.forEach((k, v) -> { + // 只解析已配置包名前缀的实体类 + if (CommonUtil.matchRegList(entityPackagesReg.getList(), v.getRight().getName())) { + try { + Class aClass = Class.forName(v.getRight().getName()); + JsonObject data = CommonUtil.getSubJsonForEntity(body, aClass); + if (!data.isEmpty()) { + Object entity = data.mapTo(aClass); + parameterValueList.put(k, entity); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + } + }); + } + } + // 调用handle 获取响应对象 + Object[] parameterValueArray = parameterValueList.values().toArray(new Object[0]); + try { + // 反射调用 + Object data = ReflectionUtil.invokeWithArguments(method, instance, parameterValueArray); + if (data != null) { + if (data instanceof JsonResult) { + fireJsonResponse(ctx, data); + } else if (data instanceof Future) { // 处理异步响应 + ((Future) data).onSuccess(res -> { + if (res instanceof JsonObject) { + fireJsonResponse(ctx, res); + } else { + fireJsonResponse(ctx, JsonResult.data(res)); + } + }).onFailure(e -> fireJsonResponse(ctx, JsonResult.error(e.getMessage()))); + } else { + ctx.response().headers().set(CONTENT_TYPE, MIMEType.TEXT_HTML.getValue()); + ctx.end(data.toString()); + } + } + } catch (Throwable e) { + e.printStackTrace(); + String err = e.getMessage(); + if (e.getCause() != null) { + if (e.getCause() instanceof InvocationTargetException) { + err = ((InvocationTargetException)e.getCause()).getTargetException().getMessage(); + } else { + err = e.getCause().getMessage(); + } + } + fireJsonResponse(ctx, JsonResult.error(err)); + } + } + + /** + * 获取DateFormat注解值 + */ + private String getFmt(Annotation[] parameterAnnotations, CtClass v) { + String fmt = ""; + if ("java.util.Date".equals(v.getName())) { + for (Annotation annotation : parameterAnnotations) { + if (annotation instanceof DateFormat) { + fmt = ((DateFormat) annotation).value(); + } + } + } + return fmt; + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/interceptor/Interceptor.java b/core/src/main/java/cn/com/yhinfo/core/interceptor/Interceptor.java new file mode 100644 index 0000000..c5b327f --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/interceptor/Interceptor.java @@ -0,0 +1,19 @@ +package cn.com.yhinfo.core.interceptor; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +/** + * 拦截器接口 + *
Create date 2021-05-06 09:20:37 + * + * @author QAIU + */ +public interface Interceptor { + + default Handler doHandle() { + return this::handle; + } + + void handle(RoutingContext context); +} diff --git a/core/src/main/java/cn/com/yhinfo/core/model/JsonResult.java b/core/src/main/java/cn/com/yhinfo/core/model/JsonResult.java new file mode 100644 index 0000000..0f32879 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/model/JsonResult.java @@ -0,0 +1,160 @@ +package cn.com.yhinfo.core.model; + + +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * 响应实体 用于和前端交互 + *
Create date 2021-05-06 09:20:37 + * + * @author QAIU + */ +public class JsonResult implements Serializable { + + private static final long serialVersionUID = 1L; + + private static final int SUCCESS_CODE = 200; + + private static final int FAIL_CODE = 500; + + private static final String SUCCESS_MESSAGE = "success"; + + private static final String FAIL_MESSAGE = "failed"; + + private int code = SUCCESS_CODE;//状态码 + + private String msg = SUCCESS_MESSAGE;//消息 + + private boolean success = true; //是否成功 + + private int count; + + private T data; + + private long timestamp = System.currentTimeMillis(); //时间戳 + + public JsonResult() { + } + + public JsonResult(T data) { + this.data = data; + } + + public JsonResult(int code, String msg, boolean success, T data) { + this.code = code; + this.msg = msg; + this.data = data; + this.success = success; + } + + public JsonResult(int code, String msg, boolean success, T data, int count) { + this(code, msg, success, data); + this.count = count; + } + + public int getCount() { + return count; + } + + public JsonResult setCount(int count) { + this.count = count; + return this; + } + + public int getCode() { + return code; + } + + public JsonResult setCode(int code) { + this.code = code; + return this; + } + + public String getMsg() { + return msg; + } + + public JsonResult setMsg(String msg) { + this.msg = msg; + return this; + } + + public T getData() { + return data; + } + + public boolean getSuccess() { + return success; + } + + public long getTimestamp() { + return timestamp; + } + + public JsonResult setData(T data) { + this.data = data; + return this; + } + + public JsonResult setSuccess(boolean success) { + this.success = success; + return this; + } + + public JsonResult setTimestamp(long timestamp) { + this.timestamp = timestamp; + return this; + } + + // 响应失败消息 + public static JsonResult error() { + return new JsonResult<>(FAIL_CODE, FAIL_MESSAGE, false, null); + } + + // 响应失败消息 + public static JsonResult error(String msg) { + if (StringUtils.isEmpty(msg)) msg = FAIL_MESSAGE; + return new JsonResult<>(FAIL_CODE, msg, false, null); + } + + // 响应失败消息和状态码 + public static JsonResult error(String msg, int code) { + if (StringUtils.isEmpty(msg)) msg = FAIL_MESSAGE; + return new JsonResult<>(code, msg, false, null); + } + + // 响应成功消息和数据实体 + public static JsonResult data(String msg, T data) { + if (StringUtils.isEmpty(msg)) msg = SUCCESS_MESSAGE; + return new JsonResult<>(SUCCESS_CODE, msg, true, data); + } + + // 响应成功消息和数据实体 + public static JsonResult data(String msg, T data, int count) { + if (StringUtils.isEmpty(msg)) msg = SUCCESS_MESSAGE; + return new JsonResult<>(SUCCESS_CODE, msg, true, data, count); + } + + // 响应数据实体 + public static JsonResult data(T data) { + return new JsonResult<>(SUCCESS_CODE, SUCCESS_MESSAGE, true, data, 0); + } + + // 响应数据实体 + public static JsonResult data(T data, int count) { + return new JsonResult<>(SUCCESS_CODE, SUCCESS_MESSAGE, true, data, count); + } + + // 响应成功消息 + public static JsonResult success(String msg) { + if (StringUtils.isEmpty(msg)) msg = SUCCESS_MESSAGE; + return new JsonResult<>(SUCCESS_CODE, msg, true, null); + } + + // 响应成功消息 + public static JsonResult success() { + return new JsonResult<>(SUCCESS_CODE, SUCCESS_MESSAGE, true, null); + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/AsyncServiceUtil.java b/core/src/main/java/cn/com/yhinfo/core/util/AsyncServiceUtil.java new file mode 100644 index 0000000..3bcb476 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/AsyncServiceUtil.java @@ -0,0 +1,20 @@ +package cn.com.yhinfo.core.util; + +import io.vertx.core.Vertx; +import io.vertx.serviceproxy.ServiceProxyBuilder; + +/** + * @author Xu Haidong + * @date 2018/8/15 + */ +public final class AsyncServiceUtil { + + public static T getAsyncServiceInstance(Class asClazz, Vertx vertx) { + String address = asClazz.getName(); + return new ServiceProxyBuilder(vertx).setAddress(address).build(asClazz); + } + + public static T getAsyncServiceInstance(Class asClazz) { + return getAsyncServiceInstance(asClazz, VertxHolder.getVertxInstance()); + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/CastUtil.java b/core/src/main/java/cn/com/yhinfo/core/util/CastUtil.java new file mode 100644 index 0000000..a87adc0 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/CastUtil.java @@ -0,0 +1,18 @@ +package cn.com.yhinfo.core.util; + +/** + * 转换为任意类型 旨在消除泛型转换时的异常 + */ +public interface CastUtil { + + /** + * 泛型转换 + * @param object 要转换的object + * @param T + * @return T + */ + @SuppressWarnings("unchecked") + static T cast(Object object) { + return (T) object; + } +} \ No newline at end of file diff --git a/core/src/main/java/cn/com/yhinfo/core/util/CommonUtil.java b/core/src/main/java/cn/com/yhinfo/core/util/CommonUtil.java new file mode 100644 index 0000000..fcf83c6 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/CommonUtil.java @@ -0,0 +1,129 @@ +package cn.com.yhinfo.core.util; + +import io.vertx.core.buffer.Buffer; +import io.vertx.core.json.JsonObject; +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.beanutils.Converter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.Socket; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.List; +import java.util.Map; + +/** + * CommonUtil + *
Create date 2021/5/8 14:52 + * + * @author QAIU + */ +public class CommonUtil { + + private static final Logger LOGGER = LoggerFactory.getLogger(CommonUtil.class); + + /** + * 匹配正则list + * + * @param regList 正则list + * @param destStr 要匹配的目标字符 + * @return 匹配成功返回true 否则返回false + */ + public static boolean matchRegList(List regList, String destStr) { + // 判断是否忽略 + for (Object ignores : regList) { + if (destStr.matches(ignores.toString())) { + return true; + } + } + return false; + } + + + /** + * 测试本机端口是否被使用 + * + * @param port port + * @return boolean + */ + public static boolean isPortUsing(int port) { + //如果该端口还在使用则返回true,否则返回false,127.0.0.1代表本机 + return isPortUsing("127.0.0.1", port); + } + + /** + * 测试主机Host的port端口是否被使用 + * + * @param host host + * @param port port + * @throws UnknownHostException + */ + public static boolean isPortUsing(String host, int port) { + boolean flag = false; + InetAddress Address; + try { + Address = InetAddress.getByName(host); + } catch (UnknownHostException e) { + return false; + } + try(Socket ignored = new Socket(Address, port)) { + //建立一个Socket连接 + flag = true; + } catch (IOException ignoredException) {} + return flag; + } + + /** + * 获取实体类匹配的json字段组成的json + * + * @param jsonObject 要解析的json + * @param clazz 对应的实体类 + * @return 子json + */ + public static JsonObject getSubJsonForEntity(JsonObject jsonObject, Class clazz) { + JsonObject data = new JsonObject(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (jsonObject.containsKey(field.getName())) { + data.put(field.getName(), jsonObject.getValue(field.getName())); + } + } + return data; + } + + /** + * 注册枚举转换器 + * + * @param enums 枚举类 + */ + @SafeVarargs + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void enumConvert(Class... enums) { + for (Class anEnum : enums) { + ConvertUtils.register(new Converter() { + public Object convert(Class type, Object value) { + return Enum.valueOf(anEnum, (String) value); + } + }, anEnum); + } + } + + /** + * 处理其他配置 + * + * @param configName configName + */ + public static void initConfig(String configName, Class tClass) { + URL resource = tClass.getResource("/conf/" + configName); + if (resource == null) throw new RuntimeException("路径不存在"); + Buffer buffer = VertxHolder.getVertxInstance().fileSystem().readFileBlocking(resource.getPath()); + JsonObject entries = new JsonObject(buffer); + Map map = entries.getMap(); + LocalConstant.put(configName, map); + LOGGER.info("读取配置{}成功", configName); + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/ConfigUtil.java b/core/src/main/java/cn/com/yhinfo/core/util/ConfigUtil.java new file mode 100644 index 0000000..07ef740 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/ConfigUtil.java @@ -0,0 +1,60 @@ +package cn.com.yhinfo.core.util; + +import io.vertx.config.ConfigRetriever; +import io.vertx.config.ConfigRetrieverOptions; +import io.vertx.config.ConfigStoreOptions; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonObject; + +/** + * 异步读取配置工具类 + *
Create date 2021/9/2 1:23 + * + * @author QAIU + */ +public class ConfigUtil { + + /** + * 异步读取配置文件 + * + * @param format 配置文件格式 + * @param path 路径 + * @param vertx vertx + * @return JsonObject的Future + */ + public static Future readConfig(String format, String path, Vertx vertx) { + // 读取yml配置 + ConfigStoreOptions store = new ConfigStoreOptions() + .setType("file") + .setFormat(format) + .setConfig(new JsonObject().put("path", path)); + + ConfigRetriever retriever = ConfigRetriever + .create(vertx, new ConfigRetrieverOptions().addStore(store)); + + return retriever.getConfig(); + } + + + /** + * 异步读取Yaml配置文件 + * + * @param path 路径 + * @param vertx vertx + * @return JsonObject的Future + */ + synchronized public static Future readYamlConfig(String path, Vertx vertx) { + return readConfig("yaml", path+".yml", vertx); + } + + /** + * 异步读取Yaml配置文件 + * + * @param path 路径 + * @return JsonObject的Future + */ + synchronized public static Future readYamlConfig(String path) { + return readConfig("yaml", path+".yml", VertxHolder.getVertxInstance()); + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/LocalConstant.java b/core/src/main/java/cn/com/yhinfo/core/util/LocalConstant.java new file mode 100644 index 0000000..3fdc70c --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/LocalConstant.java @@ -0,0 +1,43 @@ +package cn.com.yhinfo.core.util; + +import java.util.HashMap; +import java.util.Map; + +/** + * vertx 上下文外的本地容器 为不在vertx线程的方法传递数据 + *
Create date 2021-05-07 10:26:54 + * + * @author QAIU + */ +public class LocalConstant { + private static final Map LOCAL_CONST = new HashMap<>(); + + public static Map put(String k, Object v) { + if (LOCAL_CONST.containsKey(k)) return LOCAL_CONST; + LOCAL_CONST.put(k, v); + return LOCAL_CONST; + } + + public static Object get(String k) { + return LOCAL_CONST.get(k); + } + + @SuppressWarnings("unchecked") + public static T getWithCast(String k) { + return (T) LOCAL_CONST.get(k); + } + + public static boolean containsKey(String k) { + return LOCAL_CONST.containsKey(k); + } + + public static Map getMap(String k) { + return (Map) LOCAL_CONST.get(k); + } + + public static String getString(String k) { + return LOCAL_CONST.get(k).toString(); + } + + +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/ParamUtil.java b/core/src/main/java/cn/com/yhinfo/core/util/ParamUtil.java new file mode 100644 index 0000000..5cf557f --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/ParamUtil.java @@ -0,0 +1,45 @@ +package cn.com.yhinfo.core.util; + +import io.vertx.core.MultiMap; +import org.apache.commons.beanutils.BeanUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +/** + * 参数 工具类 + *
Create date 2021-04-30 09:22:18 + * + * @author QAIU + */ +public final class ParamUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(ParamUtil.class); + + public static Map multiMapToMap(MultiMap multiMap) { + if (multiMap == null) return null; + Map map = new HashMap<>(); + for (Map.Entry entry : multiMap.entries()) { + map.put(entry.getKey(), entry.getValue()); + } + return map; + } + + public static T multiMapToEntity(MultiMap multiMap,Class tClass) throws NoSuchMethodException { + Map map = multiMapToMap(multiMap); + T obj = null; + try { + obj = tClass.getDeclaredConstructor().newInstance(); + BeanUtils.populate(obj,map); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + LOGGER.error("实例化异常"); + } catch (InvocationTargetException e2) { + e2.printStackTrace(); + LOGGER.error("map2bean转换异常"); + } + return obj; + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/ReflectionUtil.java b/core/src/main/java/cn/com/yhinfo/core/util/ReflectionUtil.java new file mode 100644 index 0000000..70d0043 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/ReflectionUtil.java @@ -0,0 +1,270 @@ +package cn.com.yhinfo.core.util; + +import javassist.*; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.LocalVariableAttribute; +import javassist.bytecode.MethodInfo; +import org.apache.commons.beanutils.ConversionException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.reflections.Reflections; +import org.reflections.scanners.*; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; +import org.reflections.util.FilterBuilder; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.text.ParseException; +import java.util.*; + +/** + * 基于org.reflection和javassist的反射工具包 + * 通过包扫描实现路由地址的注解映射 + *
Create date 2021-05-07 10:26:54 + * + * @author QAIU + */ +public final class ReflectionUtil { + + /** + * 获取反射器 + * + * @param packageAddress Package address String + * @return Reflections object + */ + public static Reflections getReflections(String packageAddress) { + List packageAddressList; + if (packageAddress.contains(",")) { + packageAddressList = Arrays.asList(packageAddress.split(",")); + } else if (packageAddress.contains(";")) { + packageAddressList = Arrays.asList(packageAddress.split(";")); + } else { + packageAddressList = Collections.singletonList(packageAddress); + } + return getReflections(packageAddressList); + } + + /** + * 获取反射器 + * + * @param packageAddresses Package address List + * @return Reflections object + */ + public static Reflections getReflections(List packageAddresses) { + ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + FilterBuilder filterBuilder = new FilterBuilder(); + packageAddresses.forEach(str -> { + Collection urls = ClasspathHelper.forPackage(str.trim()); + configurationBuilder.addUrls(urls); + filterBuilder.includePackage(str.trim()); + }); + + // 采坑记录 2021-05-08 + // 发现注解api层 没有继承父类时 这里反射一直有问题(Scanner SubTypesScanner was not configured) + // 因此这里需要手动配置各种Scanner扫描器 -- https://blog.csdn.net/qq_29499107/article/details/106889781 + configurationBuilder.setScanners( + new SubTypesScanner(false), //允许getAllTypes获取所有Object的子类, 不设置为false则 getAllTypes 会报错.默认为true. + new MethodParameterNamesScanner(), //设置方法参数名称 扫描器,否则调用getConstructorParamNames 会报错 + new MethodAnnotationsScanner(), //设置方法注解 扫描器, 否则getConstructorsAnnotatedWith,getMethodsAnnotatedWith 会报错 + new MemberUsageScanner(), //设置 member 扫描器,否则 getMethodUsage 会报错, 不推荐使用,有可能会报错 Caused by: java.lang.ClassCastException: javassist.bytecode.InterfaceMethodrefInfo cannot be cast to javassist.bytecode.MethodrefInfo + new TypeAnnotationsScanner() //设置类注解 扫描器 ,否则 getTypesAnnotatedWith 会报错 + ); + + configurationBuilder.filterInputsBy(filterBuilder); + return new Reflections(configurationBuilder); + } + + /** + * 获取指定类指定方法的参数名和类型列表(忽略重载方法) + * + * @param method 方法名(不考虑重载) + * @return 参数名类型map + * @apiNote .. + */ + public static Map> getMethodParameter(Method method) { + Map> paramMap = new LinkedHashMap<>(); + try { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = pool.get(method.getDeclaringClass().getName()); + CtMethod cm = ctClass.getDeclaredMethod(method.getName()); + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + MethodInfo methodInfo = cm.getMethodInfo(); + CtClass[] parameterTypes = cm.getParameterTypes(); + CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); + LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); + + boolean flag = true; + boolean flag2 = cm.getModifiers() - 1 != AccessFlag.STATIC; + for (int j = 0, k = 0; j < parameterTypes.length + k; j++) { + // 注意这里 只能从tableLength处获取 目前还没发现问题 + String name = attr.variableName(j); + if (!"this".equals(name) && flag && flag2) { + k++; + continue; + } + flag = false; + paramMap.put(attr.variableName(j + (flag2 ? 1 : 0)), Pair.of(parameterAnnotations[j - k], parameterTypes[j - k])); + } + } catch (NotFoundException e) { + e.printStackTrace(); + } + return paramMap; + } + + /** + * 类型转换: 字符串转对应类型 + * + * @param ctClass 目标类: javassist的ctClass类对象 + * @param value 字符串值 + * @param fmt 日期格式(如果value是日期的话这个字段将有用,否则置为null或空字符串) + * @return 基本类型或目标对象 + */ + public static Object conversion(CtClass ctClass, String value, String fmt) { + if (StringUtils.isEmpty(value)) { + return null; + } + if (StringUtils.isEmpty(fmt)) { + fmt = "yyyy-MM-dd"; + } + String name = ctClass.getName(); + if (ctClass.isArray()) { + name = ctClass.getName().substring(0, ctClass.getName().length() - 2); + } + switch (name) { + case "java.lang.Boolean": + case "boolean": + return Boolean.valueOf(value); + case "java.lang.Character": + case "char": + return value.charAt(0); + case "java.lang.Byte": + case "byte": + return Byte.valueOf(value); + case "java.lang.Short": + case "short": + return Short.valueOf(value); + case "java.lang.Integer": + case "int": + return Integer.valueOf(value); + case "java.lang.Long": + case "long": + return Long.valueOf(value); + case "java.lang.Float": + case "float": + return Float.valueOf(value); + case "java.lang.Double": + case "double": + return Double.valueOf(value); + case "java.lang.String": + return value; + case "java.util.Date": + try { + return DateUtils.parseDate(value, fmt); + } catch (ParseException e) { + e.printStackTrace(); + throw new ConversionException("无法将格式化日期"); + } + default: + throw new ConversionException("无法将String类型" + value + "转为[" + name + "]"); + } + } + + /** + * 数组类型的转换 + * + * @param ctClass 目标类: javassist的ctClass类对象 + * @param value 字符串表示的数组(逗号分隔符) + * @return Array + */ + public static Object conversionArray(CtClass ctClass, String value) { + if (!isBasicTypeArray(ctClass)) throw new ConversionException("无法解析数组"); + String[] strArr = value.split(","); + List obj = new ArrayList<>(); + Arrays.stream(strArr).forEach(v -> obj.add(conversion(ctClass, v, null))); + + try { + // 暂时这么处理 + String name = "[" + ((CtPrimitiveType) ctClass.getComponentType()).getDescriptor(); + Class cls = Class.forName(name).getComponentType(); + Object arr = Array.newInstance(cls, obj.size());//初始化对应类型的数组 + + for (int i = 0; i < obj.size(); i++) { + Array.set(arr, i, obj.get(i)); + } + return arr; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 判断是否是基本类型 8种原始类型和包装类以及String类型 返回true + * + * @return bool + */ + public static boolean isBasicType(CtClass ctClass) { + if (ctClass.isPrimitive() || "java.util.Date".equals(ctClass.getName())) { + return true; + } + return ctClass.getName().matches("^java\\.lang\\.((Boolean)|(Character)|(Byte)|(Short)|(Integer)|(Long)|(Float)|(Double)|(String))$"); + } + + /** + * 判断是否是基本类型数组 8种原始数组类型和String数组 返回true + * + * @return bool + */ + public static boolean isBasicTypeArray(CtClass ctClass) { + if (!ctClass.isArray()) { + return false; + } else return (ctClass.getName().matches("^(boolen|char|byte|short|int|long|float|double|String)\\[]$")); + } + + /** + * 反射通过无参构造创建对象 + * + * @param handler 类对象 + * @return 目标对象 + * @throws NoSuchMethodException NoSuchMethodException + * @throws InvocationTargetException InvocationTargetException + * @throws InstantiationException InstantiationException + * @throws IllegalAccessException IllegalAccessException + */ + public static T newWithNoParam(Class handler) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + return handler.getConstructor().newInstance(); + } + + /** + * 反射调用有参方法 + * + * @param method 方法类对象 + * @param instance 方法所在的对象实例 + * @param arguments 方法参数 + * @return 方法返回值 + * @throws Throwable Throwable + */ + public static Object invokeWithArguments(Method method, Object instance, Object... arguments) throws Throwable { + return MethodHandles.lookup().unreflect(method).bindTo(instance).invokeWithArguments(arguments); + } + + /** + * 反射调用无参方法 + * + * @param method 方法类对象 + * @param instance 方法所在的对象实例 + * @return 方法返回值 + * @throws Throwable Throwable + */ + public static Object invoke(Method method, Object instance) throws Throwable { + return MethodHandles.lookup().unreflect(method).bindTo(instance).invoke(); + } + +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/SharedDataUtil.java b/core/src/main/java/cn/com/yhinfo/core/util/SharedDataUtil.java new file mode 100644 index 0000000..b9c4714 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/SharedDataUtil.java @@ -0,0 +1,67 @@ +package cn.com.yhinfo.core.util; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.core.shareddata.LocalMap; +import io.vertx.core.shareddata.SharedData; + +/** + * vertx 共享数据 + *
Create date 2021-05-07 10:26:54 + * + * @author QAIU + */ +public class SharedDataUtil { + + private static final SharedData sharedData = VertxHolder.getVertxInstance().sharedData(); + + public static SharedData shareData() { + return sharedData; + } + + public static LocalMap getLocalMap(String key) { + return shareData().getLocalMap(key); + } + + public static LocalMap getLocalMapWithCast(String key) { + return sharedData.getLocalMap(key); + } + + public static JsonObject getJsonConfig(String key) { + LocalMap localMap = getLocalMap("local"); + return (JsonObject) localMap.get(key); + } + + public static JsonObject getJsonObjectForCustomConfig(String key) { + return getJsonConfig("customConfig").getJsonObject(key); + } + + public static String getJsonStringForCustomConfig(String key) { + return getJsonConfig("customConfig").getString(key); + } + + public static JsonArray getJsonArrayForCustomConfig(String key) { + return getJsonConfig("customConfig").getJsonArray(key); + } + + public static T getValueForCustomConfig(String key) { + return CastUtil.cast(getJsonConfig("customConfig").getValue(key)); + } + + public static JsonObject getJsonObjectForServerConfig(String key) { + return getJsonConfig("server").getJsonObject(key); + } + + public static JsonArray getJsonArrayForServerConfig(String key) { + return getJsonConfig("server").getJsonArray(key); + } + + public static String getJsonStringForServerConfig(String key) { + return getJsonConfig("server").getString(key); + } + + public static T getValueForServerConfig(String key) { + return CastUtil.cast(getJsonConfig("server").getValue(key)); + } + +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/SnowflakeIdWorker.java b/core/src/main/java/cn/com/yhinfo/core/util/SnowflakeIdWorker.java new file mode 100644 index 0000000..876a0ae --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/SnowflakeIdWorker.java @@ -0,0 +1,243 @@ +package cn.com.yhinfo.core.util; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.NetworkInterface; + +/** + * Twitter_Snowflake
+ * SnowFlake的结构如下(每部分用-分开):
+ * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
+ * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
+ * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) + * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
+ * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
+ * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
+ * 加起来刚好64位,为一个Long型。
+ * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 + * + *
Create date 2021-04-30 09:22:18 + * + * @author Twitter, QAIU + */ +public class SnowflakeIdWorker { + + private static final Logger logger = LoggerFactory.getLogger(SnowflakeIdWorker.class); + + // ==============================Fields=========================================== + + /** + * 机器id所占的位数 + */ + private final long workerIdBits = 5L; + + /** + * 数据标识id所占的位数 + */ + private final long datacenterIdBits = 5L; + + /** + * 工作机器ID(0~31) + */ + private final long workerId; + + /** + * 数据中心ID(0~31) + */ + private final long datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + private long sequence = 0L; + + /** + * 上次生成ID的时间截 + */ + private long lastTimestamp = -1L; + + //==============================Constructors===================================== + public SnowflakeIdWorker() { + this.datacenterId = getDatacenterId(); + this.workerId = getMaxWorkerId(datacenterId); + } + + /** + * 构造函数 + * + * @param workerId 工作ID (0~31) + * @param datacenterId 数据中心ID (0~31) + */ + public SnowflakeIdWorker(long workerId, long datacenterId) { + // 支持的最大机器id,结果是31 (这个移位算法可以很快地计算出几位二进制数所能表示的最大十进制数) + + long maxWorkerId = ~(-1L << workerIdBits); + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); + } + long maxDatacenterId = ~(-1L << datacenterIdBits); + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); + } + this.workerId = workerId; + this.datacenterId = datacenterId; + } + + // ==============================Methods========================================== + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + public synchronized long nextId() { + long timestamp = timeGen(); + + //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException( + String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } + + //如果是同一时间生成的,则进行毫秒内序列 + + //序列在id中占的位数 + long sequenceBits = 12L; + if (lastTimestamp == timestamp) { + //生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) + long sequenceMask = ~(-1L << sequenceBits); + sequence = (sequence + 1) & sequenceMask; + //毫秒内序列溢出 + if (sequence == 0) { + //阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } + //时间戳改变,毫秒内序列重置 + else { + sequence = 0L; + } + + //上次生成ID的时间截 + lastTimestamp = timestamp; + + //移位并通过或运算拼到一起组成64位的ID + //机器ID向左移12位 + //数据标识id向左移17位(12+5) + long datacenterIdShift = sequenceBits + workerIdBits; + + //时间截向左移22位(5+5+12) + long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + //开始时间截 (2021-01-01) + long twepoch = 1609459200000L; + return ((timestamp - twepoch) << timestampLeftShift) // + | (datacenterId << datacenterIdShift) // + | (workerId << sequenceBits) // + | sequence; + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + protected long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + protected long timeGen() { + return System.currentTimeMillis(); + } + + /** + *

+ * 获取 maxWorkerId + *

+ */ + protected static long getMaxWorkerId(long datacenterId) { + StringBuilder mpid = new StringBuilder(); + mpid.append(datacenterId); + String name = ManagementFactory.getRuntimeMXBean().getName(); + if (StringUtils.isNotEmpty(name)) { + //GET jvmPid + mpid.append(name.split("@")[0]); + } + //MAC + PID 的 hashcode 获取16个低位 + return (mpid.toString().hashCode() & 0xffff) % ((long) 31 + 1); + } + + /** + *

+ * 数据标识id部分 + *

+ */ + protected static long getDatacenterId() { + long id = 0L; + try { + InetAddress ip = InetAddress.getLocalHost(); + NetworkInterface network = NetworkInterface.getByInetAddress(ip); + if (network == null) { + id = 1L; + } else { + byte[] mac = network.getHardwareAddress(); + if (null != mac) { + id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; + id = id % ((long) 31 + 1); + } + } + } catch (Exception e) { + logger.warn(" getDatacenterId: " + e.getMessage()); + } + return id; + } + + private static final SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(); + private static volatile SnowflakeIdWorker snowflakeIdWorkerCluster = null; + + + synchronized public static SnowflakeIdWorker idWorker() { + return snowflakeIdWorker; + } + + synchronized public static SnowflakeIdWorker idWorkerCluster(long workerId, long datacenterId) { + if (snowflakeIdWorkerCluster == null) { + snowflakeIdWorkerCluster = new SnowflakeIdWorker(workerId, datacenterId); + } + return snowflakeIdWorkerCluster; + } + //==============================Test============================================= + + /** + * 测试 + */ + public static void main(String[] args) { + final SnowflakeIdWorker snowflakeIdWorkerCluster = idWorkerCluster(0, 1); + final SnowflakeIdWorker idWorker = idWorker(); + for (int i = 0; i < 100; i++) { + long id = idWorker.nextId(); + System.out.println(Long.toBinaryString(id)); + System.out.println(id); + System.out.println("------------"); + id = snowflakeIdWorkerCluster.nextId(); + System.out.println(Long.toBinaryString(id)); + System.out.println(id); + System.out.println("------------\n"); + } + + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/util/StringCase.java b/core/src/main/java/cn/com/yhinfo/core/util/StringCase.java new file mode 100644 index 0000000..fe82540 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/StringCase.java @@ -0,0 +1,110 @@ +package cn.com.yhinfo.core.util; + +import org.apache.commons.lang3.StringUtils; + + +/** + * 驼峰式下划线命名的字符串相互转换 + * + *
Create date 2021/6/2 0:41 + * @author QAIU + */ +public class StringCase { + + /** + * 将驼峰式命名的字符串转换为下划线方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
+ * 例如: + * + *
+     * HelloWorld=》hello_world
+     * Hello_World=》hello_world
+     * HelloWorld_test=》hello_world_test
+     * 
+ * + * @param str 转换前的驼峰式命名的字符串,也可以为下划线形式 + * @return 转换后下划线方式命名的字符串 + */ + public static String toUnderlineCase(String str) { + if (StringUtils.isEmpty(str)) return str; + StringBuilder sb = new StringBuilder(); + for (String s : StringUtils.splitByCharacterTypeCamelCase(str)) { + if (!s.startsWith("_")) { + sb.append(s.toLowerCase()).append("_"); + } else { + sb.append(s); + } + } + return sb.substring(0, sb.length() - 1); + } + + /** + * @param str 转换前的驼峰式命名的字符串,也可以为下划线形式 + * @return 转换后下划线方式命名的字符串(大写) + */ + public static String toUnderlineUpperCase(String str) { + return toUnderlineCase(str).toUpperCase(); + } + + public static String toCamelCase(String str, boolean isBigCamel) { + if (StringUtils.isEmpty(str)) { + return str; + } + StringBuilder sb = new StringBuilder(); + String[] split = StringUtils.split(str, '_'); + for (int i = 0; i < split.length; i++) { + String s = split[i]; + char[] chars = s.toCharArray(); + if ((i == 0 && isBigCamel) || (i > 0 && chars[0] >= 'a')) { + chars[0] -= ('a' - 'A'); + } + sb.append(new String(chars)); + } + return sb.toString(); + } + + + /** + * 将下划线方式命名的字符串转换为大驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world=》HelloWorld + * + * @param str 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String toBigCamelCase(String str) { + return toCamelCase(str, true); + } + + + /** + * 将下划线方式命名的字符串转换为小驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world=》helloWorld + * + * @param str 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String toLittleCamelCase(String str) { + return toCamelCase(str, false); + } + + public static void main(String[] args) { + // 下划线->驼峰 + System.out.println(toLittleCamelCase("my_name_qaiu")); + System.out.println(toLittleCamelCase(null)); + System.out.println(toLittleCamelCase(" ")); + System.out.println(toLittleCamelCase("")); + // 大驼峰 + System.out.println(toBigCamelCase("my_name_qaiu")); + System.out.println(toLittleCamelCase("____my_name_qaiu")); + + // 驼峰 ->下划线 + System.out.println(toUnderlineCase("MyNameQaiu")); + System.out.println(toUnderlineCase(null)); + System.out.println(toUnderlineCase(" ")); + System.out.println(toUnderlineCase("")); + System.out.println(toUnderlineCase("__my_nameQaiu___")); + // 大写下划线 + System.out.println(toUnderlineUpperCase("MyNameQaiu")); + System.out.println(toUnderlineUpperCase("__my_nameQaiu___")); + } + +} \ No newline at end of file diff --git a/core/src/main/java/cn/com/yhinfo/core/util/VertxHolder.java b/core/src/main/java/cn/com/yhinfo/core/util/VertxHolder.java new file mode 100644 index 0000000..805287e --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/util/VertxHolder.java @@ -0,0 +1,26 @@ +package cn.com.yhinfo.core.util; + +import io.vertx.core.Vertx; + +import java.util.Objects; + +/** + * 保存vertx实例 + *
Create date 2021-04-30 09:22:18 + * + * @author QAIU + */ +public final class VertxHolder { + + private static volatile Vertx singletonVertx; + + public static synchronized void init(Vertx vertx) { + Objects.requireNonNull(vertx, "未初始化Vertx"); + singletonVertx = vertx; + } + + public static Vertx getVertxInstance() { + Objects.requireNonNull(singletonVertx, "未初始化Vertx"); + return singletonVertx; + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/verticle/ReverseProxyVerticle.java b/core/src/main/java/cn/com/yhinfo/core/verticle/ReverseProxyVerticle.java new file mode 100644 index 0000000..d916f33 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/verticle/ReverseProxyVerticle.java @@ -0,0 +1,263 @@ +package cn.com.yhinfo.core.verticle; + +import cn.com.yhinfo.core.util.CastUtil; +import cn.com.yhinfo.core.util.ConfigUtil; +import cn.com.yhinfo.core.util.SharedDataUtil; +import cn.com.yhinfo.core.util.VertxHolder; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpServer; +import io.vertx.core.http.WebSocket; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.StaticHandler; +import io.vertx.ext.web.handler.sockjs.SockJSHandler; +import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions; +import io.vertx.ext.web.proxy.handler.ProxyHandler; +import io.vertx.httpproxy.HttpProxy; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; + +/** + *

反向代理服务

+ *

可以根据配置文件自动生成代理服务

+ *

可以配置多个服务, 配置文件见示例

+ *
Create date 2021/9/2 0:41 + * + * @author QAIU + */ +public class ReverseProxyVerticle extends AbstractVerticle { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReverseProxyVerticle.class); + + private static final String PATH_PROXY_CONFIG = SharedDataUtil.getJsonConfig("globalConfig").getString("proxyConf"); + private static final Future CONFIG = ConfigUtil.readYamlConfig(PATH_PROXY_CONFIG); + private static final String DEFAULT_PATH_404 = "webroot/err/404.html"; + + private static String serverName = "Vert.x-proxy-server"; //Server name in Http response header + + + @Override + public void start(Promise startPromise) throws Exception { + CONFIG.onSuccess(this::handleProxyConfList); + startPromise.complete(); + } + + /** + * 获取主配置文件 + * + * @param config proxy config + */ + private void handleProxyConfList(JsonObject config) { + serverName = config.getString("server-name"); + JsonArray proxyConfList = config.getJsonArray("proxy"); + + proxyConfList.forEach(proxyConf -> { + if (proxyConf instanceof JsonObject) { + handleProxyConf((JsonObject) proxyConf); + } + }); + } + + /** + * 处理单个反向代理配置 + * + * @param proxyConf 代理配置 + */ + private void handleProxyConf(JsonObject proxyConf) { + // 404 path + if (proxyConf.containsKey("404")) { + System.getProperty("user.dir"); + String path = proxyConf.getString("404"); + if (StringUtils.isEmpty(path)) { + proxyConf.put("404", DEFAULT_PATH_404); + } else { + if (!path.startsWith("/")) { + path = "/" + path; + } + if (!new File(System.getProperty("user.dir") + path).exists()) { + proxyConf.put("404", DEFAULT_PATH_404); + } + } + } else { + proxyConf.put("404", DEFAULT_PATH_404); + } + + final HttpClient httpClient = VertxHolder.getVertxInstance().createHttpClient(); + Router proxyRouter = Router.router(vertx); + + // Add Server name header + proxyRouter.route().handler(ctx -> { + ctx.response().putHeader("Server", serverName); + ctx.next(); + }); + + // http api proxy + if (proxyConf.containsKey("location")) { + handleLocation(proxyConf.getJsonArray("location"), httpClient, proxyRouter); + } + + // static server + if (proxyConf.containsKey("static")) { + handleStatic(proxyConf.getJsonObject("static"), proxyRouter); + } + + // static server + if (proxyConf.containsKey("sock")) { + handleSock(proxyConf.getJsonArray("sock"), httpClient, proxyRouter); + } + + // Send 404 page + proxyRouter.errorHandler(404, ctx -> { + ctx.response().sendFile(proxyConf.getString("404")); + }); + + HttpServer server = vertx.createHttpServer(); + server.requestHandler(proxyRouter); + + Integer port = proxyConf.getInteger("listen"); + LOGGER.info("proxy server start on {} port", port); + server.listen(port); + } + + /** + * 处理静态资源配置 + * + * @param staticConf 静态资源配置 + * @param proxyRouter 代理路由 + */ + private void handleStatic(JsonObject staticConf, Router proxyRouter) { + String path = staticConf.getString("path"); + proxyRouter.route(path + "*").handler(ctx -> { + if (staticConf.containsKey("add-headers")) { + Map headers = CastUtil.cast(staticConf.getJsonObject("add-headers").getMap()); + headers.forEach(ctx.response()::putHeader); + } + ctx.next(); + }); + + final StaticHandler staticHandler = StaticHandler.create(); + if (staticConf.containsKey("root")) { + staticHandler.setWebRoot(staticConf.getString("root")); + } + if (staticConf.containsKey("directory-listing")) { + staticHandler.setDirectoryListing(staticConf.getBoolean("directory-listing")); + } else if (staticConf.containsKey("index")) { + staticHandler.setIndexPage(staticConf.getString("index")); + } + proxyRouter.route(path + "*").handler(staticHandler); + } + + /** + * 处理Location配置 代理请求Location(和nginx类似?) + * + * @param locationsConf location配置 + * @param httpClient 客户端 + * @param proxyRouter 代理路由 + */ + private void handleLocation(JsonArray locationsConf, HttpClient httpClient, Router proxyRouter) { + + locationsConf.stream().map(e -> (JsonObject) e).forEach(location -> { + // 代理规则 + String origin = location.getString("origin"); + String path = location.getString("path"); + try { + URL url = new URL("https://" + origin); + String host = url.getHost(); + int port = url.getPort(); + if (port == -1) { + port = 80; + } + String originPath = url.getPath(); + LOGGER.debug("Conf(path, originPath, host, port) ----> {},{},{},{}", path, originPath, host, port); + + // 注意这里不能origin多个代理地址, 一个实例只能代理一个origin + final HttpProxy httpProxy = HttpProxy.reverseProxy(httpClient); + httpProxy.origin(port, host); + if (StringUtils.isEmpty(path)) { + return; + } + + // 代理目标路径为空 就像nginx一样路径穿透 (相对路径) + if (StringUtils.isEmpty(originPath) || path.equals(originPath)) { + proxyRouter.route(path + "*").handler(ProxyHandler.create(httpProxy)); + } else { + proxyRouter.route(originPath + "*").handler(ProxyHandler.create(httpProxy)); + proxyRouter.route(path + "*").handler(ctx -> { + String realPath = ctx.request().path(); + if (realPath.startsWith(path)) { + // vertx web proxy暂不支持rewrite, 所以这里进行手动替换, 请求地址中的请求path前缀替换为originPath + String rePath = realPath.replaceAll("^" + path, originPath); + ctx.reroute(rePath); + } else { + ctx.next(); + } + }); + } + + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + }); + } + + /** + * 处理websocket + * + * @param confList sock配置 + * @param httpClient 客户端 + * @param proxyRouter 代理路由 + */ + private void handleSock(JsonArray confList, HttpClient httpClient, Router proxyRouter) { + // 代理规则 + confList.stream().map(e -> (JsonObject) e).forEach(conf -> { + + String origin = conf.getString("origin"); + String path = conf.getString("path"); + LOGGER.info("websocket proxy: {}, {}",origin,path); + + SockJSHandlerOptions options = new SockJSHandlerOptions() + .setHeartbeatInterval(2000) + .setRegisterWriteHandler(true); + + SockJSHandler sockJSHandler = SockJSHandler.create(VertxHolder.getVertxInstance(), options); + if (!path.startsWith("/")) { + path = "/" + path; + } + + + Router route = sockJSHandler.socketHandler(sock -> { + sock.handler(buffer -> { + Future webSocketFuture = httpClient.webSocket(8086,"127.0.0.1",sock.uri()); + webSocketFuture.onSuccess(s -> { + System.out.println(buffer.toString()); + s.write(buffer).onSuccess(v -> { + s.handler(w->{ + System.out.println("--------"+w.toString()); + }); + }); + }); + }); + sock.endHandler(v -> { + + }); + sock.closeHandler(v -> { + + }); + }); + proxyRouter.mountSubRouter("/real/serverApi/test", route); + }); + + + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/verticle/RouterVerticle.java b/core/src/main/java/cn/com/yhinfo/core/verticle/RouterVerticle.java new file mode 100644 index 0000000..fa6677d --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/verticle/RouterVerticle.java @@ -0,0 +1,74 @@ +package cn.com.yhinfo.core.verticle; + +import cn.com.yhinfo.core.handlerfactory.RouterHandlerFactory; +import cn.com.yhinfo.core.util.CommonUtil; +import cn.com.yhinfo.core.util.SharedDataUtil; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpServer; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.stomp.StompServer; +import io.vertx.ext.stomp.StompServerHandler; +import io.vertx.ext.stomp.StompServerOptions; +import io.vertx.ext.web.Router; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Http服务 注册路由 + * + * @author QAIU + */ +public class RouterVerticle extends AbstractVerticle { + + private static final Logger LOGGER = LoggerFactory.getLogger(RouterVerticle.class); + + private static final int port = SharedDataUtil.getValueForServerConfig("port"); + private static final Router router = new RouterHandlerFactory( + SharedDataUtil.getJsonStringForCustomConfig("routerLocations"), + SharedDataUtil.getJsonStringForServerConfig("contextPath")).createRouter(); + + private static final JsonObject globalConfig = SharedDataUtil.getJsonConfig("globalConfig"); + + private HttpServer server; + + static { + LOGGER.info("To start listening to port {} ......", port); + } + + @Override + public void start(Promise startPromise) { + // 端口是否占用 + if (CommonUtil.isPortUsing(port)) { + throw new RuntimeException("Start fail: the '" + port + "' port is already in use..."); + } + HttpServerOptions options; + if (globalConfig.containsKey("http") && globalConfig.getValue("http") != null) { + options = new HttpServerOptions(globalConfig.getJsonObject("http")); + } else { + options = new HttpServerOptions(); + } + options.setPort(port); + server = vertx.createHttpServer(options); + + server.requestHandler(router).webSocketHandler(s->{}).listen() + .onSuccess(s -> startPromise.complete()) + .onFailure(e -> startPromise.fail(e.getCause())); + } + + @Override + public void stop(Promise stopPromise) { + if (server == null) { + stopPromise.complete(); + return; + } + server.close(result -> { + if (result.failed()) { + stopPromise.fail(result.cause()); + } else { + stopPromise.complete(); + } + }); + } +} diff --git a/core/src/main/java/cn/com/yhinfo/core/verticle/ServiceVerticle.java b/core/src/main/java/cn/com/yhinfo/core/verticle/ServiceVerticle.java new file mode 100644 index 0000000..c91f050 --- /dev/null +++ b/core/src/main/java/cn/com/yhinfo/core/verticle/ServiceVerticle.java @@ -0,0 +1,51 @@ +package cn.com.yhinfo.core.verticle; + +import cn.com.yhinfo.core.annotaions.Service; +import cn.com.yhinfo.core.base.BaseAsyncService; +import cn.com.yhinfo.core.util.ReflectionUtil; +import cn.com.yhinfo.core.util.SharedDataUtil; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Promise; +import io.vertx.serviceproxy.ServiceBinder; +import org.reflections.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 服务注册到EventBus + *
Create date 2021-05-07 10:26:54 + * + * @author QAIU + */ +public class ServiceVerticle extends AbstractVerticle { + + Logger LOGGER = LoggerFactory.getLogger(ServiceVerticle.class); + private static final AtomicInteger ID = new AtomicInteger(1); + private static final Set> handlers; + + static { + String handlerLocations = SharedDataUtil.getJsonStringForCustomConfig("handlerLocations"); + Reflections reflections = ReflectionUtil.getReflections(handlerLocations); + handlers = reflections.getTypesAnnotatedWith(Service.class); + } + + @Override + public void start(Promise startPromise) { + ServiceBinder binder = new ServiceBinder(vertx); + if (null != handlers && handlers.size() > 0) { + handlers.forEach(asyncService -> { + try { + BaseAsyncService asInstance = (BaseAsyncService) ReflectionUtil.newWithNoParam(asyncService); + binder.setAddress(asInstance.getAddress()).register(asInstance.getAsyncInterfaceClass(), asInstance); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } + }); + LOGGER.info("registered async services -> id: {}", ID.getAndIncrement()); + } + startPromise.complete(); + } +} diff --git a/core/src/main/resources/logback.xml b/core/src/main/resources/logback.xml new file mode 100644 index 0000000..cb5856f --- /dev/null +++ b/core/src/main/resources/logback.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + ${LOG_HOME}/%d{yyyyMMdd}/run.log + + 15 + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + + + utf8 + + + + 100MB + + + + + + + 0 + + 256 + + + + + + + + ${CUSTOMER_PATTERN2} + + + + + + + + diff --git a/logs/20230420/run.log b/logs/20230420/run.log new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6c53375 --- /dev/null +++ b/pom.xml @@ -0,0 +1,120 @@ + + + 4.0.0 + + cn.qaiu + lz-cow-api + pom + 0.0.1 + + + core + web + + + + 17 + 17 + UTF-8 + 4.1.3 + ${project.basedir}/web/target/package + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.2 + + ${java.version} + ${java.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + true + + + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + + copy-static-web + package + + resources + + + + + ${project.basedir}/webroot + + + ${packageDirectory}/webroot + + + + copy-bin + package + + resources + + + + + ${project.basedir}/bin + + + ${packageDirectory} + + + + copy-db + package + + resources + + + + + ${project.basedir}/db + + + ${packageDirectory}/db + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + + + false + + + + ${project.basedir}/logs + + false + + + + + + + + diff --git a/web/assembly.xml b/web/assembly.xml new file mode 100644 index 0000000..29eac15 --- /dev/null +++ b/web/assembly.xml @@ -0,0 +1,13 @@ + + bin + + zip + + + + + target/package/ + / + + + \ No newline at end of file diff --git a/web/logs/20230420/run.log b/web/logs/20230420/run.log new file mode 100644 index 0000000..e69de29 diff --git a/web/pom.xml b/web/pom.xml new file mode 100644 index 0000000..56de4ab --- /dev/null +++ b/web/pom.xml @@ -0,0 +1,198 @@ + + + + lz-cow-api + cn.qaiu + 0.0.1 + + 4.0.0 + 0.0.1 + web + + + ${project.basedir}/target/package + 17 + UTF-8 + 2.0.5 + 6.1.0 + + + + + cn.qaiu + core + 1.0.8 + + + org.projectlombok + lombok + 1.18.26 + provided + + + + ch.qos.logback + logback-classic + 1.4.6 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + junit + junit + 4.13.2 + test + + + + + + + ${project.basedir}/target/ + ${project.build.directory}/classes + ${project.artifactId}-${project.version} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + lombok.launch.AnnotationProcessorHider$AnnotationProcessor + io.vertx.codegen.CodeGenProcessor + + + ${project.basedir}/src/main/generated + + + -AoutputDirectory=${project.basedir}/src/main -Xlint:unchecked + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + *.** + */*.xml + conf/** + + + + true + + lib/ + + false + + cn.com.yhinfo.real.AppMain + + + + ./resources/ + + + ${packageDirectory} + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.2 + + + copy-dependencies + package + + copy-dependencies + + + + test + provided + + ${packageDirectory}/lib/ + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + + copy-resources + package + + copy-resources + + + + + src/main/resources + + + ${packageDirectory}/resources + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + + + false + + + + ${basedir}/src/main/generated + + false + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + assembly.xml + + + + + make-assembly + package + + single + + + + + + + diff --git a/web/src/main/generated/cn/com/yhinfo/real/common/model/MyDataParametersMapper.java b/web/src/main/generated/cn/com/yhinfo/real/common/model/MyDataParametersMapper.java new file mode 100644 index 0000000..476fc94 --- /dev/null +++ b/web/src/main/generated/cn/com/yhinfo/real/common/model/MyDataParametersMapper.java @@ -0,0 +1,28 @@ +package cn.com.yhinfo.real.common.model; + +/** + * Mapper for {@link MyData}. + * NOTE: This class has been automatically generated from the {@link MyData} original class using Vert.x codegen. + */ +@io.vertx.codegen.annotations.VertxGen +public interface MyDataParametersMapper extends io.vertx.sqlclient.templates.TupleMapper { + + MyDataParametersMapper INSTANCE = new MyDataParametersMapper() {}; + + default io.vertx.sqlclient.Tuple map(java.util.function.Function mapping, int size, MyData params) { + java.util.Map args = map(params); + Object[] array = new Object[size]; + for (int i = 0;i < array.length;i++) { + String column = mapping.apply(i); + array[i] = args.get(column); + } + return io.vertx.sqlclient.Tuple.wrap(array); + } + + default java.util.Map map(MyData obj) { + java.util.Map params = new java.util.HashMap<>(); + params.put("id", obj.getId()); + params.put("max_size", obj.getMaxSize()); + return params; + } +} diff --git a/web/src/main/generated/cn/com/yhinfo/real/common/model/MyDataRowMapper.java b/web/src/main/generated/cn/com/yhinfo/real/common/model/MyDataRowMapper.java new file mode 100644 index 0000000..82486ee --- /dev/null +++ b/web/src/main/generated/cn/com/yhinfo/real/common/model/MyDataRowMapper.java @@ -0,0 +1,29 @@ +package cn.com.yhinfo.real.common.model; + +/** + * Mapper for {@link MyData}. + * NOTE: This class has been automatically generated from the {@link MyData} original class using Vert.x codegen. + */ +@io.vertx.codegen.annotations.VertxGen +public interface MyDataRowMapper extends io.vertx.sqlclient.templates.RowMapper { + + @io.vertx.codegen.annotations.GenIgnore + MyDataRowMapper INSTANCE = new MyDataRowMapper() { }; + + @io.vertx.codegen.annotations.GenIgnore + java.util.stream.Collector> COLLECTOR = java.util.stream.Collectors.mapping(INSTANCE::map, java.util.stream.Collectors.toList()); + + @io.vertx.codegen.annotations.GenIgnore + default MyData map(io.vertx.sqlclient.Row row) { + MyData obj = new MyData(); + Object val; + int idx; + if ((idx = row.getColumnIndex("id")) != -1 && (val = row.getString(idx)) != null) { + obj.setId((java.lang.String)val); + } + if ((idx = row.getColumnIndex("max_size")) != -1 && (val = row.getString(idx)) != null) { + obj.setMaxSize((java.lang.String)val); + } + return obj; + } +} diff --git a/web/src/main/generated/cn/com/yhinfo/real/common/model/UserInfoParametersMapper.java b/web/src/main/generated/cn/com/yhinfo/real/common/model/UserInfoParametersMapper.java new file mode 100644 index 0000000..73f2e70 --- /dev/null +++ b/web/src/main/generated/cn/com/yhinfo/real/common/model/UserInfoParametersMapper.java @@ -0,0 +1,30 @@ +package cn.com.yhinfo.real.common.model; + +/** + * Mapper for {@link UserInfo}. + * NOTE: This class has been automatically generated from the {@link UserInfo} original class using Vert.x codegen. + */ +@io.vertx.codegen.annotations.VertxGen +public interface UserInfoParametersMapper extends io.vertx.sqlclient.templates.TupleMapper { + + UserInfoParametersMapper INSTANCE = new UserInfoParametersMapper() {}; + + default io.vertx.sqlclient.Tuple map(java.util.function.Function mapping, int size, UserInfo params) { + java.util.Map args = map(params); + Object[] array = new Object[size]; + for (int i = 0;i < array.length;i++) { + String column = mapping.apply(i); + array[i] = args.get(column); + } + return io.vertx.sqlclient.Tuple.wrap(array); + } + + default java.util.Map map(UserInfo obj) { + java.util.Map params = new java.util.HashMap<>(); + params.put("permission", obj.getPermission()); + params.put("pwd_crc32", obj.getPwdCrc32()); + params.put("username", obj.getUsername()); + params.put("uuid", obj.getUuid()); + return params; + } +} diff --git a/web/src/main/generated/cn/com/yhinfo/real/common/model/UserInfoRowMapper.java b/web/src/main/generated/cn/com/yhinfo/real/common/model/UserInfoRowMapper.java new file mode 100644 index 0000000..cce0233 --- /dev/null +++ b/web/src/main/generated/cn/com/yhinfo/real/common/model/UserInfoRowMapper.java @@ -0,0 +1,35 @@ +package cn.com.yhinfo.real.common.model; + +/** + * Mapper for {@link UserInfo}. + * NOTE: This class has been automatically generated from the {@link UserInfo} original class using Vert.x codegen. + */ +@io.vertx.codegen.annotations.VertxGen +public interface UserInfoRowMapper extends io.vertx.sqlclient.templates.RowMapper { + + @io.vertx.codegen.annotations.GenIgnore + UserInfoRowMapper INSTANCE = new UserInfoRowMapper() { }; + + @io.vertx.codegen.annotations.GenIgnore + java.util.stream.Collector> COLLECTOR = java.util.stream.Collectors.mapping(INSTANCE::map, java.util.stream.Collectors.toList()); + + @io.vertx.codegen.annotations.GenIgnore + default UserInfo map(io.vertx.sqlclient.Row row) { + UserInfo obj = new UserInfo(); + Object val; + int idx; + if ((idx = row.getColumnIndex("permission")) != -1 && (val = row.getString(idx)) != null) { + obj.setPermission((java.lang.String)val); + } + if ((idx = row.getColumnIndex("pwd_crc32")) != -1 && (val = row.getString(idx)) != null) { + obj.setPwdCrc32((java.lang.String)val); + } + if ((idx = row.getColumnIndex("username")) != -1 && (val = row.getString(idx)) != null) { + obj.setUsername((java.lang.String)val); + } + if ((idx = row.getColumnIndex("uuid")) != -1 && (val = row.getString(idx)) != null) { + obj.setUuid((java.lang.String)val); + } + return obj; + } +} diff --git a/web/src/main/generated/cn/com/yhinfo/real/web/service/DbServiceVertxEBProxy.java b/web/src/main/generated/cn/com/yhinfo/real/web/service/DbServiceVertxEBProxy.java new file mode 100644 index 0000000..b701bae --- /dev/null +++ b/web/src/main/generated/cn/com/yhinfo/real/web/service/DbServiceVertxEBProxy.java @@ -0,0 +1,89 @@ +/* +* Copyright 2014 Red Hat, Inc. +* +* Red Hat licenses this file to you 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.com.yhinfo.real.web.service; + +import io.vertx.core.eventbus.DeliveryOptions; +import io.vertx.core.Vertx; +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; +import io.vertx.core.json.JsonArray; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.function.Function; +import io.vertx.serviceproxy.ServiceException; +import io.vertx.serviceproxy.ServiceExceptionMessageCodec; +import io.vertx.serviceproxy.ProxyUtils; + +import cn.com.yhinfo.real.common.model.UserInfo; +import cn.com.yhinfo.core.base.BaseAsyncService; +import io.vertx.core.Future; +/* + Generated Proxy code - DO NOT EDIT + @author Roger the Robot +*/ + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class DbServiceVertxEBProxy implements DbService { + private Vertx _vertx; + private String _address; + private DeliveryOptions _options; + private boolean closed; + + public DbServiceVertxEBProxy(Vertx vertx, String address) { + this(vertx, address, null); + } + + public DbServiceVertxEBProxy(Vertx vertx, String address, DeliveryOptions options) { + this._vertx = vertx; + this._address = address; + this._options = options; + try { + this._vertx.eventBus().registerDefaultCodec(ServiceException.class, new ServiceExceptionMessageCodec()); + } catch (IllegalStateException ex) { + } + } + + @Override + public Future sayOk(String data){ + if (closed) return io.vertx.core.Future.failedFuture("Proxy is closed"); + JsonObject _json = new JsonObject(); + _json.put("data", data); + + DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); + _deliveryOptions.addHeader("action", "sayOk"); + return _vertx.eventBus().request(_address, _json, _deliveryOptions).map(msg -> { + return msg.body(); + }); + } + @Override + public Future sayOk2(String data, UserInfo holder){ + if (closed) return io.vertx.core.Future.failedFuture("Proxy is closed"); + JsonObject _json = new JsonObject(); + _json.put("data", data); + _json.put("holder", holder != null ? holder.toJson() : null); + + DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); + _deliveryOptions.addHeader("action", "sayOk2"); + return _vertx.eventBus().request(_address, _json, _deliveryOptions).map(msg -> { + return msg.body(); + }); + } +} diff --git a/web/src/main/generated/cn/com/yhinfo/real/web/service/DbServiceVertxProxyHandler.java b/web/src/main/generated/cn/com/yhinfo/real/web/service/DbServiceVertxProxyHandler.java new file mode 100644 index 0000000..1f573e9 --- /dev/null +++ b/web/src/main/generated/cn/com/yhinfo/real/web/service/DbServiceVertxProxyHandler.java @@ -0,0 +1,140 @@ +/* +* Copyright 2014 Red Hat, Inc. +* +* Red Hat licenses this file to you 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.com.yhinfo.real.web.service; + +import cn.com.yhinfo.real.web.service.DbService; +import io.vertx.core.Vertx; +import io.vertx.core.Handler; +import io.vertx.core.AsyncResult; +import io.vertx.core.eventbus.EventBus; +import io.vertx.core.eventbus.Message; +import io.vertx.core.eventbus.MessageConsumer; +import io.vertx.core.eventbus.DeliveryOptions; +import io.vertx.core.eventbus.ReplyException; +import io.vertx.core.json.JsonObject; +import io.vertx.core.json.JsonArray; +import java.util.Collection; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import io.vertx.serviceproxy.ProxyHandler; +import io.vertx.serviceproxy.ServiceException; +import io.vertx.serviceproxy.ServiceExceptionMessageCodec; +import io.vertx.serviceproxy.HelperUtils; +import io.vertx.serviceproxy.ServiceBinder; + +import cn.com.yhinfo.real.common.model.UserInfo; +import cn.com.yhinfo.core.base.BaseAsyncService; +import io.vertx.core.Future; +/* + Generated Proxy code - DO NOT EDIT + @author Roger the Robot +*/ + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class DbServiceVertxProxyHandler extends ProxyHandler { + + public static final long DEFAULT_CONNECTION_TIMEOUT = 5 * 60; // 5 minutes + private final Vertx vertx; + private final DbService service; + private final long timerID; + private long lastAccessed; + private final long timeoutSeconds; + private final boolean includeDebugInfo; + + public DbServiceVertxProxyHandler(Vertx vertx, DbService service){ + this(vertx, service, DEFAULT_CONNECTION_TIMEOUT); + } + + public DbServiceVertxProxyHandler(Vertx vertx, DbService service, long timeoutInSecond){ + this(vertx, service, true, timeoutInSecond); + } + + public DbServiceVertxProxyHandler(Vertx vertx, DbService service, boolean topLevel, long timeoutInSecond){ + this(vertx, service, true, timeoutInSecond, false); + } + + public DbServiceVertxProxyHandler(Vertx vertx, DbService service, boolean topLevel, long timeoutSeconds, boolean includeDebugInfo) { + this.vertx = vertx; + this.service = service; + this.includeDebugInfo = includeDebugInfo; + this.timeoutSeconds = timeoutSeconds; + try { + this.vertx.eventBus().registerDefaultCodec(ServiceException.class, + new ServiceExceptionMessageCodec()); + } catch (IllegalStateException ex) {} + if (timeoutSeconds != -1 && !topLevel) { + long period = timeoutSeconds * 1000 / 2; + if (period > 10000) { + period = 10000; + } + this.timerID = vertx.setPeriodic(period, this::checkTimedOut); + } else { + this.timerID = -1; + } + accessed(); + } + + + private void checkTimedOut(long id) { + long now = System.nanoTime(); + if (now - lastAccessed > timeoutSeconds * 1000000000) { + close(); + } + } + + @Override + public void close() { + if (timerID != -1) { + vertx.cancelTimer(timerID); + } + super.close(); + } + + private void accessed() { + this.lastAccessed = System.nanoTime(); + } + + public void handle(Message msg) { + try{ + JsonObject json = msg.body(); + String action = msg.headers().get("action"); + if (action == null) throw new IllegalStateException("action not specified"); + accessed(); + switch (action) { + case "sayOk": { + service.sayOk((java.lang.String)json.getValue("data")).onComplete(HelperUtils.createHandler(msg, includeDebugInfo)); + break; + } + case "sayOk2": { + service.sayOk2((java.lang.String)json.getValue("data"), + json.getJsonObject("holder") != null ? new cn.com.yhinfo.real.common.model.UserInfo((JsonObject)json.getJsonObject("holder")) : null).onComplete(HelperUtils.createHandler(msg, includeDebugInfo)); + break; + } + default: throw new IllegalStateException("Invalid action: " + action); + } + } catch (Throwable t) { + if (includeDebugInfo) msg.reply(new ServiceException(500, t.getMessage(), HelperUtils.generateDebugInfo(t))); + else msg.reply(new ServiceException(500, t.getMessage())); + throw t; + } + } +} \ No newline at end of file diff --git a/web/src/main/generated/cn/com/yhinfo/real/web/service/UserServiceVertxEBProxy.java b/web/src/main/generated/cn/com/yhinfo/real/web/service/UserServiceVertxEBProxy.java new file mode 100644 index 0000000..f395d4d --- /dev/null +++ b/web/src/main/generated/cn/com/yhinfo/real/web/service/UserServiceVertxEBProxy.java @@ -0,0 +1,76 @@ +/* +* Copyright 2014 Red Hat, Inc. +* +* Red Hat licenses this file to you 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.com.yhinfo.real.web.service; + +import io.vertx.core.eventbus.DeliveryOptions; +import io.vertx.core.Vertx; +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; +import io.vertx.core.json.JsonArray; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.function.Function; +import io.vertx.serviceproxy.ServiceException; +import io.vertx.serviceproxy.ServiceExceptionMessageCodec; +import io.vertx.serviceproxy.ProxyUtils; + +import cn.com.yhinfo.core.base.BaseAsyncService; +import cn.com.yhinfo.real.web.model.RealUser; +import io.vertx.core.Future; +/* + Generated Proxy code - DO NOT EDIT + @author Roger the Robot +*/ + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class UserServiceVertxEBProxy implements UserService { + private Vertx _vertx; + private String _address; + private DeliveryOptions _options; + private boolean closed; + + public UserServiceVertxEBProxy(Vertx vertx, String address) { + this(vertx, address, null); + } + + public UserServiceVertxEBProxy(Vertx vertx, String address, DeliveryOptions options) { + this._vertx = vertx; + this._address = address; + this._options = options; + try { + this._vertx.eventBus().registerDefaultCodec(ServiceException.class, new ServiceExceptionMessageCodec()); + } catch (IllegalStateException ex) { + } + } + + @Override + public Future login(RealUser user){ + if (closed) return io.vertx.core.Future.failedFuture("Proxy is closed"); + JsonObject _json = new JsonObject(); + _json.put("user", user != null ? user.toJson() : null); + + DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); + _deliveryOptions.addHeader("action", "login"); + return _vertx.eventBus().request(_address, _json, _deliveryOptions).map(msg -> { + return msg.body(); + }); + } +} diff --git a/web/src/main/generated/cn/com/yhinfo/real/web/service/UserServiceVertxProxyHandler.java b/web/src/main/generated/cn/com/yhinfo/real/web/service/UserServiceVertxProxyHandler.java new file mode 100644 index 0000000..dbac9d3 --- /dev/null +++ b/web/src/main/generated/cn/com/yhinfo/real/web/service/UserServiceVertxProxyHandler.java @@ -0,0 +1,135 @@ +/* +* Copyright 2014 Red Hat, Inc. +* +* Red Hat licenses this file to you 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.com.yhinfo.real.web.service; + +import cn.com.yhinfo.real.web.service.UserService; +import io.vertx.core.Vertx; +import io.vertx.core.Handler; +import io.vertx.core.AsyncResult; +import io.vertx.core.eventbus.EventBus; +import io.vertx.core.eventbus.Message; +import io.vertx.core.eventbus.MessageConsumer; +import io.vertx.core.eventbus.DeliveryOptions; +import io.vertx.core.eventbus.ReplyException; +import io.vertx.core.json.JsonObject; +import io.vertx.core.json.JsonArray; +import java.util.Collection; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import io.vertx.serviceproxy.ProxyHandler; +import io.vertx.serviceproxy.ServiceException; +import io.vertx.serviceproxy.ServiceExceptionMessageCodec; +import io.vertx.serviceproxy.HelperUtils; +import io.vertx.serviceproxy.ServiceBinder; + +import cn.com.yhinfo.core.base.BaseAsyncService; +import cn.com.yhinfo.real.web.model.RealUser; +import io.vertx.core.Future; +/* + Generated Proxy code - DO NOT EDIT + @author Roger the Robot +*/ + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class UserServiceVertxProxyHandler extends ProxyHandler { + + public static final long DEFAULT_CONNECTION_TIMEOUT = 5 * 60; // 5 minutes + private final Vertx vertx; + private final UserService service; + private final long timerID; + private long lastAccessed; + private final long timeoutSeconds; + private final boolean includeDebugInfo; + + public UserServiceVertxProxyHandler(Vertx vertx, UserService service){ + this(vertx, service, DEFAULT_CONNECTION_TIMEOUT); + } + + public UserServiceVertxProxyHandler(Vertx vertx, UserService service, long timeoutInSecond){ + this(vertx, service, true, timeoutInSecond); + } + + public UserServiceVertxProxyHandler(Vertx vertx, UserService service, boolean topLevel, long timeoutInSecond){ + this(vertx, service, true, timeoutInSecond, false); + } + + public UserServiceVertxProxyHandler(Vertx vertx, UserService service, boolean topLevel, long timeoutSeconds, boolean includeDebugInfo) { + this.vertx = vertx; + this.service = service; + this.includeDebugInfo = includeDebugInfo; + this.timeoutSeconds = timeoutSeconds; + try { + this.vertx.eventBus().registerDefaultCodec(ServiceException.class, + new ServiceExceptionMessageCodec()); + } catch (IllegalStateException ex) {} + if (timeoutSeconds != -1 && !topLevel) { + long period = timeoutSeconds * 1000 / 2; + if (period > 10000) { + period = 10000; + } + this.timerID = vertx.setPeriodic(period, this::checkTimedOut); + } else { + this.timerID = -1; + } + accessed(); + } + + + private void checkTimedOut(long id) { + long now = System.nanoTime(); + if (now - lastAccessed > timeoutSeconds * 1000000000) { + close(); + } + } + + @Override + public void close() { + if (timerID != -1) { + vertx.cancelTimer(timerID); + } + super.close(); + } + + private void accessed() { + this.lastAccessed = System.nanoTime(); + } + + public void handle(Message msg) { + try{ + JsonObject json = msg.body(); + String action = msg.headers().get("action"); + if (action == null) throw new IllegalStateException("action not specified"); + accessed(); + switch (action) { + case "login": { + service.login(json.getJsonObject("user") != null ? new cn.com.yhinfo.real.web.model.RealUser((JsonObject)json.getJsonObject("user")) : null).onComplete(HelperUtils.createHandler(msg, includeDebugInfo)); + break; + } + default: throw new IllegalStateException("Invalid action: " + action); + } + } catch (Throwable t) { + if (includeDebugInfo) msg.reply(new ServiceException(500, t.getMessage(), HelperUtils.generateDebugInfo(t))); + else msg.reply(new ServiceException(500, t.getMessage())); + throw t; + } + } +} \ No newline at end of file diff --git a/web/src/main/java/cn/com/yhinfo/real/AppMain.java b/web/src/main/java/cn/com/yhinfo/real/AppMain.java new file mode 100644 index 0000000..715b46a --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/AppMain.java @@ -0,0 +1,29 @@ +package cn.com.yhinfo.real; + +import cn.com.yhinfo.core.Deploy; +import io.vertx.core.json.JsonObject; + + +/** + * 程序入口 + *
Create date 2021-05-08 13:00:01 + * + * @author qiu + */ +public class AppMain { + + public static void main(String[] args) { + // 注册枚举类型转换器 + Deploy.instance().start(args, AppMain::exec); + } + + /** + * + * @param jsonObject 配置 + */ + private static void exec(JsonObject jsonObject) { + // + } + + +} diff --git a/web/src/main/java/cn/com/yhinfo/real/common/ToJson.java b/web/src/main/java/cn/com/yhinfo/real/common/ToJson.java new file mode 100644 index 0000000..b04cdf0 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/common/ToJson.java @@ -0,0 +1,22 @@ +package cn.com.yhinfo.real.common; + +import io.vertx.core.json.JsonObject; + +/** + * sinoreal2-web
+ * 实现此接口 POJO转JSON对象 + * + * @author QAIU + *
Create date 2021/8/27 11:40 + */ +public interface ToJson { + + /** + * POJO转JSON对象 + * + * @return Json Object + */ + default JsonObject toJson() { + return JsonObject.mapFrom(this); + } +} diff --git a/web/src/main/java/cn/com/yhinfo/real/common/interceptorImpl/DefaultInterceptor.java b/web/src/main/java/cn/com/yhinfo/real/common/interceptorImpl/DefaultInterceptor.java new file mode 100644 index 0000000..b10cfc2 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/common/interceptorImpl/DefaultInterceptor.java @@ -0,0 +1,45 @@ +package cn.com.yhinfo.real.common.interceptorImpl; + +import cn.com.yhinfo.core.base.BaseHttpApi; +import cn.com.yhinfo.core.interceptor.Interceptor; +import cn.com.yhinfo.core.model.JsonResult; +import cn.com.yhinfo.core.util.CommonUtil; +import cn.com.yhinfo.core.util.SharedDataUtil; +import cn.com.yhinfo.core.util.VertxHolder; +import io.vertx.core.json.JsonArray; +import io.vertx.core.shareddata.LocalMap; +import io.vertx.ext.web.RoutingContext; +import lombok.extern.slf4j.Slf4j; +import lombok.val; + +/** + * 默认拦截器实现 + * 校验用户是否合法
+ * TODO 暂时只做简单实现 + */ +@Slf4j +public class DefaultInterceptor implements Interceptor, BaseHttpApi { + + private final JsonArray ignores = SharedDataUtil.getJsonArrayForCustomConfig("ignoresReg"); + + @Override + public void handle(RoutingContext ctx) { + + // 判断是否忽略 + if (CommonUtil.matchRegList(ignores.getList(), ctx.request().path())) { + ctx.next(); + return; + } + // 执行拦截 + val token = ctx.request().getHeader("token"); + + + LocalMap tokenMap = SharedDataUtil.getLocalMapWithCast("token"); + if (token != null && tokenMap != null && tokenMap.containsKey(token)) { + VertxHolder.getVertxInstance().getOrCreateContext().put("username", tokenMap.get(token)); + ctx.next(); + } else { + fireJsonResponse(ctx, JsonResult.error("没有权限", 401)); + } + } +} diff --git a/web/src/main/java/cn/com/yhinfo/real/common/model/MyData.java b/web/src/main/java/cn/com/yhinfo/real/common/model/MyData.java new file mode 100644 index 0000000..1b2409c --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/common/model/MyData.java @@ -0,0 +1,34 @@ +package cn.com.yhinfo.real.common.model; + +import io.vertx.codegen.annotations.DataObject; +import io.vertx.codegen.format.SnakeCase; +import io.vertx.core.json.JsonObject; +import io.vertx.sqlclient.templates.annotations.ParametersMapped; +import io.vertx.sqlclient.templates.annotations.RowMapped; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * + * @author QAIU + *
Create date 2021/7/22 3:34 + */ +@DataObject +@RowMapped(formatter = SnakeCase.class) +@ParametersMapped(formatter = SnakeCase.class) +@Data +@NoArgsConstructor +public class MyData implements Serializable { + public static final long serialVersionUID = 1L; + + private String id; + + private String maxSize; + + + public MyData(JsonObject jsonObject) { + // TODO + } +} diff --git a/web/src/main/java/cn/com/yhinfo/real/common/model/UserInfo.java b/web/src/main/java/cn/com/yhinfo/real/common/model/UserInfo.java new file mode 100644 index 0000000..4c853f2 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/common/model/UserInfo.java @@ -0,0 +1,40 @@ +package cn.com.yhinfo.real.common.model; + +import cn.com.yhinfo.real.common.ToJson; +import io.vertx.codegen.annotations.DataObject; +import io.vertx.codegen.format.SnakeCase; +import io.vertx.core.json.JsonObject; +import io.vertx.sqlclient.templates.annotations.ParametersMapped; +import io.vertx.sqlclient.templates.annotations.RowMapped; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * sinoreal2-web + * + * @author QAIU + *
Create date 2021/8/10 11:10 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@DataObject +@RowMapped(formatter = SnakeCase.class) +@ParametersMapped(formatter = SnakeCase.class) +public class UserInfo implements ToJson { + + private String username; + + private String permission; + + private String pwdCrc32; + + private String uuid; + + public UserInfo(JsonObject jsonObject) { + this.username = jsonObject.getString("username"); + this.permission = jsonObject.getString("permission"); + this.pwdCrc32 = jsonObject.getString("pwdCrc32"); + } +} diff --git a/web/src/main/java/cn/com/yhinfo/real/common/util/ArrayUtil.java b/web/src/main/java/cn/com/yhinfo/real/common/util/ArrayUtil.java new file mode 100644 index 0000000..12ad6d9 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/common/util/ArrayUtil.java @@ -0,0 +1,20 @@ +package cn.com.yhinfo.real.common.util; + +public class ArrayUtil { + + public static int[] parseIntArray(String[] arr) { + int[] ints = new int[arr.length]; + for (int i = 0; i < ints.length; i++) { + ints[i] = Integer.parseInt(arr[i]); + } + return ints; + } + + public static float[] parseFloatArray(String[] arr) { + float[] ints = new float[arr.length]; + for (int i = 0; i < ints.length; i++) { + ints[i] = Float.parseFloat(arr[i]); + } + return ints; + } +} diff --git a/web/src/main/java/cn/com/yhinfo/real/common/util/ConnectUtil.java b/web/src/main/java/cn/com/yhinfo/real/common/util/ConnectUtil.java new file mode 100644 index 0000000..80e1fe9 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/common/util/ConnectUtil.java @@ -0,0 +1,19 @@ +package cn.com.yhinfo.real.common.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 获取连接 + * + * @author QAIU + */ +public enum ConnectUtil { + + // 实现枚举单例 + INSTANCE; + + private static final Logger LOGGER = LoggerFactory.getLogger(ConnectUtil.class); + + +} diff --git a/web/src/main/java/cn/com/yhinfo/real/package-info.java b/web/src/main/java/cn/com/yhinfo/real/package-info.java new file mode 100644 index 0000000..86c05b1 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/package-info.java @@ -0,0 +1,10 @@ +/** + * sinoreal2-web + *
Create date 2021/7/8 13:29 + * + * @author QAIU + */ +@ModuleGen(name = "proxy", groupPackage = "cn.com.yhinfo.real", useFutures = true) +package cn.com.yhinfo.real; + +import io.vertx.codegen.annotations.ModuleGen; \ No newline at end of file diff --git a/web/src/main/java/cn/com/yhinfo/real/web/http/ServerApi.java b/web/src/main/java/cn/com/yhinfo/real/web/http/ServerApi.java new file mode 100644 index 0000000..918ef4b --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/web/http/ServerApi.java @@ -0,0 +1,76 @@ +package cn.com.yhinfo.real.web.http; + +import cn.com.yhinfo.core.annotaions.RouteHandler; +import cn.com.yhinfo.core.annotaions.RouteMapping; +import cn.com.yhinfo.core.annotaions.SockRouteMapper; +import cn.com.yhinfo.core.enums.RouteMethod; +import cn.com.yhinfo.core.model.JsonResult; +import cn.com.yhinfo.core.util.AsyncServiceUtil; +import cn.com.yhinfo.core.util.SnowflakeIdWorker; +import cn.com.yhinfo.core.util.VertxHolder; +import cn.com.yhinfo.real.web.model.RealUser; +import cn.com.yhinfo.real.web.service.UserService; +import io.vertx.core.Future; +import io.vertx.ext.web.handler.sockjs.SockJSSocket; +import lombok.extern.slf4j.Slf4j; + +/** + * 连接服务API + *
Create date 2021/4/28 9:15 + * + * @author QAIU + */ +@Slf4j +@RouteHandler("serverApi") +public class ServerApi { + + private final UserService userService = AsyncServiceUtil.getAsyncServiceInstance(UserService.class); + + @RouteMapping(value = "/login", method = RouteMethod.POST) + public Future login(RealUser user) { + log.info("<------- login: {}", user.getUsername()); + return userService.login(user); + } + + long sid = 0; + + @SockRouteMapper(value = "/test") + public void test02(SockJSSocket sock) { + String s = sock.writeHandlerID(); + System.out.println("客户端连接 --> " + s); + sock.handler(sock::write); + sock.endHandler(v -> System.out.println("客户端断开")); + String id = sock.writeHandlerID(); + System.out.println("客户端连接 --> " + id); +// sock.handler(sock::write); + sock.handler(buffer -> { + sock.write("服务端开始处理------->"); + final String msg = buffer.toString(); + if ("1".equals(msg)) { + sid = VertxHolder.getVertxInstance().setPeriodic(1000, v -> + sock.write(v + "-->" + SnowflakeIdWorker.idWorker().nextId())); + } else { + if (sid != 0) { + if (VertxHolder.getVertxInstance().cancelTimer(sid)) { + sock.write(sid + " -----> 定时推送取消"); + } + } else { + + sock.write(msg + "----- ok"); + } + } + }); + sock.endHandler(v -> { + System.out.println("客户端断开"); + if (VertxHolder.getVertxInstance().cancelTimer(sid)) { + sock.write(sid + " -----> 定时推送取消"); + } + }); + } + + @RouteMapping(value = "/test2", method = RouteMethod.GET) + public JsonResult test01() { + return JsonResult.data("ok"); + } + +} diff --git a/web/src/main/java/cn/com/yhinfo/real/web/model/RealUser.java b/web/src/main/java/cn/com/yhinfo/real/web/model/RealUser.java new file mode 100644 index 0000000..d57a19f --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/web/model/RealUser.java @@ -0,0 +1,22 @@ +package cn.com.yhinfo.real.web.model; + +import cn.com.yhinfo.real.common.ToJson; +import io.vertx.codegen.annotations.DataObject; +import io.vertx.core.json.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@DataObject +public class RealUser implements ToJson { + private String username; + private String password; + + public RealUser(JsonObject json) { + this.username = json.getString("username"); + this.password = json.getString("password"); + } +} diff --git a/web/src/main/java/cn/com/yhinfo/real/web/service/DbService.java b/web/src/main/java/cn/com/yhinfo/real/web/service/DbService.java new file mode 100644 index 0000000..0a4da83 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/web/service/DbService.java @@ -0,0 +1,19 @@ +package cn.com.yhinfo.real.web.service; + +import cn.com.yhinfo.core.base.BaseAsyncService; +import cn.com.yhinfo.real.common.model.UserInfo; +import io.vertx.codegen.annotations.ProxyGen; +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; + +/** + * sinoreal2-web + *
Create date 2021/7/12 17:16 + * + * @author QAIU + */ +@ProxyGen +public interface DbService extends BaseAsyncService { + Future sayOk(String data); + Future sayOk2(String data, UserInfo holder); +} diff --git a/web/src/main/java/cn/com/yhinfo/real/web/service/JdkProxyFactory.java b/web/src/main/java/cn/com/yhinfo/real/web/service/JdkProxyFactory.java new file mode 100644 index 0000000..5000f5c --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/web/service/JdkProxyFactory.java @@ -0,0 +1,18 @@ +package cn.com.yhinfo.real.web.service; + +import cn.com.yhinfo.core.util.CastUtil; + +import java.lang.reflect.Proxy; + +/** + * JDK代理类工厂 + */ +public class JdkProxyFactory { + public static T getProxy(T target) { + return CastUtil.cast(Proxy.newProxyInstance( + target.getClass().getClassLoader(), + target.getClass().getInterfaces(), + new ServiceJdkProxy<>(target)) + ); + } +} \ No newline at end of file diff --git a/web/src/main/java/cn/com/yhinfo/real/web/service/ServiceJdkProxy.java b/web/src/main/java/cn/com/yhinfo/real/web/service/ServiceJdkProxy.java new file mode 100644 index 0000000..061ee6e --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/web/service/ServiceJdkProxy.java @@ -0,0 +1,29 @@ +package cn.com.yhinfo.real.web.service; + +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * sinoreal2-web + *
Create date 2021/8/25 14:28 + * + * @author QAIU + */ +@Slf4j +public class ServiceJdkProxy implements InvocationHandler { + + private final T target; + + public ServiceJdkProxy(T target) { + this.target = target; + } + + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { + return method.invoke(target, args); + } +} diff --git a/web/src/main/java/cn/com/yhinfo/real/web/service/UserService.java b/web/src/main/java/cn/com/yhinfo/real/web/service/UserService.java new file mode 100644 index 0000000..cfae4dc --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/web/service/UserService.java @@ -0,0 +1,17 @@ +package cn.com.yhinfo.real.web.service; + +import cn.com.yhinfo.core.base.BaseAsyncService; +import cn.com.yhinfo.real.web.model.RealUser; +import io.vertx.codegen.annotations.ProxyGen; +import io.vertx.core.Future; + +/** + * sinoreal2-web + *
Create date 2021/8/27 14:06 + * + * @author QAIU + */ +@ProxyGen +public interface UserService extends BaseAsyncService { + Future login(RealUser user); +} diff --git a/web/src/main/java/cn/com/yhinfo/real/web/service/impl/DbServiceImpl.java b/web/src/main/java/cn/com/yhinfo/real/web/service/impl/DbServiceImpl.java new file mode 100644 index 0000000..6b90534 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/web/service/impl/DbServiceImpl.java @@ -0,0 +1,38 @@ +package cn.com.yhinfo.real.web.service.impl; + +import cn.com.yhinfo.core.annotaions.Service; +import cn.com.yhinfo.core.model.JsonResult; +import cn.com.yhinfo.real.common.model.UserInfo; +import cn.com.yhinfo.real.web.service.DbService; +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; +import lombok.extern.slf4j.Slf4j; + +/** + * sinoreal2-web + *
Create date 2021/7/12 17:26 + * + * @author QAIU + */ +@Slf4j +@Service +public class DbServiceImpl implements DbService { + @Override + public Future sayOk(String data) { + log.info("say ok1 -> wait..."); + try { + Thread.sleep(4000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return Future.succeededFuture(JsonObject.mapFrom(JsonResult.data("Hi: " + data))); + } + + @Override + public Future sayOk2(String data, UserInfo holder) { +// val context = VertxHolder.getVertxInstance().getOrCreateContext(); +// log.info("say ok2 -> " + context.get("username")); +// log.info("--> {}", holder.toString()); + return Future.succeededFuture(JsonObject.mapFrom(JsonResult.data("Hi: " + data))); + } +} diff --git a/web/src/main/java/cn/com/yhinfo/real/web/service/impl/UserServiceImpl.java b/web/src/main/java/cn/com/yhinfo/real/web/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..5315272 --- /dev/null +++ b/web/src/main/java/cn/com/yhinfo/real/web/service/impl/UserServiceImpl.java @@ -0,0 +1,22 @@ +package cn.com.yhinfo.real.web.service.impl; + +import cn.com.yhinfo.core.annotaions.Service; +import cn.com.yhinfo.real.web.model.RealUser; +import cn.com.yhinfo.real.web.service.UserService; +import io.vertx.core.Future; + +/** + * sinoreal2-web + *
Create date 2021/8/27 14:09 + * + * @author QAIU + */ +@Service +public class UserServiceImpl implements UserService { + + @Override + public Future login(RealUser user) { + + return Future.succeededFuture("111"); + } +} diff --git a/web/src/main/resources/1.http b/web/src/main/resources/1.http new file mode 100644 index 0000000..a1c08ec --- /dev/null +++ b/web/src/main/resources/1.http @@ -0,0 +1,163 @@ + +### +POST http://127.0.0.1:8088/real/serverApi/login +Content-Type: application/x-www-form-urlencoded + +username=sa&password=sinoreal + +### +POST http://47.114.185.111:8088/real/serverApi/login +Content-Type: application/x-www-form-urlencoded + +username=sa&password=sinoreal + +### + +GET http://127.0.0.1:8088/real/basePointApi/getTables +token: cab5bcd2fc250f27c3984205fbffc46e +Content-Type: application/json + +### + +GET http://127.0.0.1:8088/real/basePointApi/getTags?tablemask=JTdevice +token: 7670b1a3da5e22ffc42a1e738ea4f0f6 + +### +GET http://127.0.0.1:8088/real/basePointApi/getSnapshotDataByTag/adasd?aaa=3 +token: 7670b1a3da5e22ffc42a1e738ea4f0f6 + +### + +POST http://127.0.0.1:8088/real/serverApi/login +Content-Type: application/json + +{ + "username": "sa", + "password": "sinoreal" +} + +### + +POST http://127.0.0.1:8088/real/basePointApi/updateTag +token: cab5bcd2fc250f27c3984205fbffc46e +Content-Type: application/json + +{ + "userints": [123223,35356], + "id": 123, + "equation": "asd", + "trigger": "RTDB_EVENT_TRIGGER", + "shutdown": true +} + + + +### + +GET http://127.0.0.1:8088/real/basePointApi/getTagById/753 +token: eb7d391ad89d4bb4a81897af8829f0e8 + +### + +GET http://127.0.0.1:8088/real/basePointApi/aaaa +token: cab5bcd2fc250f27c3984205fbffc46e + +### + +GET http://127.0.0.1:8088/real/serverApi/test + +### + +POST http://127.0.0.1:8088/real/serverApi/addUser +Content-Type: application/x-www-form-urlencoded +token: 2b4769b63c90adb6490cfe6e449da90b + +username=sa1&password=sinoreal&permission=3 + + +### +POST http://127.0.0.1:8088/real/serverApi/removeUser +Content-Type: application/x-www-form-urlencoded +token: 2b4769b63c90adb6490cfe6e449da90b + +username=sa1 + + +### + +POST http://127.0.0.1:8088/real/serverApi/updatePassword +Content-Type: application/x-www-form-urlencoded +token: 33c21f7cf053b90a713f1f9e124d0335 + +oldPassword=sinoreal1&newPassword=sinoreal + + +### + +POST http://127.0.0.1:8088/real/serverApi/changePriv +Content-Type: application/x-www-form-urlencoded +token: 2b4769b63c90adb6490cfe6e449da90b + +username=sa1&permission=3 + + +### +POST http://127.0.0.1:8088/real/serverApi/addAuthorization +Content-Type: application/x-www-form-urlencoded +token: 2b4769b63c90adb6490cfe6e449da90b + +addr=192.168.1.56&mask=255.255.255.255&permission=3&description=测试信任666111 + + +### +POST http://127.0.0.1:8088/real/serverApi/removeAuthorization +Content-Type: application/x-www-form-urlencoded +token: 2b4769b63c90adb6490cfe6e449da90b + +addr=192.168.1.56&mask=255.255.255.255 + + +### +POST http://127.0.0.1:8088/real/historyApi/getHistory +Content-Type: application/x-www-form-urlencoded +token: 2b4769b63c90adb6490cfe6e449da90b + +startDate=2021-05-17 11:03&endDate=2021-05-17 11:04&interval=3 + +### +POST http://127.0.0.1:8088/real/historyApi/getHistory +Content-Type: application/x-www-form-urlencoded +token: 2b4769b63c90adb6490cfe6e449da90b + +startDate=2021-05-17 11:03 + +### +GET http://127.0.0.1:8088/real/dict/getDictByName?name=dict2 +token: 7670b1a3da5e22ffc42a1e738ea4f0f6 + +### +tagmask=*&desc=*&_PointType=Every&_ValueTypeString=*&_TimeAccuracy=-1&_SearchCondition=SEARCH_NULL&SearchMaskValue=*&source=*&instrument=*& + +### +http://127.0.0.1:8088/real/basePointApi/getTags?tablemask=demo02&tagmask=*&desc=*&_PointType=Every&_ValueTypeString=*&_TimeAccuracy=-1&_SearchCondition=SEARCH_NULL&SearchMaskValue=*&source=*&instrument=*&pageNumber=1&pageSize=10 +token: eb7d391ad89d4bb4a81897af8829f0e8 + +### +http://127.0.0.1:8088/real/serverApi/getFile?path=D: +token: 7670b1a3da5e22ffc42a1e738ea4f0f6 + +### +#http://127.0.0.1:8088/real/serverApi/hello1/:msg +http://127.0.0.1:8088/real/serverApi/hello1/ok1 +token: a3cada4c97be40d3bc35cfe6ec1288ab + +### +http://127.0.0.1:8088/real/serverApi/hello2/ok2 +token: a3cada4c97be40d3bc35cfe6ec1288ab + + + + +### +http://127.0.0.1:8085/real/basePointApi/getTags?tablemask=demo02&tagmask=*&desc=*&unit=*&pointType=Every&valueTypeString=*&timeAccuracy=-1&searchCondition=SEARCH_NULL&SearchMaskValue=*&source=*&instrument=*&pageNumber=1&pageSize=10&accurateSearch= +token: c423c04a55964571bd34aaa1683229e8 diff --git a/web/src/main/resources/2.http b/web/src/main/resources/2.http new file mode 100644 index 0000000..c5871d5 --- /dev/null +++ b/web/src/main/resources/2.http @@ -0,0 +1,47 @@ +### +http://127.0.0.1:8088/real/test + +### +POST http://127.0.0.1:8088/real/serverApi/login +Content-Type: application/x-www-form-urlencoded + +username=sa&password=sinoreal +### +POST http://47.114.185.111:8070/real/serverApi/login +Content-Type: application/x-www-form-urlencoded + +username=sa&password=sinoreal + +### +http://127.0.0.1:8088/real/serverApi/hello2/ok2 +token: 11f1a7ad9dd907bf1fa6a9e79277d053 + + + +### +http://127.0.0.1:8088/real/test2 + + +### +POST http://127.0.0.1:8088/real/serverApi/getConnections +token: 370ba165d3164049b7704e8b3d595930 + +### +POST http://127.0.0.1:8085/real/serverApi/getConnectionInfo +token: 21f99c6080074ae79cda2e988ab2bdb8 + +### +http://127.0.0.1:7070/demo/foo + +### +http://127.0.0.1:8085/api/foo + + +### +http://127.0.0.1:8085/real/serverApi/thread-test +token: c1b89b3193bd4498be77b6e782e0df38 + + +### +http://127.0.0.1:8085/ +Accept: application/json diff --git a/web/src/main/resources/app-dev.yml b/web/src/main/resources/app-dev.yml new file mode 100644 index 0000000..2c1cb7c --- /dev/null +++ b/web/src/main/resources/app-dev.yml @@ -0,0 +1,33 @@ +# 服务配置 +server: + port: 6400 + contextPath: /api + enableStaticHtmlService: false + staticResourcePath: webroot/ +# 反向代理服务器配置路径(不用加后缀) +proxyConf: server-proxy + +vertx: + eventLoopPoolSize: 8 + workerPoolSize: 20 +custom: + asyncServiceInstances: 8 + routerLocations: cn.com.yhinfo.real.web.http + interceptorClassPath: cn.com.yhinfo.real.common.interceptorImpl.DefaultInterceptor + handlerLocations: cn.com.yhinfo.real.web.service + ignoresReg: + - .*/login$ + - .*/test.*$ + entityPackagesReg: + - ^cn\.com\.yhinfo\.real\.web\.model\..* + - ^sinereal\.core\..* + otherConfig: + - dictionaries.json + errorPage404: /index.html + indexPage: /test2 + sharedLogin: true +lzConfig: + config: '111' + +cowConfig: + config: '111' diff --git a/web/src/main/resources/app.yml b/web/src/main/resources/app.yml new file mode 100644 index 0000000..cb7bef5 --- /dev/null +++ b/web/src/main/resources/app.yml @@ -0,0 +1,7 @@ +# 要激活的配置: dev--连接本地数据库; prod连接线上数据库 +active: dev +# 框架版本号 和主版本号 +version_vertx: 4.1.3 +version_app: 0.0.1 +# 公司名称 -> LOGO版权文字 +copyright: QAIU diff --git a/web/src/main/resources/conf/dictionaries.json b/web/src/main/resources/conf/dictionaries.json new file mode 100644 index 0000000..0db3279 --- /dev/null +++ b/web/src/main/resources/conf/dictionaries.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/web/src/main/resources/curl/curl.sh b/web/src/main/resources/curl/curl.sh new file mode 100644 index 0000000..5aa1389 --- /dev/null +++ b/web/src/main/resources/curl/curl.sh @@ -0,0 +1,3 @@ +curl -F "file=@C:\Users\qaiu\Desktop\real\sinoreal2-web\web\src\main\resources\logback.xml" -i -XPOST 127.0.0.1:8088/demo/basePointApi/importTags + +curl -F "file=@C:\Users\qaiu\Desktop\3.csv" -i -XPOST 127.0.0.1:8088/demo/basePointApi/importTags diff --git a/web/src/main/resources/server-proxy.yml b/web/src/main/resources/server-proxy.yml new file mode 100644 index 0000000..8ed4fdc --- /dev/null +++ b/web/src/main/resources/server-proxy.yml @@ -0,0 +1,30 @@ +# 反向代理 +server-name: Vert.x-proxy-server(v4.1.2) + +proxy: + - listen: 8085 + # 404的路径 + 404: webroot/real-html/index.html + static: + path: / +# add-headers: +# x-token: ABC + root: webroot/real-html/ + index: realIndex + location: + - path: /real/ + origin: 127.0.0.1:8088 + - path: /api/ + origin: 127.0.0.1:7070/demo/ + + - listen: 8086 + static: + path: /t2/ + root: webroot/test/ + index: sockTest.html +# location: +# - path: /real/ +# origin: 127.0.0.1:8088 +# sock: +# - path: /real/ +# origin: 127.0.0.1:8088 diff --git a/web/src/test/java/cn/com/yhinfo/test/StompTest.java b/web/src/test/java/cn/com/yhinfo/test/StompTest.java new file mode 100644 index 0000000..37f531d --- /dev/null +++ b/web/src/test/java/cn/com/yhinfo/test/StompTest.java @@ -0,0 +1,42 @@ +package cn.com.yhinfo.test; + +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpServer; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.ext.stomp.StompServer; +import io.vertx.ext.stomp.StompServerHandler; +import io.vertx.ext.stomp.StompServerOptions; + +import java.util.Arrays; + +/** + * sinoreal2-web + *

create 2021/9/18 12:10

+ * + * @author QAIU + */ +public class StompTest { + + + + public static void main(String[] args) { + + Vertx vertx = Vertx.vertx(); + + StompServer stompServer = StompServer.create(vertx, new StompServerOptions() + .setPort(-1) // 禁用 tcp 端口,这一项是可选的 + .setWebsocketBridge(true) // 开启 websocket 支持 + .setWebsocketPath("/stomp")) // 配置 websocket 路径,默认是 /stomp + .handler(StompServerHandler.create(vertx)); + Future http = vertx.createHttpServer( + new HttpServerOptions().setWebSocketSubProtocols(Arrays.asList("v10.stomp", "v11.stomp")) + ) + .webSocketHandler(stompServer.webSocketHandler()) + .listen(8080); + http.onSuccess(res->{ + System.out.println("okk"); + }); + + } +} diff --git a/web/src/test/java/cn/com/yhinfo/test/Test01.java b/web/src/test/java/cn/com/yhinfo/test/Test01.java new file mode 100644 index 0000000..910dd7d --- /dev/null +++ b/web/src/test/java/cn/com/yhinfo/test/Test01.java @@ -0,0 +1,223 @@ +package cn.com.yhinfo.test; + +import io.vertx.ext.web.RoutingContext; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.beanutils.Converter; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.junit.Test; + +import java.lang.reflect.InvocationTargetException; +import java.text.ParseException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + *
Create date 2021/4/29 15:27 + * + * @author QAIU + */ +@Slf4j +public class Test01 { + + public static class A { + String name; + String num; + String num2; + String num3; + + Integer num5; + + public Integer getNum5() { + return num5; + } + + public void setNum5(Integer num5) { + this.num5 = num5; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getNum() { + return num; + } + + public void setNum(String num) { + this.num = num; + } + + public String getNum2() { + return num2; + } + + public void setNum2(String num2) { + this.num2 = num2; + } + + public String getNum3() { + return num3; + } + + public void setNum3(String num3) { + this.num3 = num3; + } + } + + + public static class B0 { + int num; + + public int getNum() { + return num; + } + + public void setNum(int num) { + this.num = num; + } + + } + + public static class B extends B0 { + String name; + + boolean flag; + int num4; + Date date; + String dateStr; + + Integer num5; + + public Boolean getFlag() { + return flag; + } + + public void setFlag(Boolean flag) { + this.flag = flag; + } + + public Integer getNum5() { + return num5; + } + + public void setNum5(Integer num5) { + this.num5 = num5; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getDateStr() { + return dateStr; + } + + public void setDateStr(String dateStr) { + this.dateStr = dateStr; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getNum4() { + return num4; + } + + public void setNum4(int num4) { + this.num4 = num4; + } + + @Override + public String toString() { + return "B{" + + "num=" + num + + ", name='" + name + '\'' + + ", flag=" + flag + + ", num4=" + num4 + + ", date=" + date + + ", dateStr='" + dateStr + '\'' + + ", num5=" + num5 + + '}'; + } + } + + + public static T getParamsToBean(RoutingContext ctx, Class tClass) { +// ObjectUtils.identityToString() + return null; + } + + @Test + public void test01() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + + A a = new A(); + a.setName("asd"); + a.setNum("123"); + a.setNum2("123"); + a.setNum3("123"); + a.setNum5(9999); + B b = new B(); + BeanUtils.copyProperties(b, a); + System.out.println(b); + a.setNum5(233); + System.out.println(b); + Map map = new HashMap<>(); + map.put("name", "小米"); + map.put("flag", "1"); + map.put("num", "553454344"); + map.put("num2", "123"); + map.put("num4", "q"); + map.put("dateStr", new Date()); + map.put("date", "2021-01-01"); + B b1 = new B(); + + ConvertUtils.register( + new Converter() { + @Override + public T convert(Class clazz, Object value) { + //字符串转换为日期 + try { + return (T) DateUtils.parseDate(value.toString(), "yyyy-MM-dd"); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + }, Date.class); + + + ConvertUtils.register( + new Converter() { + @Override + public T convert(Class clazz, Object value) { + //日期->字符串 + try { + return (T) DateFormatUtils.format((Date) value, "yyyy-MM-dd"); + } catch (Exception e) { + return (T) value; + } + } + }, String.class); + + BeanUtils.populate(b1, map); + log.info("---------> {}", b1); + } + +} diff --git a/web/src/test/java/cn/com/yhinfo/test/Test02.java b/web/src/test/java/cn/com/yhinfo/test/Test02.java new file mode 100644 index 0000000..413a9a0 --- /dev/null +++ b/web/src/test/java/cn/com/yhinfo/test/Test02.java @@ -0,0 +1,85 @@ +package cn.com.yhinfo.test; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.NotFoundException; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.ExceptionsAttribute; +import javassist.bytecode.LocalVariableAttribute; +import javassist.bytecode.MethodInfo; +import org.junit.Test; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class Test02 { + + + public String[] getParameterName(Class className, String method) { + String[] paramNames = null; + try { + ClassPool pool = ClassPool.getDefault(); + CtClass ctClass = pool.get(className.getName()); + CtMethod cm = ctClass.getDeclaredMethod(method); + MethodInfo methodInfo = cm.getMethodInfo(); + CtClass[] parameterTypes = cm.getParameterTypes(); + + for (CtClass parameterType : parameterTypes) { + System.out.println(parameterType.getDeclaringClass()); + System.out.println(parameterType.getName() + "----" + parameterType.getSimpleName()); + } + CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); + LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute + .getAttribute(LocalVariableAttribute.tag); + paramNames = new String[cm.getParameterTypes().length]; + CtClass[] exceptionTypes = cm.getExceptionTypes(); + ExceptionsAttribute exceptionsAttribute = methodInfo.getExceptionsAttribute(); + + for (int j = 0; j < paramNames.length; j++) { + String s = attr.variableName(attr.tableLength() - paramNames.length + j); + paramNames[j] = s; + } + + } catch (NotFoundException e) { + e.printStackTrace(); + } + return paramNames; + } + + @Test + public void test01() throws NoSuchMethodException { + + // +// Method[] methods = RealUser.class.getMethods(); +// for (Method m : methods) { +// if (m.getName().equals("setUsername2")) { +// Class[] parameterTypes = m.getParameterTypes(); +// for (Class type : parameterTypes) { +// System.out.println(type + "--"+type.getName()); +// System.out.println(type.isPrimitive()); +// System.out.println("------------"); +// } +// } +// } + } + + @Test + public void test2() { + System.out.println(("java.lang.Double".matches("^java\\.lang\\.((Integer)|(Double))$"))); + } + + + @Test + public void test3() { + Map map = new LinkedHashMap(); + map.put("1", "1"); + map.put("2", "11"); + map.put("3", "111"); + + System.out.println(map); + map.put("1", "12"); + System.out.println(map); + + } +} \ No newline at end of file diff --git a/web/src/test/java/cn/com/yhinfo/test/TestOS.java b/web/src/test/java/cn/com/yhinfo/test/TestOS.java new file mode 100644 index 0000000..746fb77 --- /dev/null +++ b/web/src/test/java/cn/com/yhinfo/test/TestOS.java @@ -0,0 +1,161 @@ +package cn.com.yhinfo.test; + +import java.io.*; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.URL; +import java.util.*; + +public class TestOS { + //通过截取cmd流方式得到计算机的配置信息(不好) + public static List getIpAddress() { + Process p = null; + List address = new ArrayList(); + try { + p = new ProcessBuilder("ipconfig", "/all").start(); + } catch (Exception e) { + return address; + } + StringBuffer sb = new StringBuffer(); + //读取进程输出值 + InputStream inputStream = p.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); + String s = ""; + try { + while ((s = br.readLine()) != null) { + sb.append(s + "\n"); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + inputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + System.out.println(sb); + return address; + } + + public static void getIpconfig() { + Map map = System.getenv(); + System.out.println(map.get("USERNAME"));//获取username + System.out.println(map.get("COMPUTERNAME"));//获取计算机名 + System.out.println(map.get("USERDOMAIN"));//获取计算机域名 + } + + //得到计算机的ip地址和mac地址 + public static void getConfig() { + try { + InetAddress address = InetAddress.getLocalHost(); + NetworkInterface ni = NetworkInterface.getByInetAddress(address); + //ni.getInetAddresses().nextElement().getAddress(); + byte[] mac = ni.getHardwareAddress(); + String sIP = address.getHostAddress(); + String sMAC = ""; + Formatter formatter = new Formatter(); + for (int i = 0; i < mac.length; i++) { + sMAC = formatter.format(Locale.getDefault(), "%02X%s", mac[i], + (i < mac.length - 1) ? + "-" : "").toString(); + } + System.out.println("IP:" + sIP); + System.out.println("MAC:" + sMAC); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //得到计算机的ip,名称,操作系统名称,操作系统版本号 + public static void Config() { + try { + InetAddress addr = InetAddress.getLocalHost(); + String ip = addr.getHostAddress().toString(); //获取本机ip + String hostName = addr.getHostName().toString(); //获取本机计算机名称 + System.out.println("本机IP:" + ip + "\n本机名称:" + hostName); + Properties props = System.getProperties(); + System.out.println("操作系统的名称:" + props.getProperty("os.name")); + System.out.println("操作系统的版本号:" + props.getProperty("os.version")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //其他的一些东西,会实用到的时候的 + public static void all() { + Properties props = System.getProperties(); + System.out.println("Java的执行环境版本号:" + props.getProperty("java.version")); + System.out.println("Java的执行环境供应商:" + props.getProperty("java.vendor")); + System.out.println("Java供应商的URL:" + props.getProperty("java.vendor.url")); + System.out.println("Java的安装路径:" + props.getProperty("java.home")); + System.out.println("Java的虚拟机规范版本号:" + props.getProperty("java.vm.specification.version")); + System.out.println("Java的虚拟机规范供应商:" + props.getProperty("java.vm.specification.vendor")); + System.out.println("Java的虚拟机规范名称:" + props.getProperty("java.vm.specification.name")); + System.out.println("Java的虚拟机实现版本号:" + props.getProperty("java.vm.version")); + System.out.println("Java的虚拟机实现供应商:" + props.getProperty("java.vm.vendor")); + System.out.println("Java的虚拟机实现名称:" + props.getProperty("java.vm.name")); + System.out.println("Java执行时环境规范版本号:" + props.getProperty("java.specification.version")); + System.out.println("Java执行时环境规范供应商:" + props.getProperty("java.specification.vender")); + System.out.println("Java执行时环境规范名称:" + props.getProperty("java.specification.name")); + System.out.println("Java的类格式版本号号:" + props.getProperty("java.class.version")); + System.out.println("Java的类路径:" + props.getProperty("java.class.path")); + System.out.println("载入库时搜索的路径列表:" + props.getProperty("java.library.path")); + System.out.println("默认的暂时文件路径:" + props.getProperty("java.io.tmpdir")); + System.out.println("一个或多个扩展文件夹的路径:" + props.getProperty("java.ext.dirs")); + System.out.println("操作系统的名称:" + props.getProperty("os.name")); + System.out.println("操作系统的构架:" + props.getProperty("os.arch")); + System.out.println("操作系统的版本号:" + props.getProperty("os.version")); + System.out.println("文件分隔符:" + props.getProperty("file.separator")); + //在 unix 系统中是"/" + System.out.println("路径分隔符:" + props.getProperty("path.separator")); + //在 unix 系统中是":" + System.out.println("行分隔符:" + props.getProperty("line.separator")); + //在 unix 系统中是"/n" + System.out.println("用户的账户名称:" + props.getProperty("user.name")); + System.out.println("用户的主文件夹:" + props.getProperty("user.home")); + System.out.println("用户的当前工作文件夹:" + props.getProperty("user.dir")); + } + + + public void showURL() throws IOException { + + // 第一种:获取类加载的根路径 D:\git\daotie\daotie\target\classes + File f = new File(this.getClass().getResource("/").getPath()); + System.out.println(f); + + // 获取当前类的所在工程路径; 如果不加“/” 获取当前类的加载目录 D:\git\daotie\daotie\target\classes\my + File f2 = new File(this.getClass().getResource("").getPath()); + System.out.println(f2); + + // 第二种:获取项目路径 D:\git\daotie\daotie + File directory = new File("");// 参数为空 + String courseFile = directory.getCanonicalPath(); + System.out.println(courseFile); + + + // 第三种: file:/D:/git/daotie/daotie/target/classes/ + URL xmlpath = this.getClass().getClassLoader().getResource(""); + System.out.println(xmlpath); + + + // 第四种: D:\git\daotie\daotie + System.out.println(System.getProperty("user.dir")); + /* + * 结果: C:\Documents and Settings\Administrator\workspace\projectName + * 获取当前工程路径 + */ + + // 第五种: 获取所有的类路径 包括jar包的路径 + System.out.println(System.getProperty("java.class.path")); + + } + + public static void main(String[] args) throws IOException { +// getConfig(); +// Config(); +// all(); +// new TestOS().showURL(); + System.out.println(File.separator); + } +} \ No newline at end of file diff --git a/web/src/test/java/cn/com/yhinfo/test/WebProxyExamples.java b/web/src/test/java/cn/com/yhinfo/test/WebProxyExamples.java new file mode 100644 index 0000000..9a6b2e6 --- /dev/null +++ b/web/src/test/java/cn/com/yhinfo/test/WebProxyExamples.java @@ -0,0 +1,169 @@ +package cn.com.yhinfo.test; + +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.StaticHandler; +import io.vertx.ext.web.proxy.handler.ProxyHandler; +import io.vertx.httpproxy.HttpProxy; +import io.vertx.httpproxy.ProxyRequest; +import io.vertx.httpproxy.ProxyResponse; +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * @author Emad Alblueshi + */ + +public class WebProxyExamples { + + public void origin() { + HttpServer backendServer = vertx.createHttpServer(); + + Router backendRouter = Router.router(vertx); + backendRouter.route().handler(ctx -> { + System.out.println(ctx.request().path()); + ctx.next(); + }); + + backendRouter.route(HttpMethod.GET, "/demo/foo").handler(rc -> rc.response() + .putHeader("content-type", "text/html") + .end("

I'm the target resource111!

")); + backendRouter.route(HttpMethod.GET, "/demo/a") + .handler(rc -> rc.response().putHeader("content-type", "text/html").end("AAA")); + backendRouter.route(HttpMethod.GET, "/demo/b") + .handler(rc -> rc.response().putHeader("content-type", "text/html").end("BBB")); + + backendServer.requestHandler(backendRouter).listen(7070); + } + + /* + + /a -> 7070/foo/a + /aaa/b -> '7070/foo/' -> 7070/foo/b + /aaa/b -> /foo/b -> 7070/foo/b + + /aaa/b -> '7070/foo' -> 7070/foob + /aaa/a -> '7070/' -> 7070/aaa/a + /aaa/a -> '7070/aaa/' -> 7070/aaa/a + + + */ + + public Vertx vertx = Vertx.vertx(); + public HttpClient proxyClient = vertx.createHttpClient(); + // 创建 http代理处理器 + HttpProxy httpProxy = HttpProxy.reverseProxy(proxyClient); + // 代理处理器绑定到路由 + Router proxyRouter = Router.router(vertx); + + public void route() { + httpProxy.origin(7070, "localhost"); + + proxyRouter.route("/demo/*").handler(ProxyHandler.create(httpProxy)); + proxyRouter.route("/api/*").handler(ctx -> ctx.reroute(ctx.request().path().replaceAll("^/api/", "/demo/"))); + +// Router r1 = Router.router(vertx); +// r1.route().handler(ctx -> { +// int statusCode = ctx.response().getStatusCode(); +// if (statusCode == 404) { +// ctx.response().write("subRouter ---------------> 404"); +// ctx.end(); +// } +// }); + + proxyRouter.route("/*").handler(StaticHandler.create("webroot/test")); + + proxyRouter.errorHandler(404, this::handle404); + +// proxyRouter.route("/api/*").handler(ctx -> ctx.end("123123")); + // 路由绑定到代理服务器 + HttpServer proxyServer = vertx.createHttpServer(); + proxyServer.requestHandler(proxyRouter); + proxyServer.listen(1080); + } + + private void handle404(RoutingContext routingContext) { + routingContext.end(routingContext.request().path() + "-------> 404"); + } + + public void routeShort(Vertx vertx, Router proxyRouter) { + HttpClient proxyClient = vertx.createHttpClient(); + + HttpProxy httpProxy = HttpProxy.reverseProxy(proxyClient); + + proxyRouter + .route(HttpMethod.GET, "/*") + .handler(ProxyHandler.create(httpProxy, 7070, "localhost")); + + + } + + + public void lowLevel() { + HttpServer proxyServer = vertx.createHttpServer(); + proxyServer.requestHandler(outboundRequest -> { + ProxyRequest proxyRequest = ProxyRequest.reverseProxy(outboundRequest); + + proxyClient.request(proxyRequest.getMethod(), 443, "qaiu.top", proxyRequest.getURI()) + .compose(proxyRequest::send) + // Send the proxy response + .onSuccess(ProxyResponse::send) + .onFailure(err -> { + // Release the request + proxyRequest.release(); + + // Send error + outboundRequest.response().setStatusCode(500) + .send(); + }); + }).listen(8181); + } + + + public void multi(Vertx vertx, Router proxyRouter) { + HttpClient proxyClient = vertx.createHttpClient(); + + HttpProxy httpProxy1 = HttpProxy.reverseProxy(proxyClient); + httpProxy1.origin(7070, "localhost"); + + HttpProxy httpProxy2 = HttpProxy.reverseProxy(proxyClient); + httpProxy2.origin(6060, "localhost"); + + proxyRouter + .route(HttpMethod.GET, "/foo").handler(ProxyHandler.create(httpProxy1)); + + proxyRouter + .route(HttpMethod.GET, "/bar").handler(ProxyHandler.create(httpProxy2)); + } + + @Test + public void test1() throws IOException, URISyntaxException { +// URL url = new URL("www.runoob.com/html/html-tutorial.html"); + URI uri = new URI("http://www.runoob.com"); + + System.out.println(StringUtils.isEmpty(uri.getPath())); + } + + public static void main(String[] args) { + final WebProxyExamples examples = new WebProxyExamples(); + examples.vertx.executeBlocking(rs -> { + rs.complete(); + examples.origin(); + }); + examples.vertx.executeBlocking(rs -> { + rs.complete(); + examples.route(); + }); + System.out.println("ok"); + } + +} + diff --git a/webroot/err/404.html b/webroot/err/404.html new file mode 100644 index 0000000..241fc2d --- /dev/null +++ b/webroot/err/404.html @@ -0,0 +1,15 @@ + + + + + 404 not fount + + +
+

+ server error - 404 not fount +

+

-- vert.x反向代理服务器(version 4.1.2)

+
+ + \ No newline at end of file diff --git a/webroot/test/sockTest.html b/webroot/test/sockTest.html new file mode 100644 index 0000000..5ffc271 --- /dev/null +++ b/webroot/test/sockTest.html @@ -0,0 +1,52 @@ + + + + + + 测试021 + + +
+ +
+ + + \ No newline at end of file diff --git a/webroot/test/sockjs-min.js b/webroot/test/sockjs-min.js new file mode 100644 index 0000000..2aa2cf8 --- /dev/null +++ b/webroot/test/sockjs-min.js @@ -0,0 +1,27 @@ +/* SockJS client, version 0.2.1, http://sockjs.org, MIT License + +Copyright (C) 2011 VMware, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// JSON2 by Douglas Crockford (minified). +var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c1?this._listeners[a]=d.slice(0,e).concat(d.slice(e+1)):delete this._listeners[a];return}return},d.prototype.dispatchEvent=function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d=3e3&&a<=4999},c.countRTO=function(a){var b;return a>100?b=3*a:b=a+200,b},c.log=function(){b.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)},c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}},c.amendUrl=function(b){var c=a.location;if(!b)throw new Error("Wrong url for SockJS");return b.indexOf("//")===0&&(b=c.protocol+b),b.indexOf("/")===0&&(b=c.protocol+"//"+c.host+b),b=b.replace(/[/]+$/,""),b},c.arrIndexOf=function(a,b){for(var c=0;c=0},c.delay=function(a,b){return typeof a=="function"&&(b=a,a=0),setTimeout(b,a)};var i=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j={"\0":"\\u0000","\x01":"\\u0001","\x02":"\\u0002","\x03":"\\u0003","\x04":"\\u0004","\x05":"\\u0005","\x06":"\\u0006","\x07":"\\u0007","\b":"\\b","\t":"\\t","\n":"\\n","\x0b":"\\u000b","\f":"\\f","\r":"\\r","\x0e":"\\u000e","\x0f":"\\u000f","\x10":"\\u0010","\x11":"\\u0011","\x12":"\\u0012","\x13":"\\u0013","\x14":"\\u0014","\x15":"\\u0015","\x16":"\\u0016","\x17":"\\u0017","\x18":"\\u0018","\x19":"\\u0019","\x1a":"\\u001a","\x1b":"\\u001b","\x1c":"\\u001c","\x1d":"\\u001d","\x1e":"\\u001e","\x1f":"\\u001f",'"':'\\"',"\\":"\\\\","\x7f":"\\u007f","\x80":"\\u0080","\x81":"\\u0081","\x82":"\\u0082","\x83":"\\u0083","\x84":"\\u0084","\x85":"\\u0085","\x86":"\\u0086","\x87":"\\u0087","\x88":"\\u0088","\x89":"\\u0089","\x8a":"\\u008a","\x8b":"\\u008b","\x8c":"\\u008c","\x8d":"\\u008d","\x8e":"\\u008e","\x8f":"\\u008f","\x90":"\\u0090","\x91":"\\u0091","\x92":"\\u0092","\x93":"\\u0093","\x94":"\\u0094","\x95":"\\u0095","\x96":"\\u0096","\x97":"\\u0097","\x98":"\\u0098","\x99":"\\u0099","\x9a":"\\u009a","\x9b":"\\u009b","\x9c":"\\u009c","\x9d":"\\u009d","\x9e":"\\u009e","\x9f":"\\u009f","\xad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"},k=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,l,m=JSON&&JSON.stringify||function(a){return i.lastIndex=0,i.test(a)&&(a=a.replace(i,function(a){return j[a]})),'"'+a+'"'},n=function(a){var b,c={},d=[];for(b=0;b<65536;b++)d.push(String.fromCharCode(b));return a.lastIndex=0,d.join("").replace(a,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),a.lastIndex=0,c};c.quote=function(a){var b=m(a);return k.lastIndex=0,k.test(b)?(l||(l=n(k)),b.replace(k,function(a){return l[a]})):b};var o=["websocket","xdr-streaming","xhr-streaming","iframe-eventsource","iframe-htmlfile","xdr-polling","xhr-polling","iframe-xhr-polling","jsonp-polling"];c.probeProtocols=function(){var a={};for(var b=0;b0&&h(a)};return c.websocket!==!1&&h(["websocket"]),d["xdr-streaming"]&&!c.cookie_needed?e.push("xdr-streaming"):h(["xhr-streaming","iframe-eventsource","iframe-htmlfile"]),d["xdr-polling"]&&!c.cookie_needed?e.push("xdr-polling"):h(["xhr-polling","iframe-xhr-polling","jsonp-polling"]),e};var p="_sockjs_global";c.createHook=function(){var a="a"+c.random_string(8);if(!(p in b)){var d={};b[p]=function(a){return a in d||(d[a]={id:a,del:function(){delete d[a]}}),d[a]}}return b[p](a)},c.attachMessage=function(a){c.attachEvent("message",a)},c.attachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.addEventListener(c,d,!1):(a.attachEvent("on"+c,d),b.attachEvent("on"+c,d))},c.detachMessage=function(a){c.detachEvent("message",a)},c.detachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.removeEventListener(c,d,!1):(a.detachEvent("on"+c,d),b.detachEvent("on"+c,d))};var q={};c.unload_add=function(a){var b=c.random_string(8);return q[b]=a,b},c.unload_del=function(a){a in q&&delete q[a]},c.attachEvent("unload",function(){for(var a in q)q[a]()}),c.createIframe=function(b,d){var e=a.createElement("iframe"),f,g=function(){clearTimeout(f);try{e.onload=null}catch(a){}e.onerror=null},h=function(){e&&(g(),e.src="about:blank",setTimeout(function(){e&&e.parentNode.removeChild(e),e=null},0),c.detachEvent("unload",h))},i=function(a){e&&(h(),d(a))};return e.src=b,e.style.display="none",e.style.position="absolute",e.onerror=function(){i("onerror")},e.onload=function(){clearTimeout(f),f=setTimeout(function(){i("onload timeout")},2e3)},a.body.appendChild(e),f=setTimeout(function(){i("timeout")},5e3),c.attachEvent("unload",h),{iframe:e,cleanup:h,loaded:g}},c.createHtmlfile=function(a,d){var e=new ActiveXObject("htmlfile"),f,g,i=function(){clearTimeout(f)},j=function(){if(e){i(),c.detachEvent("unload",j);try{g.src="about:blank"}catch(a){}g.parentNode.removeChild(g),g=e=null,CollectGarbage()}},k=function(a){e&&(j(),d(a))};e.open(),e.write('