diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml index 746fc8d79..e20118b3a 100644 --- a/.github/workflows/release-notes.yml +++ b/.github/workflows/release-notes.yml @@ -62,6 +62,8 @@ jobs: labels: [ "feature" ] - title: "🐞 Bug Fixes" labels: [ "bug" ] + - title: "💥 Breaking Change" + labels: [ "break" ] - title: "💎 Enhancements" labels: [ "better", "devops", "quality" ] - title: "📝 Documentation" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b6bcea9b7..f6b335264 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -148,7 +148,7 @@ jobs: EOF - name: Qodana Scan - uses: JetBrains/qodana-action@v2023.3 + uses: JetBrains/qodana-action@v2024.1 if: steps.settings.outputs.QODANA_SCAN == 'true' env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} @@ -156,7 +156,7 @@ jobs: - name: Test Coverage ${{ steps.settings.outputs.WINGS_VERSION }} ${{ steps.settings.outputs.GIT_BRANCH }} if: steps.settings.outputs.MVN_COVERAGE == 'true' run: | - mvn -P '!module-example,!module-devs' -Dmaven.test.skip=true clean install + mvn -P 'repo-snapshot,!module-example,!module-devs' -Dmaven.test.skip=true clean install mvn -pl ':devs-codegen' -Ddevs-initdb=true clean test mvn -P 'report-coverage,!module-example,!module-devs' -Dmaven.test.failure.ignore=$TESTFAILS_IGNORE test mvn -P 'report-coverage' -pl ':devs-coverage' -am jacoco:report-aggregate @@ -181,7 +181,7 @@ jobs: if: steps.settings.outputs.MVN_DEPLOY_OSSRH == 'true' run: > mvn - -P 'deploy,deploy-oss,!module-example,!module-devs' + -P 'repo-snapshot,deploy,deploy-oss,!module-example,!module-devs' ${{ steps.settings.outputs.MVN_REVISION }} -Dgpg.passphrase=${MVN_GPG_PASS} -Dorg.slf4j.simpleLogger.log.lombok=WARN @@ -200,7 +200,7 @@ jobs: continue-on-error: ${{ inputs.deployAltrh != 'true' }} run: > mvn - -P 'deploy,deploy-alt,deploy-old,!module-example,!module-devs' + -P 'repo-snapshot,deploy,deploy-alt,deploy-old,!module-example,!module-devs' ${{ steps.settings.outputs.MVN_REVISION }} -Dgpg.passphrase=${MVN_GPG_PASS} -Dorg.slf4j.simpleLogger.log.lombok=WARN diff --git a/WingsBoot.t.md b/WingsBoot.t.md index 05978ad1d..99e60d542 100644 --- a/WingsBoot.t.md +++ b/WingsBoot.t.md @@ -39,6 +39,8 @@ Use `t.md` as local [Test Management](https://www.jetbrains.com/help/idea/test-m * 11033 ThisLazyCglibTest: thisLazy with cglib * 11034 ThisLazyProxyTest: thisLazy with default jdk proxy * 11035 TypedClassTest: ResolvableType sugar +* 11036 CommonPropHelperTest: comma delimited string +* 11037 CommonPropHelperTest: resource string ## 12 Faceless @@ -323,6 +325,7 @@ Use `t.md` as local [Test Management](https://www.jetbrains.com/help/idea/test-m * 13125 JsonHelperCompatibleTest: jackson basic type compatible * 13126 FastJsonTest: fastjson helper json path * 13127 TypeReferenceTest: TypeReference, TypeDescriptor, ResolvableType +* 13128 LogViewerTest: only match header line ## 14 Warlock @@ -411,6 +414,7 @@ Use `t.md` as local [Test Management](https://www.jetbrains.com/help/idea/test-m * 14083 DatabaseShard0Test: clean and init shard_0 schema * 14084 DatabaseShard1Test: clean and init shard_1 schema * 14085 ExceptionStackTest: MessageException is stackless by default +* 14086 JournalControllerTest: ttl/plain context check ## 15 Tiny @@ -430,3 +434,9 @@ Use `t.md` as local [Test Management](https://www.jetbrains.com/help/idea/test-m * 15014 TinyTaskExecServiceTest: sleep 70s and check task * 15015 MailNoticeTest: title dryrun mailNotice * 15016 MailSenderManagerTest: title dryrun batch mail +* 15017 TinyMailServiceDbTest: mock fail and check database +* 15018 TinyTrackServiceTest: tiny track AOP service +* 15019 TinyTrackServiceTest: tiny track Mvc controller +* 15020 TinyTaskBeatServiceImplTest: beat mills cron/rate/idle +* 15021 TinyTaskExecServiceImplTest: calculate next schedule + diff --git a/changelog.md b/changelog.md index 01afd6cf1..40308cd6f 100644 --- a/changelog.md +++ b/changelog.md @@ -8,7 +8,7 @@ Help Yourself [Commits] and [History] Notable [Changes] in [WingsDoc] -[History]: https://github.com/trydofor/pro.fessional.wings/tags -[Commits]: https://github.com/trydofor/pro.fessional.wings/commits/develop +[History]: https://github.com/trydofor/professional-wings/tags +[Commits]: https://github.com/trydofor/professional-wings/commits/develop [Changes]: https://wings.fessional.pro/9-example/9a.wings-change/ [WingsDoc]: https://wings.fessional.pro/ diff --git a/example/winx-common/src/main/resources/wings-flywave/master/00-init/2022-0222u01-demo-init.sql b/example/winx-common/src/main/resources/wings-flywave/master/00-init/2022-0222u01-demo-init.sql index b11449786..660325bc6 100644 --- a/example/winx-common/src/main/resources/wings-flywave/master/00-init/2022-0222u01-demo-init.sql +++ b/example/winx-common/src/main/resources/wings-flywave/master/00-init/2022-0222u01-demo-init.sql @@ -1 +1,3 @@ DROP TABLE IF EXISTS `winx_user_detail`; -- 210/User Detail; + +-- CALL FLYWAVE('2022-0222u01-demo-init.sql'); \ No newline at end of file diff --git a/example/winx-common/src/main/resources/wings-flywave/master/00-init/2022-0222v01-demo-init.sql b/example/winx-common/src/main/resources/wings-flywave/master/00-init/2022-0222v01-demo-init.sql index 5f3213079..0e681e095 100644 --- a/example/winx-common/src/main/resources/wings-flywave/master/00-init/2022-0222v01-demo-init.sql +++ b/example/winx-common/src/main/resources/wings-flywave/master/00-init/2022-0222v01-demo-init.sql @@ -2,13 +2,13 @@ -- CREATE DATABASE `wings_example` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; CREATE TABLE `winx_user_detail` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', + `id` BIGINT NOT NULL COMMENT 'primary key', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime(sys)', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', - `user_id` BIGINT(20) NOT NULL COMMENT 'win_user_basis.id', - `user_type` INT(11) NOT NULL DEFAULT '0' COMMENT 'user type/21001##:customer|operator|helpdesk', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', + `user_id` BIGINT NOT NULL COMMENT 'win_user_basis.id', + `user_type` INT NOT NULL DEFAULT '0' COMMENT 'user type/21001##:customer|operator|helpdesk', `email` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'user email', PRIMARY KEY (`id`), UNIQUE INDEX uq_user_id (`user_id`), @@ -26,3 +26,5 @@ VALUES (2100100, 'user_type', 'user_type', 'User Type', 'classpath:/wings-tmpl/C (2100101, 'user_type', 'customer', 'customer', 'customer'), (2100102, 'user_type', 'operator', 'operator', 'operator'), (2100103, 'user_type', 'helpdesk', 'helpdesk', 'helpdesk'); + +-- CALL FLYWAVE('2022-0222v01-demo-init.sql'); \ No newline at end of file diff --git a/example/winx-devops/src/test/java/com/moilioncircle/wings/devops/project/Devops1SchemaManagerTest.java b/example/winx-devops/src/test/java/com/moilioncircle/wings/devops/project/Devops1SchemaManagerTest.java index abf306b33..edebf4e2c 100644 --- a/example/winx-devops/src/test/java/com/moilioncircle/wings/devops/project/Devops1SchemaManagerTest.java +++ b/example/winx-devops/src/test/java/com/moilioncircle/wings/devops/project/Devops1SchemaManagerTest.java @@ -21,6 +21,12 @@ "spring.datasource.url=" + Devops0ProjectConstant.JDBC_URL, "spring.datasource.username=" + Devops0ProjectConstant.JDBC_USER, "spring.datasource.password=" + Devops0ProjectConstant.JDBC_PASS, +// "wings.silencer.i18n.zoneid=UTC", + "wings.enabled.faceless.flywave=true", + "wings.faceless.flywave.auto-init=true", + "wings.faceless.flywave.checker=true", + "java.awt.headless=false", + "debug=true" }) class Devops1SchemaManagerTest { @@ -37,6 +43,15 @@ void initSchema() { ); } + @Test + void bumpSchema() { + final Warlock1SchemaManager manager = new Warlock1SchemaManager(schemaRevisionManager); + manager.mergeBumping(2022_0222_01L, + includeWarlockPath(), + Helper::master + ); + } + @Test void resetSchema() { long revi = 2022_0222_01L; diff --git a/example/winx-devops/src/test/java/com/moilioncircle/wings/devops/project/Devops7EnumsDumperTest.java b/example/winx-devops/src/test/java/com/moilioncircle/wings/devops/project/Devops7EnumsDumperTest.java index cd3d9d34f..8e0d54582 100644 --- a/example/winx-devops/src/test/java/com/moilioncircle/wings/devops/project/Devops7EnumsDumperTest.java +++ b/example/winx-devops/src/test/java/com/moilioncircle/wings/devops/project/Devops7EnumsDumperTest.java @@ -8,7 +8,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.io.ResourceLoader; import pro.fessional.mirana.data.CodeEnum; -import pro.fessional.wings.silencer.spring.help.SubclassSpringLoader; +import pro.fessional.wings.silencer.support.SubclassGather; import java.util.Map; @@ -27,7 +27,7 @@ public class Devops7EnumsDumperTest { @Test public void dumpCodeEnum() { - SubclassSpringLoader loader = new SubclassSpringLoader(resourceLoader); + SubclassGather loader = new SubclassGather(resourceLoader); Class superEnum = CodeEnum.class; Map, Enum[]> enums = loader.loadSubEnums("pro.fessional", superEnum); diff --git a/observe/docs b/observe/docs index c30360bc3..14ebccb0d 160000 --- a/observe/docs +++ b/observe/docs @@ -1 +1 @@ -Subproject commit c30360bc3a91f2e1fb12c621add778d3c70f61c1 +Subproject commit 14ebccb0d5142c697c8e1c26714477f1e205282c diff --git a/observe/mirana b/observe/mirana index 4468526da..532d397fc 160000 --- a/observe/mirana +++ b/observe/mirana @@ -1 +1 @@ -Subproject commit 4468526dab95bb10df5bcabded576aeae224acd0 +Subproject commit 532d397fc5284f4817a4a82c870b3f9e35050637 diff --git a/observe/scripts/wings-docker.sh b/observe/scripts/wings-docker.sh index c10ac3054..c773a2077 100755 --- a/observe/scripts/wings-docker.sh +++ b/observe/scripts/wings-docker.sh @@ -37,14 +37,14 @@ $IMAGE_ENV_JAVA_ADD \ ####### function show_help() { - echo -e '\033[32m clean \033[m clean docker- build dir of boot-jar' - echo -e '\033[32m print \033[m print docker- Dockerfile of boot-jar' - echo -e '\033[32m unzip \033[m unzip docker- files of boot-jar' - echo -e '\033[32m build \033[m build docker- image of boot-jar' - echo -e '\033[32m help \033[m show this' - echo -e '\033[32m \033[m only dependencies + spring-boot-loader' - echo -e '\033[32m \033[m + napshot-dependencies + application' - echo -e '\033[32m \033[m + ' + echo -e '\033[32m clean \033[0m clean docker- build dir of boot-jar' + echo -e '\033[32m print \033[0m print docker- Dockerfile of boot-jar' + echo -e '\033[32m unzip \033[0m unzip docker- files of boot-jar' + echo -e '\033[32m build \033[0m build docker- image of boot-jar' + echo -e '\033[32m help \033[0m show this' + echo -e '\033[32m \033[0m only dependencies + spring-boot-loader' + echo -e '\033[32m \033[0m + napshot-dependencies + application' + echo -e '\033[32m \033[0m + ' } function docker_file() { diff --git a/observe/scripts/wings-mysql-dump.sh b/observe/scripts/wings-mysql-dump.sh index 992769e46..a63b60a85 100755 --- a/observe/scripts/wings-mysql-dump.sh +++ b/observe/scripts/wings-mysql-dump.sh @@ -26,10 +26,10 @@ dumpopts=${*:3} logxopts="--no-data" confopts=--defaults-extra-file=$extracnf if [[ -f "$extracnf" ]]; then - echo -e "\033[0;33mNOTE: defaults-extra-file \033[m" + echo -e "\033[0;33mNOTE: defaults-extra-file \033[0m" grep -E "^(host|port|user)" "$extracnf" else - echo -e "\033[0;31mERROR: should specific mysql config(at param-1), eg. ~/my.cnf\033[m" + echo -e "\033[0;31mERROR: should specific mysql config(at param-1), eg. ~/my.cnf\033[0m" cat << 'EOF' [client] protocol=tcp @@ -52,9 +52,9 @@ unalias mysql >/dev/null 2>&1 unalias mysqldump >/dev/null 2>&1 if [[ "$database" == "" ]]; then - echo -e "\033[0;31mWARN: need database(at param-2) to dump, eg.\033[m" + echo -e "\033[0;31mWARN: need database(at param-2) to dump, eg.\033[0m" echo "./wings-mysql-dump.sh wings-mysql-client.cnf database --no-data" - echo -e "\033[0;33mNOTE:current databases \033[m" + echo -e "\033[0;33mNOTE:current databases \033[0m" # shellcheck disable=SC2086 mysql $confopts -N -e "show databases;" exit @@ -81,7 +81,7 @@ if [[ $logs_cnt == 0 ]]; then echo "no logs tables to dump" echo "-- no logs tables to dump" > "$dump_logs_file" else - echo -e "\033[0;33mNOTE: dump logs tables without data, count=$logs_cnt\033[m" + echo -e "\033[0;33mNOTE: dump logs tables without data, count=$logs_cnt\033[0m" # shellcheck disable=SC2046,SC2086 if mysqldump $confopts $dumpopts $logxopts \ @@ -98,7 +98,7 @@ if [[ $main_cnt == 0 ]]; then echo "no main tables to dump" echo "-- no main tables to dump" > "$dump_main_file" else - echo -e "\033[0;33mNOTE: dump main tables with data, count=$main_cnt\033[m" + echo -e "\033[0;33mNOTE: dump main tables with data, count=$main_cnt\033[0m" # shellcheck disable=SC2046,SC2086 if mysqldump $confopts $dumpopts \ "$database" $(grep -vE '\$|__' "$dump_tbl_file") > "$dump_main_file"; then @@ -109,13 +109,13 @@ else fi fi -echo -e "\033[0;33mNOTE: dump file $dump_head\033[m" +echo -e "\033[0;33mNOTE: dump file $dump_head\033[0m" echo >> "$dump_tbl_file" # shellcheck disable=SC2010 ls -lsh |grep "$dump_head" | tee -a "$dump_tbl_file" -echo -e "\033[0;33mNOTE: tips for zip, scp, restore \033[m" +echo -e "\033[0;33mNOTE: tips for zip, scp, restore \033[0m" tee -a "$dump_tip_file" << EOF ## checksum md5sum -c $dump_md5_file # checksum @@ -153,7 +153,7 @@ cat $dump_logs_file $dump_main_file \\ ./reset-password.sh \$mycnf \$newdb; EOF -echo -e "\033[0;33mNOTE: tar files into $dump_tar_file \033[m" +echo -e "\033[0;33mNOTE: tar files into $dump_tar_file \033[0m" tar -czf "$dump_tar_file" "$dump_tip_file" "$dump_tbl_file" "$dump_logs_file" "$dump_main_file" \ && md5sum "$dump_tar_file" | tee "$dump_md5_file" \ && rm -f "$dump_tbl_file" "$dump_logs_file" "$dump_main_file" diff --git a/observe/scripts/wings-mysql-user.sh b/observe/scripts/wings-mysql-user.sh index b6677651e..51a3d2938 100755 --- a/observe/scripts/wings-mysql-user.sh +++ b/observe/scripts/wings-mysql-user.sh @@ -33,7 +33,7 @@ command="$2" option="$3" if [[ "$command" == "" || "$command" == "help" || ! -f "$userenv" ]]; then - echo -e '\033[37;42;1mNOTE: users env file\033[m' + echo -e '\033[37;42;1mNOTE: users env file\033[0m' # https://dev.mysql.com/doc/refman/8.0/en/account-management-statements.html cat <<'EOF' execute=false @@ -54,7 +54,7 @@ host_app=10.11.% host_dev=% host_dba=% EOF - echo -e '\033[37;42;1mNOTE: user manage\033[m' + echo -e '\033[37;42;1mNOTE: user manage\033[0m' cat <<'EOF' RENAME USER 'trydofor'@'%' TO 'trydofor'@'127.0.%'; DROP USER IF EXISTS 'trydofor'@'%'; @@ -91,7 +91,7 @@ fi [[ "$host_dev" == "" ]] && host_dev=% [[ "$host_dba" == "" ]] && host_dba=% -echo -e '\033[37;42;1mNOTE: users and passwd\033[m' +echo -e '\033[37;42;1mNOTE: users and passwd\033[0m' grep -v '^#' </dev/null 2>&1 if [[ "$command" == "create" ]]; then @@ -148,7 +148,7 @@ if [[ "$execute" == "true" ]]; then unalias mysql >/dev/null 2>&1 if [[ -f "$option" ]]; then - echo -e "\033[0;33mNOTE: current option file \033[m" + echo -e "\033[0;33mNOTE: current option file \033[0m" cat "$option" mysql --defaults-extra-file="$option" -vvv --force < "$temp_sql" else diff --git a/observe/scripts/wings-release.sh b/observe/scripts/wings-release.sh index 4e9b362d4..c1acded3b 100755 --- a/observe/scripts/wings-release.sh +++ b/observe/scripts/wings-release.sh @@ -1,11 +1,14 @@ #!/bin/bash -e -THIS_VERSION=2024-03-05 +THIS_VERSION=2024-08-08 cat < /data/wings-script/wings-release.sh" +# my-release.env" # If PACK_JAR is directory, SUB_FLAT determines the overwrite behavior. ################################################# EOF @@ -81,8 +84,9 @@ function build_mvn() { echo '#Generated by Wings Release Script' >$_git_log git branch -v >>$_git_log git log --pretty=format:'%H - %an, %ad %d : %s' --graph -10 >>$_git_log + echo >>$_git_log find . -type d -name 'resources' | grep '/src/main/' | while read -r res; do - echo "$res" >>$_res_log + echo "$res/$_git_log" >>$_res_log cp $_git_log "$res/" done @@ -90,15 +94,20 @@ function build_mvn() { # shellcheck disable=SC2086 mvn $MVN_PACK - echo -e "\033[37;42;1m ==== GitLogs $WORK_DIR ==== \033[0m" + echo -e "\033[37;42;1m ==== Git $WORK_DIR ==== \033[0m" cat "$_git_log" - echo - echo -e "\033[32m ==== git-log into ==== \033[0m" + echo -e "\033[32m ==== $_git_log ==== \033[0m" cat "$_res_log" - echo -e "\033[32m ==== git status ==== \033[0m" + + while read -r res; do + [[ -f "$res" ]] && rm -f "$res" + done < "$_res_log" rm -f "$_git_log" "$_res_log" + + echo -e "\033[32m ==== status ==== \033[0m" git status - echo -e "\033[32m ==== mvn version ==== \033[0m" + + echo -e "\033[37;42;1m ==== Mvn version ==== \033[0m" mvn --version } @@ -122,7 +131,7 @@ function build_web() { fi # build - echo -e "\033[32m web pack $_cmd \033[m" + echo -e "\033[32m web pack $_cmd \033[0m" if [[ "$_cmd" == "pnpm" ]]; then pnpm install pnpm $WEB_PACK @@ -224,11 +233,50 @@ echo "work-dir=$WORK_DIR" # check arg case "$1" in + last) + check_cmd git + echo -e "\033[37;42;1m ==== GIT $WORK_DIR ==== \033[0m" + git status + git log --pretty=format:'%H - %an, %ad %d : %s' --graph -10 + echo -e "\033[37;42;1m ==== DST package ==== \033[0m" + _dst="" + for _jar in $PACK_JAR; do + if [[ -f "$_jar" || -d "$_jar" ]]; then + ls -l "$_jar" + _dst=$(realpath "$_jar") + else + _tmp=$(find . -type f -name "$_jar") + if [[ -f "$_tmp" ]]; then + ls -l "$_tmp" + _dst=$(realpath "$_tmp") + fi + fi + done + + echo -e "\033[37;42;1m ==== DST build info ==== \033[0m" + if [[ -d "$_dst" ]]; then + find "$_jar" -maxdepth 1 -name 'index.html' | while read -r _idx; do + echo "$_dst/index.html" + grep 'WingsGitHash' "$_idx" | sed -n 's/.*\(\).*/\1/p' + break + done + elif [[ -f "$_dst" && $_dst == *.jar ]]; then + giti=$(jar tf "$_dst" 2>/dev/null | grep git.properties ) + if [[ "$giti" != "" ]]; then + tmp="./tmp-$BOOT_MD5" + mkdir -p "$tmp" + (cd "$tmp" && jar xf "$_dst" "$giti") + echo "$_dst" + grep -vE '=$' "$tmp/$giti" + rm -rf "$tmp" + fi + fi + ;; pull) check_cmd git echo -e "\033[37;42;1m ==== PULL $WORK_DIR ==== \033[0m" - git pull + git pull --force echo -e "\033[37;42;1m ==== DONE $WORK_DIR ==== \033[0m" git status git log --pretty=format:'%H - %an, %ad %d : %s' --graph -10 @@ -250,23 +298,23 @@ case "$1" in _tmp=$(find . -type f -name "$_jar") if [[ ! -f "$_tmp" ]]; then echo -e "\033[31mERROR: not file. $_jar \033[0m" - _jar_info="$_jar_info skip \033[31m $_jar \033[m => not find\n" + _jar_info="$_jar_info skip \033[31m $_jar \033[0m => not find\n" continue fi fi _rp=$(realpath "$_tmp") if [[ "$_yna" != "a" ]]; then - echo -e "[y/n/a]? \033[32m $_jar \033[m => $_rp" + echo -e "[y/n/a]? \033[32m $_jar \033[0m => $_rp" read -r _yna /data/wings-script/wings-starter.sh" + echo -e "# my-starter.env" + echo -e "# only one boot.jar run on one host, rename it to run more copies." + echo -e "# 'BOOT_ARG|JAVA_ARG|JDK8_ARG|JDK9_ARG' can be lazily evaluated" + echo -e "# in evaluation, ' for delayed, \" for immediate." + echo -e "#################################################" + echo -e '\033[32m docker \033[0m start in docker with console log' + echo -e '\033[32m start \033[0m start the {boot-jar} and tail the log' + echo -e '\033[32m starts \033[0m start but Not wait log' + echo -e '\033[32m stop [snd=30]\033[0m stop the {boot-jar} gracefully in {snd} seconds' + echo -e '\033[32m stops [snd=30]\033[0m stop but Not confirm' + echo -e '\033[32m status \033[0m show the {boot-jar} runtime status' + echo -e '\033[32m warn \033[0m monitor the {boot-jar} and log' + echo -e '\033[32m live \033[0m monitor the {boot-jar} and auto restart if lost pid' + echo -e '\033[32m clean [days=30] [y] \033[0m clean up boot out/log/jar {days} ago but newest' + echo -e '\033[32m clean-out [days=30] [y] \033[0m clean up boot-out {days} ago but newest' + echo -e '\033[32m clean-log [days=30] [y] \033[0m clean up boot-log {days} ago but newest' + echo -e '\033[32m clean-jar [days=30] [y] \033[0m clean up boot-jar {days} ago but newest' + echo -e '\033[32m config \033[0m print config envs' + echo -e '\033[32m tail \033[0m tail boot log or out' + echo -e '\033[32m less \033[0m less boot log or out' echo - echo -e '\033[32m docker \033[m start in docker with console log' - echo -e '\033[32m start \033[m start the {boot-jar} and tail the log' - echo -e '\033[32m starts \033[m start but Not wait log' - echo -e '\033[32m stop [snd=30]\033[m stop the {boot-jar} gracefully in {snd} seconds' - echo -e '\033[32m stops [snd=30]\033[m stop but Not confirm' - echo -e '\033[32m status \033[m show the {boot-jar} runtime status' - echo -e '\033[32m warn \033[m monitor the {boot-jar} and log' - echo -e '\033[32m live \033[m monitor the {boot-jar} and auto restart if lost pid' - echo -e '\033[32m clean [days=30] [y] \033[m clean up log-file {days} ago but newest' - echo -e '\033[32m clean-jar [days=30] [y] \033[m clean up boot-jar {days} ago but newest' - echo -e '\033[32m config \033[m print config envs' - echo -e '\033[32m tail \033[m tail boot log or out' - echo - echo -e '\033[32m cron \033[m show the {boot-jar} crontab usage' - echo -e '\033[32m free \033[m check memory free' - echo -e '\033[32m check \033[m check shell command' - echo -e '\033[32m help \033[m print help message' + echo -e '\033[32m cron \033[0m show the {boot-jar} crontab usage' + echo -e '\033[32m free \033[0m check memory free' + echo -e '\033[32m check \033[0m check shell command' + echo -e '\033[32m help \033[0m print help message' echo - echo -e 'default is \033[37;43;1m start \033[m, for example' + echo -e 'default is \033[37;43;1m start \033[0m, for example' echo -e './wings-starter.sh' echo -e './wings-starter.sh status' echo -e './wings-starter.sh boot.jar start' @@ -137,14 +142,45 @@ function print_args() { echo "BOOT_ARG=$BOOT_ARG" echo -e "\033[37;42;1mINFO: ==== java arguments ==== \033[0m" echo "$JAVA_OPT" + + git_jar="${BOOT_JAR}_${BOOT_MD5}" + if [[ ! -f "$git_jar" ]]; then + git_jar="${BOOT_JAR}" + fi + if [[ -f "$git_jar" ]]; then + tmp="./tmp-$BOOT_MD5" + jls="$tmp/jar-list.txt" + mkdir -p "$tmp" + jar tf "$git_jar" > "$jls" + + giti=$(grep 'git.properties' "$jls") + if [[ "$giti" != "" ]]; then + (cd "$tmp" && jar xf "$git_jar" "$giti") + if [[ -f "$tmp/$giti" ]]; then + echo -e "\033[37;42;1mINFO: ==== git build info ==== \033[0m" + grep -vE '=$' "$tmp/$giti" + fi + fi + + wngi=$(grep -E 'silencer-[1-9].*.jar' "$jls") + if [[ "$wngi" != "" ]]; then + (cd "$tmp" && jar xf "$git_jar" "$wngi" && jar xf "$wngi" git.properties) + if [[ -f "$tmp/git.properties" ]]; then + echo -e "\033[37;42;1mINFO: ==== git wings info ==== \033[0m" + grep -vE '=$' "$tmp/git.properties" + fi + fi + + rm -rf "$tmp" + fi } function check_cmd() { cmd=$(printf "%-10s" "$1") if info=$(which "$1") >/dev/null 2>&1; then - echo -e "\033[32m $cmd \033[m $info" + echo -e "\033[32m $cmd \033[0m $info" else - echo -e "\033[31m $cmd not found \033[m" + echo -e "\033[31m $cmd not found \033[0m" fi } @@ -159,6 +195,7 @@ function check_user() { function check_java() { echo -e "\033[37;42;1mINFO: ==== java version ==== \033[0m" + echo -e "JAVA_HOME=\033[32m$JAVA_HOME\033[0m" if ! java -version; then echo -e "\033[37;41;1mERROR: can not found 'java' in the $PATH \033[0m" exit @@ -202,7 +239,7 @@ function check_boot() { function safe_start() { # safe backup md5sum "$BOOT_JAR" >"$file_md5" - BOOT_MD5=$(awk '{print $1}' <"$file_md5") + BOOT_MD5=$(awk '{print $1}' "$file_md5") # `_` as delimiter safe_jar="${BOOT_JAR}_${BOOT_MD5}" if [[ ! -f "$safe_jar" ]]; then @@ -229,7 +266,6 @@ if [[ -L "$this_file" ]]; then link_file=$(realpath "$this_file") link_envs=${link_file%.*}.env if [[ -f "$link_envs" ]]; then - echo -e "\033[37;42;1mINFO: load link-envs form $link_envs ==== \033[0m" # shellcheck disable=SC1090 source "$link_envs" fi @@ -237,7 +273,6 @@ fi this_envs=${this_file%.*}.env if [[ -f "$this_envs" ]]; then - echo -e "\033[37;42;1mINFO: load this-envs form $this_envs ==== \033[0m" # shellcheck disable=SC1090 source "$this_envs" else @@ -245,7 +280,6 @@ else fi if [[ -f "$BOOT_ENVF" ]]; then - echo -e "\033[37;42;1mINFO: load boot-envs form $BOOT_ENVF ==== \033[0m" # shellcheck disable=SC1090 source "$BOOT_ENVF" fi @@ -274,11 +308,11 @@ case "$ARGS_RUN" in cron) this_path=$(realpath -s "$this_file") echo -e "\033[37;43;1mNOTE: ==== crontab usage ==== \033[0m" - echo -e "\033[32m crontab -e -u $USER_RUN \033[m" - echo -e "\033[32m crontab -l -u $USER_RUN \033[m" - echo -e "\033[32m */5 * * * * $this_path warn \033[m" - echo -e "\033[32m */5 * * * * $this_path live >> $this_path.cron \033[m" - echo -e "\033[32m 0 0 * * * $this_path clean 30 y \033[m" + echo -e "\033[32m crontab -e -u $USER_RUN \033[0m" + echo -e "\033[32m crontab -l -u $USER_RUN \033[0m" + echo -e "\033[32m */5 * * * * $this_path warn \033[0m" + echo -e "\033[32m */5 * * * * $this_path live >> $this_path.cron \033[0m" + echo -e "\033[32m 0 0 * * * $this_path clean 30 y \033[0m" exit ;; free) @@ -333,9 +367,8 @@ case "$ARGS_RUN" in exit ;; help) - echo -e '\033[37;42;1mNOTE: help info, use the following\033[m' + echo -e '\033[37;42;1mNOTE: help info, use the following\033[0m' print_help - print_envs exit ;; esac @@ -366,14 +399,13 @@ fi # boot md5 file_md5="${JAR_NAME}.md5" if [[ -f "$file_md5" ]]; then - BOOT_MD5=$(awk '{print $1}' <"$file_md5") + BOOT_MD5=$(awk '{print $1}' "$file_md5") fi # java home & path if [[ "$JDK_HOME" != "" && "$JDK_HOME" != "$JAVA_HOME" ]]; then PATH=$JDK_HOME/bin:$PATH JAVA_HOME=$JDK_HOME - echo -e "\033[37;42;1mINFO: ==== JAVA_HOME=$JAVA_HOME ==== \033[0m" fi # lazy env eval @@ -414,7 +446,7 @@ case "$ARGS_RUN" in pgrep -alf "$grep_key" fi ;; - start*) + start | starts) print_envs check_java @@ -442,9 +474,9 @@ case "$ARGS_RUN" in if [[ "$ARGS_RUN" != "start" ]]; then echo -e "\033[37;42;1mINFO: tail -f $BOOT_OUT \033[0m" timeout -s 9 10 tail -f "$BOOT_OUT" - echo -e "\033[37;43;1m====== ${BOOT_JAR//?/=} ======\033[0m" - echo -e "\033[37;43;1m====== $BOOT_JAR ======\033[0m" - echo -e "\033[37;43;1m====== ${BOOT_JAR//?/=} ======\033[0m" + echo -e "\033[37;42;1m =====${BOOT_JAR//?/=}===== \033[0m" + echo -e "\033[37;42;1m ==== $BOOT_JAR ==== \033[0m" + echo -e "\033[37;42;1m =====${BOOT_JAR//?/=}===== \033[0m" exit fi @@ -483,7 +515,7 @@ case "$ARGS_RUN" in tail -f "$tail_log" fi ;; - stop*) + stop | stops) if [[ $count -eq 0 ]]; then echo -e "\033[37;43;1mNOTE: not running $BOOT_JAR \033[0m" exit @@ -542,23 +574,14 @@ case "$ARGS_RUN" in ;; status) print_envs + check_java + print_args + if [[ $count -eq 0 ]]; then echo -e "\033[37;41;1mERROR: not running $BOOT_JAR \033[0m" exit fi - check_java - print_args - - tail_num=20 - if [[ -f "$BOOT_OUT" ]]; then - echo -e "\033[37;42;1mINFO: tail -n $tail_num $BOOT_OUT \033[0m" - tail -n $tail_num "$BOOT_OUT" - fi - if [[ -f "$BOOT_LOG" ]]; then - echo -e "\033[37;42;1mINFO: tail -n $tail_num $BOOT_LOG \033[0m" - tail -n $tail_num "$BOOT_LOG" - fi pid=$(awk '{print $1}' "$BOOT_PID") cid=$(pgrep -f "$grep_key") echo -e "\033[37;42;1mINFO: boot.pid=$pid \033[0m" @@ -569,48 +592,57 @@ case "$ARGS_RUN" in echo -e "\033[31mWARN: pid not match, proc-pid=$cid, file-pid=$pid \033[0m" fi - # shellcheck disable=SC2009 - mrs=$(ps -o rss "$cid" | grep -v RSS | numfmt --grouping) - # shellcheck disable=SC2009 - mvs=$(ps -o vsz "$cid" | grep -v VSZ | numfmt --grouping) - echo -e "\033[37;42;1mINFO: ps -o rss -o vsz $cid \033[0m" - echo -e "Resident (RSS) = $(printf '%*s' 12 $mrs) Kb" - echo -e "Virtual (VSZ) = $(printf '%*s' 12 $mvs) Kb" + stm=$(ps -o lstart= "$cid") + etm=$(ps -o etime= "$cid" | tr -d ' ') + mrs=$(ps -o rss= "$cid" | numfmt --grouping) + mvs=$(ps -o vsz= "$cid" | numfmt --grouping) + echo -e "\033[37;42;1mINFO: ps -o rss,vsz,etime,lstart $cid \033[0m" + echo -e "Started Time = $stm" + echo -e "Elapsed Time = $etm" + echo -e "Resident (RSS) = $(printf '%12s' "$mrs") Kb" + echo -e "Virtual (VSZ) = $(printf '%12s' "$mvs") Kb" if [[ "$USER_RUN" == "$USER" ]]; then echo -e "\033[37;42;1mINFO: $(which jstat) -gcutil $cid 1000 3 \033[0m" jstat -gcutil "$cid" 1000 3 - echo -e "\033[37;42;1mINFO: $(which jstat) -gc $cid 1000 3 \033[0m" - jstat -gc "$cid" 1000 3 else echo -e "\033[37;43;1mNOTE: sudo $(which jstat) -gcutil $cid 1000 3 \033[0m" echo -e "\033[37;43;1mNOTE: sudo $(which jstat) -gc $cid 1000 3 \033[0m" fi - if id | grep -q '(sudo)'; then if which jhsdb &> /dev/null; then _jhsdb=$(which jhsdb) - echo -e "\033[37;42;1mINFO: sudo $_jhsdb jmap --heap --pid $cid \033[m" + echo -e "\033[37;42;1mINFO: sudo $_jhsdb jmap --heap --pid $cid \033[0m" sudo "$_jhsdb" jmap --heap --pid "$cid" else _jmap=$(which jmap) - echo -e "\033[37;42;1mINFO: sudo $_jmap -heap $cid \033[m" + echo -e "\033[37;42;1mINFO: sudo $_jmap -heap $cid \033[0m" sudo "$_jmap" -heap "$cid" fi else if which jhsdb &> /dev/null; then - echo -e "\033[37;43;1mNOTE: sudo $(which jhsdb) jmap --heap --pid $cid \033[m" + echo -e "\033[37;43;1mNOTE: sudo $(which jhsdb) jmap --heap --pid $cid \033[0m" else - echo -e "\033[37;43;1mNOTE: sudo $(which jmap) -heap $cid \033[m" + echo -e "\033[37;43;1mNOTE: sudo $(which jmap) -heap $cid \033[0m" fi fi - echo -e "\033[37;43;1mNOTE: ==== useful tool ==== \033[0m" - echo -e "\033[32m profiler.sh -d 30 -f profile.svg $cid \033[m https://github.com/jvm-profiling-tools/async-profiler" - echo -e "\033[32m $(which java) -jar arthas-boot.jar $cid \033[m https://github.com/alibaba/arthas" + tail_num=20 + if [[ -f "$BOOT_OUT" ]]; then + echo -e "\033[37;42;1mINFO: tail -n $tail_num $BOOT_OUT \033[0m" + tail -n $tail_num "$BOOT_OUT" + fi + if [[ -f "$BOOT_LOG" ]]; then + echo -e "\033[37;42;1mINFO: tail -n $tail_num $BOOT_LOG \033[0m" + tail -n $tail_num "$BOOT_LOG" + fi + + echo -e "\033[37;42;1mINFO: ==== useful tool ==== \033[0m" + echo -e "\033[32m profiler.sh -d 30 -f profile.svg $cid \033[0m https://github.com/jvm-profiling-tools/async-profiler" + echo -e "\033[32m $(which java) -jar arthas-boot.jar $cid \033[0m https://github.com/alibaba/arthas" ;; - tail) + tail | less) file_log=$BOOT_LOG if [[ ! -f "$file_log" ]]; then file_log=$BOOT_OUT @@ -619,9 +651,14 @@ case "$ARGS_RUN" in echo -e "\033[37;41;1mERROR: no log found \033[0m" exit fi - tail_num=20 - echo -e "\033[37;42;1mINFO: tail -f -n $tail_num $file_log \033[0m" - tail -f -n $tail_num "$file_log" + if [[ "$ARGS_RUN" == "tail" ]]; then + tail_num=20 + echo -e "\033[37;42;1mINFO: tail -f -n $tail_num $file_log \033[0m" + tail -f -n $tail_num "$file_log" + else + echo -e "\033[37;42;1mINFO: less $file_log \033[0m" + less "$file_log" + fi ;; warn) warn_got='' @@ -668,75 +705,105 @@ case "$ARGS_RUN" in check_user safe_start ;; - clean) + clean | clean-out | clean-log | clean-jar) dys="$2" if [[ "$dys" == "" ]]; then dys=30 fi nwt=5 - echo -e "\033[32m top log ${nwt}-newest ${JAR_NAME} \033[m" - # shellcheck disable=SC2012 - ls -lt "./${JAR_NAME}"-* | head -n $nwt - echo -e "\033[32m find $(pwd) -name \"${JAR_NAME}-*\" -type f -mtime +$dys \033[m" - old=$(find . -name "${JAR_NAME}-*" -type f -mtime +$dys | wc -l) - if [[ $old -gt 10 ]]; then - exs="newest-log-${JAR_NAME}.tmp" - # shellcheck disable=SC2012 - ls -t "./${JAR_NAME}"-* | head -n $nwt >"$exs" - find . -name "${JAR_NAME}-*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 ls -lt - echo -e "\033[37;43;1mNOTE: ==== clear ${dys}-days ago log file ==== \033[0m" - check_user - - yon="$3" - if [[ "$3" == "" ]]; then - echo -e "\033[31mWARN: press to rm them all, pwd=${WORK_DIR} \033[0m" - read -r yon - fi - if [[ "$yon" == "y" ]]; then - find . -name "${JAR_NAME}-*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 rm -f - fi - rm -f "$exs" - else - echo -e "\033[37;42;1mNOTE: few ${dys}-days ago logs, pwd=${WORK_DIR} \033[0m" - fi - ;; - clean-jar) - dys="$2" - if [[ "$dys" == "" ]]; then - dys=30 - fi - jrt=$(dirname "$BOOT_JAR") - nwt=5 - echo -e "\033[32m top jar ${nwt}-newest ${JAR_NAME} \033[m" - # shellcheck disable=SC2012 - ls -lt "${jrt}/${JAR_NAME}"[_-]* | head -n $nwt - echo -e "\033[32m find $jrt -name \"${JAR_NAME}[_-]*\" -type f -mtime +$dys \033[m" - old=$(find "$jrt" -name "${JAR_NAME}[_-]*" -type f -mtime +$dys | wc -l) - if [[ $old -gt 10 ]]; then - exs="newest-jar-${JAR_NAME}.tmp" + case "$ARGS_RUN" in clean | clean-out) + echo -e "\033[32m top out ${nwt}-newest of ${JAR_NAME} \033[0m" # shellcheck disable=SC2012 - ls -t "${jrt}/${JAR_NAME}"[_-]* | head -n $nwt >"$exs" - - find "$jrt" -name "${JAR_NAME}[_-]*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 ls -lt - echo -e "\033[37;43;1mNOTE: ==== clear ${dys}-days ago jar, exclude top ${nwt}-newest ==== \033[0m" - check_user + ls -lt "./${JAR_NAME}"-* | head -n $nwt + echo -e "\033[32m find $(pwd) -name \"${JAR_NAME}-*\" -type f -mtime +$dys \033[0m" + old=$(find . -name "${JAR_NAME}-*" -type f -mtime +$dys | wc -l) + if [[ $old -gt 10 ]]; then + exs="newest-out-${JAR_NAME}.tmp" + # shellcheck disable=SC2012 + ls -t "./${JAR_NAME}"-* | head -n $nwt >"$exs" + + find . -name "${JAR_NAME}-*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 ls -lt + echo -e "\033[37;43;1mNOTE: ==== clear ${dys}-days ago out file ==== \033[0m" + check_user + + yon="$3" + if [[ "$3" == "" ]]; then + echo -e "\033[31mWARN: press to rm them all, pwd=${WORK_DIR} \033[0m" + read -r yon + fi + if [[ "$yon" == "y" ]]; then + find . -name "${JAR_NAME}-*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 rm -f + fi + rm -f "$exs" + else + echo -e "\033[37;42;1mNOTE: few ${dys}-days ago outs, pwd=${WORK_DIR} \033[0m" + fi + esac - yon="$3" - if [[ "$3" == "" ]]; then - echo -e "\033[31mWARN: press to rm them all, pwd=$jrt \033[0m" - read -r yon + case "$ARGS_RUN" in clean | clean-log) + echo -e "\033[32m top log ${nwt}-newest of ${JAR_NAME} \033[0m" + jrt=$(dirname "$BOOT_LOG") + jnm=$(basename "$BOOT_LOG") + # shellcheck disable=SC2012 + ls -lt "${BOOT_LOG}".* | head -n $nwt + echo -e "\033[32m find $jrt -name \"${jnm}.*\" -type f -mtime +$dys \033[0m" + old=$(find "$jrt" -name "${jnm}-*" -type f -mtime +$dys | wc -l) + if [[ $old -gt 10 ]]; then + exs="newest-log-${JAR_NAME}.tmp" + # shellcheck disable=SC2012 + ls -t "${BOOT_LOG}".* | head -n $nwt >"$exs" + + find "$jrt" -name "${jnm}.*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 ls -lt + echo -e "\033[37;43;1mNOTE: ==== clear ${dys}-days ago log file ==== \033[0m" + check_user + + yon="$3" + if [[ "$3" == "" ]]; then + echo -e "\033[31mWARN: press to rm them all, pwd=$jrt \033[0m" + read -r yon + fi + if [[ "$yon" == "y" ]]; then + find "$jrt" -name "${jnm}.*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 rm -f + fi + rm -f "$exs" + else + echo -e "\033[37;42;1mNOTE: few ${dys}-days ago logs, pwd=${jrt} \033[0m" fi - if [[ "$yon" == "y" ]]; then - find "$jrt" -name "${JAR_NAME}[_-]*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 rm -f + esac + + case "$ARGS_RUN" in clean | clean-jar) + echo -e "\033[32m top jar ${nwt}-newest of ${JAR_NAME} \033[0m" + jrt=$(dirname "$BOOT_JAR") + # shellcheck disable=SC2012 + ls -lt "${jrt}/${JAR_NAME}"[_-]* | head -n $nwt + echo -e "\033[32m find $jrt -name \"${JAR_NAME}[_-]*\" -type f -mtime +$dys \033[0m" + old=$(find "$jrt" -name "${JAR_NAME}[_-]*" -type f -mtime +$dys | wc -l) + if [[ $old -gt 10 ]]; then + exs="newest-jar-${JAR_NAME}.tmp" + # shellcheck disable=SC2012 + ls -t "${jrt}/${JAR_NAME}"[_-]* | head -n $nwt >"$exs" + + find "$jrt" -name "${JAR_NAME}[_-]*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 ls -lt + echo -e "\033[37;43;1mNOTE: ==== clear ${dys}-days ago jar, exclude top ${nwt}-newest ==== \033[0m" + check_user + + yon="$3" + if [[ "$3" == "" ]]; then + echo -e "\033[31mWARN: press to rm them all, pwd=$jrt \033[0m" + read -r yon + fi + if [[ "$yon" == "y" ]]; then + find "$jrt" -name "${JAR_NAME}[_-]*" -type f -mtime +$dys -print0 | grep -zvFf "$exs" | xargs -0 rm -f + fi + rm -f "$exs" + else + echo -e "\033[37;42;1mNOTE: few ${dys}-days ago jars, pwd=$jrt \033[0m" fi - rm -f "$exs" - else - echo -e "\033[37;42;1mNOTE: few ${dys}-days ago jars, pwd=$jrt \033[0m" - fi + esac ;; config) - echo -e '\033[37;42;1mNOTE: ==== conf env ==== \033[m' + echo -e '\033[37;42;1mNOTE: ==== conf env ==== \033[0m' echo "WORK_DIR=$WORK_DIR" echo "TAIL_LOG=$TAIL_LOG" echo "USER_RUN=$USER_RUN" @@ -760,8 +827,10 @@ case "$ARGS_RUN" in echo "WARN_RUN=$WARN_RUN" ;; *) - echo -e '\033[37;41;1mERROR: unsupported command, use the following\033[m' + echo -e "\033[37;41;1mERROR: unknown $ARGS_RUN, see the following\033[0m" print_help print_envs + check_java + print_args ;; esac diff --git a/observe/scripts/wings-testing.sh b/observe/scripts/wings-testing.sh index 86c85301e..f706b4c3e 100644 --- a/observe/scripts/wings-testing.sh +++ b/observe/scripts/wings-testing.sh @@ -23,7 +23,7 @@ if [[ "$MAVEN_OPTS" == "" ]]; then fi if [[ "$LOG_LEVEL" == "" ]]; then - echo -e "\033[32m log level (INFO)? [INFO|DEBUG|WARN|ERROR]\033[m" + echo -e "\033[32m log level (INFO)? [INFO|DEBUG|WARN|ERROR]\033[0m" read -r _ans org.springframework.boot spring-boot-starter-parent - 3.2.2 + 3.2.8 @@ -23,7 +23,7 @@ 3.2.130-SNAPSHOT - 3.2.2 + 3.2.8 ${revision} @@ -40,47 +40,47 @@ 1.5.5.Final 0.2.0 - 33.0.0-jre - 2.15.1 + 33.2.1-jre + 2.16.1 4.4 - 1.11.0 + 1.12.0 2.2.3 - 2.7.3-SNAPSHOT + 2.7.3 1.5.1 2.3.3 - 2.0.51 + 2.0.52 ${fastjson2.version} 5.6.0 2.14.5 - 1.77 + 1.78.1 1.16.6 - 5.4.1 + 5.5.0 - 3.0.2 + 3.2.0 - 2.3.0 - 3.2.2 - 7.4.0 - 2.9.0 - 2.25.0 - 1.2.21 - 1.37 + 2.5.0 + 3.2.3 + 7.12.1 + 2.11.0 + 2.29.0 + 1.2.23 + 1.37 1.6.0 1.0 - 3.1.0 - 1.6.13 + 3.2.4 + 1.7.0 4.3.0 - 0.8.11 - 1.9.10 + 0.8.12 + 1.9.20 1.18.20.0 - 3.2.0 - 3.4.1 + 3.3.0 + 3.4.3 0.3.0 - 3.4.1 - 2.16.2 + 3.5.0 + 2.17.1 ${project.basedir}/src/main/java ${project.basedir}/src/main/java-gen @@ -295,6 +295,11 @@ tiny-mail ${wings.version} + + pro.fessional.wings + tiny-grow + ${wings.version} + org.jetbrains.kotlin @@ -317,8 +322,15 @@ org.apache.shardingsphere - shardingsphere-jdbc-core + shardingsphere-jdbc ${shardingsphere.version} + + + + org.apache.shardingsphere + shardingsphere-test-util + + org.apache.shardingsphere - shardingsphere-jdbc-core + shardingsphere-jdbc ${shardingsphere.version} @@ -155,9 +158,62 @@ druid-spring-boot-starter ${druid.version} + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.codehaus.mojo + versions-maven-plugin + + ${dependency-updates-file} + 100 + false + false + + .*[.-]android.* + .*[.-]SNAPSHOT.* + .*[.-]alpha.* + .*[.-]Beta.* + .*[.-]RC.* + .*[.-]M.* + \d{8,}\.\d{6,} + .*-b.* + + + org.springframework.boot:spring-boot + org.jetbrains:annotations + org.mapstruct:mapstruct-processor + com.google.guava:guava + commons-io:commons-io + org.apache.commons:commons-collections4 + org.apache.commons:commons-text + org.joda:joda-convert + pro.fessional:mirana + pro.fessional:meepo + pro.fessional:kaptcha + com.alibaba.fastjson2:fastjson2 + com.alibaba:fastjson + com.esotericsoftware:kryo + com.alibaba:transmittable-thread-local + org.bouncycastle:bcpkix-jdk18on + me.zhyd.oauth:JustAuth + org.apache.shardingsphere:shardingsphere-jdbc + org.apache.servicecomb:java-chassis-core + org.springdoc:springdoc-openapi-starter-webmvc-ui + de.codecentric:spring-boot-admin-starter-client + io.sentry:sentry-spring-boot-starter-jakarta + com.squareup.retrofit2:retrofit + io.qameta.allure:allure-java-commons + com.alibaba:druid-spring-boot-starter + org.openjdk.jmh:jmh-core + + + org.codehaus.mojo flatten-maven-plugin @@ -178,6 +234,11 @@ nexus-staging-maven-plugin ${nexus-staging-plugin.version} + + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin.version} + org.eluder.coveralls coveralls-maven-plugin @@ -188,15 +249,20 @@ jacoco-maven-plugin ${jacoco-maven-plugin.version} + + org.projectlombok + lombok-maven-plugin + ${lombok-maven-plugin.version} + org.jetbrains.dokka dokka-maven-plugin ${dokka-maven-plugin.version} - org.projectlombok - lombok-maven-plugin - ${lombok-maven-plugin.version} + io.github.git-commit-id + git-commit-id-maven-plugin + ${git-commit-id-maven-plugin.version} org.codehaus.mojo @@ -208,56 +274,11 @@ jib-maven-plugin ${jib-maven-plugin.version} - org.apache.maven.plugins maven-enforcer-plugin ${maven-enforcer-plugin.version} - - org.codehaus.mojo - versions-maven-plugin - - ${dependency-updates-file} - 100 - false - false - - .*[.-]android.* - .*[.-]SNAPSHOT.* - .*[.-]Beta.* - \d{8,}\.\d{6,} - .*-b.* - - - org.springframework.boot:spring-boot - org.jetbrains:annotations - org.mapstruct:mapstruct-processor - com.google.guava:guava - commons-io:commons-io - org.apache.commons:commons-collections4 - org.apache.commons:commons-text - org.joda:joda-convert - pro.fessional:mirana - pro.fessional:meepo - pro.fessional:kaptcha - com.alibaba.fastjson2:fastjson2 - com.alibaba:fastjson - com.esotericsoftware:kryo - com.alibaba:transmittable-thread-local - org.bouncycastle:bcpkix-jdk18on - me.zhyd.oauth:JustAuth - org.apache.shardingsphere:shardingsphere-jdbc-core - org.apache.servicecomb:java-chassis-core - org.springdoc:springdoc-openapi-starter-webmvc-ui - de.codecentric:spring-boot-admin-starter-client - io.sentry:sentry-spring-boot-starter-jakarta - com.squareup.retrofit2:retrofit - io.qameta.allure:allure-java-commons - com.alibaba:druid-spring-boot-starter - - - diff --git a/radiant/pom.xml b/radiant/pom.xml index 26774b98c..87a998045 100644 --- a/radiant/pom.xml +++ b/radiant/pom.xml @@ -18,6 +18,7 @@ Radiant components under GhostShip Model + tiny-grow tiny-mail tiny-task diff --git a/radiant/tiny-grow/pom.xml b/radiant/tiny-grow/pom.xml new file mode 100644 index 000000000..407ce54fa --- /dev/null +++ b/radiant/tiny-grow/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + + pro.fessional.wings + radiant + ${revision} + + + tiny-grow + jar + + Radiant::Tiny::Grow + Tiny data tracking, growth analysis + + + + pro.fessional.wings + faceless-jooq + true + + + pro.fessional.wings + slardar-webmvc + true + + + + pro.fessional.wings + faceless-flywave + true + + + + pro.fessional.wings + faceless-codegen + test + + + pro.fessional.wings + testing-docker + test + + + diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/TinyGrowDatabase.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/TinyGrowDatabase.java new file mode 100644 index 000000000..cfb3e64b0 --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/TinyGrowDatabase.java @@ -0,0 +1,8 @@ +package pro.fessional.wings.tiny.grow.database; + +/** + * @author trydofor + * @since 2023-11-22 + */ +public interface TinyGrowDatabase { +} diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/DefaultCatalogTinyGrow.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/DefaultCatalogTinyGrow.java new file mode 100644 index 000000000..9f85f9072 --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/DefaultCatalogTinyGrow.java @@ -0,0 +1,47 @@ +/* + * This file is generated by jOOQ. + */ +package pro.fessional.wings.tiny.grow.database.autogen; + + +import org.jooq.Constants; +import org.jooq.impl.CatalogImpl; + +import javax.annotation.processing.Generated; + + +/** + * The catalog . + */ +@Generated( + value = { + "https://www.jooq.org", + "jOOQ version:3.18.9" + }, + comments = "This class is generated by jOOQ" +) +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class DefaultCatalogTinyGrow extends CatalogImpl { + + private static final long serialVersionUID = 1L; + + /** + * The reference instance of DEFAULT_CATALOG + */ + public static final DefaultCatalogTinyGrow DEFAULT_CATALOG = new DefaultCatalogTinyGrow(); + + /** + * No further instances allowed + */ + private DefaultCatalogTinyGrow() { + super(""); + } + + /** + * A reference to the 3.18 minor release of the code generator. If this + * doesn't compile, it's because the runtime library uses an older minor + * release, namely: 3.18. You can turn off the generation of this reference + * by specifying /configuration/generator/generate/jooqVersionReference + */ + private static final String REQUIRE_RUNTIME_JOOQ_VERSION = Constants.VERSION_3_18; +} diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/DefaultSchemaTinyGrow.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/DefaultSchemaTinyGrow.java new file mode 100644 index 000000000..00f04e7ea --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/DefaultSchemaTinyGrow.java @@ -0,0 +1,62 @@ +/* + * This file is generated by jOOQ. + */ +package pro.fessional.wings.tiny.grow.database.autogen; + + +import org.jooq.Catalog; +import org.jooq.Table; +import org.jooq.impl.SchemaImpl; +import pro.fessional.wings.tiny.grow.database.autogen.tables.WinGrowTrackTable; + +import javax.annotation.processing.Generated; +import java.util.Arrays; +import java.util.List; + + +/** + * The schema wings. + */ +@Generated( + value = { + "https://www.jooq.org", + "jOOQ version:3.18.9", + "schema version:2020102801" + }, + comments = "This class is generated by jOOQ" +) +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class DefaultSchemaTinyGrow extends SchemaImpl { + + private static final long serialVersionUID = 1L; + + /** + * The reference instance of DEFAULT_SCHEMA + */ + public static final DefaultSchemaTinyGrow DEFAULT_SCHEMA = new DefaultSchemaTinyGrow(); + + /** + * The table win_grow_track. + */ + public final WinGrowTrackTable WinGrowTrack = WinGrowTrackTable.WinGrowTrack; + + /** + * No further instances allowed + */ + private DefaultSchemaTinyGrow() { + super("", null); + } + + + @Override + public Catalog getCatalog() { + return DefaultCatalogTinyGrow.DEFAULT_CATALOG; + } + + @Override + public final List> getTables() { + return Arrays.asList( + WinGrowTrackTable.WinGrowTrack + ); + } +} diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/TablesTinyGrow.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/TablesTinyGrow.java new file mode 100644 index 000000000..4617b22c0 --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/TablesTinyGrow.java @@ -0,0 +1,30 @@ +/* + * This file is generated by jOOQ. + */ +package pro.fessional.wings.tiny.grow.database.autogen; + + +import pro.fessional.wings.tiny.grow.database.autogen.tables.WinGrowTrackTable; + +import javax.annotation.processing.Generated; + + +/** + * Convenience access to all tables in the default schema. + */ +@Generated( + value = { + "https://www.jooq.org", + "jOOQ version:3.18.9", + "schema version:2020102801" + }, + comments = "This class is generated by jOOQ" +) +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class TablesTinyGrow { + + /** + * The table win_grow_track. + */ + public static final WinGrowTrackTable WinGrowTrack = WinGrowTrackTable.WinGrowTrack; +} diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/WinGrowTrackTable.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/WinGrowTrackTable.java new file mode 100644 index 000000000..902a859d1 --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/WinGrowTrackTable.java @@ -0,0 +1,277 @@ +/* + * This file is generated by jOOQ. + */ +package pro.fessional.wings.tiny.grow.database.autogen.tables; + + +import org.jetbrains.annotations.NotNull; +import org.jooq.Field; +import org.jooq.Function19; +import org.jooq.Name; +import org.jooq.Records; +import org.jooq.Row19; +import org.jooq.Schema; +import org.jooq.SelectField; +import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.TableOptions; +import org.jooq.UniqueKey; +import org.jooq.impl.DSL; +import org.jooq.impl.Internal; +import org.jooq.impl.SQLDataType; +import org.jooq.impl.TableImpl; +import pro.fessional.wings.faceless.database.jooq.WingsJournalTable; +import pro.fessional.wings.faceless.service.lightid.LightIdAware; +import pro.fessional.wings.tiny.grow.database.autogen.DefaultSchemaTinyGrow; +import pro.fessional.wings.tiny.grow.database.autogen.tables.records.WinGrowTrackRecord; + +import javax.annotation.processing.Generated; +import java.time.LocalDateTime; +import java.util.function.Function; + + +/** + * The table wings.win_grow_track. + */ +@Generated( + value = { + "https://www.jooq.org", + "jOOQ version:3.18.9", + "schema version:2020102801" + }, + comments = "This class is generated by jOOQ" +) +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class WinGrowTrackTable extends TableImpl implements WingsJournalTable, LightIdAware { + + private static final long serialVersionUID = 1L; + + /** + * The reference instance of win_grow_track + */ + public static final WinGrowTrackTable WinGrowTrack = new WinGrowTrackTable(); + public static final WinGrowTrackTable asK2 = WinGrowTrack.as(pro.fessional.wings.faceless.database.jooq.WingsJooqEnv.uniqueAlias()); + + /** + * The class holding records for this type + */ + @Override + public Class getRecordType() { + return WinGrowTrackRecord.class; + } + + /** + * The column win_grow_track.id. + */ + public final TableField Id = createField(DSL.name("id"), SQLDataType.BIGINT.nullable(false), this, ""); + + /** + * The column win_grow_track.create_dt. + */ + public final TableField CreateDt = createField(DSL.name("create_dt"), SQLDataType.LOCALDATETIME(3).nullable(false).defaultValue(DSL.field(DSL.raw("CURRENT_TIMESTAMP(3)"), SQLDataType.LOCALDATETIME)), this, ""); + + /** + * The column win_grow_track.track_key. + */ + public final TableField TrackKey = createField(DSL.name("track_key"), SQLDataType.VARCHAR(500).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + + /** + * The column win_grow_track.track_ref. + */ + public final TableField TrackRef = createField(DSL.name("track_ref"), SQLDataType.VARCHAR(100).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + + /** + * The column win_grow_track.track_app. + */ + public final TableField TrackApp = createField(DSL.name("track_app"), SQLDataType.VARCHAR(100).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + + /** + * The column win_grow_track.track_env. + */ + public final TableField TrackEnv = createField(DSL.name("track_env"), SQLDataType.CLOB, this, ""); + + /** + * The column win_grow_track.track_ins. + */ + public final TableField TrackIns = createField(DSL.name("track_ins"), SQLDataType.CLOB, this, ""); + + /** + * The column win_grow_track.track_out. + */ + public final TableField TrackOut = createField(DSL.name("track_out"), SQLDataType.CLOB, this, ""); + + /** + * The column win_grow_track.track_err. + */ + public final TableField TrackErr = createField(DSL.name("track_err"), SQLDataType.CLOB, this, ""); + + /** + * The column win_grow_track.elapse_ms. + */ + public final TableField ElapseMs = createField(DSL.name("elapse_ms"), SQLDataType.BIGINT.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BIGINT)), this, ""); + + /** + * The column win_grow_track.user_key. + */ + public final TableField UserKey = createField(DSL.name("user_key"), SQLDataType.BIGINT.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BIGINT)), this, ""); + + /** + * The column win_grow_track.user_ref. + */ + public final TableField UserRef = createField(DSL.name("user_ref"), SQLDataType.BIGINT.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BIGINT)), this, ""); + + /** + * The column win_grow_track.data_key. + */ + public final TableField DataKey = createField(DSL.name("data_key"), SQLDataType.BIGINT.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BIGINT)), this, ""); + + /** + * The column win_grow_track.data_ref. + */ + public final TableField DataRef = createField(DSL.name("data_ref"), SQLDataType.BIGINT.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BIGINT)), this, ""); + + /** + * The column win_grow_track.data_opt. + */ + public final TableField DataOpt = createField(DSL.name("data_opt"), SQLDataType.BIGINT.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BIGINT)), this, ""); + + /** + * The column win_grow_track.code_key. + */ + public final TableField CodeKey = createField(DSL.name("code_key"), SQLDataType.VARCHAR(100).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + + /** + * The column win_grow_track.code_ref. + */ + public final TableField CodeRef = createField(DSL.name("code_ref"), SQLDataType.VARCHAR(100).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + + /** + * The column win_grow_track.code_opt. + */ + public final TableField CodeOpt = createField(DSL.name("code_opt"), SQLDataType.VARCHAR(100).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + + /** + * The column win_grow_track.word_ref. + */ + public final TableField WordRef = createField(DSL.name("word_ref"), SQLDataType.VARCHAR(800).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + + private WinGrowTrackTable(Name alias, Table aliased) { + this(alias, aliased, null); + } + + private WinGrowTrackTable(Name alias, Table aliased, Field[] parameters) { + super(alias, null, aliased, parameters, DSL.comment(""), TableOptions.table()); + } + + /** + * Create an aliased win_grow_track table reference + */ + public WinGrowTrackTable(String alias) { + this(DSL.name(alias), WinGrowTrack); + } + + /** + * Create an aliased win_grow_track table reference + */ + public WinGrowTrackTable(Name alias) { + this(alias, WinGrowTrack); + } + + /** + * Create a win_grow_track table reference + */ + public WinGrowTrackTable() { + this(DSL.name("win_grow_track"), null); + } + + @Override + public Schema getSchema() { + return aliased() ? null : DefaultSchemaTinyGrow.DEFAULT_SCHEMA; + } + + @Override + public UniqueKey getPrimaryKey() { + return Internal.createUniqueKey(WinGrowTrackTable.WinGrowTrack, DSL.name("KEY_win_grow_track_PRIMARY"), new TableField[] { WinGrowTrackTable.WinGrowTrack.Id }, true); + } + + @Override + public WinGrowTrackTable as(String alias) { + return new WinGrowTrackTable(DSL.name(alias), this); + } + + @Override + public WinGrowTrackTable as(Name alias) { + return new WinGrowTrackTable(alias, this); + } + + @Override + public WinGrowTrackTable as(Table alias) { + return new WinGrowTrackTable(alias.getQualifiedName(), this); + } + + /** + * Rename this table + */ + @Override + public WinGrowTrackTable rename(String name) { + return new WinGrowTrackTable(DSL.name(name), null); + } + + /** + * Rename this table + */ + @Override + public WinGrowTrackTable rename(Name name) { + return new WinGrowTrackTable(name, null); + } + + /** + * Rename this table + */ + @Override + public WinGrowTrackTable rename(Table name) { + return new WinGrowTrackTable(name.getQualifiedName(), null); + } + + // ------------------------------------------------------------------------- + // Row19 type methods + // ------------------------------------------------------------------------- + + @Override + public Row19 fieldsRow() { + return (Row19) super.fieldsRow(); + } + + /** + * Convenience mapping calling {@link SelectField#convertFrom(Function)}. + */ + public SelectField mapping(Function19 from) { + return convertFrom(Records.mapping(from)); + } + + /** + * Convenience mapping calling {@link SelectField#convertFrom(Class, + * Function)}. + */ + public SelectField mapping(Class toType, Function19 from) { + return convertFrom(toType, Records.mapping(from)); + } + + /** + * LightIdAware seqName + */ + @Override + @NotNull + public String getSeqName() { + return "win_grow_track"; + } + + /** + * alias asK2 + */ + @Override + @NotNull + public WinGrowTrackTable getAliasTable() { + return asK2; + } +} diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/daos/WinGrowTrackDao.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/daos/WinGrowTrackDao.java new file mode 100644 index 000000000..49b21bda8 --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/daos/WinGrowTrackDao.java @@ -0,0 +1,709 @@ +/* + * This file is generated by jOOQ. + */ +package pro.fessional.wings.tiny.grow.database.autogen.tables.daos; + + +import org.jooq.Configuration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import pro.fessional.wings.faceless.database.jooq.WingsJooqDaoJournalImpl; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.tiny.grow.database.autogen.tables.WinGrowTrackTable; +import pro.fessional.wings.tiny.grow.database.autogen.tables.pojos.WinGrowTrack; +import pro.fessional.wings.tiny.grow.database.autogen.tables.records.WinGrowTrackRecord; + +import javax.annotation.processing.Generated; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + + +/** + * The table wings.win_grow_track. + */ +@Generated( + value = { + "https://www.jooq.org", + "jOOQ version:3.18.9", + "schema version:2020102801" + }, + comments = "This class is generated by jOOQ" +) +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +@Repository +@ConditionalWingsEnabled +public class WinGrowTrackDao extends WingsJooqDaoJournalImpl { + + /** + * Create a new WinGrowTrackDao without any configuration + */ + public WinGrowTrackDao() { + super(WinGrowTrackTable.WinGrowTrack, WinGrowTrack.class); + } + + /** + * Create a new WinGrowTrackDao with an attached configuration + */ + @Autowired + public WinGrowTrackDao(Configuration configuration) { + super(WinGrowTrackTable.WinGrowTrack, WinGrowTrack.class, configuration); + } + + @Override + public Long getId(WinGrowTrack object) { + return object.getId(); + } + + /** + * Fetch records that have id BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfId(Long lowerInclusive, Long upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.Id, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfIdLive(Long lowerInclusive, Long upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.Id, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have id IN (values) + */ + public List fetchById(Long... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.Id, values); + } + + public List fetchById(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.Id, values); + } + + + public List fetchByIdLive(Long... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.Id, values); + } + + public List fetchByIdLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.Id, values); + } + + /** + * Fetch a unique record that has id = value + */ + public WinGrowTrack fetchOneById(Long value) { + return fetchOne(WinGrowTrackTable.WinGrowTrack.Id, value); + } + + + public WinGrowTrack fetchOneByIdLive(Long value) { + return fetchOneLive(WinGrowTrackTable.WinGrowTrack.Id, value); + } + + /** + * Fetch a unique record that has id = value + */ + public Optional fetchOptionalById(Long value) { + return fetchOptional(WinGrowTrackTable.WinGrowTrack.Id, value); + } + + + public Optional fetchOptionalByIdLive(Long value) { + return fetchOptionalLive(WinGrowTrackTable.WinGrowTrack.Id, value); + } + + /** + * Fetch records that have create_dt BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfCreateDt(LocalDateTime lowerInclusive, LocalDateTime upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.CreateDt, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfCreateDtLive(LocalDateTime lowerInclusive, LocalDateTime upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.CreateDt, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have create_dt IN (values) + */ + public List fetchByCreateDt(LocalDateTime... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.CreateDt, values); + } + + public List fetchByCreateDt(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.CreateDt, values); + } + + + public List fetchByCreateDtLive(LocalDateTime... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.CreateDt, values); + } + + public List fetchByCreateDtLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.CreateDt, values); + } + + /** + * Fetch records that have track_key BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfTrackKey(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.TrackKey, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfTrackKeyLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.TrackKey, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have track_key IN (values) + */ + public List fetchByTrackKey(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackKey, values); + } + + public List fetchByTrackKey(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackKey, values); + } + + + public List fetchByTrackKeyLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackKey, values); + } + + public List fetchByTrackKeyLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackKey, values); + } + + /** + * Fetch records that have track_ref BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfTrackRef(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.TrackRef, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfTrackRefLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.TrackRef, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have track_ref IN (values) + */ + public List fetchByTrackRef(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackRef, values); + } + + public List fetchByTrackRef(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackRef, values); + } + + + public List fetchByTrackRefLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackRef, values); + } + + public List fetchByTrackRefLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackRef, values); + } + + /** + * Fetch records that have track_app BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfTrackApp(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.TrackApp, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfTrackAppLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.TrackApp, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have track_app IN (values) + */ + public List fetchByTrackApp(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackApp, values); + } + + public List fetchByTrackApp(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackApp, values); + } + + + public List fetchByTrackAppLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackApp, values); + } + + public List fetchByTrackAppLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackApp, values); + } + + /** + * Fetch records that have track_env BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfTrackEnv(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.TrackEnv, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfTrackEnvLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.TrackEnv, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have track_env IN (values) + */ + public List fetchByTrackEnv(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackEnv, values); + } + + public List fetchByTrackEnv(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackEnv, values); + } + + + public List fetchByTrackEnvLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackEnv, values); + } + + public List fetchByTrackEnvLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackEnv, values); + } + + /** + * Fetch records that have track_ins BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfTrackIns(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.TrackIns, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfTrackInsLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.TrackIns, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have track_ins IN (values) + */ + public List fetchByTrackIns(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackIns, values); + } + + public List fetchByTrackIns(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackIns, values); + } + + + public List fetchByTrackInsLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackIns, values); + } + + public List fetchByTrackInsLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackIns, values); + } + + /** + * Fetch records that have track_out BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfTrackOut(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.TrackOut, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfTrackOutLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.TrackOut, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have track_out IN (values) + */ + public List fetchByTrackOut(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackOut, values); + } + + public List fetchByTrackOut(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackOut, values); + } + + + public List fetchByTrackOutLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackOut, values); + } + + public List fetchByTrackOutLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackOut, values); + } + + /** + * Fetch records that have track_err BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfTrackErr(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.TrackErr, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfTrackErrLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.TrackErr, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have track_err IN (values) + */ + public List fetchByTrackErr(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackErr, values); + } + + public List fetchByTrackErr(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.TrackErr, values); + } + + + public List fetchByTrackErrLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackErr, values); + } + + public List fetchByTrackErrLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.TrackErr, values); + } + + /** + * Fetch records that have elapse_ms BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfElapseMs(Long lowerInclusive, Long upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.ElapseMs, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfElapseMsLive(Long lowerInclusive, Long upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.ElapseMs, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have elapse_ms IN (values) + */ + public List fetchByElapseMs(Long... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.ElapseMs, values); + } + + public List fetchByElapseMs(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.ElapseMs, values); + } + + + public List fetchByElapseMsLive(Long... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.ElapseMs, values); + } + + public List fetchByElapseMsLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.ElapseMs, values); + } + + /** + * Fetch records that have user_key BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfUserKey(Long lowerInclusive, Long upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.UserKey, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfUserKeyLive(Long lowerInclusive, Long upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.UserKey, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have user_key IN (values) + */ + public List fetchByUserKey(Long... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.UserKey, values); + } + + public List fetchByUserKey(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.UserKey, values); + } + + + public List fetchByUserKeyLive(Long... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.UserKey, values); + } + + public List fetchByUserKeyLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.UserKey, values); + } + + /** + * Fetch records that have user_ref BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfUserRef(Long lowerInclusive, Long upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.UserRef, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfUserRefLive(Long lowerInclusive, Long upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.UserRef, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have user_ref IN (values) + */ + public List fetchByUserRef(Long... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.UserRef, values); + } + + public List fetchByUserRef(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.UserRef, values); + } + + + public List fetchByUserRefLive(Long... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.UserRef, values); + } + + public List fetchByUserRefLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.UserRef, values); + } + + /** + * Fetch records that have data_key BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfDataKey(Long lowerInclusive, Long upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.DataKey, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfDataKeyLive(Long lowerInclusive, Long upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.DataKey, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have data_key IN (values) + */ + public List fetchByDataKey(Long... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.DataKey, values); + } + + public List fetchByDataKey(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.DataKey, values); + } + + + public List fetchByDataKeyLive(Long... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.DataKey, values); + } + + public List fetchByDataKeyLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.DataKey, values); + } + + /** + * Fetch records that have data_ref BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfDataRef(Long lowerInclusive, Long upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.DataRef, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfDataRefLive(Long lowerInclusive, Long upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.DataRef, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have data_ref IN (values) + */ + public List fetchByDataRef(Long... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.DataRef, values); + } + + public List fetchByDataRef(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.DataRef, values); + } + + + public List fetchByDataRefLive(Long... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.DataRef, values); + } + + public List fetchByDataRefLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.DataRef, values); + } + + /** + * Fetch records that have data_opt BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfDataOpt(Long lowerInclusive, Long upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.DataOpt, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfDataOptLive(Long lowerInclusive, Long upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.DataOpt, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have data_opt IN (values) + */ + public List fetchByDataOpt(Long... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.DataOpt, values); + } + + public List fetchByDataOpt(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.DataOpt, values); + } + + + public List fetchByDataOptLive(Long... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.DataOpt, values); + } + + public List fetchByDataOptLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.DataOpt, values); + } + + /** + * Fetch records that have code_key BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfCodeKey(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.CodeKey, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfCodeKeyLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.CodeKey, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have code_key IN (values) + */ + public List fetchByCodeKey(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.CodeKey, values); + } + + public List fetchByCodeKey(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.CodeKey, values); + } + + + public List fetchByCodeKeyLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.CodeKey, values); + } + + public List fetchByCodeKeyLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.CodeKey, values); + } + + /** + * Fetch records that have code_ref BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfCodeRef(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.CodeRef, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfCodeRefLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.CodeRef, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have code_ref IN (values) + */ + public List fetchByCodeRef(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.CodeRef, values); + } + + public List fetchByCodeRef(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.CodeRef, values); + } + + + public List fetchByCodeRefLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.CodeRef, values); + } + + public List fetchByCodeRefLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.CodeRef, values); + } + + /** + * Fetch records that have code_opt BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfCodeOpt(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.CodeOpt, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfCodeOptLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.CodeOpt, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have code_opt IN (values) + */ + public List fetchByCodeOpt(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.CodeOpt, values); + } + + public List fetchByCodeOpt(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.CodeOpt, values); + } + + + public List fetchByCodeOptLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.CodeOpt, values); + } + + public List fetchByCodeOptLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.CodeOpt, values); + } + + /** + * Fetch records that have word_ref BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfWordRef(String lowerInclusive, String upperInclusive) { + return fetchRange(WinGrowTrackTable.WinGrowTrack.WordRef, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfWordRefLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinGrowTrackTable.WinGrowTrack.WordRef, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have word_ref IN (values) + */ + public List fetchByWordRef(String... values) { + return fetch(WinGrowTrackTable.WinGrowTrack.WordRef, values); + } + + public List fetchByWordRef(Collection values) { + return fetch(WinGrowTrackTable.WinGrowTrack.WordRef, values); + } + + + public List fetchByWordRefLive(String... values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.WordRef, values); + } + + public List fetchByWordRefLive(Collection values) { + return fetchLive(WinGrowTrackTable.WinGrowTrack.WordRef, values); + } +} diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/interfaces/IWinGrowTrack.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/interfaces/IWinGrowTrack.java new file mode 100644 index 000000000..2b36b4788 --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/interfaces/IWinGrowTrack.java @@ -0,0 +1,233 @@ +/* + * This file is generated by jOOQ. + */ +package pro.fessional.wings.tiny.grow.database.autogen.tables.interfaces; + + +import pro.fessional.wings.faceless.service.journal.JournalAware; + +import javax.annotation.processing.Generated; +import java.io.Serializable; +import java.time.LocalDateTime; + + +/** + * The table wings.win_grow_track. + */ +@Generated( + value = { + "https://www.jooq.org", + "jOOQ version:3.18.9", + "schema version:2020102801" + }, + comments = "This class is generated by jOOQ" +) +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public interface IWinGrowTrack extends JournalAware, Serializable { + + /** + * Setter for win_grow_track.id. + */ + public void setId(Long value); + + /** + * Getter for win_grow_track.id. + */ + public Long getId(); + + /** + * Setter for win_grow_track.create_dt. + */ + public void setCreateDt(LocalDateTime value); + + /** + * Getter for win_grow_track.create_dt. + */ + public LocalDateTime getCreateDt(); + + /** + * Setter for win_grow_track.track_key. + */ + public void setTrackKey(String value); + + /** + * Getter for win_grow_track.track_key. + */ + public String getTrackKey(); + + /** + * Setter for win_grow_track.track_ref. + */ + public void setTrackRef(String value); + + /** + * Getter for win_grow_track.track_ref. + */ + public String getTrackRef(); + + /** + * Setter for win_grow_track.track_app. + */ + public void setTrackApp(String value); + + /** + * Getter for win_grow_track.track_app. + */ + public String getTrackApp(); + + /** + * Setter for win_grow_track.track_env. + */ + public void setTrackEnv(String value); + + /** + * Getter for win_grow_track.track_env. + */ + public String getTrackEnv(); + + /** + * Setter for win_grow_track.track_ins. + */ + public void setTrackIns(String value); + + /** + * Getter for win_grow_track.track_ins. + */ + public String getTrackIns(); + + /** + * Setter for win_grow_track.track_out. + */ + public void setTrackOut(String value); + + /** + * Getter for win_grow_track.track_out. + */ + public String getTrackOut(); + + /** + * Setter for win_grow_track.track_err. + */ + public void setTrackErr(String value); + + /** + * Getter for win_grow_track.track_err. + */ + public String getTrackErr(); + + /** + * Setter for win_grow_track.elapse_ms. + */ + public void setElapseMs(Long value); + + /** + * Getter for win_grow_track.elapse_ms. + */ + public Long getElapseMs(); + + /** + * Setter for win_grow_track.user_key. + */ + public void setUserKey(Long value); + + /** + * Getter for win_grow_track.user_key. + */ + public Long getUserKey(); + + /** + * Setter for win_grow_track.user_ref. + */ + public void setUserRef(Long value); + + /** + * Getter for win_grow_track.user_ref. + */ + public Long getUserRef(); + + /** + * Setter for win_grow_track.data_key. + */ + public void setDataKey(Long value); + + /** + * Getter for win_grow_track.data_key. + */ + public Long getDataKey(); + + /** + * Setter for win_grow_track.data_ref. + */ + public void setDataRef(Long value); + + /** + * Getter for win_grow_track.data_ref. + */ + public Long getDataRef(); + + /** + * Setter for win_grow_track.data_opt. + */ + public void setDataOpt(Long value); + + /** + * Getter for win_grow_track.data_opt. + */ + public Long getDataOpt(); + + /** + * Setter for win_grow_track.code_key. + */ + public void setCodeKey(String value); + + /** + * Getter for win_grow_track.code_key. + */ + public String getCodeKey(); + + /** + * Setter for win_grow_track.code_ref. + */ + public void setCodeRef(String value); + + /** + * Getter for win_grow_track.code_ref. + */ + public String getCodeRef(); + + /** + * Setter for win_grow_track.code_opt. + */ + public void setCodeOpt(String value); + + /** + * Getter for win_grow_track.code_opt. + */ + public String getCodeOpt(); + + /** + * Setter for win_grow_track.word_ref. + */ + public void setWordRef(String value); + + /** + * Getter for win_grow_track.word_ref. + */ + public String getWordRef(); + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + /** + * Load data from another generated Record/POJO implementing the common + * interface IWinGrowTrack + */ + public void from(IWinGrowTrack from); + + /** + * Copy data into another generated Record/POJO implementing the common + * interface IWinGrowTrack + */ + public E into(E into); +} diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/pojos/WinGrowTrack.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/pojos/WinGrowTrack.java new file mode 100644 index 000000000..b89ae914d --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/pojos/WinGrowTrack.java @@ -0,0 +1,1851 @@ +/* + * This file is generated by jOOQ. + */ +package pro.fessional.wings.tiny.grow.database.autogen.tables.pojos; + + +import pro.fessional.wings.tiny.grow.database.autogen.tables.interfaces.IWinGrowTrack; + +import javax.annotation.processing.Generated; +import java.beans.Transient; +import java.time.LocalDateTime; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + + +/** + * The table wings.win_grow_track. + */ +@Generated( + value = { + "https://www.jooq.org", + "jOOQ version:3.18.9", + "schema version:2020102801" + }, + comments = "This class is generated by jOOQ" +) +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class WinGrowTrack implements IWinGrowTrack { + + private static final long serialVersionUID = 1L; + + private Long id; + private LocalDateTime createDt; + private String trackKey; + private String trackRef; + private String trackApp; + private String trackEnv; + private String trackIns; + private String trackOut; + private String trackErr; + private Long elapseMs; + private Long userKey; + private Long userRef; + private Long dataKey; + private Long dataRef; + private Long dataOpt; + private String codeKey; + private String codeRef; + private String codeOpt; + private String wordRef; + + public WinGrowTrack() {} + + public WinGrowTrack(IWinGrowTrack value) { + this.id = value.getId(); + this.createDt = value.getCreateDt(); + this.trackKey = value.getTrackKey(); + this.trackRef = value.getTrackRef(); + this.trackApp = value.getTrackApp(); + this.trackEnv = value.getTrackEnv(); + this.trackIns = value.getTrackIns(); + this.trackOut = value.getTrackOut(); + this.trackErr = value.getTrackErr(); + this.elapseMs = value.getElapseMs(); + this.userKey = value.getUserKey(); + this.userRef = value.getUserRef(); + this.dataKey = value.getDataKey(); + this.dataRef = value.getDataRef(); + this.dataOpt = value.getDataOpt(); + this.codeKey = value.getCodeKey(); + this.codeRef = value.getCodeRef(); + this.codeOpt = value.getCodeOpt(); + this.wordRef = value.getWordRef(); + } + + public WinGrowTrack( + Long id, + LocalDateTime createDt, + String trackKey, + String trackRef, + String trackApp, + String trackEnv, + String trackIns, + String trackOut, + String trackErr, + Long elapseMs, + Long userKey, + Long userRef, + Long dataKey, + Long dataRef, + Long dataOpt, + String codeKey, + String codeRef, + String codeOpt, + String wordRef + ) { + this.id = id; + this.createDt = createDt; + this.trackKey = trackKey; + this.trackRef = trackRef; + this.trackApp = trackApp; + this.trackEnv = trackEnv; + this.trackIns = trackIns; + this.trackOut = trackOut; + this.trackErr = trackErr; + this.elapseMs = elapseMs; + this.userKey = userKey; + this.userRef = userRef; + this.dataKey = dataKey; + this.dataRef = dataRef; + this.dataOpt = dataOpt; + this.codeKey = codeKey; + this.codeRef = codeRef; + this.codeOpt = codeOpt; + this.wordRef = wordRef; + } + + /** + * Getter for win_grow_track.id. + */ + @Override + public Long getId() { + return this.id; + } + + /** + * Setter for win_grow_track.id. + */ + @Override + public void setId(Long id) { + this.id = id; + } + + @Transient + public void setIdIf(Long id, boolean bool) { + if (bool) { + this.id = id; + } + } + + @Transient + public void setIdIf(Supplier id, boolean bool) { + if (bool) { + this.id = id.get(); + } + } + + @Transient + public void setIdIf(Long id, Predicate bool) { + if (bool.test(id)) { + this.id = id; + } + } + + @Transient + public void setIdIf(Long id, Predicate bool, Supplier... ids) { + if (bool.test(id)) { + this.id = id; + return; + } + for (Supplier supplier : ids) { + id = supplier.get(); + if (bool.test(id)) { + this.id = id; + return; + } + } + } + + @Transient + public void setIdIfNot(Long id, Predicate bool) { + if (!bool.test(id)) { + this.id = id; + } + } + + @Transient + public void setIdIfNot(Long id, Predicate bool, Supplier... ids) { + if (!bool.test(id)) { + this.id = id; + return; + } + for (Supplier supplier : ids) { + id = supplier.get(); + if (!bool.test(id)) { + this.id = id; + return; + } + } + } + + @Transient + public void setIdIf(UnaryOperator id) { + this.id = id.apply(this.id); + } + + + /** + * Getter for win_grow_track.create_dt. + */ + @Override + public LocalDateTime getCreateDt() { + return this.createDt; + } + + /** + * Setter for win_grow_track.create_dt. + */ + @Override + public void setCreateDt(LocalDateTime createDt) { + this.createDt = createDt; + } + + @Transient + public void setCreateDtIf(LocalDateTime createDt, boolean bool) { + if (bool) { + this.createDt = createDt; + } + } + + @Transient + public void setCreateDtIf(Supplier createDt, boolean bool) { + if (bool) { + this.createDt = createDt.get(); + } + } + + @Transient + public void setCreateDtIf(LocalDateTime createDt, Predicate bool) { + if (bool.test(createDt)) { + this.createDt = createDt; + } + } + + @Transient + public void setCreateDtIf(LocalDateTime createDt, Predicate bool, Supplier... createDts) { + if (bool.test(createDt)) { + this.createDt = createDt; + return; + } + for (Supplier supplier : createDts) { + createDt = supplier.get(); + if (bool.test(createDt)) { + this.createDt = createDt; + return; + } + } + } + + @Transient + public void setCreateDtIfNot(LocalDateTime createDt, Predicate bool) { + if (!bool.test(createDt)) { + this.createDt = createDt; + } + } + + @Transient + public void setCreateDtIfNot(LocalDateTime createDt, Predicate bool, Supplier... createDts) { + if (!bool.test(createDt)) { + this.createDt = createDt; + return; + } + for (Supplier supplier : createDts) { + createDt = supplier.get(); + if (!bool.test(createDt)) { + this.createDt = createDt; + return; + } + } + } + + @Transient + public void setCreateDtIf(UnaryOperator createDt) { + this.createDt = createDt.apply(this.createDt); + } + + + /** + * Getter for win_grow_track.track_key. + */ + @Override + public String getTrackKey() { + return this.trackKey; + } + + /** + * Setter for win_grow_track.track_key. + */ + @Override + public void setTrackKey(String trackKey) { + this.trackKey = trackKey; + } + + @Transient + public void setTrackKeyIf(String trackKey, boolean bool) { + if (bool) { + this.trackKey = trackKey; + } + } + + @Transient + public void setTrackKeyIf(Supplier trackKey, boolean bool) { + if (bool) { + this.trackKey = trackKey.get(); + } + } + + @Transient + public void setTrackKeyIf(String trackKey, Predicate bool) { + if (bool.test(trackKey)) { + this.trackKey = trackKey; + } + } + + @Transient + public void setTrackKeyIf(String trackKey, Predicate bool, Supplier... trackKeys) { + if (bool.test(trackKey)) { + this.trackKey = trackKey; + return; + } + for (Supplier supplier : trackKeys) { + trackKey = supplier.get(); + if (bool.test(trackKey)) { + this.trackKey = trackKey; + return; + } + } + } + + @Transient + public void setTrackKeyIfNot(String trackKey, Predicate bool) { + if (!bool.test(trackKey)) { + this.trackKey = trackKey; + } + } + + @Transient + public void setTrackKeyIfNot(String trackKey, Predicate bool, Supplier... trackKeys) { + if (!bool.test(trackKey)) { + this.trackKey = trackKey; + return; + } + for (Supplier supplier : trackKeys) { + trackKey = supplier.get(); + if (!bool.test(trackKey)) { + this.trackKey = trackKey; + return; + } + } + } + + @Transient + public void setTrackKeyIf(UnaryOperator trackKey) { + this.trackKey = trackKey.apply(this.trackKey); + } + + + /** + * Getter for win_grow_track.track_ref. + */ + @Override + public String getTrackRef() { + return this.trackRef; + } + + /** + * Setter for win_grow_track.track_ref. + */ + @Override + public void setTrackRef(String trackRef) { + this.trackRef = trackRef; + } + + @Transient + public void setTrackRefIf(String trackRef, boolean bool) { + if (bool) { + this.trackRef = trackRef; + } + } + + @Transient + public void setTrackRefIf(Supplier trackRef, boolean bool) { + if (bool) { + this.trackRef = trackRef.get(); + } + } + + @Transient + public void setTrackRefIf(String trackRef, Predicate bool) { + if (bool.test(trackRef)) { + this.trackRef = trackRef; + } + } + + @Transient + public void setTrackRefIf(String trackRef, Predicate bool, Supplier... trackRefs) { + if (bool.test(trackRef)) { + this.trackRef = trackRef; + return; + } + for (Supplier supplier : trackRefs) { + trackRef = supplier.get(); + if (bool.test(trackRef)) { + this.trackRef = trackRef; + return; + } + } + } + + @Transient + public void setTrackRefIfNot(String trackRef, Predicate bool) { + if (!bool.test(trackRef)) { + this.trackRef = trackRef; + } + } + + @Transient + public void setTrackRefIfNot(String trackRef, Predicate bool, Supplier... trackRefs) { + if (!bool.test(trackRef)) { + this.trackRef = trackRef; + return; + } + for (Supplier supplier : trackRefs) { + trackRef = supplier.get(); + if (!bool.test(trackRef)) { + this.trackRef = trackRef; + return; + } + } + } + + @Transient + public void setTrackRefIf(UnaryOperator trackRef) { + this.trackRef = trackRef.apply(this.trackRef); + } + + + /** + * Getter for win_grow_track.track_app. + */ + @Override + public String getTrackApp() { + return this.trackApp; + } + + /** + * Setter for win_grow_track.track_app. + */ + @Override + public void setTrackApp(String trackApp) { + this.trackApp = trackApp; + } + + @Transient + public void setTrackAppIf(String trackApp, boolean bool) { + if (bool) { + this.trackApp = trackApp; + } + } + + @Transient + public void setTrackAppIf(Supplier trackApp, boolean bool) { + if (bool) { + this.trackApp = trackApp.get(); + } + } + + @Transient + public void setTrackAppIf(String trackApp, Predicate bool) { + if (bool.test(trackApp)) { + this.trackApp = trackApp; + } + } + + @Transient + public void setTrackAppIf(String trackApp, Predicate bool, Supplier... trackApps) { + if (bool.test(trackApp)) { + this.trackApp = trackApp; + return; + } + for (Supplier supplier : trackApps) { + trackApp = supplier.get(); + if (bool.test(trackApp)) { + this.trackApp = trackApp; + return; + } + } + } + + @Transient + public void setTrackAppIfNot(String trackApp, Predicate bool) { + if (!bool.test(trackApp)) { + this.trackApp = trackApp; + } + } + + @Transient + public void setTrackAppIfNot(String trackApp, Predicate bool, Supplier... trackApps) { + if (!bool.test(trackApp)) { + this.trackApp = trackApp; + return; + } + for (Supplier supplier : trackApps) { + trackApp = supplier.get(); + if (!bool.test(trackApp)) { + this.trackApp = trackApp; + return; + } + } + } + + @Transient + public void setTrackAppIf(UnaryOperator trackApp) { + this.trackApp = trackApp.apply(this.trackApp); + } + + + /** + * Getter for win_grow_track.track_env. + */ + @Override + public String getTrackEnv() { + return this.trackEnv; + } + + /** + * Setter for win_grow_track.track_env. + */ + @Override + public void setTrackEnv(String trackEnv) { + this.trackEnv = trackEnv; + } + + @Transient + public void setTrackEnvIf(String trackEnv, boolean bool) { + if (bool) { + this.trackEnv = trackEnv; + } + } + + @Transient + public void setTrackEnvIf(Supplier trackEnv, boolean bool) { + if (bool) { + this.trackEnv = trackEnv.get(); + } + } + + @Transient + public void setTrackEnvIf(String trackEnv, Predicate bool) { + if (bool.test(trackEnv)) { + this.trackEnv = trackEnv; + } + } + + @Transient + public void setTrackEnvIf(String trackEnv, Predicate bool, Supplier... trackEnvs) { + if (bool.test(trackEnv)) { + this.trackEnv = trackEnv; + return; + } + for (Supplier supplier : trackEnvs) { + trackEnv = supplier.get(); + if (bool.test(trackEnv)) { + this.trackEnv = trackEnv; + return; + } + } + } + + @Transient + public void setTrackEnvIfNot(String trackEnv, Predicate bool) { + if (!bool.test(trackEnv)) { + this.trackEnv = trackEnv; + } + } + + @Transient + public void setTrackEnvIfNot(String trackEnv, Predicate bool, Supplier... trackEnvs) { + if (!bool.test(trackEnv)) { + this.trackEnv = trackEnv; + return; + } + for (Supplier supplier : trackEnvs) { + trackEnv = supplier.get(); + if (!bool.test(trackEnv)) { + this.trackEnv = trackEnv; + return; + } + } + } + + @Transient + public void setTrackEnvIf(UnaryOperator trackEnv) { + this.trackEnv = trackEnv.apply(this.trackEnv); + } + + + /** + * Getter for win_grow_track.track_ins. + */ + @Override + public String getTrackIns() { + return this.trackIns; + } + + /** + * Setter for win_grow_track.track_ins. + */ + @Override + public void setTrackIns(String trackIns) { + this.trackIns = trackIns; + } + + @Transient + public void setTrackInsIf(String trackIns, boolean bool) { + if (bool) { + this.trackIns = trackIns; + } + } + + @Transient + public void setTrackInsIf(Supplier trackIns, boolean bool) { + if (bool) { + this.trackIns = trackIns.get(); + } + } + + @Transient + public void setTrackInsIf(String trackIns, Predicate bool) { + if (bool.test(trackIns)) { + this.trackIns = trackIns; + } + } + + @Transient + public void setTrackInsIf(String trackIns, Predicate bool, Supplier... trackInss) { + if (bool.test(trackIns)) { + this.trackIns = trackIns; + return; + } + for (Supplier supplier : trackInss) { + trackIns = supplier.get(); + if (bool.test(trackIns)) { + this.trackIns = trackIns; + return; + } + } + } + + @Transient + public void setTrackInsIfNot(String trackIns, Predicate bool) { + if (!bool.test(trackIns)) { + this.trackIns = trackIns; + } + } + + @Transient + public void setTrackInsIfNot(String trackIns, Predicate bool, Supplier... trackInss) { + if (!bool.test(trackIns)) { + this.trackIns = trackIns; + return; + } + for (Supplier supplier : trackInss) { + trackIns = supplier.get(); + if (!bool.test(trackIns)) { + this.trackIns = trackIns; + return; + } + } + } + + @Transient + public void setTrackInsIf(UnaryOperator trackIns) { + this.trackIns = trackIns.apply(this.trackIns); + } + + + /** + * Getter for win_grow_track.track_out. + */ + @Override + public String getTrackOut() { + return this.trackOut; + } + + /** + * Setter for win_grow_track.track_out. + */ + @Override + public void setTrackOut(String trackOut) { + this.trackOut = trackOut; + } + + @Transient + public void setTrackOutIf(String trackOut, boolean bool) { + if (bool) { + this.trackOut = trackOut; + } + } + + @Transient + public void setTrackOutIf(Supplier trackOut, boolean bool) { + if (bool) { + this.trackOut = trackOut.get(); + } + } + + @Transient + public void setTrackOutIf(String trackOut, Predicate bool) { + if (bool.test(trackOut)) { + this.trackOut = trackOut; + } + } + + @Transient + public void setTrackOutIf(String trackOut, Predicate bool, Supplier... trackOuts) { + if (bool.test(trackOut)) { + this.trackOut = trackOut; + return; + } + for (Supplier supplier : trackOuts) { + trackOut = supplier.get(); + if (bool.test(trackOut)) { + this.trackOut = trackOut; + return; + } + } + } + + @Transient + public void setTrackOutIfNot(String trackOut, Predicate bool) { + if (!bool.test(trackOut)) { + this.trackOut = trackOut; + } + } + + @Transient + public void setTrackOutIfNot(String trackOut, Predicate bool, Supplier... trackOuts) { + if (!bool.test(trackOut)) { + this.trackOut = trackOut; + return; + } + for (Supplier supplier : trackOuts) { + trackOut = supplier.get(); + if (!bool.test(trackOut)) { + this.trackOut = trackOut; + return; + } + } + } + + @Transient + public void setTrackOutIf(UnaryOperator trackOut) { + this.trackOut = trackOut.apply(this.trackOut); + } + + + /** + * Getter for win_grow_track.track_err. + */ + @Override + public String getTrackErr() { + return this.trackErr; + } + + /** + * Setter for win_grow_track.track_err. + */ + @Override + public void setTrackErr(String trackErr) { + this.trackErr = trackErr; + } + + @Transient + public void setTrackErrIf(String trackErr, boolean bool) { + if (bool) { + this.trackErr = trackErr; + } + } + + @Transient + public void setTrackErrIf(Supplier trackErr, boolean bool) { + if (bool) { + this.trackErr = trackErr.get(); + } + } + + @Transient + public void setTrackErrIf(String trackErr, Predicate bool) { + if (bool.test(trackErr)) { + this.trackErr = trackErr; + } + } + + @Transient + public void setTrackErrIf(String trackErr, Predicate bool, Supplier... trackErrs) { + if (bool.test(trackErr)) { + this.trackErr = trackErr; + return; + } + for (Supplier supplier : trackErrs) { + trackErr = supplier.get(); + if (bool.test(trackErr)) { + this.trackErr = trackErr; + return; + } + } + } + + @Transient + public void setTrackErrIfNot(String trackErr, Predicate bool) { + if (!bool.test(trackErr)) { + this.trackErr = trackErr; + } + } + + @Transient + public void setTrackErrIfNot(String trackErr, Predicate bool, Supplier... trackErrs) { + if (!bool.test(trackErr)) { + this.trackErr = trackErr; + return; + } + for (Supplier supplier : trackErrs) { + trackErr = supplier.get(); + if (!bool.test(trackErr)) { + this.trackErr = trackErr; + return; + } + } + } + + @Transient + public void setTrackErrIf(UnaryOperator trackErr) { + this.trackErr = trackErr.apply(this.trackErr); + } + + + /** + * Getter for win_grow_track.elapse_ms. + */ + @Override + public Long getElapseMs() { + return this.elapseMs; + } + + /** + * Setter for win_grow_track.elapse_ms. + */ + @Override + public void setElapseMs(Long elapseMs) { + this.elapseMs = elapseMs; + } + + @Transient + public void setElapseMsIf(Long elapseMs, boolean bool) { + if (bool) { + this.elapseMs = elapseMs; + } + } + + @Transient + public void setElapseMsIf(Supplier elapseMs, boolean bool) { + if (bool) { + this.elapseMs = elapseMs.get(); + } + } + + @Transient + public void setElapseMsIf(Long elapseMs, Predicate bool) { + if (bool.test(elapseMs)) { + this.elapseMs = elapseMs; + } + } + + @Transient + public void setElapseMsIf(Long elapseMs, Predicate bool, Supplier... elapseMss) { + if (bool.test(elapseMs)) { + this.elapseMs = elapseMs; + return; + } + for (Supplier supplier : elapseMss) { + elapseMs = supplier.get(); + if (bool.test(elapseMs)) { + this.elapseMs = elapseMs; + return; + } + } + } + + @Transient + public void setElapseMsIfNot(Long elapseMs, Predicate bool) { + if (!bool.test(elapseMs)) { + this.elapseMs = elapseMs; + } + } + + @Transient + public void setElapseMsIfNot(Long elapseMs, Predicate bool, Supplier... elapseMss) { + if (!bool.test(elapseMs)) { + this.elapseMs = elapseMs; + return; + } + for (Supplier supplier : elapseMss) { + elapseMs = supplier.get(); + if (!bool.test(elapseMs)) { + this.elapseMs = elapseMs; + return; + } + } + } + + @Transient + public void setElapseMsIf(UnaryOperator elapseMs) { + this.elapseMs = elapseMs.apply(this.elapseMs); + } + + + /** + * Getter for win_grow_track.user_key. + */ + @Override + public Long getUserKey() { + return this.userKey; + } + + /** + * Setter for win_grow_track.user_key. + */ + @Override + public void setUserKey(Long userKey) { + this.userKey = userKey; + } + + @Transient + public void setUserKeyIf(Long userKey, boolean bool) { + if (bool) { + this.userKey = userKey; + } + } + + @Transient + public void setUserKeyIf(Supplier userKey, boolean bool) { + if (bool) { + this.userKey = userKey.get(); + } + } + + @Transient + public void setUserKeyIf(Long userKey, Predicate bool) { + if (bool.test(userKey)) { + this.userKey = userKey; + } + } + + @Transient + public void setUserKeyIf(Long userKey, Predicate bool, Supplier... userKeys) { + if (bool.test(userKey)) { + this.userKey = userKey; + return; + } + for (Supplier supplier : userKeys) { + userKey = supplier.get(); + if (bool.test(userKey)) { + this.userKey = userKey; + return; + } + } + } + + @Transient + public void setUserKeyIfNot(Long userKey, Predicate bool) { + if (!bool.test(userKey)) { + this.userKey = userKey; + } + } + + @Transient + public void setUserKeyIfNot(Long userKey, Predicate bool, Supplier... userKeys) { + if (!bool.test(userKey)) { + this.userKey = userKey; + return; + } + for (Supplier supplier : userKeys) { + userKey = supplier.get(); + if (!bool.test(userKey)) { + this.userKey = userKey; + return; + } + } + } + + @Transient + public void setUserKeyIf(UnaryOperator userKey) { + this.userKey = userKey.apply(this.userKey); + } + + + /** + * Getter for win_grow_track.user_ref. + */ + @Override + public Long getUserRef() { + return this.userRef; + } + + /** + * Setter for win_grow_track.user_ref. + */ + @Override + public void setUserRef(Long userRef) { + this.userRef = userRef; + } + + @Transient + public void setUserRefIf(Long userRef, boolean bool) { + if (bool) { + this.userRef = userRef; + } + } + + @Transient + public void setUserRefIf(Supplier userRef, boolean bool) { + if (bool) { + this.userRef = userRef.get(); + } + } + + @Transient + public void setUserRefIf(Long userRef, Predicate bool) { + if (bool.test(userRef)) { + this.userRef = userRef; + } + } + + @Transient + public void setUserRefIf(Long userRef, Predicate bool, Supplier... userRefs) { + if (bool.test(userRef)) { + this.userRef = userRef; + return; + } + for (Supplier supplier : userRefs) { + userRef = supplier.get(); + if (bool.test(userRef)) { + this.userRef = userRef; + return; + } + } + } + + @Transient + public void setUserRefIfNot(Long userRef, Predicate bool) { + if (!bool.test(userRef)) { + this.userRef = userRef; + } + } + + @Transient + public void setUserRefIfNot(Long userRef, Predicate bool, Supplier... userRefs) { + if (!bool.test(userRef)) { + this.userRef = userRef; + return; + } + for (Supplier supplier : userRefs) { + userRef = supplier.get(); + if (!bool.test(userRef)) { + this.userRef = userRef; + return; + } + } + } + + @Transient + public void setUserRefIf(UnaryOperator userRef) { + this.userRef = userRef.apply(this.userRef); + } + + + /** + * Getter for win_grow_track.data_key. + */ + @Override + public Long getDataKey() { + return this.dataKey; + } + + /** + * Setter for win_grow_track.data_key. + */ + @Override + public void setDataKey(Long dataKey) { + this.dataKey = dataKey; + } + + @Transient + public void setDataKeyIf(Long dataKey, boolean bool) { + if (bool) { + this.dataKey = dataKey; + } + } + + @Transient + public void setDataKeyIf(Supplier dataKey, boolean bool) { + if (bool) { + this.dataKey = dataKey.get(); + } + } + + @Transient + public void setDataKeyIf(Long dataKey, Predicate bool) { + if (bool.test(dataKey)) { + this.dataKey = dataKey; + } + } + + @Transient + public void setDataKeyIf(Long dataKey, Predicate bool, Supplier... dataKeys) { + if (bool.test(dataKey)) { + this.dataKey = dataKey; + return; + } + for (Supplier supplier : dataKeys) { + dataKey = supplier.get(); + if (bool.test(dataKey)) { + this.dataKey = dataKey; + return; + } + } + } + + @Transient + public void setDataKeyIfNot(Long dataKey, Predicate bool) { + if (!bool.test(dataKey)) { + this.dataKey = dataKey; + } + } + + @Transient + public void setDataKeyIfNot(Long dataKey, Predicate bool, Supplier... dataKeys) { + if (!bool.test(dataKey)) { + this.dataKey = dataKey; + return; + } + for (Supplier supplier : dataKeys) { + dataKey = supplier.get(); + if (!bool.test(dataKey)) { + this.dataKey = dataKey; + return; + } + } + } + + @Transient + public void setDataKeyIf(UnaryOperator dataKey) { + this.dataKey = dataKey.apply(this.dataKey); + } + + + /** + * Getter for win_grow_track.data_ref. + */ + @Override + public Long getDataRef() { + return this.dataRef; + } + + /** + * Setter for win_grow_track.data_ref. + */ + @Override + public void setDataRef(Long dataRef) { + this.dataRef = dataRef; + } + + @Transient + public void setDataRefIf(Long dataRef, boolean bool) { + if (bool) { + this.dataRef = dataRef; + } + } + + @Transient + public void setDataRefIf(Supplier dataRef, boolean bool) { + if (bool) { + this.dataRef = dataRef.get(); + } + } + + @Transient + public void setDataRefIf(Long dataRef, Predicate bool) { + if (bool.test(dataRef)) { + this.dataRef = dataRef; + } + } + + @Transient + public void setDataRefIf(Long dataRef, Predicate bool, Supplier... dataRefs) { + if (bool.test(dataRef)) { + this.dataRef = dataRef; + return; + } + for (Supplier supplier : dataRefs) { + dataRef = supplier.get(); + if (bool.test(dataRef)) { + this.dataRef = dataRef; + return; + } + } + } + + @Transient + public void setDataRefIfNot(Long dataRef, Predicate bool) { + if (!bool.test(dataRef)) { + this.dataRef = dataRef; + } + } + + @Transient + public void setDataRefIfNot(Long dataRef, Predicate bool, Supplier... dataRefs) { + if (!bool.test(dataRef)) { + this.dataRef = dataRef; + return; + } + for (Supplier supplier : dataRefs) { + dataRef = supplier.get(); + if (!bool.test(dataRef)) { + this.dataRef = dataRef; + return; + } + } + } + + @Transient + public void setDataRefIf(UnaryOperator dataRef) { + this.dataRef = dataRef.apply(this.dataRef); + } + + + /** + * Getter for win_grow_track.data_opt. + */ + @Override + public Long getDataOpt() { + return this.dataOpt; + } + + /** + * Setter for win_grow_track.data_opt. + */ + @Override + public void setDataOpt(Long dataOpt) { + this.dataOpt = dataOpt; + } + + @Transient + public void setDataOptIf(Long dataOpt, boolean bool) { + if (bool) { + this.dataOpt = dataOpt; + } + } + + @Transient + public void setDataOptIf(Supplier dataOpt, boolean bool) { + if (bool) { + this.dataOpt = dataOpt.get(); + } + } + + @Transient + public void setDataOptIf(Long dataOpt, Predicate bool) { + if (bool.test(dataOpt)) { + this.dataOpt = dataOpt; + } + } + + @Transient + public void setDataOptIf(Long dataOpt, Predicate bool, Supplier... dataOpts) { + if (bool.test(dataOpt)) { + this.dataOpt = dataOpt; + return; + } + for (Supplier supplier : dataOpts) { + dataOpt = supplier.get(); + if (bool.test(dataOpt)) { + this.dataOpt = dataOpt; + return; + } + } + } + + @Transient + public void setDataOptIfNot(Long dataOpt, Predicate bool) { + if (!bool.test(dataOpt)) { + this.dataOpt = dataOpt; + } + } + + @Transient + public void setDataOptIfNot(Long dataOpt, Predicate bool, Supplier... dataOpts) { + if (!bool.test(dataOpt)) { + this.dataOpt = dataOpt; + return; + } + for (Supplier supplier : dataOpts) { + dataOpt = supplier.get(); + if (!bool.test(dataOpt)) { + this.dataOpt = dataOpt; + return; + } + } + } + + @Transient + public void setDataOptIf(UnaryOperator dataOpt) { + this.dataOpt = dataOpt.apply(this.dataOpt); + } + + + /** + * Getter for win_grow_track.code_key. + */ + @Override + public String getCodeKey() { + return this.codeKey; + } + + /** + * Setter for win_grow_track.code_key. + */ + @Override + public void setCodeKey(String codeKey) { + this.codeKey = codeKey; + } + + @Transient + public void setCodeKeyIf(String codeKey, boolean bool) { + if (bool) { + this.codeKey = codeKey; + } + } + + @Transient + public void setCodeKeyIf(Supplier codeKey, boolean bool) { + if (bool) { + this.codeKey = codeKey.get(); + } + } + + @Transient + public void setCodeKeyIf(String codeKey, Predicate bool) { + if (bool.test(codeKey)) { + this.codeKey = codeKey; + } + } + + @Transient + public void setCodeKeyIf(String codeKey, Predicate bool, Supplier... codeKeys) { + if (bool.test(codeKey)) { + this.codeKey = codeKey; + return; + } + for (Supplier supplier : codeKeys) { + codeKey = supplier.get(); + if (bool.test(codeKey)) { + this.codeKey = codeKey; + return; + } + } + } + + @Transient + public void setCodeKeyIfNot(String codeKey, Predicate bool) { + if (!bool.test(codeKey)) { + this.codeKey = codeKey; + } + } + + @Transient + public void setCodeKeyIfNot(String codeKey, Predicate bool, Supplier... codeKeys) { + if (!bool.test(codeKey)) { + this.codeKey = codeKey; + return; + } + for (Supplier supplier : codeKeys) { + codeKey = supplier.get(); + if (!bool.test(codeKey)) { + this.codeKey = codeKey; + return; + } + } + } + + @Transient + public void setCodeKeyIf(UnaryOperator codeKey) { + this.codeKey = codeKey.apply(this.codeKey); + } + + + /** + * Getter for win_grow_track.code_ref. + */ + @Override + public String getCodeRef() { + return this.codeRef; + } + + /** + * Setter for win_grow_track.code_ref. + */ + @Override + public void setCodeRef(String codeRef) { + this.codeRef = codeRef; + } + + @Transient + public void setCodeRefIf(String codeRef, boolean bool) { + if (bool) { + this.codeRef = codeRef; + } + } + + @Transient + public void setCodeRefIf(Supplier codeRef, boolean bool) { + if (bool) { + this.codeRef = codeRef.get(); + } + } + + @Transient + public void setCodeRefIf(String codeRef, Predicate bool) { + if (bool.test(codeRef)) { + this.codeRef = codeRef; + } + } + + @Transient + public void setCodeRefIf(String codeRef, Predicate bool, Supplier... codeRefs) { + if (bool.test(codeRef)) { + this.codeRef = codeRef; + return; + } + for (Supplier supplier : codeRefs) { + codeRef = supplier.get(); + if (bool.test(codeRef)) { + this.codeRef = codeRef; + return; + } + } + } + + @Transient + public void setCodeRefIfNot(String codeRef, Predicate bool) { + if (!bool.test(codeRef)) { + this.codeRef = codeRef; + } + } + + @Transient + public void setCodeRefIfNot(String codeRef, Predicate bool, Supplier... codeRefs) { + if (!bool.test(codeRef)) { + this.codeRef = codeRef; + return; + } + for (Supplier supplier : codeRefs) { + codeRef = supplier.get(); + if (!bool.test(codeRef)) { + this.codeRef = codeRef; + return; + } + } + } + + @Transient + public void setCodeRefIf(UnaryOperator codeRef) { + this.codeRef = codeRef.apply(this.codeRef); + } + + + /** + * Getter for win_grow_track.code_opt. + */ + @Override + public String getCodeOpt() { + return this.codeOpt; + } + + /** + * Setter for win_grow_track.code_opt. + */ + @Override + public void setCodeOpt(String codeOpt) { + this.codeOpt = codeOpt; + } + + @Transient + public void setCodeOptIf(String codeOpt, boolean bool) { + if (bool) { + this.codeOpt = codeOpt; + } + } + + @Transient + public void setCodeOptIf(Supplier codeOpt, boolean bool) { + if (bool) { + this.codeOpt = codeOpt.get(); + } + } + + @Transient + public void setCodeOptIf(String codeOpt, Predicate bool) { + if (bool.test(codeOpt)) { + this.codeOpt = codeOpt; + } + } + + @Transient + public void setCodeOptIf(String codeOpt, Predicate bool, Supplier... codeOpts) { + if (bool.test(codeOpt)) { + this.codeOpt = codeOpt; + return; + } + for (Supplier supplier : codeOpts) { + codeOpt = supplier.get(); + if (bool.test(codeOpt)) { + this.codeOpt = codeOpt; + return; + } + } + } + + @Transient + public void setCodeOptIfNot(String codeOpt, Predicate bool) { + if (!bool.test(codeOpt)) { + this.codeOpt = codeOpt; + } + } + + @Transient + public void setCodeOptIfNot(String codeOpt, Predicate bool, Supplier... codeOpts) { + if (!bool.test(codeOpt)) { + this.codeOpt = codeOpt; + return; + } + for (Supplier supplier : codeOpts) { + codeOpt = supplier.get(); + if (!bool.test(codeOpt)) { + this.codeOpt = codeOpt; + return; + } + } + } + + @Transient + public void setCodeOptIf(UnaryOperator codeOpt) { + this.codeOpt = codeOpt.apply(this.codeOpt); + } + + + /** + * Getter for win_grow_track.word_ref. + */ + @Override + public String getWordRef() { + return this.wordRef; + } + + /** + * Setter for win_grow_track.word_ref. + */ + @Override + public void setWordRef(String wordRef) { + this.wordRef = wordRef; + } + + @Transient + public void setWordRefIf(String wordRef, boolean bool) { + if (bool) { + this.wordRef = wordRef; + } + } + + @Transient + public void setWordRefIf(Supplier wordRef, boolean bool) { + if (bool) { + this.wordRef = wordRef.get(); + } + } + + @Transient + public void setWordRefIf(String wordRef, Predicate bool) { + if (bool.test(wordRef)) { + this.wordRef = wordRef; + } + } + + @Transient + public void setWordRefIf(String wordRef, Predicate bool, Supplier... wordRefs) { + if (bool.test(wordRef)) { + this.wordRef = wordRef; + return; + } + for (Supplier supplier : wordRefs) { + wordRef = supplier.get(); + if (bool.test(wordRef)) { + this.wordRef = wordRef; + return; + } + } + } + + @Transient + public void setWordRefIfNot(String wordRef, Predicate bool) { + if (!bool.test(wordRef)) { + this.wordRef = wordRef; + } + } + + @Transient + public void setWordRefIfNot(String wordRef, Predicate bool, Supplier... wordRefs) { + if (!bool.test(wordRef)) { + this.wordRef = wordRef; + return; + } + for (Supplier supplier : wordRefs) { + wordRef = supplier.get(); + if (!bool.test(wordRef)) { + this.wordRef = wordRef; + return; + } + } + } + + @Transient + public void setWordRefIf(UnaryOperator wordRef) { + this.wordRef = wordRef.apply(this.wordRef); + } + + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final WinGrowTrack other = (WinGrowTrack) obj; + if (this.id == null) { + if (other.id != null) + return false; + } + else if (!this.id.equals(other.id)) + return false; + if (this.createDt == null) { + if (other.createDt != null) + return false; + } + else if (!this.createDt.equals(other.createDt)) + return false; + if (this.trackKey == null) { + if (other.trackKey != null) + return false; + } + else if (!this.trackKey.equals(other.trackKey)) + return false; + if (this.trackRef == null) { + if (other.trackRef != null) + return false; + } + else if (!this.trackRef.equals(other.trackRef)) + return false; + if (this.trackApp == null) { + if (other.trackApp != null) + return false; + } + else if (!this.trackApp.equals(other.trackApp)) + return false; + if (this.trackEnv == null) { + if (other.trackEnv != null) + return false; + } + else if (!this.trackEnv.equals(other.trackEnv)) + return false; + if (this.trackIns == null) { + if (other.trackIns != null) + return false; + } + else if (!this.trackIns.equals(other.trackIns)) + return false; + if (this.trackOut == null) { + if (other.trackOut != null) + return false; + } + else if (!this.trackOut.equals(other.trackOut)) + return false; + if (this.trackErr == null) { + if (other.trackErr != null) + return false; + } + else if (!this.trackErr.equals(other.trackErr)) + return false; + if (this.elapseMs == null) { + if (other.elapseMs != null) + return false; + } + else if (!this.elapseMs.equals(other.elapseMs)) + return false; + if (this.userKey == null) { + if (other.userKey != null) + return false; + } + else if (!this.userKey.equals(other.userKey)) + return false; + if (this.userRef == null) { + if (other.userRef != null) + return false; + } + else if (!this.userRef.equals(other.userRef)) + return false; + if (this.dataKey == null) { + if (other.dataKey != null) + return false; + } + else if (!this.dataKey.equals(other.dataKey)) + return false; + if (this.dataRef == null) { + if (other.dataRef != null) + return false; + } + else if (!this.dataRef.equals(other.dataRef)) + return false; + if (this.dataOpt == null) { + if (other.dataOpt != null) + return false; + } + else if (!this.dataOpt.equals(other.dataOpt)) + return false; + if (this.codeKey == null) { + if (other.codeKey != null) + return false; + } + else if (!this.codeKey.equals(other.codeKey)) + return false; + if (this.codeRef == null) { + if (other.codeRef != null) + return false; + } + else if (!this.codeRef.equals(other.codeRef)) + return false; + if (this.codeOpt == null) { + if (other.codeOpt != null) + return false; + } + else if (!this.codeOpt.equals(other.codeOpt)) + return false; + if (this.wordRef == null) { + if (other.wordRef != null) + return false; + } + else if (!this.wordRef.equals(other.wordRef)) + return false; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); + result = prime * result + ((this.createDt == null) ? 0 : this.createDt.hashCode()); + result = prime * result + ((this.trackKey == null) ? 0 : this.trackKey.hashCode()); + result = prime * result + ((this.trackRef == null) ? 0 : this.trackRef.hashCode()); + result = prime * result + ((this.trackApp == null) ? 0 : this.trackApp.hashCode()); + result = prime * result + ((this.trackEnv == null) ? 0 : this.trackEnv.hashCode()); + result = prime * result + ((this.trackIns == null) ? 0 : this.trackIns.hashCode()); + result = prime * result + ((this.trackOut == null) ? 0 : this.trackOut.hashCode()); + result = prime * result + ((this.trackErr == null) ? 0 : this.trackErr.hashCode()); + result = prime * result + ((this.elapseMs == null) ? 0 : this.elapseMs.hashCode()); + result = prime * result + ((this.userKey == null) ? 0 : this.userKey.hashCode()); + result = prime * result + ((this.userRef == null) ? 0 : this.userRef.hashCode()); + result = prime * result + ((this.dataKey == null) ? 0 : this.dataKey.hashCode()); + result = prime * result + ((this.dataRef == null) ? 0 : this.dataRef.hashCode()); + result = prime * result + ((this.dataOpt == null) ? 0 : this.dataOpt.hashCode()); + result = prime * result + ((this.codeKey == null) ? 0 : this.codeKey.hashCode()); + result = prime * result + ((this.codeRef == null) ? 0 : this.codeRef.hashCode()); + result = prime * result + ((this.codeOpt == null) ? 0 : this.codeOpt.hashCode()); + result = prime * result + ((this.wordRef == null) ? 0 : this.wordRef.hashCode()); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("WinGrowTrack ("); + + sb.append(id); + sb.append(", ").append(createDt); + sb.append(", ").append(trackKey); + sb.append(", ").append(trackRef); + sb.append(", ").append(trackApp); + sb.append(", ").append(trackEnv); + sb.append(", ").append(trackIns); + sb.append(", ").append(trackOut); + sb.append(", ").append(trackErr); + sb.append(", ").append(elapseMs); + sb.append(", ").append(userKey); + sb.append(", ").append(userRef); + sb.append(", ").append(dataKey); + sb.append(", ").append(dataRef); + sb.append(", ").append(dataOpt); + sb.append(", ").append(codeKey); + sb.append(", ").append(codeRef); + sb.append(", ").append(codeOpt); + sb.append(", ").append(wordRef); + + sb.append(")"); + return sb.toString(); + } + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + @Override + public void from(IWinGrowTrack from) { + setId(from.getId()); + setCreateDt(from.getCreateDt()); + setTrackKey(from.getTrackKey()); + setTrackRef(from.getTrackRef()); + setTrackApp(from.getTrackApp()); + setTrackEnv(from.getTrackEnv()); + setTrackIns(from.getTrackIns()); + setTrackOut(from.getTrackOut()); + setTrackErr(from.getTrackErr()); + setElapseMs(from.getElapseMs()); + setUserKey(from.getUserKey()); + setUserRef(from.getUserRef()); + setDataKey(from.getDataKey()); + setDataRef(from.getDataRef()); + setDataOpt(from.getDataOpt()); + setCodeKey(from.getCodeKey()); + setCodeRef(from.getCodeRef()); + setCodeOpt(from.getCodeOpt()); + setWordRef(from.getWordRef()); + } + + @Override + public E into(E into) { + into.from(this); + return into; + } +} diff --git a/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/records/WinGrowTrackRecord.java b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/records/WinGrowTrackRecord.java new file mode 100644 index 000000000..70ec6ac55 --- /dev/null +++ b/radiant/tiny-grow/src/main/java-gen/pro/fessional/wings/tiny/grow/database/autogen/tables/records/WinGrowTrackRecord.java @@ -0,0 +1,888 @@ +/* + * This file is generated by jOOQ. + */ +package pro.fessional.wings.tiny.grow.database.autogen.tables.records; + + +import org.jooq.Field; +import org.jooq.Record1; +import org.jooq.Record19; +import org.jooq.Row19; +import org.jooq.impl.UpdatableRecordImpl; +import pro.fessional.wings.tiny.grow.database.autogen.tables.WinGrowTrackTable; +import pro.fessional.wings.tiny.grow.database.autogen.tables.interfaces.IWinGrowTrack; +import pro.fessional.wings.tiny.grow.database.autogen.tables.pojos.WinGrowTrack; + +import javax.annotation.processing.Generated; +import java.time.LocalDateTime; + + +/** + * The table wings.win_grow_track. + */ +@Generated( + value = { + "https://www.jooq.org", + "jOOQ version:3.18.9", + "schema version:2020102801" + }, + comments = "This class is generated by jOOQ" +) +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class WinGrowTrackRecord extends UpdatableRecordImpl implements Record19, IWinGrowTrack { + + private static final long serialVersionUID = 1L; + + /** + * Setter for win_grow_track.id. + */ + @Override + public void setId(Long value) { + set(0, value); + } + + /** + * Getter for win_grow_track.id. + */ + @Override + public Long getId() { + return (Long) get(0); + } + + /** + * Setter for win_grow_track.create_dt. + */ + @Override + public void setCreateDt(LocalDateTime value) { + set(1, value); + } + + /** + * Getter for win_grow_track.create_dt. + */ + @Override + public LocalDateTime getCreateDt() { + return (LocalDateTime) get(1); + } + + /** + * Setter for win_grow_track.track_key. + */ + @Override + public void setTrackKey(String value) { + set(2, value); + } + + /** + * Getter for win_grow_track.track_key. + */ + @Override + public String getTrackKey() { + return (String) get(2); + } + + /** + * Setter for win_grow_track.track_ref. + */ + @Override + public void setTrackRef(String value) { + set(3, value); + } + + /** + * Getter for win_grow_track.track_ref. + */ + @Override + public String getTrackRef() { + return (String) get(3); + } + + /** + * Setter for win_grow_track.track_app. + */ + @Override + public void setTrackApp(String value) { + set(4, value); + } + + /** + * Getter for win_grow_track.track_app. + */ + @Override + public String getTrackApp() { + return (String) get(4); + } + + /** + * Setter for win_grow_track.track_env. + */ + @Override + public void setTrackEnv(String value) { + set(5, value); + } + + /** + * Getter for win_grow_track.track_env. + */ + @Override + public String getTrackEnv() { + return (String) get(5); + } + + /** + * Setter for win_grow_track.track_ins. + */ + @Override + public void setTrackIns(String value) { + set(6, value); + } + + /** + * Getter for win_grow_track.track_ins. + */ + @Override + public String getTrackIns() { + return (String) get(6); + } + + /** + * Setter for win_grow_track.track_out. + */ + @Override + public void setTrackOut(String value) { + set(7, value); + } + + /** + * Getter for win_grow_track.track_out. + */ + @Override + public String getTrackOut() { + return (String) get(7); + } + + /** + * Setter for win_grow_track.track_err. + */ + @Override + public void setTrackErr(String value) { + set(8, value); + } + + /** + * Getter for win_grow_track.track_err. + */ + @Override + public String getTrackErr() { + return (String) get(8); + } + + /** + * Setter for win_grow_track.elapse_ms. + */ + @Override + public void setElapseMs(Long value) { + set(9, value); + } + + /** + * Getter for win_grow_track.elapse_ms. + */ + @Override + public Long getElapseMs() { + return (Long) get(9); + } + + /** + * Setter for win_grow_track.user_key. + */ + @Override + public void setUserKey(Long value) { + set(10, value); + } + + /** + * Getter for win_grow_track.user_key. + */ + @Override + public Long getUserKey() { + return (Long) get(10); + } + + /** + * Setter for win_grow_track.user_ref. + */ + @Override + public void setUserRef(Long value) { + set(11, value); + } + + /** + * Getter for win_grow_track.user_ref. + */ + @Override + public Long getUserRef() { + return (Long) get(11); + } + + /** + * Setter for win_grow_track.data_key. + */ + @Override + public void setDataKey(Long value) { + set(12, value); + } + + /** + * Getter for win_grow_track.data_key. + */ + @Override + public Long getDataKey() { + return (Long) get(12); + } + + /** + * Setter for win_grow_track.data_ref. + */ + @Override + public void setDataRef(Long value) { + set(13, value); + } + + /** + * Getter for win_grow_track.data_ref. + */ + @Override + public Long getDataRef() { + return (Long) get(13); + } + + /** + * Setter for win_grow_track.data_opt. + */ + @Override + public void setDataOpt(Long value) { + set(14, value); + } + + /** + * Getter for win_grow_track.data_opt. + */ + @Override + public Long getDataOpt() { + return (Long) get(14); + } + + /** + * Setter for win_grow_track.code_key. + */ + @Override + public void setCodeKey(String value) { + set(15, value); + } + + /** + * Getter for win_grow_track.code_key. + */ + @Override + public String getCodeKey() { + return (String) get(15); + } + + /** + * Setter for win_grow_track.code_ref. + */ + @Override + public void setCodeRef(String value) { + set(16, value); + } + + /** + * Getter for win_grow_track.code_ref. + */ + @Override + public String getCodeRef() { + return (String) get(16); + } + + /** + * Setter for win_grow_track.code_opt. + */ + @Override + public void setCodeOpt(String value) { + set(17, value); + } + + /** + * Getter for win_grow_track.code_opt. + */ + @Override + public String getCodeOpt() { + return (String) get(17); + } + + /** + * Setter for win_grow_track.word_ref. + */ + @Override + public void setWordRef(String value) { + set(18, value); + } + + /** + * Getter for win_grow_track.word_ref. + */ + @Override + public String getWordRef() { + return (String) get(18); + } + + // ------------------------------------------------------------------------- + // Primary key information + // ------------------------------------------------------------------------- + + @Override + public Record1 key() { + return (Record1) super.key(); + } + + // ------------------------------------------------------------------------- + // Record19 type implementation + // ------------------------------------------------------------------------- + + @Override + public Row19 fieldsRow() { + return (Row19) super.fieldsRow(); + } + + @Override + public Row19 valuesRow() { + return (Row19) super.valuesRow(); + } + + @Override + public Field field1() { + return WinGrowTrackTable.WinGrowTrack.Id; + } + + @Override + public Field field2() { + return WinGrowTrackTable.WinGrowTrack.CreateDt; + } + + @Override + public Field field3() { + return WinGrowTrackTable.WinGrowTrack.TrackKey; + } + + @Override + public Field field4() { + return WinGrowTrackTable.WinGrowTrack.TrackRef; + } + + @Override + public Field field5() { + return WinGrowTrackTable.WinGrowTrack.TrackApp; + } + + @Override + public Field field6() { + return WinGrowTrackTable.WinGrowTrack.TrackEnv; + } + + @Override + public Field field7() { + return WinGrowTrackTable.WinGrowTrack.TrackIns; + } + + @Override + public Field field8() { + return WinGrowTrackTable.WinGrowTrack.TrackOut; + } + + @Override + public Field field9() { + return WinGrowTrackTable.WinGrowTrack.TrackErr; + } + + @Override + public Field field10() { + return WinGrowTrackTable.WinGrowTrack.ElapseMs; + } + + @Override + public Field field11() { + return WinGrowTrackTable.WinGrowTrack.UserKey; + } + + @Override + public Field field12() { + return WinGrowTrackTable.WinGrowTrack.UserRef; + } + + @Override + public Field field13() { + return WinGrowTrackTable.WinGrowTrack.DataKey; + } + + @Override + public Field field14() { + return WinGrowTrackTable.WinGrowTrack.DataRef; + } + + @Override + public Field field15() { + return WinGrowTrackTable.WinGrowTrack.DataOpt; + } + + @Override + public Field field16() { + return WinGrowTrackTable.WinGrowTrack.CodeKey; + } + + @Override + public Field field17() { + return WinGrowTrackTable.WinGrowTrack.CodeRef; + } + + @Override + public Field field18() { + return WinGrowTrackTable.WinGrowTrack.CodeOpt; + } + + @Override + public Field field19() { + return WinGrowTrackTable.WinGrowTrack.WordRef; + } + + @Override + public Long component1() { + return getId(); + } + + @Override + public LocalDateTime component2() { + return getCreateDt(); + } + + @Override + public String component3() { + return getTrackKey(); + } + + @Override + public String component4() { + return getTrackRef(); + } + + @Override + public String component5() { + return getTrackApp(); + } + + @Override + public String component6() { + return getTrackEnv(); + } + + @Override + public String component7() { + return getTrackIns(); + } + + @Override + public String component8() { + return getTrackOut(); + } + + @Override + public String component9() { + return getTrackErr(); + } + + @Override + public Long component10() { + return getElapseMs(); + } + + @Override + public Long component11() { + return getUserKey(); + } + + @Override + public Long component12() { + return getUserRef(); + } + + @Override + public Long component13() { + return getDataKey(); + } + + @Override + public Long component14() { + return getDataRef(); + } + + @Override + public Long component15() { + return getDataOpt(); + } + + @Override + public String component16() { + return getCodeKey(); + } + + @Override + public String component17() { + return getCodeRef(); + } + + @Override + public String component18() { + return getCodeOpt(); + } + + @Override + public String component19() { + return getWordRef(); + } + + @Override + public Long value1() { + return getId(); + } + + @Override + public LocalDateTime value2() { + return getCreateDt(); + } + + @Override + public String value3() { + return getTrackKey(); + } + + @Override + public String value4() { + return getTrackRef(); + } + + @Override + public String value5() { + return getTrackApp(); + } + + @Override + public String value6() { + return getTrackEnv(); + } + + @Override + public String value7() { + return getTrackIns(); + } + + @Override + public String value8() { + return getTrackOut(); + } + + @Override + public String value9() { + return getTrackErr(); + } + + @Override + public Long value10() { + return getElapseMs(); + } + + @Override + public Long value11() { + return getUserKey(); + } + + @Override + public Long value12() { + return getUserRef(); + } + + @Override + public Long value13() { + return getDataKey(); + } + + @Override + public Long value14() { + return getDataRef(); + } + + @Override + public Long value15() { + return getDataOpt(); + } + + @Override + public String value16() { + return getCodeKey(); + } + + @Override + public String value17() { + return getCodeRef(); + } + + @Override + public String value18() { + return getCodeOpt(); + } + + @Override + public String value19() { + return getWordRef(); + } + + @Override + public WinGrowTrackRecord value1(Long value) { + setId(value); + return this; + } + + @Override + public WinGrowTrackRecord value2(LocalDateTime value) { + setCreateDt(value); + return this; + } + + @Override + public WinGrowTrackRecord value3(String value) { + setTrackKey(value); + return this; + } + + @Override + public WinGrowTrackRecord value4(String value) { + setTrackRef(value); + return this; + } + + @Override + public WinGrowTrackRecord value5(String value) { + setTrackApp(value); + return this; + } + + @Override + public WinGrowTrackRecord value6(String value) { + setTrackEnv(value); + return this; + } + + @Override + public WinGrowTrackRecord value7(String value) { + setTrackIns(value); + return this; + } + + @Override + public WinGrowTrackRecord value8(String value) { + setTrackOut(value); + return this; + } + + @Override + public WinGrowTrackRecord value9(String value) { + setTrackErr(value); + return this; + } + + @Override + public WinGrowTrackRecord value10(Long value) { + setElapseMs(value); + return this; + } + + @Override + public WinGrowTrackRecord value11(Long value) { + setUserKey(value); + return this; + } + + @Override + public WinGrowTrackRecord value12(Long value) { + setUserRef(value); + return this; + } + + @Override + public WinGrowTrackRecord value13(Long value) { + setDataKey(value); + return this; + } + + @Override + public WinGrowTrackRecord value14(Long value) { + setDataRef(value); + return this; + } + + @Override + public WinGrowTrackRecord value15(Long value) { + setDataOpt(value); + return this; + } + + @Override + public WinGrowTrackRecord value16(String value) { + setCodeKey(value); + return this; + } + + @Override + public WinGrowTrackRecord value17(String value) { + setCodeRef(value); + return this; + } + + @Override + public WinGrowTrackRecord value18(String value) { + setCodeOpt(value); + return this; + } + + @Override + public WinGrowTrackRecord value19(String value) { + setWordRef(value); + return this; + } + + @Override + public WinGrowTrackRecord values(Long value1, LocalDateTime value2, String value3, String value4, String value5, String value6, String value7, String value8, String value9, Long value10, Long value11, Long value12, Long value13, Long value14, Long value15, String value16, String value17, String value18, String value19) { + value1(value1); + value2(value2); + value3(value3); + value4(value4); + value5(value5); + value6(value6); + value7(value7); + value8(value8); + value9(value9); + value10(value10); + value11(value11); + value12(value12); + value13(value13); + value14(value14); + value15(value15); + value16(value16); + value17(value17); + value18(value18); + value19(value19); + return this; + } + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + @Override + public void from(IWinGrowTrack from) { + setId(from.getId()); + setCreateDt(from.getCreateDt()); + setTrackKey(from.getTrackKey()); + setTrackRef(from.getTrackRef()); + setTrackApp(from.getTrackApp()); + setTrackEnv(from.getTrackEnv()); + setTrackIns(from.getTrackIns()); + setTrackOut(from.getTrackOut()); + setTrackErr(from.getTrackErr()); + setElapseMs(from.getElapseMs()); + setUserKey(from.getUserKey()); + setUserRef(from.getUserRef()); + setDataKey(from.getDataKey()); + setDataRef(from.getDataRef()); + setDataOpt(from.getDataOpt()); + setCodeKey(from.getCodeKey()); + setCodeRef(from.getCodeRef()); + setCodeOpt(from.getCodeOpt()); + setWordRef(from.getWordRef()); + resetChangedOnNotNull(); + } + + @Override + public E into(E into) { + into.from(this); + return into; + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Create a detached WinGrowTrackRecord + */ + public WinGrowTrackRecord() { + super(WinGrowTrackTable.WinGrowTrack); + } + + /** + * Create a detached, initialised WinGrowTrackRecord + */ + public WinGrowTrackRecord(Long id, LocalDateTime createDt, String trackKey, String trackRef, String trackApp, String trackEnv, String trackIns, String trackOut, String trackErr, Long elapseMs, Long userKey, Long userRef, Long dataKey, Long dataRef, Long dataOpt, String codeKey, String codeRef, String codeOpt, String wordRef) { + super(WinGrowTrackTable.WinGrowTrack); + + setId(id); + setCreateDt(createDt); + setTrackKey(trackKey); + setTrackRef(trackRef); + setTrackApp(trackApp); + setTrackEnv(trackEnv); + setTrackIns(trackIns); + setTrackOut(trackOut); + setTrackErr(trackErr); + setElapseMs(elapseMs); + setUserKey(userKey); + setUserRef(userRef); + setDataKey(dataKey); + setDataRef(dataRef); + setDataOpt(dataOpt); + setCodeKey(codeKey); + setCodeRef(codeRef); + setCodeOpt(codeOpt); + setWordRef(wordRef); + resetChangedOnNotNull(); + } + + /** + * Create a detached, initialised WinGrowTrackRecord + */ + public WinGrowTrackRecord(WinGrowTrack value) { + super(WinGrowTrackTable.WinGrowTrack); + + if (value != null) { + setId(value.getId()); + setCreateDt(value.getCreateDt()); + setTrackKey(value.getTrackKey()); + setTrackRef(value.getTrackRef()); + setTrackApp(value.getTrackApp()); + setTrackEnv(value.getTrackEnv()); + setTrackIns(value.getTrackIns()); + setTrackOut(value.getTrackOut()); + setTrackErr(value.getTrackErr()); + setElapseMs(value.getElapseMs()); + setUserKey(value.getUserKey()); + setUserRef(value.getUserRef()); + setDataKey(value.getDataKey()); + setDataRef(value.getDataRef()); + setDataOpt(value.getDataOpt()); + setCodeKey(value.getCodeKey()); + setCodeRef(value.getCodeRef()); + setCodeOpt(value.getCodeOpt()); + setWordRef(value.getWordRef()); + resetChangedOnNotNull(); + } + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/bean/TinyTrackConfiguration.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/bean/TinyTrackConfiguration.java new file mode 100644 index 000000000..3fd007bac --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/bean/TinyTrackConfiguration.java @@ -0,0 +1,31 @@ +package pro.fessional.wings.tiny.grow.spring.bean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.tiny.grow.database.TinyGrowDatabase; +import pro.fessional.wings.tiny.grow.track.TinyTrackService; + + +/** + * @author trydofor + * @since 2024-07-27 + */ + +@Configuration(proxyBeanMethods = false) +@ConditionalWingsEnabled +public class TinyTrackConfiguration { + + private static final Log log = LogFactory.getLog(TinyTrackConfiguration.class); + + @Configuration(proxyBeanMethods = false) + @ConditionalWingsEnabled + @ComponentScan(basePackageClasses = { TinyGrowDatabase.class, TinyTrackService.class }) + public static class DaoServScan { + public DaoServScan() { + log.info("TinyGrow spring-scan database, service"); + } + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/conf/TinyGrowAutoConfiguration.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/conf/TinyGrowAutoConfiguration.java new file mode 100644 index 000000000..faf9a0fa1 --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/conf/TinyGrowAutoConfiguration.java @@ -0,0 +1,19 @@ +package pro.fessional.wings.tiny.grow.spring.conf; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.context.annotation.Import; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.tiny.grow.spring.bean.TinyTrackConfiguration; +import pro.fessional.wings.tiny.grow.spring.prop.TinyTrackOmitProp; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@AutoConfiguration +@ConditionalWingsEnabled +@ConfigurationPropertiesScan(basePackageClasses = TinyTrackOmitProp.class) +@Import(TinyTrackConfiguration.class) +public class TinyGrowAutoConfiguration { +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/prop/TinyTrackOmitProp.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/prop/TinyTrackOmitProp.java new file mode 100644 index 000000000..2d3af66ac --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/spring/prop/TinyTrackOmitProp.java @@ -0,0 +1,45 @@ +package pro.fessional.wings.tiny.grow.spring.prop; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Collections; +import java.util.Map; +import java.util.regex.Pattern; + +import static pro.fessional.wings.tiny.grow.spring.prop.TinyTrackOmitProp.Key; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@Data +@ConfigurationProperties(Key) +public class TinyTrackOmitProp { + + public static final String Key = "wings.tiny.grow.track.omit"; + + /** + * omit the property if it is instance of. empty means disable + * + * @see #Key$clazz + */ + private Map> clazz = Collections.emptyMap(); + public static final String Key$clazz = Key + ".clazz"; + + /** + * omit the property if its name equals. empty means disable + * + * @see #Key$equal + */ + private Map equal = Collections.emptyMap(); + public static final String Key$equal = Key + ".equal"; + + /** + * omit the property if its name match regex. empty means disable + * + * @see #Key$regex + */ + private Map regex = Collections.emptyMap(); + public static final String Key$regex = Key + ".regex"; +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTrackHelper.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTrackHelper.java new file mode 100644 index 000000000..65f867e80 --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTrackHelper.java @@ -0,0 +1,154 @@ +package pro.fessional.wings.tiny.grow.track; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.function.SingletonSupplier; +import pro.fessional.mirana.func.Lam; +import pro.fessional.mirana.pain.ThrowableUtil; +import pro.fessional.mirana.time.ThreadNow; +import pro.fessional.wings.silencer.spring.help.ApplicationContextHelper; + +import java.lang.reflect.Method; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * @author trydofor + * @since 2024-07-25 + */ +@Slf4j +public class TinyTrackHelper { + + private static final SingletonSupplier TrackService = ApplicationContextHelper.getSingletonSupplier(TinyTrackService.class); + + public static R track(@NotNull Lam.Ref key, @NotNull Function fun) { + return track(key.method, fun); + } + + public static R track(@NotNull Method key, @NotNull Function fun) { + TinyTrackService service = TrackService.obtain(); + TinyTracking tracking = service.begin(key); + return track(service, tracking, fun); + } + + public static R track(@NotNull Enum key, @NotNull Function fun) { + TinyTrackService service = TrackService.obtain(); + TinyTracking tracking = service.begin(key); + return track(service, tracking, fun); + } + + public static R track(@NotNull String key, @NotNull Function fun) { + TinyTrackService service = TrackService.obtain(); + TinyTracking tracking = service.begin(key); + return track(service, tracking, fun); + } + + public static R track(@NotNull String key, @NotNull String ref, @NotNull Function fun) { + TinyTrackService service = TrackService.obtain(); + TinyTracking tracking = service.begin(key, ref); + return track(service, tracking, fun); + } + + private static R track(@NotNull TinyTrackService service, @NotNull TinyTracking tracking, @NotNull Function fun) { + try { + R out = fun.apply(tracking); + if (tracking.getOut() == null) { + tracking.setOut(out); + } + return out; + } + catch (Throwable e) { + if (tracking.getErr() == null) { + tracking.setErr(e); + } + throw ThrowableUtil.runtime(e); + } + finally { + tracking.setElapse(ThreadNow.millis() - tracking.getBegin()); + service.track(tracking, true); + } + } + + public static TrackWrapper track(@NotNull Lam.Ref key) { + return track(key.method); + } + + public static TrackWrapper track(@NotNull Method key) { + TinyTrackService service = TrackService.obtain(); + TinyTracking tracking = service.begin(key); + return new TrackWrapper(tracking, service); + } + + public static TrackWrapper track(@NotNull Enum key) { + TinyTrackService service = TrackService.obtain(); + TinyTracking tracking = service.begin(key); + return new TrackWrapper(tracking, service); + } + + public static TrackWrapper track(@NotNull String key) { + TinyTrackService service = TrackService.obtain(); + TinyTracking tracking = service.begin(key); + return new TrackWrapper(tracking, service); + } + + public static TrackWrapper track(@NotNull String key, @NotNull String ref) { + TinyTrackService service = TrackService.obtain(); + TinyTracking tracking = service.begin(key, ref); + return new TrackWrapper(tracking, service); + } + + /** + *
+     * try(tryTrack) {
+     *   // biz logic
+     * };
+     * 
+ */ + @RequiredArgsConstructor + @Getter + public static class TrackWrapper implements AutoCloseable { + + @NotNull + @Delegate(types = TinyTracking.class) + private final TinyTracking tracking; + + @NotNull + private final TinyTrackService service; + + /** + * set properties without exception thrown + */ + public void safeSet(@NotNull Consumer fun) { + try { + fun.accept(tracking); + } + catch (Throwable e) { + log.warn("safeSet get error", e); + } + } + + /** + * set properties and out without exception thrown + */ + public R safeOut(@NotNull Function fun) { + R out = null; + try { + out = fun.apply(tracking); + tracking.setOut(out); + } + catch (Throwable e) { + log.warn("safeOut get error", e); + } + return out; + } + + @Override + public void close() { + tracking.setElapse(ThreadNow.millis() - tracking.getBegin()); + service.track(tracking, true); + } + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTrackService.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTrackService.java new file mode 100644 index 000000000..881a6fd7d --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTrackService.java @@ -0,0 +1,82 @@ +package pro.fessional.wings.tiny.grow.track; + +import org.jetbrains.annotations.NotNull; +import pro.fessional.mirana.cast.EnumConvertor; +import pro.fessional.mirana.cast.MethodConvertor; + +import java.lang.reflect.Method; +import java.util.concurrent.FutureTask; + +/** + * Data tracking in async, never throws + * + * @author trydofor + * @since 2024-07-24 + */ +public interface TinyTrackService { + + /** + * async executor + */ + FutureTask async(Runnable run); + + /** + * begin a tracking with key and ref + */ + @NotNull + TinyTracking begin(@NotNull String key, @NotNull String ref); + + /** + * post the tracking, fire and forget, never throws + */ + void track(@NotNull TinyTracking tracking, boolean async); + + /** + * async post the tracking, fire and forget, never throws + */ + default void track(@NotNull TinyTracking tracking) { + track(tracking, true); + } + + /** + * raw string key and 'string' ref + */ + @NotNull + default TinyTracking begin(@NotNull String key) { + return begin(key, "string"); + } + + /** + * method signature key and 'method' ref. + * e.g. a.b.c.MyClass#method(String,int) + */ + @NotNull + default TinyTracking begin(@NotNull Method key) { + String str = MethodConvertor.method2Str(key); + return begin(str, "method"); + } + + /** + * enum signature key and 'enum' ref. + * e.g. a.b.c.MyEnum#Name + */ + @NotNull + default TinyTracking begin(@NotNull Enum key) { + String str = EnumConvertor.enum2Str(key); + return begin(str, "enum"); + } + + /** + * sync prepare tracking prop, e.g. env, app + */ + interface Preparer { + void prepare(@NotNull TinyTracking tracking); + } + + /** + * aysnc collect tracking to different impl, e.g. Dao to database + */ + interface Collector { + void collect(@NotNull TinyTracking tracking); + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTracker.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTracker.java new file mode 100644 index 000000000..5d93088e3 --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTracker.java @@ -0,0 +1,59 @@ +package pro.fessional.wings.tiny.grow.track; + +import org.intellij.lang.annotations.Language; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * data tracking by AOP + * + * @author trydofor + * @since 2024-07-24 + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface TinyTracker { + + /** + * track key instead of AOP auto + */ + String key() default ""; + + /** + * track ref instead of AOP auto + */ + String ref() default ""; + + /** + *
+     * The name of this AOP object's method that will mix the `TinyTracking` after return/throw,
+     * its parameters is the `TinyTracking` prepended to the parameters of the AOP method.
+     *
+     * * saveOrder(long, Order) - the AOP method
+     * * saveOrder(TinyTracking, long, Order) - same as AOP method if mix is empty
+     * * saveOrderMix(TinyTracking, long, Order) - if mix is saveOrderMix
+     * 
+ * + * @see TinyTracking + */ + String mix() default ""; + + /** + * omit the property if it is instance of + */ + Class[] omitClass() default {}; + + /** + * omit the property if its name equals + */ + String[] omitEqual() default {}; + + /** + * omit the property if its name match regex + */ + @Language("regexp") + String[] omitRegex() default {}; +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTracking.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTracking.java new file mode 100644 index 000000000..0e5f0dca6 --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/TinyTracking.java @@ -0,0 +1,93 @@ +package pro.fessional.wings.tiny.grow.track; + +import lombok.Data; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * @author trydofor + * @since 2024-07-28 + */ +@Data +public class TinyTracking { + protected final long begin; + protected final String key; + protected final String ref; + + protected String app; + @NotNull + protected Map env = new LinkedHashMap<>(); + @NotNull + protected Object[] ins = new Object[0]; + protected Object out; + protected Throwable err; + protected long elapse; + + protected long userKey; + protected long userRef; + + protected long dataKey; + protected long dataRef; + protected long dataOpt; + + protected String codeKey; + protected String codeRef; + protected String codeOpt; + + protected String wordRef; + + /** + * rule set of Class/String/Pattern + */ + @NotNull + protected final Set omitRule = new HashSet<>(); + + public void setIns(Object... ins) { + this.ins = ins; + } + + public void addEnv(String key, Object value) { + env.put(key, value); + } + + public void addEnv(Map envs) { + env.putAll(envs); + } + + /** + *
+     * support rule,
+     * * Class - object is instance of
+     * * String - name equals
+     * * Pattern - name matches regexp
+     * * Collection - any of above type
+     * * Object[] - any of above type
+     * 
+ */ + public void addOmit(Object omit) { + if (omit == null) { + return; + } + else if (omit instanceof Class clz) { + omitRule.add(clz); + } + else if (omit instanceof String str) { + if (!str.isEmpty()) omitRule.add(str); + } + else if (omit instanceof Pattern ptn) { + if (!ptn.pattern().isEmpty()) omitRule.add(ptn); + } + else if (omit instanceof Collection col) { + for (Object o : col) addOmit(o); + } + else if (omit instanceof Object[] arr) { + for (Object o : arr) addOmit(o); + } + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackAround.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackAround.java new file mode 100644 index 000000000..c1e7bd951 --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackAround.java @@ -0,0 +1,163 @@ +package pro.fessional.wings.tiny.grow.track.impl; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import pro.fessional.mirana.cast.MethodConvertor; +import pro.fessional.mirana.data.Null; +import pro.fessional.mirana.time.ThreadNow; +import pro.fessional.wings.silencer.spring.WingsOrdered; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.tiny.grow.track.TinyTrackService; +import pro.fessional.wings.tiny.grow.track.TinyTracker; +import pro.fessional.wings.tiny.grow.track.TinyTracking; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +/** + * @author trydofor + * @since 2024-07-24 + */ +@Aspect +@Component +@ConditionalWingsEnabled +@Order(WingsOrdered.Lv3Service) +@Slf4j +public class TinyTrackAround { + + @Setter(onMethod_ = { @Autowired }) + protected TinyTrackService tinyTrackService; + + @Around("@annotation(pro.fessional.wings.tiny.grow.track.TinyTracker)") + public Object track(ProceedingJoinPoint joinPoint) throws Throwable { + final Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); + final TinyTracker anno = method.getAnnotation(TinyTracker.class); + + final TinyTracking tracking = tryTrack(method, anno); + if (tracking == null) { + return joinPoint.proceed(); + } + + tracking.setIns(joinPoint.getArgs()); + tracking.addOmit(omitRule(method, anno)); + + try { + Object out = joinPoint.proceed(); + tracking.setOut(out); + return out; + } + catch (Throwable e) { + tracking.setErr(e); + throw e; + } + finally { + try { + mixAsync(tracking, method, anno, joinPoint); + } + catch (Throwable e) { + // noinspection StringConcatenationArgumentToLogCall + log.error("tiny-track fails to mixAsync, method=" + MethodConvertor.method2Str(method), e); + } + } + } + + protected void mixAsync(@NotNull TinyTracking tracking, @NotNull Method method, @NotNull TinyTracker anno, @NotNull JoinPoint joinPoint) { + // biz thread + tracking.setElapse(ThreadNow.millis() - tracking.getBegin()); + + // async thread + tinyTrackService.async(() -> { + final Method mix = findMixer(method, anno); + if (!Null.asNull(mix)) { + try { + Object[] pm = joinPoint.getArgs(); + Object[] pn = new Object[pm.length + 1]; + pn[0] = tracking; + System.arraycopy(pm, 0, pn, 1, pm.length); + mix.invoke(joinPoint.getTarget(), pn); + } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.error("tiny-track fails to mix=" + MethodConvertor.method2Str(mix), e); + } + } + // track + tinyTrackService.track(tracking, false); + }); + } + + @Nullable + protected TinyTracking tryTrack(@NotNull Method method, @NotNull TinyTracker anno) { + try { + final String key = anno.key(); + if (StringUtils.isEmpty(key)) { + return tinyTrackService.begin(method); + } + + String ref = anno.ref(); + if (StringUtils.isEmpty(ref)) { + return tinyTrackService.begin(key); + } + + return tinyTrackService.begin(key, ref); + } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.error("tiny-track fails to beginTracking, method=" + MethodConvertor.method2Str(method), e); + return null; + } + } + + private final ConcurrentHashMap> omitRules = new ConcurrentHashMap<>(); + + protected Set omitRule(@NotNull Method method, @NotNull TinyTracker anno) { + return omitRules.computeIfAbsent(method, k -> { + Set set = new HashSet<>(); + Collections.addAll(set, anno.omitClass()); + Collections.addAll(set, anno.omitEqual()); + for (String ptn : anno.omitRegex()) { + set.add(Pattern.compile(ptn)); + } + return set; + }); + } + + private final ConcurrentHashMap mixMethod = new ConcurrentHashMap<>(); + + @Nullable + protected Method findMixer(@NotNull Method method, @NotNull TinyTracker anno) { + return mixMethod.computeIfAbsent(method, k -> { + try { + String nm = anno.mix(); + if (nm == null || nm.isBlank()) { + nm = method.getName(); + } + Class[] pm = method.getParameterTypes(); + Class[] pn = new Class[pm.length + 1]; + pn[0] = TinyTracking.class; + System.arraycopy(pm, 0, pn, 1, pm.length); + Method md = method.getDeclaringClass().getDeclaredMethod(nm, pn); + md.setAccessible(true); + return md; + } + catch (Exception e) { + return Null.Mtd; + } + }); + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackCollectorDaoImpl.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackCollectorDaoImpl.java new file mode 100644 index 000000000..330a698ae --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackCollectorDaoImpl.java @@ -0,0 +1,76 @@ +package pro.fessional.wings.tiny.grow.track.impl; + +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import pro.fessional.mirana.pain.ThrowableUtil; +import pro.fessional.mirana.time.DateLocaling; +import pro.fessional.wings.faceless.service.lightid.LightIdService; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.slardar.fastjson.FastJsonHelper; +import pro.fessional.wings.slardar.fastjson.filter.ExcludePropertyPreFilter; +import pro.fessional.wings.tiny.grow.database.autogen.tables.daos.WinGrowTrackDao; +import pro.fessional.wings.tiny.grow.database.autogen.tables.pojos.WinGrowTrack; +import pro.fessional.wings.tiny.grow.track.TinyTrackService; +import pro.fessional.wings.tiny.grow.track.TinyTracking; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@Service +@ConditionalWingsEnabled +public class TinyTrackCollectorDaoImpl implements TinyTrackService.Collector { + + @Setter(onMethod_ = { @Autowired }) + protected WinGrowTrackDao winGrowTrackDao; + + @Setter(onMethod_ = { @Autowired }) + protected LightIdService lightIdService; + + @Override + @Transactional + public void collect(@NotNull TinyTracking tracking) { + WinGrowTrack pojo = new WinGrowTrack(); + pojo.setId(lightIdService.getId(winGrowTrackDao.getTable())); + + buildProp(pojo, tracking); + buildExec(pojo, tracking); + + winGrowTrackDao.insert(pojo); + } + + protected void buildProp(@NotNull WinGrowTrack pojo, @NotNull TinyTracking tracking) { + pojo.setCreateDt(DateLocaling.sysLdt(tracking.getBegin())); + + pojo.setTrackKey(tracking.getKey()); + pojo.setTrackRef(tracking.getRef()); + pojo.setTrackApp(tracking.getApp()); + + pojo.setElapseMs(tracking.getElapse()); + + pojo.setUserKey(tracking.getUserKey()); + pojo.setUserRef(tracking.getUserRef()); + + pojo.setDataKey(tracking.getDataKey()); + pojo.setDataRef(tracking.getDataRef()); + pojo.setDataOpt(tracking.getDataOpt()); + + pojo.setCodeKey(tracking.getCodeKey()); + pojo.setCodeRef(tracking.getCodeRef()); + pojo.setCodeOpt(tracking.getCodeOpt()); + + pojo.setWordRef(tracking.getWordRef()); + } + + protected void buildExec(@NotNull WinGrowTrack pojo, @NotNull TinyTracking tracking) { + ExcludePropertyPreFilter filter = new ExcludePropertyPreFilter(tracking.getOmitRule()); + + pojo.setTrackEnv(FastJsonHelper.string(tracking.getEnv(), filter)); + pojo.setTrackIns(FastJsonHelper.string(tracking.getIns(), filter)); + pojo.setTrackOut(FastJsonHelper.string(tracking.getOut(), filter)); + pojo.setTrackErr(ThrowableUtil.toString(tracking.getErr())); + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackPreparerPropImpl.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackPreparerPropImpl.java new file mode 100644 index 000000000..33aecc18f --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackPreparerPropImpl.java @@ -0,0 +1,34 @@ +package pro.fessional.wings.tiny.grow.track.impl; + +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import pro.fessional.wings.silencer.modulate.RuntimeMode; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.silencer.spring.help.ApplicationContextHelper; +import pro.fessional.wings.tiny.grow.spring.prop.TinyTrackOmitProp; +import pro.fessional.wings.tiny.grow.track.TinyTrackService; +import pro.fessional.wings.tiny.grow.track.TinyTracking; + +/** + * @author trydofor + * @since 2024-08-01 + */ +@Service +@ConditionalWingsEnabled +public class TinyTrackPreparerPropImpl implements TinyTrackService.Preparer { + + @Setter(onMethod_ = { @Autowired }) + protected TinyTrackOmitProp tinyTrackOmitProp; + + @Override + public void prepare(@NotNull TinyTracking tracking) { + tracking.setApp(ApplicationContextHelper.getApplicationName()); + tracking.addEnv("run", RuntimeMode.getRunMode().name()); + + tracking.addOmit(tinyTrackOmitProp.getClazz().values()); + tracking.addOmit(tinyTrackOmitProp.getEqual().values()); + tracking.addOmit(tinyTrackOmitProp.getRegex().values()); + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackPreparerTermImpl.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackPreparerTermImpl.java new file mode 100644 index 000000000..7cd207e23 --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackPreparerTermImpl.java @@ -0,0 +1,43 @@ +package pro.fessional.wings.tiny.grow.track.impl; + +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.stereotype.Service; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.slardar.context.TerminalContext; +import pro.fessional.wings.tiny.grow.track.TinyTrackService; +import pro.fessional.wings.tiny.grow.track.TinyTracking; + +import static pro.fessional.wings.slardar.context.TerminalAttribute.TerminalAddr; +import static pro.fessional.wings.slardar.context.TerminalAttribute.TerminalAgent; + +/** + * @author trydofor + * @since 2024-08-01 + */ +@Service +@ConditionalWingsEnabled +@ConditionalOnClass(TerminalContext.class) +public class TinyTrackPreparerTermImpl implements TinyTrackService.Preparer { + + @Override + public void prepare(@NotNull TinyTracking tracking) { + final TerminalContext.Context ctx = TerminalContext.get(false); + if (ctx.isNull()) return; + + tracking.addEnv("locale", ctx.getLocale().toLanguageTag()); + tracking.addEnv("zoneid", ctx.getZoneId().getId()); + long id = ctx.getUserId(); + tracking.addEnv("userId", id); + + if (id > 0) { + tracking.addEnv("authType", ctx.getAuthType().name()); + tracking.addEnv("username", ctx.getUsername()); + } + + String adr = ctx.getTerminal(TerminalAddr); + if (adr != null) tracking.addEnv("addr", adr); + String agt = ctx.getTerminal(TerminalAgent); + if (agt != null) tracking.addEnv("agent", agt); + } +} diff --git a/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackServiceImpl.java b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackServiceImpl.java new file mode 100644 index 000000000..b72775843 --- /dev/null +++ b/radiant/tiny-grow/src/main/java/pro/fessional/wings/tiny/grow/track/impl/TinyTrackServiceImpl.java @@ -0,0 +1,107 @@ +package pro.fessional.wings.tiny.grow.track.impl; + +import com.alibaba.ttl.threadpool.TtlExecutors; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import pro.fessional.mirana.time.ThreadNow; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.tiny.grow.track.TinyTrackService; +import pro.fessional.wings.tiny.grow.track.TinyTracking; + +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; + +import static org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME; + +/** + * @author trydofor + * @since 2024-07-26 + */ +@Service +@ConditionalWingsEnabled +@Slf4j +public class TinyTrackServiceImpl implements TinyTrackService, InitializingBean, DisposableBean { + + @Setter(onMethod_ = { @Autowired(required = false), @Qualifier(DEFAULT_TASK_EXECUTOR_BEAN_NAME) }) + private Executor executor; + private boolean innerExecutor = false; + + @Setter(onMethod_ = { @Autowired }) + protected List trackCollector; + + @Setter(onMethod_ = { @Autowired }) + protected List trackPreparer; + + @Override + public FutureTask async(Runnable run) { + FutureTask future = new FutureTask<>(run, null); + executor.execute(future); + return future; + } + + @Override + @NotNull + public TinyTracking begin(@NotNull String key, @NotNull String ref) { + final TinyTracking tracking = new TinyTracking(ThreadNow.millis(), key, ref); + + for (Preparer pr : trackPreparer) { + try { + pr.prepare(tracking); + } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.error("tiny-track skip failed preparer=" + pr.getClass(), e); + } + } + + return tracking; + } + + @Override + public void track(@NotNull TinyTracking tracking, boolean async) { + if (async) { + executor.execute(() -> track(tracking)); + } + else { + track(tracking); + } + } + + @Override + public void track(@NotNull TinyTracking tracking) { + for (Collector cl : trackCollector) { + try { + cl.collect(tracking); + } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.error("tiny-track skip failed collector=" + cl.getClass(), e); + } + } + } + + @Override + public void afterPropertiesSet() { + if (executor == null) { + log.warn("should reuse autowired thread pool"); + executor = TtlExecutors.getTtlExecutorService(Executors.newWorkStealingPool(2)); + innerExecutor = true; + } + } + + @Override + public void destroy() { + if (innerExecutor && executor instanceof ExecutorService es) { + es.shutdown(); + } + } +} diff --git a/radiant/tiny-grow/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/radiant/tiny-grow/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 000000000..a93730dd6 --- /dev/null +++ b/radiant/tiny-grow/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,23 @@ +{ + "groups": [ + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.database.autogen.tables.daos"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.spring.conf"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.track.impl"} + ], + "properties": [ + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.database.autogen.tables.daos.WinGrowTrackDao", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.spring.bean.TinyTrackConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.spring.bean.TinyTrackConfiguration$DaoServScan", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.spring.conf.TinyGrowAutoConfiguration", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.track.impl.TinyTrackAround", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.track.impl.TinyTrackCollectorDaoImpl", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.track.impl.TinyTrackPreparerPropImpl", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.track.impl.TinyTrackPreparerTermImpl", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.grow.track.impl.TinyTrackServiceImpl", "type": "java.lang.Boolean"} + ], + "hints": [] +} \ No newline at end of file diff --git a/radiant/tiny-grow/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/radiant/tiny-grow/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..3f43c3cfa --- /dev/null +++ b/radiant/tiny-grow/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +pro.fessional.wings.tiny.grow.spring.conf.TinyGrowAutoConfiguration diff --git a/radiant/tiny-grow/src/main/resources/wings-conf/wings-flywave-fit-79.properties b/radiant/tiny-grow/src/main/resources/wings-conf/wings-flywave-fit-79.properties new file mode 100644 index 000000000..3511220a0 --- /dev/null +++ b/radiant/tiny-grow/src/main/resources/wings-conf/wings-flywave-fit-79.properties @@ -0,0 +1,3 @@ +wings.faceless.flywave.fit.tiny-grow.path=classpath*:/wings-flywave/master/08-grow/*.sql +wings.faceless.flywave.fit.tiny-grow.revi=2020_1028_01L +wings.faceless.flywave.fit.tiny-grow.lost=EXEC diff --git a/radiant/tiny-grow/src/main/resources/wings-conf/wings-tinytrack-omit-79.properties b/radiant/tiny-grow/src/main/resources/wings-conf/wings-tinytrack-omit-79.properties new file mode 100644 index 000000000..acd472ae2 --- /dev/null +++ b/radiant/tiny-grow/src/main/resources/wings-conf/wings-tinytrack-omit-79.properties @@ -0,0 +1,11 @@ +## omit the property if it is instance of. empty means disable +wings.tiny.grow.track.omit.clazz[jakarta.ServletRequest]=jakarta.servlet.ServletRequest +wings.tiny.grow.track.omit.clazz[jakarta.ServletResponse]=jakarta.servlet.ServletResponse +wings.tiny.grow.track.omit.clazz[io.InputStream]=java.io.InputStream +wings.tiny.grow.track.omit.clazz[io.OutputStream]=java.io.OutputStream + +## omit the property if its name equals. empty means disable +#wings.tiny.grow.track.omit.equal[password]=password + +## omit the property if its name match regex. empty means disable +#wings.tiny.grow.track.omit.regex[pass]=(?i)pass diff --git a/radiant/tiny-grow/src/main/resources/wings-flywave/master/08-grow/2020-10-28u01-tiny_grow.sql b/radiant/tiny-grow/src/main/resources/wings-flywave/master/08-grow/2020-10-28u01-tiny_grow.sql new file mode 100644 index 000000000..cb4cf9c12 --- /dev/null +++ b/radiant/tiny-grow/src/main/resources/wings-flywave/master/08-grow/2020-10-28u01-tiny_grow.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS `win_grow_track`; -- 145/Grow Tracking; + +-- CALL FLYWAVE('2020-10-28u01-tiny_grow.sql'); \ No newline at end of file diff --git a/radiant/tiny-grow/src/main/resources/wings-flywave/master/08-grow/2020-10-28v01-tiny_grow.sql b/radiant/tiny-grow/src/main/resources/wings-flywave/master/08-grow/2020-10-28v01-tiny_grow.sql new file mode 100644 index 000000000..47a2f1aa6 --- /dev/null +++ b/radiant/tiny-grow/src/main/resources/wings-flywave/master/08-grow/2020-10-28v01-tiny_grow.sql @@ -0,0 +1,30 @@ +-- Error Code: 1071. Specified key was too long; max key length is 3072 bytes +CREATE TABLE `win_grow_track` ( + `id` BIGINT NOT NULL COMMENT 'primary key', + `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime', + `track_key` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'track key, method/enum', + `track_ref` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'method/enum/url/str', + `track_app` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'app name', + `track_env` TEXT NULL COMMENT 'env, terminal context, json', + `track_ins` TEXT NULL COMMENT 'inputs param, json array', + `track_out` TEXT NULL COMMENT 'output return, json', + `track_err` TEXT NULL COMMENT 'error exception, json', + `elapse_ms` BIGINT NOT NULL DEFAULT '0' COMMENT 'elapse mills', + `user_key` BIGINT NOT NULL DEFAULT '0' COMMENT 'key user, data owner', + `user_ref` BIGINT NOT NULL DEFAULT '0' COMMENT 'ref user, data operator', + `data_key` BIGINT NOT NULL DEFAULT '0' COMMENT 'key data, order id', + `data_ref` BIGINT NOT NULL DEFAULT '0' COMMENT 'ref data, trade id', + `data_opt` BIGINT NOT NULL DEFAULT '0' COMMENT 'optional data, payment id', + `code_key` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'key code, order num', + `code_ref` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'ref code, trade seq', + `code_opt` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'optional code, message id', + `word_ref` VARCHAR(800) NOT NULL DEFAULT '' COMMENT 'ref word, mark', + PRIMARY KEY (`id`), + INDEX ix_track_key (`track_key`), + INDEX ix_user_key (`user_key`), + INDEX ix_data_key (`data_key`), + INDEX ix_code_key (`code_key`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='145/Grow Tracking'; + +-- CALL FLYWAVE('2020-10-28v01-tiny_grow.sql'); \ No newline at end of file diff --git a/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/TestTinyGrowApplication.java b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/TestTinyGrowApplication.java new file mode 100644 index 000000000..7f957ac06 --- /dev/null +++ b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/TestTinyGrowApplication.java @@ -0,0 +1,19 @@ +package pro.fessional.wings.tiny.app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import pro.fessional.wings.testing.silencer.TestingPropertyHelper; + +/** + * @author trydofor + * @since 2019-07-20 + */ +@SpringBootApplication +public class TestTinyGrowApplication { + + public static void main(String[] args) { + TestingPropertyHelper.autoSetWingsRootDir(); + SpringApplication.run(TestTinyGrowApplication.class, args); + } + +} diff --git a/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/controller/TestTrackController.java b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/controller/TestTrackController.java new file mode 100644 index 000000000..1def00d5c --- /dev/null +++ b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/controller/TestTrackController.java @@ -0,0 +1,27 @@ +package pro.fessional.wings.tiny.app.controller; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import pro.fessional.wings.tiny.app.service.TestTrackData; +import pro.fessional.wings.tiny.app.service.impl.TestTrackCollectorImpl; +import pro.fessional.wings.tiny.grow.track.TinyTracker; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@Slf4j +@RestController +public class TestTrackController { + + @TinyTracker(omitClass = HttpServletRequest.class) + @RequestMapping("/test/track.json") + public TestTrackData track(@RequestParam("id") long id, @RequestParam("str") String str, HttpServletRequest ignore) { + TestTrackCollectorImpl.CodeKeys.putIfAbsent(str, Boolean.TRUE); + log.info("track31 code-key={}", str); + return new TestTrackData(id, str); + } +} diff --git a/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrack1Service.java b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrack1Service.java new file mode 100644 index 000000000..4b3558293 --- /dev/null +++ b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrack1Service.java @@ -0,0 +1,78 @@ +package pro.fessional.wings.tiny.app.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import pro.fessional.mirana.pain.ThrowableUtil; +import pro.fessional.wings.tiny.app.service.impl.TestTrackCollectorImpl; +import pro.fessional.wings.tiny.grow.track.TinyTrackHelper; +import pro.fessional.wings.tiny.grow.track.TinyTracker; +import pro.fessional.wings.tiny.grow.track.TinyTracking; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@Service +@Slf4j +public class TestTrack1Service { + @TinyTracker + public TestTrackData track(long id, String str) { + TestTrackCollectorImpl.CodeKeys.putIfAbsent(str, Boolean.TRUE); + log.info("track11 code-key={}", str); + return new TestTrackData(id, str); + } + + protected void track(TinyTracking trk, long id, String str) { + trk.setDataKey(id); + trk.setCodeKey(str); + } + + @Transactional + @TinyTracker + public TestTrackData trackTx(long id, String str) { + TestTrackCollectorImpl.CodeKeys.putIfAbsent(str, Boolean.TRUE); + log.info("track12 code-key={}", str); + return new TestTrackData(id, str); + } + + public TestTrackData track13(long id, String str) { + final String key = "pro.fessional.wings.tiny.app.service.TestTrack1Service#track13(long,String)"; + return TinyTrackHelper.track(key, trk -> { + TestTrackCollectorImpl.CodeKeys.putIfAbsent(str, Boolean.TRUE); + log.info("track13 code-key={}", str); + + trk.setIns(id, str); + trk.setDataKey(id); + trk.setCodeKey(str); + + return new TestTrackData(id, str); + }); + } + + public TestTrackData track14(long id, String str) { + final String key = "pro.fessional.wings.tiny.app.service.TestTrack1Service#track14(long,String)"; + final var trk = TinyTrackHelper.track(key); + try { + final TestTrackData out = new TestTrackData(id, str); + + TestTrackCollectorImpl.CodeKeys.putIfAbsent(str, Boolean.TRUE); + log.info("track14 code-key={}", str); + + trk.setIns(id, str); + trk.setDataKey(id); + trk.setCodeKey(str); + + trk.setOut(out); + + return out; + } + catch (Throwable e) { + trk.setErr(e); + throw ThrowableUtil.runtime(e); + } + finally { + trk.close(); + } + } +} diff --git a/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrack2Service.java b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrack2Service.java new file mode 100644 index 000000000..3649c1bf7 --- /dev/null +++ b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrack2Service.java @@ -0,0 +1,13 @@ +package pro.fessional.wings.tiny.app.service; + +/** + * @author trydofor + * @since 2024-07-27 + */ +public interface TestTrack2Service { + + TestTrackData track(long id, String str); + + TestTrackData trackTx(long id, String str); + +} diff --git a/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrackData.java b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrackData.java new file mode 100644 index 000000000..0a7dede4a --- /dev/null +++ b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/TestTrackData.java @@ -0,0 +1,25 @@ +package pro.fessional.wings.tiny.app.service; + +import lombok.Data; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@Data +public class TestTrackData { + + private String password = "exclude password equal"; + private String secret = "exclude secret regex"; + private InputStream download = new ByteArrayInputStream("123".getBytes()); + private long id; + private String str; + + public TestTrackData(long id, String str) { + this.id = id; + this.str = str; + } +} diff --git a/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/impl/TestTrack2ServiceImpl.java b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/impl/TestTrack2ServiceImpl.java new file mode 100644 index 000000000..cdc768ca2 --- /dev/null +++ b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/impl/TestTrack2ServiceImpl.java @@ -0,0 +1,40 @@ +package pro.fessional.wings.tiny.app.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import pro.fessional.wings.tiny.app.service.TestTrack2Service; +import pro.fessional.wings.tiny.app.service.TestTrackData; +import pro.fessional.wings.tiny.grow.track.TinyTracker; +import pro.fessional.wings.tiny.grow.track.TinyTracking; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@Service +@Slf4j +public class TestTrack2ServiceImpl implements TestTrack2Service { + + @TinyTracker + @Override + public TestTrackData track(long id, String str) { + TestTrackCollectorImpl.CodeKeys.putIfAbsent(str, Boolean.TRUE); + log.info("track21 code-key={}", str); + return new TestTrackData(id, str); + } + + private void track(TinyTracking trk, long id, String str) { + trk.setDataKey(id); + trk.setCodeKey(str); + } + + @Override + @Transactional + @TinyTracker + public TestTrackData trackTx(long id, String str) { + TestTrackCollectorImpl.CodeKeys.putIfAbsent(str, Boolean.TRUE); + log.info("track22 code-key={}", str); + return new TestTrackData(id, str); + } +} diff --git a/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/impl/TestTrackCollectorImpl.java b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/impl/TestTrackCollectorImpl.java new file mode 100644 index 000000000..270b35df5 --- /dev/null +++ b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/app/service/impl/TestTrackCollectorImpl.java @@ -0,0 +1,27 @@ +package pro.fessional.wings.tiny.app.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Service; +import pro.fessional.wings.tiny.grow.track.TinyTrackService; +import pro.fessional.wings.tiny.grow.track.TinyTracking; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@Service +@Slf4j +public class TestTrackCollectorImpl implements TinyTrackService.Collector { + + public static final ConcurrentHashMap CodeKeys = new ConcurrentHashMap<>(); + + @Override + public void collect(@NotNull TinyTracking tracking) { + String ck = (String) tracking.getIns()[1]; + log.info("done code-key={}, tracking={}", ck, tracking); + CodeKeys.remove(ck); + } +} diff --git a/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/grow/track/TinyTrackServiceTest.java b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/grow/track/TinyTrackServiceTest.java new file mode 100644 index 000000000..6f399f76d --- /dev/null +++ b/radiant/tiny-grow/src/test/java/pro/fessional/wings/tiny/grow/track/TinyTrackServiceTest.java @@ -0,0 +1,123 @@ +package pro.fessional.wings.tiny.grow.track; + +import io.qameta.allure.TmsLink; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import pro.fessional.mirana.id.Ulid; +import pro.fessional.mirana.time.Sleep; +import pro.fessional.mirana.time.ThreadNow; +import pro.fessional.wings.tiny.app.service.TestTrack1Service; +import pro.fessional.wings.tiny.app.service.TestTrack2Service; +import pro.fessional.wings.tiny.app.service.impl.TestTrackCollectorImpl; +import pro.fessional.wings.tiny.grow.database.autogen.tables.daos.WinGrowTrackDao; +import pro.fessional.wings.tiny.grow.database.autogen.tables.pojos.WinGrowTrack; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author trydofor + * @since 2024-07-27 + */ +@SpringBootTest(properties = { + "wings.tiny.grow.track.omit.clazz[jakarta.ServletRequest]=", // disable, use annotation + "wings.tiny.grow.track.omit.equal[password]=password", + "wings.tiny.grow.track.omit.regex[secret]=(?i).*secret", +}) +@Slf4j +@AutoConfigureMockMvc +class TinyTrackServiceTest { + + @Setter(onMethod_ = { @Autowired }) + protected TestTrack1Service testTrack1Service; + + @Setter(onMethod_ = { @Autowired }) + protected TestTrack2Service testTrack2Service; + + @Setter(onMethod_ = { @Autowired }) + protected WinGrowTrackDao winGrowTrackDao; + + @Setter(onMethod_ = { @Autowired }) + private MockMvc mvc; + + private final long now = (ThreadNow.millis() / 100) * 100; + + @Test + @TmsLink("C15018") + void service() { + String key11 = Ulid.next(); + String key12 = Ulid.next(); + testTrack1Service.track(now + 11, key11); + testTrack1Service.trackTx(now + 12, key12); + waitCodeKey(key11, key12); + + checkPojo(11, key11, false, "pro.fessional.wings.tiny.app.service.TestTrack1Service#track(long,String)", "Local", now + 11, key11); + checkPojo(12, key12, false, "pro.fessional.wings.tiny.app.service.TestTrack1Service#trackTx(long,String)", "Local", 0, ""); + + String key13 = Ulid.next(); + String key14 = Ulid.next(); + testTrack1Service.track13(now + 13, key13); + testTrack1Service.track14(now + 14, key14); + waitCodeKey(key13, key14); + + checkPojo(13, key13, false, "pro.fessional.wings.tiny.app.service.TestTrack1Service#track13(long,String)", "Local", now + 13, key13); + checkPojo(14, key14, false, "pro.fessional.wings.tiny.app.service.TestTrack1Service#track14(long,String)", "Local", now + 14, key14); + + String key21 = Ulid.next(); + String key22 = Ulid.next(); + testTrack2Service.track(now + 21, key21); + testTrack2Service.trackTx(now + 22, key22); + waitCodeKey(key21, key22); + + checkPojo(21, key21, false, "pro.fessional.wings.tiny.app.service.impl.TestTrack2ServiceImpl#track(long,String)", "Local", now + 21, key21); + checkPojo(22, key22, false, "pro.fessional.wings.tiny.app.service.impl.TestTrack2ServiceImpl#trackTx(long,String)", "Local", 0, ""); + } + + @Test + @TmsLink("C15019") + void mvc() throws Exception { + String key31 = Ulid.next(); + final MvcResult mvcResult = mvc.perform(get("/test/track.json") + .contentType(MediaType.APPLICATION_JSON) + .param("id", String.valueOf(now + 31)) + .param("str", key31) + ) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + final String body0 = mvcResult.getResponse().getContentAsString(); + waitCodeKey(key31); + checkPojo(31, key31, true, "pro.fessional.wings.tiny.app.controller.TestTrackController#track(long,String,HttpServletRequest)", "zoneid", 0, ""); + } + + private void waitCodeKey(String... cks) { + for (String ck : cks) { + while (TestTrackCollectorImpl.CodeKeys.containsKey(ck)) { + log.info("wait key={}", ck); + Sleep.ignoreInterrupt(1_000); + } + } + } + + private void checkPojo(int id, String str, boolean web, String key, String env, long dkey, String ckey) { + long iid = now + id; + String ins = (web ? "[%s,\"%s\",{}]" : "[%s,\"%s\"]").formatted(iid, str); + String out = "{\"id\":%s,\"str\":\"%s\"}".formatted(iid, str); + WinGrowTrack pojo = winGrowTrackDao.fetchOne(t -> t.TrackIns.eq(ins)); + Assertions.assertNotNull(pojo, ins); + Assertions.assertEquals(key, pojo.getTrackKey()); + Assertions.assertTrue(pojo.getTrackEnv().contains(env)); + Assertions.assertEquals(dkey, pojo.getDataKey()); + Assertions.assertEquals(ckey, pojo.getCodeKey()); + Assertions.assertEquals(out, pojo.getTrackOut()); + } +} \ No newline at end of file diff --git a/radiant/tiny-grow/src/test/resources/application.properties b/radiant/tiny-grow/src/test/resources/application.properties new file mode 100644 index 000000000..cc9dc3295 --- /dev/null +++ b/radiant/tiny-grow/src/test/resources/application.properties @@ -0,0 +1,6 @@ +server.port=8086 +spring.application.name=tiny-grow + +wings.enabled.faceless.flywave=true +wings.faceless.flywave.auto-init=true +wings.faceless.flywave.fit.tiny-grow.revi=2019_0512_01L, 2019_0520_01L, 2020_1028_01L diff --git a/radiant/tiny-grow/src/test/resources/wings-conf/spring-datasource-99.properties b/radiant/tiny-grow/src/test/resources/wings-conf/spring-datasource-99.properties new file mode 100644 index 000000000..5d8d9afb4 --- /dev/null +++ b/radiant/tiny-grow/src/test/resources/wings-conf/spring-datasource-99.properties @@ -0,0 +1 @@ +testing.dbname=wings_tiny diff --git a/radiant/tiny-grow/src/test/resources/wings-conf/wings-tinytask-test.properties b/radiant/tiny-grow/src/test/resources/wings-conf/wings-tinytask-test.properties new file mode 100644 index 000000000..3b19786fe --- /dev/null +++ b/radiant/tiny-grow/src/test/resources/wings-conf/wings-tinytask-test.properties @@ -0,0 +1,16 @@ +wings.tiny.task.define[pro.fessional.wings.tiny.app.service.TestServiceAuto#strVoid].timing-rate=30 + +wings.tiny.task.define[voidVoidAuto].enabled=false +wings.tiny.task.define[voidVoidAuto].timing-rate=30 + +### +wings.tiny.task.define[pro.fessional.wings.tiny.app.service.TestServiceManual#strVoid].enabled=true +wings.tiny.task.define[pro.fessional.wings.tiny.app.service.TestServiceManual#strVoid].autorun=false +wings.tiny.task.define[pro.fessional.wings.tiny.app.service.TestServiceManual#strVoid].timing-idle=30 + +wings.tiny.task.define[voidStrManual].enabled=true +wings.tiny.task.define[voidStrManual].tasker-name=voidStrManual +wings.tiny.task.define[voidStrManual].timing-rate=30 + +wings.tiny.task.define[voidVoidManual].enabled=true +wings.tiny.task.define[voidVoidManual].timing-rate=30 diff --git a/radiant/tiny-mail/pom.xml b/radiant/tiny-mail/pom.xml index 098c2991c..9681f8dc5 100644 --- a/radiant/tiny-mail/pom.xml +++ b/radiant/tiny-mail/pom.xml @@ -16,6 +16,10 @@ Tiny Mail component + + pro.fessional.wings + faceless-jooq + pro.fessional.wings slardar @@ -29,11 +33,7 @@ slardar-webmvc true - - pro.fessional.wings - faceless-codegen - true - + pro.fessional.wings faceless-flywave diff --git a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/WinMailSenderTable.java b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/WinMailSenderTable.java index 8b9948194..ce9ab73d0 100644 --- a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/WinMailSenderTable.java +++ b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/WinMailSenderTable.java @@ -36,8 +36,8 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", - "schema version:2020102701" + "jOOQ version:3.18.9", + "schema version:2020102801" }, comments = "This class is generated by jOOQ" ) @@ -108,17 +108,17 @@ public Class getRecordType() { /** * The column win_mail_sender.mail_to. */ - public final TableField MailTo = createField(DSL.name("mail_to"), SQLDataType.VARCHAR(500).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField MailTo = createField(DSL.name("mail_to"), SQLDataType.VARCHAR(900).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); /** * The column win_mail_sender.mail_cc. */ - public final TableField MailCc = createField(DSL.name("mail_cc"), SQLDataType.VARCHAR(500).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField MailCc = createField(DSL.name("mail_cc"), SQLDataType.VARCHAR(900).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); /** * The column win_mail_sender.mail_bcc. */ - public final TableField MailBcc = createField(DSL.name("mail_bcc"), SQLDataType.VARCHAR(500).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField MailBcc = createField(DSL.name("mail_bcc"), SQLDataType.VARCHAR(900).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); /** * The column win_mail_sender.mail_reply. @@ -143,18 +143,28 @@ public Class getRecordType() { /** * The column win_mail_sender.mail_file. */ - public final TableField MailFile = createField(DSL.name("mail_file"), SQLDataType.VARCHAR(9000).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField MailFile = createField(DSL.name("mail_file"), SQLDataType.CLOB, this, ""); /** * The column win_mail_sender.mail_mark. */ - public final TableField MailMark = createField(DSL.name("mail_mark"), SQLDataType.VARCHAR(200).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField MailMark = createField(DSL.name("mail_mark"), SQLDataType.VARCHAR(900).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); /** * The column win_mail_sender.mail_date. */ public final TableField MailDate = createField(DSL.name("mail_date"), SQLDataType.LOCALDATETIME(3).nullable(false).defaultValue(DSL.inline("1000-01-01 00:00:00.000", SQLDataType.LOCALDATETIME)), this, ""); + /** + * The column win_mail_sender.lazy_bean. + */ + public final TableField LazyBean = createField(DSL.name("lazy_bean"), SQLDataType.VARCHAR(300).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + + /** + * The column win_mail_sender.lazy_para. + */ + public final TableField LazyPara = createField(DSL.name("lazy_para"), SQLDataType.CLOB, this, ""); + /** * The column win_mail_sender.last_send. */ @@ -311,7 +321,6 @@ public WinMailSenderTable rename(Table name) { public String getSeqName() { return "win_mail_sender"; } - /** * alias asW3 @@ -321,7 +330,6 @@ public String getSeqName() { public WinMailSenderTable getAliasTable() { return asW3; } - /** * The colDel delete_dt condition diff --git a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/daos/WinMailSenderDao.java b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/daos/WinMailSenderDao.java index fc506951c..ff6238682 100644 --- a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/daos/WinMailSenderDao.java +++ b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/daos/WinMailSenderDao.java @@ -26,7 +26,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -707,6 +707,72 @@ public List fetchByMailDateLive(Collectionlazy_bean BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfLazyBean(String lowerInclusive, String upperInclusive) { + return fetchRange(WinMailSenderTable.WinMailSender.LazyBean, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfLazyBeanLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinMailSenderTable.WinMailSender.LazyBean, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have lazy_bean IN (values) + */ + public List fetchByLazyBean(String... values) { + return fetch(WinMailSenderTable.WinMailSender.LazyBean, values); + } + + public List fetchByLazyBean(Collection values) { + return fetch(WinMailSenderTable.WinMailSender.LazyBean, values); + } + + + public List fetchByLazyBeanLive(String... values) { + return fetchLive(WinMailSenderTable.WinMailSender.LazyBean, values); + } + + public List fetchByLazyBeanLive(Collection values) { + return fetchLive(WinMailSenderTable.WinMailSender.LazyBean, values); + } + + /** + * Fetch records that have lazy_para BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfLazyPara(String lowerInclusive, String upperInclusive) { + return fetchRange(WinMailSenderTable.WinMailSender.LazyPara, lowerInclusive, upperInclusive); + } + + + public List fetchRangeOfLazyParaLive(String lowerInclusive, String upperInclusive) { + return fetchRangeLive(WinMailSenderTable.WinMailSender.LazyPara, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have lazy_para IN (values) + */ + public List fetchByLazyPara(String... values) { + return fetch(WinMailSenderTable.WinMailSender.LazyPara, values); + } + + public List fetchByLazyPara(Collection values) { + return fetch(WinMailSenderTable.WinMailSender.LazyPara, values); + } + + + public List fetchByLazyParaLive(String... values) { + return fetchLive(WinMailSenderTable.WinMailSender.LazyPara, values); + } + + public List fetchByLazyParaLive(Collection values) { + return fetchLive(WinMailSenderTable.WinMailSender.LazyPara, values); + } + /** * Fetch records that have last_send BETWEEN lowerInclusive AND * upperInclusive diff --git a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/interfaces/IWinMailSender.java b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/interfaces/IWinMailSender.java index 0a626881a..3dcdad88a 100644 --- a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/interfaces/IWinMailSender.java +++ b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/interfaces/IWinMailSender.java @@ -17,7 +17,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -215,6 +215,26 @@ public interface IWinMailSender extends JournalAware, Serializable { */ public LocalDateTime getMailDate(); + /** + * Setter for win_mail_sender.lazy_bean. + */ + public void setLazyBean(String value); + + /** + * Getter for win_mail_sender.lazy_bean. + */ + public String getLazyBean(); + + /** + * Setter for win_mail_sender.lazy_para. + */ + public void setLazyPara(String value); + + /** + * Getter for win_mail_sender.lazy_para. + */ + public String getLazyPara(); + /** * Setter for win_mail_sender.last_send. */ diff --git a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/pojos/WinMailSender.java b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/pojos/WinMailSender.java index 42dfd2326..4ec032ec9 100644 --- a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/pojos/WinMailSender.java +++ b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/pojos/WinMailSender.java @@ -49,6 +49,8 @@ public class WinMailSender implements IWinMailSender { private String mailFile; private String mailMark; private LocalDateTime mailDate; + private String lazyBean; + private String lazyPara; private LocalDateTime lastSend; private String lastFail; private LocalDateTime lastDone; @@ -86,6 +88,8 @@ public WinMailSender(IWinMailSender value) { this.mailFile = value.getMailFile(); this.mailMark = value.getMailMark(); this.mailDate = value.getMailDate(); + this.lazyBean = value.getLazyBean(); + this.lazyPara = value.getLazyPara(); this.lastSend = value.getLastSend(); this.lastFail = value.getLastFail(); this.lastDone = value.getLastDone(); @@ -122,6 +126,8 @@ public WinMailSender( String mailFile, String mailMark, LocalDateTime mailDate, + String lazyBean, + String lazyPara, LocalDateTime lastSend, String lastFail, LocalDateTime lastDone, @@ -156,6 +162,8 @@ public WinMailSender( this.mailFile = mailFile; this.mailMark = mailMark; this.mailDate = mailDate; + this.lazyBean = lazyBean; + this.lazyPara = lazyPara; this.lastSend = lastSend; this.lastFail = lastFail; this.lastDone = lastDone; @@ -1692,6 +1700,166 @@ public void setMailDateIf(UnaryOperator mailDate) { } + /** + * Getter for win_mail_sender.lazy_bean. + */ + @Override + public String getLazyBean() { + return this.lazyBean; + } + + /** + * Setter for win_mail_sender.lazy_bean. + */ + @Override + public void setLazyBean(String lazyBean) { + this.lazyBean = lazyBean; + } + + @Transient + public void setLazyBeanIf(String lazyBean, boolean bool) { + if (bool) { + this.lazyBean = lazyBean; + } + } + + @Transient + public void setLazyBeanIf(Supplier lazyBean, boolean bool) { + if (bool) { + this.lazyBean = lazyBean.get(); + } + } + + @Transient + public void setLazyBeanIf(String lazyBean, Predicate bool) { + if (bool.test(lazyBean)) { + this.lazyBean = lazyBean; + } + } + + @Transient + public void setLazyBeanIf(String lazyBean, Predicate bool, Supplier... lazyBeans) { + if (bool.test(lazyBean)) { + this.lazyBean = lazyBean; + return; + } + for (Supplier supplier : lazyBeans) { + lazyBean = supplier.get(); + if (bool.test(lazyBean)) { + this.lazyBean = lazyBean; + return; + } + } + } + + @Transient + public void setLazyBeanIfNot(String lazyBean, Predicate bool) { + if (!bool.test(lazyBean)) { + this.lazyBean = lazyBean; + } + } + + @Transient + public void setLazyBeanIfNot(String lazyBean, Predicate bool, Supplier... lazyBeans) { + if (!bool.test(lazyBean)) { + this.lazyBean = lazyBean; + return; + } + for (Supplier supplier : lazyBeans) { + lazyBean = supplier.get(); + if (!bool.test(lazyBean)) { + this.lazyBean = lazyBean; + return; + } + } + } + + @Transient + public void setLazyBeanIf(UnaryOperator lazyBean) { + this.lazyBean = lazyBean.apply(this.lazyBean); + } + + + /** + * Getter for win_mail_sender.lazy_para. + */ + @Override + public String getLazyPara() { + return this.lazyPara; + } + + /** + * Setter for win_mail_sender.lazy_para. + */ + @Override + public void setLazyPara(String lazyPara) { + this.lazyPara = lazyPara; + } + + @Transient + public void setLazyParaIf(String lazyPara, boolean bool) { + if (bool) { + this.lazyPara = lazyPara; + } + } + + @Transient + public void setLazyParaIf(Supplier lazyPara, boolean bool) { + if (bool) { + this.lazyPara = lazyPara.get(); + } + } + + @Transient + public void setLazyParaIf(String lazyPara, Predicate bool) { + if (bool.test(lazyPara)) { + this.lazyPara = lazyPara; + } + } + + @Transient + public void setLazyParaIf(String lazyPara, Predicate bool, Supplier... lazyParas) { + if (bool.test(lazyPara)) { + this.lazyPara = lazyPara; + return; + } + for (Supplier supplier : lazyParas) { + lazyPara = supplier.get(); + if (bool.test(lazyPara)) { + this.lazyPara = lazyPara; + return; + } + } + } + + @Transient + public void setLazyParaIfNot(String lazyPara, Predicate bool) { + if (!bool.test(lazyPara)) { + this.lazyPara = lazyPara; + } + } + + @Transient + public void setLazyParaIfNot(String lazyPara, Predicate bool, Supplier... lazyParas) { + if (!bool.test(lazyPara)) { + this.lazyPara = lazyPara; + return; + } + for (Supplier supplier : lazyParas) { + lazyPara = supplier.get(); + if (!bool.test(lazyPara)) { + this.lazyPara = lazyPara; + return; + } + } + } + + @Transient + public void setLazyParaIf(UnaryOperator lazyPara) { + this.lazyPara = lazyPara.apply(this.lazyPara); + } + + /** * Getter for win_mail_sender.last_send. */ @@ -2935,6 +3103,18 @@ else if (!this.mailMark.equals(other.mailMark)) } else if (!this.mailDate.equals(other.mailDate)) return false; + if (this.lazyBean == null) { + if (other.lazyBean != null) + return false; + } + else if (!this.lazyBean.equals(other.lazyBean)) + return false; + if (this.lazyPara == null) { + if (other.lazyPara != null) + return false; + } + else if (!this.lazyPara.equals(other.lazyPara)) + return false; if (this.lastSend == null) { if (other.lastSend != null) return false; @@ -3045,6 +3225,8 @@ public int hashCode() { result = prime * result + ((this.mailFile == null) ? 0 : this.mailFile.hashCode()); result = prime * result + ((this.mailMark == null) ? 0 : this.mailMark.hashCode()); result = prime * result + ((this.mailDate == null) ? 0 : this.mailDate.hashCode()); + result = prime * result + ((this.lazyBean == null) ? 0 : this.lazyBean.hashCode()); + result = prime * result + ((this.lazyPara == null) ? 0 : this.lazyPara.hashCode()); result = prime * result + ((this.lastSend == null) ? 0 : this.lastSend.hashCode()); result = prime * result + ((this.lastFail == null) ? 0 : this.lastFail.hashCode()); result = prime * result + ((this.lastDone == null) ? 0 : this.lastDone.hashCode()); @@ -3085,6 +3267,8 @@ public String toString() { sb.append(", ").append(mailFile); sb.append(", ").append(mailMark); sb.append(", ").append(mailDate); + sb.append(", ").append(lazyBean); + sb.append(", ").append(lazyPara); sb.append(", ").append(lastSend); sb.append(", ").append(lastFail); sb.append(", ").append(lastDone); @@ -3129,6 +3313,8 @@ public void from(IWinMailSender from) { setMailFile(from.getMailFile()); setMailMark(from.getMailMark()); setMailDate(from.getMailDate()); + setLazyBean(from.getLazyBean()); + setLazyPara(from.getLazyPara()); setLastSend(from.getLastSend()); setLastFail(from.getLastFail()); setLastDone(from.getLastDone()); diff --git a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/records/WinMailSenderRecord.java b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/records/WinMailSenderRecord.java index b9dfa0919..dbb6bd4bd 100644 --- a/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/records/WinMailSenderRecord.java +++ b/radiant/tiny-mail/src/main/java-gen/pro/fessional/wings/tiny/mail/database/autogen/tables/records/WinMailSenderRecord.java @@ -20,7 +20,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -334,12 +334,44 @@ public LocalDateTime getMailDate() { return (LocalDateTime) get(18); } + /** + * Setter for win_mail_sender.lazy_bean. + */ + @Override + public void setLazyBean(String value) { + set(19, value); + } + + /** + * Getter for win_mail_sender.lazy_bean. + */ + @Override + public String getLazyBean() { + return (String) get(19); + } + + /** + * Setter for win_mail_sender.lazy_para. + */ + @Override + public void setLazyPara(String value) { + set(20, value); + } + + /** + * Getter for win_mail_sender.lazy_para. + */ + @Override + public String getLazyPara() { + return (String) get(20); + } + /** * Setter for win_mail_sender.last_send. */ @Override public void setLastSend(LocalDateTime value) { - set(19, value); + set(21, value); } /** @@ -347,7 +379,7 @@ public void setLastSend(LocalDateTime value) { */ @Override public LocalDateTime getLastSend() { - return (LocalDateTime) get(19); + return (LocalDateTime) get(21); } /** @@ -355,7 +387,7 @@ public LocalDateTime getLastSend() { */ @Override public void setLastFail(String value) { - set(20, value); + set(22, value); } /** @@ -363,7 +395,7 @@ public void setLastFail(String value) { */ @Override public String getLastFail() { - return (String) get(20); + return (String) get(22); } /** @@ -371,7 +403,7 @@ public String getLastFail() { */ @Override public void setLastDone(LocalDateTime value) { - set(21, value); + set(23, value); } /** @@ -379,7 +411,7 @@ public void setLastDone(LocalDateTime value) { */ @Override public LocalDateTime getLastDone() { - return (LocalDateTime) get(21); + return (LocalDateTime) get(23); } /** @@ -387,7 +419,7 @@ public LocalDateTime getLastDone() { */ @Override public void setLastCost(Integer value) { - set(22, value); + set(24, value); } /** @@ -395,7 +427,7 @@ public void setLastCost(Integer value) { */ @Override public Integer getLastCost() { - return (Integer) get(22); + return (Integer) get(24); } /** @@ -403,7 +435,7 @@ public Integer getLastCost() { */ @Override public void setNextSend(LocalDateTime value) { - set(23, value); + set(25, value); } /** @@ -411,7 +443,7 @@ public void setNextSend(LocalDateTime value) { */ @Override public LocalDateTime getNextSend() { - return (LocalDateTime) get(23); + return (LocalDateTime) get(25); } /** @@ -419,7 +451,7 @@ public LocalDateTime getNextSend() { */ @Override public void setNextLock(Integer value) { - set(24, value); + set(26, value); } /** @@ -427,7 +459,7 @@ public void setNextLock(Integer value) { */ @Override public Integer getNextLock() { - return (Integer) get(24); + return (Integer) get(26); } /** @@ -435,7 +467,7 @@ public Integer getNextLock() { */ @Override public void setSumSend(Integer value) { - set(25, value); + set(27, value); } /** @@ -443,7 +475,7 @@ public void setSumSend(Integer value) { */ @Override public Integer getSumSend() { - return (Integer) get(25); + return (Integer) get(27); } /** @@ -451,7 +483,7 @@ public Integer getSumSend() { */ @Override public void setSumFail(Integer value) { - set(26, value); + set(28, value); } /** @@ -459,7 +491,7 @@ public void setSumFail(Integer value) { */ @Override public Integer getSumFail() { - return (Integer) get(26); + return (Integer) get(28); } /** @@ -467,7 +499,7 @@ public Integer getSumFail() { */ @Override public void setSumDone(Integer value) { - set(27, value); + set(29, value); } /** @@ -475,7 +507,7 @@ public void setSumDone(Integer value) { */ @Override public Integer getSumDone() { - return (Integer) get(27); + return (Integer) get(29); } /** @@ -483,7 +515,7 @@ public Integer getSumDone() { */ @Override public void setMaxFail(Integer value) { - set(28, value); + set(30, value); } /** @@ -491,7 +523,7 @@ public void setMaxFail(Integer value) { */ @Override public Integer getMaxFail() { - return (Integer) get(28); + return (Integer) get(30); } /** @@ -499,7 +531,7 @@ public Integer getMaxFail() { */ @Override public void setMaxDone(Integer value) { - set(29, value); + set(31, value); } /** @@ -507,7 +539,7 @@ public void setMaxDone(Integer value) { */ @Override public Integer getMaxDone() { - return (Integer) get(29); + return (Integer) get(31); } /** @@ -515,7 +547,7 @@ public Integer getMaxDone() { */ @Override public void setRefType(Integer value) { - set(30, value); + set(32, value); } /** @@ -523,7 +555,7 @@ public void setRefType(Integer value) { */ @Override public Integer getRefType() { - return (Integer) get(30); + return (Integer) get(32); } /** @@ -531,7 +563,7 @@ public Integer getRefType() { */ @Override public void setRefKey1(Long value) { - set(31, value); + set(33, value); } /** @@ -539,7 +571,7 @@ public void setRefKey1(Long value) { */ @Override public Long getRefKey1() { - return (Long) get(31); + return (Long) get(33); } /** @@ -547,7 +579,7 @@ public Long getRefKey1() { */ @Override public void setRefKey2(String value) { - set(32, value); + set(34, value); } /** @@ -555,7 +587,7 @@ public void setRefKey2(String value) { */ @Override public String getRefKey2() { - return (String) get(32); + return (String) get(34); } // ------------------------------------------------------------------------- @@ -592,6 +624,8 @@ public void from(IWinMailSender from) { setMailFile(from.getMailFile()); setMailMark(from.getMailMark()); setMailDate(from.getMailDate()); + setLazyBean(from.getLazyBean()); + setLazyPara(from.getLazyPara()); setLastSend(from.getLastSend()); setLastFail(from.getLastFail()); setLastDone(from.getLastDone()); @@ -629,7 +663,7 @@ public WinMailSenderRecord() { /** * Create a detached, initialised WinMailSenderRecord */ - public WinMailSenderRecord(Long id, LocalDateTime createDt, LocalDateTime modifyDt, LocalDateTime deleteDt, Long commitId, String mailApps, String mailRuns, String mailConf, String mailFrom, String mailTo, String mailCc, String mailBcc, String mailReply, String mailSubj, String mailText, Boolean mailHtml, String mailFile, String mailMark, LocalDateTime mailDate, LocalDateTime lastSend, String lastFail, LocalDateTime lastDone, Integer lastCost, LocalDateTime nextSend, Integer nextLock, Integer sumSend, Integer sumFail, Integer sumDone, Integer maxFail, Integer maxDone, Integer refType, Long refKey1, String refKey2) { + public WinMailSenderRecord(Long id, LocalDateTime createDt, LocalDateTime modifyDt, LocalDateTime deleteDt, Long commitId, String mailApps, String mailRuns, String mailConf, String mailFrom, String mailTo, String mailCc, String mailBcc, String mailReply, String mailSubj, String mailText, Boolean mailHtml, String mailFile, String mailMark, LocalDateTime mailDate, String lazyBean, String lazyPara, LocalDateTime lastSend, String lastFail, LocalDateTime lastDone, Integer lastCost, LocalDateTime nextSend, Integer nextLock, Integer sumSend, Integer sumFail, Integer sumDone, Integer maxFail, Integer maxDone, Integer refType, Long refKey1, String refKey2) { super(WinMailSenderTable.WinMailSender); setId(id); @@ -651,6 +685,8 @@ public WinMailSenderRecord(Long id, LocalDateTime createDt, LocalDateTime modify setMailFile(mailFile); setMailMark(mailMark); setMailDate(mailDate); + setLazyBean(lazyBean); + setLazyPara(lazyPara); setLastSend(lastSend); setLastFail(lastFail); setLastDone(lastDone); @@ -694,6 +730,8 @@ public WinMailSenderRecord(WinMailSender value) { setMailFile(value.getMailFile()); setMailMark(value.getMailMark()); setMailDate(value.getMailDate()); + setLazyBean(value.getLazyBean()); + setLazyPara(value.getLazyPara()); setLastSend(value.getLastSend()); setLastFail(value.getLastFail()); setLastDone(value.getLastDone()); diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailConfigProvider.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailConfigProvider.java index c7e3a4e1a..6571db80f 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailConfigProvider.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailConfigProvider.java @@ -54,8 +54,8 @@ public TinyMailConfig bynamedConfig(String name) { @Contract("_->new") public TinyMailConfig combineConfig(@Nullable TinyMailConfig that) { final TinyMailConfig newConf = new TinyMailConfig(); - newConf.adopt(that); - newConf.merge(configProp.getDefault()); + TinyMailConfig.ConfSetter.toAny(newConf, that); + TinyMailConfig.ConfSetter.toInvalid(newConf, configProp.getDefault()); return newConf; } } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailNotice.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailNotice.java index 0cdff99a5..483d6bd14 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailNotice.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailNotice.java @@ -1,5 +1,6 @@ package pro.fessional.wings.tiny.mail.sender; +import com.alibaba.ttl.threadpool.TtlExecutors; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -7,6 +8,7 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -15,6 +17,7 @@ import java.util.Collections; import java.util.Map; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME; @@ -25,7 +28,7 @@ */ @Slf4j @RequiredArgsConstructor -public class MailNotice implements SmallNotice, InitializingBean { +public class MailNotice implements SmallNotice, InitializingBean, DisposableBean { @NotNull @Getter protected final MailConfigProvider configProvider; @@ -34,6 +37,7 @@ public class MailNotice implements SmallNotice, InitializingBean @Setter(onMethod_ = { @Autowired(required = false), @Qualifier(DEFAULT_TASK_SCHEDULER_BEAN_NAME) }) private Executor executor; + private boolean innerExecutor = false; @Setter @Getter private Map configs = Collections.emptyMap(); @@ -64,7 +68,7 @@ public TinyMailConfig provideConfig(@Nullable String name, boolean combine) { @Override public boolean send(TinyMailConfig config, String subject, String content) { TinyMailMessage message = new TinyMailMessage(); - message.adopt(config); + TinyMailConfig.ConfSetter.toAny(message, config); message.setSubject(subject); message.setContent(content); senderManager.singleSend(message); @@ -91,7 +95,15 @@ public void emit(TinyMailConfig config, String subject, String content) { public void afterPropertiesSet() { if (executor == null) { log.warn("should reuse autowired thread pool"); - executor = Executors.newSingleThreadExecutor(); + executor = TtlExecutors.getTtlExecutorService(Executors.newWorkStealingPool(2)); + innerExecutor = true; + } + } + + @Override + public void destroy() { + if (innerExecutor && executor instanceof ExecutorService es) { + es.shutdown(); } } } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailRetryException.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailRetryException.java new file mode 100644 index 000000000..2324a774f --- /dev/null +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailRetryException.java @@ -0,0 +1,24 @@ +package pro.fessional.wings.tiny.mail.sender; + +import lombok.Getter; +import org.springframework.mail.MailException; + +/** + * should retry at nextEpoch + * + * @author trydofor + * @since 2023-01-03 + */ +@Getter +public class MailRetryException extends MailException { + + /** + * Epoch mills to retry + */ + private final long nextEpoch; + + public MailRetryException(long next, Throwable cause) { + super("retry at epoch=" + next + " for exception", cause); + this.nextEpoch = next; + } +} diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailSenderManager.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailSenderManager.java index 4349474b1..a4e9ab663 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailSenderManager.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailSenderManager.java @@ -4,6 +4,7 @@ import jakarta.mail.internet.MimeMessage; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; @@ -17,6 +18,7 @@ import pro.fessional.mirana.time.Sleep; import pro.fessional.mirana.time.ThreadNow; import pro.fessional.wings.faceless.convention.EmptySugar; +import pro.fessional.wings.silencer.support.PropHelper; import pro.fessional.wings.tiny.mail.spring.prop.TinyMailSenderProp; import java.math.BigDecimal; @@ -122,9 +124,11 @@ public void singleSend(@NotNull TinyMailMessage message, long maxWait, @Nullable if (wt.host) { mailHostWait.put(host, wt.wait); } + // noinspection StringConcatenationArgumentToLogCall log.warn("failed to send and host wait for " + (wt.wait - now) + "ms, message=" + message.toMainString(), me); throw new MailWaitException(wt.wait, wt.host, wt.stop, me); } + // noinspection StringConcatenationArgumentToLogCall log.warn("failed to send message= " + message.toMainString(), me); throw me; } @@ -187,8 +191,11 @@ public List batchSend(Collection message final long slp = Sleep.ignoreInterrupt(10, 2000); log.info("batch mail dryrun and sleep {} ms", slp); final long avg = slp / dryrunCount; + long end = ThreadNow.millis(); for (BatchResult rst : results) { - if (rst.costMillis < 0) rst.costMillis = avg; + if (rst.costMillis == 0) continue; + rst.costMillis = avg; + rst.exitMillis = end; } } @@ -219,6 +226,7 @@ public List batchSend(Collection message for (String host : hosts) { mailHostWait.put(host, wt.wait); } + // noinspection StringConcatenationArgumentToLogCall log.warn("failed to send and host wait for " + (wt.wait - now) + "ms, hosts=" + hosts, me); } @@ -244,9 +252,9 @@ public List batchSend(Collection message long avg = (now - start) / len; for (BatchResult br : result) { br.costMillis = avg; - br.doneMillis = now; + br.exitMillis = now; if (br.exception != null) { - log.warn("failed to batch send message, " + br.tinyMessage.toMainString()); + log.warn("failed to batch send message, {}", br.tinyMessage.toMainString()); } } @@ -368,9 +376,20 @@ private MimeMessage prepareMimeMessage(TinyMailMessage message, MimeMessagePrepa } final Map files = message.getAttachment(); - if (files != null) { - for (Map.Entry en : files.entrySet()) { - helper.addAttachment(en.getKey(), en.getValue()); + for (Map.Entry en : files.entrySet()) { + final String name = en.getKey(); + final String n1 = PropHelper.removeOptional(name, null); + if (n1 != null) { + try { + helper.addAttachment(n1, en.getValue()); + } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.warn("ignore error of optional resource, name=" + name, e); + } + } + else { + helper.addAttachment(name, en.getValue()); } } @@ -461,8 +480,8 @@ public static class BatchResult { @Getter private long costMillis = 0; @Getter - private long doneMillis = 0; - @Getter + private long exitMillis = 0; + @Getter @Setter private Exception exception; private JavaMailSender mailSender; diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailStopException.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailStopException.java new file mode 100644 index 000000000..3f5bb0126 --- /dev/null +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailStopException.java @@ -0,0 +1,22 @@ +package pro.fessional.wings.tiny.mail.sender; + +import lombok.Getter; +import org.springframework.mail.MailException; + +/** + * stop sending, maybe format, prepare error + * + * @author trydofor + * @since 2023-01-03 + */ +@Getter +public class MailStopException extends MailException { + + public MailStopException(String msg) { + super(msg); + } + + public MailStopException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailWaitException.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailWaitException.java index d90362d49..e07522b8f 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailWaitException.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/MailWaitException.java @@ -4,26 +4,26 @@ import org.springframework.mail.MailException; /** + * should wait until waitEpoch, then may retry + * * @author trydofor * @since 2023-01-03 */ +@Getter public class MailWaitException extends MailException { /** * Epoch mills to wait */ - @Getter private final long waitEpoch; /** * Whether it is a host-level wait */ - @Getter private final boolean hostLevel; /** * Whether to stop sending than to wait */ - @Getter private final boolean stopRetry; public MailWaitException(long epoch, boolean host, boolean stop, Throwable cause) { diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/TinyMailConfig.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/TinyMailConfig.java index 9c35a5abd..ee8e3ebdf 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/TinyMailConfig.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/TinyMailConfig.java @@ -5,12 +5,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.boot.autoconfigure.mail.MailProperties; +import pro.fessional.mirana.cond.IfSetter; import java.util.Arrays; import java.util.Objects; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.mergeNotValue; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.notValue; +import static pro.fessional.wings.silencer.support.PropHelper.invalid; +import static pro.fessional.wings.silencer.support.PropHelper.mergeToInvalid; /** * hashCode and equals with @@ -39,14 +40,17 @@ public class TinyMailConfig extends MailProperties { /** * default mail to */ + @NotNull protected String[] to; /** * default mail cc */ + @Nullable protected String[] cc; /** * default mail bcc */ + @Nullable protected String[] bcc; /** * default mail reply @@ -101,82 +105,59 @@ public int hashCode() { return result; } - /** - * use all properties from that - */ - public void adopt(MailProperties that) { - if (that == null) return; - setHost(that.getHost()); - setPort(that.getPort()); - setUsername(that.getUsername()); - setPassword(that.getPassword()); - setProtocol(that.getProtocol()); - setDefaultEncoding(that.getDefaultEncoding()); - getProperties().putAll(that.getProperties()); - } + public static final IfSetter PropSetter = (thiz, that, absent, present) -> { + if (that == null) return thiz; + + if (absent == IfSetter.Absent.Invalid) { + if (invalid(thiz.getHost())) thiz.setHost(that.getHost()); + if (thiz.getPort() == null) thiz.setPort(that.getPort()); + if (invalid(thiz.getUsername())) thiz.setUsername(that.getUsername()); + if (invalid(thiz.getPassword())) thiz.setPassword(that.getPassword()); + if (invalid(thiz.getProtocol())) thiz.setProtocol(that.getProtocol()); + if (thiz.getDefaultEncoding() == null) thiz.setDefaultEncoding(that.getDefaultEncoding()); + mergeToInvalid(thiz.getProperties(), that.getProperties()); + } + else { + thiz.setHost(that.getHost()); + thiz.setPort(that.getPort()); + thiz.setUsername(that.getUsername()); + thiz.setPassword(that.getPassword()); + thiz.setProtocol(that.getProtocol()); + thiz.setDefaultEncoding(that.getDefaultEncoding()); + thiz.getProperties().putAll(that.getProperties()); + } - /** - * use all properties from that - */ - public void adopt(TinyMailConfig that) { - if (that == null) return; - adopt((MailProperties) that); - dryrun = that.dryrun; - name = that.name; - from = that.from; - to = that.to; - cc = that.cc; - bcc = that.bcc; - reply = that.reply; - html = that.html; - } + return thiz; + }; - /** - * if this.property is invalid, then use that.property. - * except for 'properties' which merge value only if key matches. - */ - public void merge(MailProperties that) { - if (that == null) return; + public static final IfSetter ConfSetter = (thiz, that, absent, present) -> { + if (that == null) return thiz; - if (notValue(getHost())) { - setHost(that.getHost()); - } - if (getPort() == null) { - setPort(that.getPort()); - } - if (notValue(getUsername())) { - setUsername(that.getUsername()); - } - final String password = getPassword(); - if (notValue(password)) { - setPassword(that.getPassword()); - } - if (notValue(getProtocol())) { - setProtocol(that.getProtocol()); + PropSetter.set(thiz, that, absent, present); + + if (absent == IfSetter.Absent.Invalid) { + if (thiz.dryrun == null) thiz.dryrun = that.dryrun; + if (invalid(thiz.name)) thiz.name = that.name; + if (invalid(thiz.from)) thiz.from = that.from; + if (thiz.to == null) thiz.to = that.to; + if (thiz.cc == null) thiz.cc = that.cc; + if (thiz.bcc == null) thiz.bcc = that.bcc; + if (invalid(thiz.reply)) thiz.reply = that.reply; + if (thiz.html == null) thiz.html = that.html; } - if (getDefaultEncoding() == null) { - setDefaultEncoding(that.getDefaultEncoding()); + else { + thiz.dryrun = that.dryrun; + thiz.name = that.name; + thiz.from = that.from; + thiz.to = that.to; + thiz.cc = that.cc; + thiz.bcc = that.bcc; + thiz.reply = that.reply; + thiz.html = that.html; } - mergeNotValue(getProperties(), that.getProperties()); - } - - /** - * if this.property is invalid, then use that.property. - */ - public void merge(TinyMailConfig that) { - if (that == null) return; - merge((MailProperties) that); - - if (dryrun == null) dryrun = that.dryrun; - if (notValue(name)) name = that.name; - if (notValue(from)) from = that.from; - if (to == null) to = that.to; - if (cc == null) cc = that.cc; - if (bcc == null) bcc = that.bcc; - if (notValue(reply)) reply = that.reply; - if (html == null) html = that.html; - } + return thiz; + }; public interface Loader { /** diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/TinyMailMessage.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/TinyMailMessage.java index 7ec90bf13..f799a970f 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/TinyMailMessage.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/sender/TinyMailMessage.java @@ -2,7 +2,9 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; import org.springframework.core.io.Resource; +import pro.fessional.mirana.cond.IfSetter; import pro.fessional.mirana.text.WhiteUtil; import java.util.Collections; @@ -38,10 +40,11 @@ public class TinyMailMessage extends TinyMailConfig { protected String content; /** - * Mail attachments and its names + * Mail attachments and its names (can prefix `optional:`) */ protected Map attachment = null; + @NotNull public Map getAttachment() { return attachment != null ? attachment : Collections.emptyMap(); } @@ -60,33 +63,6 @@ public String toMainString() { return sb.toString(); } - /** - * Use all `that` values - */ - public void adopt(TinyMailMessage that) { - if (that == null) return; - super.adopt(that); - bizId = that.bizId; - bizMark = that.bizMark; - subject = that.subject; - content = that.content; - attachment = that.attachment; - } - - /** - * Use `that` value if `this` is invalid - */ - public void merge(TinyMailMessage that) { - if (that == null) return; - super.merge(that); - - if (bizId == null) bizId = that.bizId; - if (bizMark == null) bizMark = that.bizMark; - if (isEmpty(subject)) subject = that.subject; - if (isEmpty(content)) content = that.content; - if (attachment == null) attachment = that.attachment; - } - public boolean asHtml() { if (html != null) return html; return asHtml(content, true); @@ -104,4 +80,28 @@ public static boolean asHtml(String str, boolean elze) { return elze; } + + public static final IfSetter MessageSetter = (thiz, that, absent, present) -> { + if (that == null) return thiz; + + TinyMailConfig.ConfSetter.set(thiz,that,absent, present); + + if (absent == IfSetter.Absent.Invalid) { + if (thiz.bizId == null) thiz.bizId = that.bizId; + if (thiz.bizMark == null) thiz.bizMark = that.bizMark; + if (isEmpty(thiz.subject)) thiz.subject = that.subject; + if (isEmpty(thiz.content)) thiz.content = that.content; + if (thiz.attachment == null) thiz.attachment = that.attachment; + } + else { + thiz.bizId = that.bizId; + thiz.bizMark = that.bizMark; + thiz.subject = that.subject; + thiz.content = that.content; + thiz.attachment = that.attachment; + } + + return thiz; + }; + } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMail.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMail.java index 7b5d1a5bb..823e94f21 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMail.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMail.java @@ -1,9 +1,13 @@ package pro.fessional.wings.tiny.mail.service; import lombok.Data; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.springframework.core.io.Resource; +import pro.fessional.wings.silencer.support.PropHelper; import java.time.LocalDateTime; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -23,6 +27,7 @@ public class TinyMail { /** * Mail to, use default if null */ + @Nullable protected String[] to; public void setTo(String... to) { @@ -32,6 +37,7 @@ public void setTo(String... to) { /** * Mail cc, use default if null */ + @Nullable protected String[] cc; public void setCc(String... cc) { @@ -41,6 +47,7 @@ public void setCc(String... cc) { /** * Mail bcc, use default if null */ + @Nullable protected String[] bcc; public void setBcc(String... bcc) { @@ -51,11 +58,7 @@ public void setBcc(String... bcc) { * Mail reply, use default if empty */ protected String reply; - /** - * Whether to send html mail (text/html), otherwise text mail(text/plain). - * use default if null - */ - protected Boolean html; + /** * Mail subject, use default if empty */ @@ -65,9 +68,15 @@ public void setBcc(String... bcc) { */ protected String content; /** - * Mail attachment, use default if null + * Mail attachment and its name (can prefix `optional:`), use default if null */ + @Nullable protected Map attachment = null; + /** + * Whether to send html mail (text/html), otherwise text mail(text/plain). + * use default if null + */ + protected Boolean html; /** * Business keyword ot mark, space seperated, use default if null */ @@ -78,6 +87,16 @@ public void setBcc(String... bcc) { */ protected LocalDateTime date; + /** + * lazy bean to edit mail if mail_text is null + */ + protected String lazyBean; + + /** + * lazy para of lazy bean + */ + protected String lazyPara; + /** * Max count of fail, defaults to system configuration */ @@ -103,6 +122,10 @@ public void setBcc(String... bcc) { private String refKey2; // + public void setMailLazy(@NotNull TinyMailLazy bean, @Nullable Object para) { + lazyBean = bean.lazyBean(); + lazyPara = bean.lazyPara(para); + } public void setContentText(String content) { this.content = content; @@ -118,4 +141,11 @@ public void setContentHtml(String content, Boolean html) { this.content = content; this.html = html; } + + public void putAttachment(@NotNull String name, @NotNull Resource resource, boolean optional) { + if (attachment == null) attachment = new LinkedHashMap<>(); + if (optional) name = PropHelper.prefixOptional(name); + + attachment.put(name, resource); + } } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailLazy.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailLazy.java new file mode 100644 index 000000000..5a838856c --- /dev/null +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailLazy.java @@ -0,0 +1,41 @@ +package pro.fessional.wings.tiny.mail.service; + +import lombok.Data; +import org.jetbrains.annotations.Nullable; +import org.springframework.core.io.Resource; +import pro.fessional.wings.slardar.fastjson.FastJsonHelper; + +import java.util.Map; + +/** + * @author trydofor + * @since 2024-07-12 + */ +public interface TinyMailLazy { + + /** + * get the registered name of bean, for safe reason + */ + default String lazyBean() { + return this.getClass().getName(); + } + + default String lazyPara(@Nullable Object para) { + return para == null ? null : FastJsonHelper.string(para); + } + + /** + * use lazyPara to edit the lazy mail if get nonnull item. + * stop sending if get exception or all null items + */ + @Nullable + Edit lazyEdit(@Nullable String para); + + @Data + class Edit { + private String subject = null; + private String content = null; + private Map attachment = null; + private Boolean html; + } +} diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailPlain.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailPlain.java index 4e4d3196d..818d93a43 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailPlain.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailPlain.java @@ -1,6 +1,8 @@ package pro.fessional.wings.tiny.mail.service; import lombok.Data; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.time.LocalDateTime; import java.util.Collections; @@ -86,10 +88,20 @@ public class TinyMailPlain { private String mark; /** - * Mail timed delivery time, system time zone + * Schedule to send mail, system time zone */ private LocalDateTime date; + /** + * lazy bean to edit mail if mail_text is null + */ + private String lazyBean; + + /** + * lazy para of lazy bean + */ + private String lazyPara; + /** * Next send time, system time zone, update when non-null */ @@ -169,4 +181,11 @@ public class TinyMailPlain { * Send parameter, whether to check the sending condition, otherwise it is forced to send */ private Boolean check; + + // + public void setMailLazy(@NotNull TinyMailLazy bean, @Nullable Object para) { + lazyBean = bean.lazyBean(); + lazyPara = bean.lazyPara(para); + } + } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailService.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailService.java index d002bf5d0..a635f91fc 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailService.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/TinyMailService.java @@ -16,27 +16,64 @@ public interface TinyMailService { /** - * Sync send, return success or not, or throw exception. - * If not success, async retry + * send success + */ + int Success = 0; + + /** + * failed to check before send. e.g. prepare and check prop, lock, format + */ + int ErrCheck = -1; + + /** + * other than MailRetryException after check + */ + int ErrOther = -2; + + /** + *
+     * Save first, then Sync single send. and return,
+     * - true, if send success.
+     * - false, if check fail, e.g. prop, lock or format.
+     * - throw if send fail, MailRetryException if async retry.
+     * 
+ * + * @throws MailRetryException if retry + * @throws Exception if unhandled */ boolean send(@NotNull TinyMail message, boolean retry); /** - * Sync send, fire and forget, no exception throw. - * If not success, async retry + *
+     * Save first, then Sync single send, fire and forget, no exception throw. and return,
+     * - {@value #ErrOther}, if throw non MailRetryException.
+     * - {@value #ErrCheck}, if check fail, e.g. prop, lock or format.
+     * - {@value #Success}, if send success.
+     * - mills > now(), estimated retry time, if fail and async retry
+     * 
*/ - boolean post(@NotNull TinyMail message, boolean retry); + long post(@NotNull TinyMail message, boolean retry); /** - * Async, no exception throw. auto batch send. - * Return the estimated sending time, `-1` for failure - * If not success, async retry. + *
+     * Save first, then Async batch send, fire and forget, no exception throw. and return,
+     * - {@value #ErrOther}, if throw non MailRetryException.
+     * - {@value #ErrCheck}, if check fail, e.g. prop, lock or format.
+     * - mills > now(), estimated send or retry (when error) time
+     * 
*/ long emit(@NotNull TinyMail message, boolean retry); /** - * Sync send, return success or not, or throw exception. - * If not success, async retry + *
+     * Save first, then Sync single send. and return,
+     * - true, if send success.
+     * - false, if check fail, e.g. prop, lock or format.
+     * - throw if send fail, MailRetryException if async retry.
+     * 
+ * + * @throws MailRetryException if retry + * @throws Exception if unhandled */ default boolean send(@NotNull TinyMailPlain message) { final long id = save(message); @@ -44,18 +81,26 @@ default boolean send(@NotNull TinyMailPlain message) { } /** - * Sync send, fire and forget, no exception throw. - * If not success, async retry + *
+     * Save first, then Sync single send, fire and forget, no exception throw. and return,
+     * - {@value #ErrOther}, if throw non MailRetryException.
+     * - {@value #ErrCheck}, if check fail, e.g. prop, lock or format.
+     * - {@value #Success}, if send success.
+     * - mills > now(), estimated retry time, if fail and async retry
+     * 
*/ - default boolean post(@NotNull TinyMailPlain message) { + default long post(@NotNull TinyMailPlain message) { final long id = save(message); return post(id, BoxedCastUtil.orFalse(message.getRetry()), BoxedCastUtil.orFalse(message.getCheck())); } /** - * Async, no exception throw. auto batch send. - * Return the estimated sending time, `-1` for failure - * If not success, async retry. + *
+     * Save first, then Async batch send, fire and forget, no exception throw. and return,
+     * - {@value #ErrOther}, if throw non MailRetryException.
+     * - {@value #ErrCheck}, if check fail, e.g. prop, lock or format.
+     * - mills > now(), estimated send or retry (when error) time
+     * 
*/ default long emit(@NotNull TinyMailPlain message) { final long id = save(message); @@ -63,45 +108,83 @@ default long emit(@NotNull TinyMailPlain message) { } /** - * Sync send, fire and forget, no exception throw. - * If not success, async retry, whether to check state before sending + *
+     * Save first, then Sync single send. and return,
+     * - true, if send success.
+     * - false, if check fail, e.g. prop, lock or format.
+     * - throw if send fail, MailRetryException if async retry.
+     * 
+ * + * @throws MailRetryException if retry + * @throws Exception if unhandled */ boolean send(long id, boolean retry, boolean check); /** - * Sync send, fire and forget, no exception throw. - * If not success, async retry, whether to check state before sending + *
+     * Save first, then Sync single send, fire and forget, no exception throw. and return,
+     * - {@value #ErrOther}, if throw non MailRetryException.
+     * - {@value #ErrCheck}, if check fail, e.g. prop, lock or format.
+     * - {@value #Success}, if send success.
+     * - mills > now(), estimated retry time, if fail and async retry
+     * 
*/ - boolean post(long id, boolean retry, boolean check); + long post(long id, boolean retry, boolean check); /** - * Async, no exception throw. auto batch send. - * Return the estimated sending time, `-1` for failure - * If not success, async retry, whether to check state before sending + *
+     * Save first, then Async batch send, fire and forget, no exception throw. and return,
+     * - {@value #ErrOther}, if throw non MailRetryException.
+     * - {@value #ErrCheck}, if check fail, e.g. prop, lock or format.
+     * - mills > now(), estimated send or retry (when error) time
+     * 
*/ long emit(long id, boolean retry, boolean check); /** - * Create(id is empty) or edit a mail, return the id + * Create(id is empty) or edit a mail with check, return the id. + * NOTE: no schedule to send, need manually send/post/emit it. + */ + default long save(@NotNull TinyMailPlain message) { + return save(message, true); + } + + /** + * Create(id is empty) or edit a mail with check, return the id. + * NOTE: no schedule to send, need manually send/post/emit it. */ - long save(@NotNull TinyMailPlain message); + long save(@NotNull TinyMailPlain message, boolean check); /** - * Sync scan the mail to resend, return the count, and send them async + *
+     * Sync scan the unsent mail to resend them async, return the count. and if idel is
+     * * null, only scan, nothing to idle
+     * * > 0, adjust the scheduled scan interval mills
+     * * = 0, disable the scheduled scan
+     * * < 0, reset to scan-idle prop if adjusted before
+     * 
*/ - int scan(); + int scan(Long idle); + /** + * Sync scan the unsent mail to resend them async, return the count. + */ + default int scan() { + return scan(null); + } /** - * Create the mail, and auto send it in sync or async way. - * `-1` means failure, `0` means sync send, - * otherwise means async send at estimated sending time + *
+     * Save first, then auto post/emit by its mail-date. and retrun,
+     * - {@value #ErrCheck}, if check fail, e.g. prop, lock or format.
+     * - {@value #Success}, if send success.
+     * - mills > now(), estimated retry time, if fail and async retry
+     * 
*/ default long auto(@NotNull TinyMail message, boolean retry) { final LocalDateTime md = message.getDate(); if (md == null || md.isBefore(ThreadNow.localDateTime())) { - final boolean ok = send(message, retry); - return ok ? 0 : -1; + return post(message, retry); } else { return emit(message, retry); @@ -109,16 +192,18 @@ default long auto(@NotNull TinyMail message, boolean retry) { } /** - * Create the mail, and auto send it in sync or async way. - * `-1` means failure, `0` means sync send, - * otherwise means async send at estimated sending time + *
+     * Save first, then auto post/emit by its mail-date. and retrun,
+     * - {@value #ErrCheck}, if check fail, e.g. prop, lock or format.
+     * - {@value #Success}, if send success.
+     * - mills > now(), estimated retry time, if fail and async retry
+     * 
*/ default long auto(@NotNull TinyMailPlain message) { final long id = save(message); final LocalDateTime md = message.getDate(); if (md == null || md.isBefore(ThreadNow.localDateTime())) { - final boolean ok = send(id, BoxedCastUtil.orFalse(message.getRetry()), BoxedCastUtil.orFalse(message.getCheck())); - return ok ? 0 : -1; + return post(id, BoxedCastUtil.orFalse(message.getRetry()), BoxedCastUtil.orFalse(message.getCheck())); } else { return emit(id, BoxedCastUtil.orFalse(message.getRetry()), BoxedCastUtil.orFalse(message.getCheck())); @@ -133,11 +218,11 @@ interface StatusHook { /** * hook status, return true will stop mail next send * - * @param po mail info - * @param cost send cost - * @param exception error if fail + * @param po mail info + * @param cost send cost + * @param error if fail * @return whether stop next send */ - boolean stop(@NotNull WinMailSender po, long cost, Exception exception); + boolean stop(@NotNull WinMailSender po, long cost, Exception error); } } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/impl/TinyMailListServiceImpl.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/impl/TinyMailListServiceImpl.java index ad0104e58..375edfff1 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/impl/TinyMailListServiceImpl.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/impl/TinyMailListServiceImpl.java @@ -72,6 +72,8 @@ public class TinyMailListServiceImpl implements TinyMailListService, Initializin bo.setHtml(po.getMailHtml()); bo.setMark(po.getMailMark()); bo.setDate(po.getMailDate()); + bo.setLazyBean(po.getLazyBean()); + bo.setLazyPara(po.getLazyPara()); bo.setCreateDt(po.getCreateDt()); bo.setLastSend(po.getLastSend()); @@ -198,17 +200,18 @@ public TinyMailPlain loadDetail(long id) { @Override public void afterPropertiesSet() { final WinMailSenderTable t = winMailSenderDao.getTable(); + // skip large field plainFields = new SelectField[]{ t.Id, t.MailApps, t.MailRuns, t.MailConf, t.MailFrom, t.MailTo, t.MailCc, t.MailBcc, - t.MailReply, t.MailSubj, /*t.MailText,*/ t.MailFile, - t.MailHtml, t.MailMark, t.MailDate, + t.MailReply, t.MailSubj, /*t.MailText, t.MailFile,*/ + t.MailHtml, t.MailMark, t.MailDate, t.LazyBean, /*t.LazyPara,*/ t.CreateDt, t.LastSend, /*t.LastFail,*/ t.LastDone, t.NextSend, t.SumSend, t.SumFail, t.SumDone, t.MaxFail, t.MaxDone, }; sortsFields.put("id", t.Id); sortsFields.put("done", t.LastDone); - sortsFields.put("fail", t.LastFail); + sortsFields.put("send", t.LastSend); } } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/impl/TinyMailServiceImpl.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/impl/TinyMailServiceImpl.java index 62f91aca7..8b3c57161 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/impl/TinyMailServiceImpl.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/service/impl/TinyMailServiceImpl.java @@ -1,48 +1,57 @@ package pro.fessional.wings.tiny.mail.service.impl; -import com.fasterxml.jackson.databind.JsonNode; import jakarta.mail.MessagingException; import lombok.Data; import lombok.Setter; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.task.TaskSchedulingProperties; +import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.mail.MailParseException; import org.springframework.mail.MailSendException; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.stereotype.Service; +import org.springframework.util.function.SingletonSupplier; import pro.fessional.mirana.best.AssertArgs; +import pro.fessional.mirana.best.Param; import pro.fessional.mirana.cast.BoxedCastUtil; -import pro.fessional.mirana.data.Null; +import pro.fessional.mirana.cond.IfSetter; import pro.fessional.mirana.pain.ThrowableUtil; import pro.fessional.mirana.time.DateLocaling; import pro.fessional.mirana.time.ThreadNow; +import pro.fessional.wings.faceless.convention.EmptySugar; import pro.fessional.wings.faceless.convention.EmptyValue; import pro.fessional.wings.faceless.service.journal.JournalService; import pro.fessional.wings.faceless.service.lightid.LightIdService; import pro.fessional.wings.silencer.modulate.RunMode; import pro.fessional.wings.silencer.modulate.RuntimeMode; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; -import pro.fessional.wings.silencer.spring.help.CommonPropHelper; -import pro.fessional.wings.slardar.jackson.JacksonHelper; +import pro.fessional.wings.silencer.support.PropHelper; +import pro.fessional.wings.slardar.async.TaskSchedulerHelper; +import pro.fessional.wings.slardar.fastjson.FastJsonHelper; import pro.fessional.wings.tiny.mail.database.autogen.tables.WinMailSenderTable; import pro.fessional.wings.tiny.mail.database.autogen.tables.daos.WinMailSenderDao; import pro.fessional.wings.tiny.mail.database.autogen.tables.pojos.WinMailSender; import pro.fessional.wings.tiny.mail.sender.MailConfigProvider; +import pro.fessional.wings.tiny.mail.sender.MailRetryException; import pro.fessional.wings.tiny.mail.sender.MailSenderManager; import pro.fessional.wings.tiny.mail.sender.MailSenderManager.BatchResult; +import pro.fessional.wings.tiny.mail.sender.MailStopException; import pro.fessional.wings.tiny.mail.sender.MailWaitException; import pro.fessional.wings.tiny.mail.sender.TinyMailConfig; import pro.fessional.wings.tiny.mail.sender.TinyMailMessage; import pro.fessional.wings.tiny.mail.service.TinyMail; +import pro.fessional.wings.tiny.mail.service.TinyMailLazy; import pro.fessional.wings.tiny.mail.service.TinyMailPlain; import pro.fessional.wings.tiny.mail.service.TinyMailService; import pro.fessional.wings.tiny.mail.spring.prop.TinyMailServiceProp; @@ -50,18 +59,23 @@ import java.time.Instant; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.PriorityBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.PriorityQueue; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; -import static org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.arrayOrNull; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.notValue; +import static pro.fessional.wings.silencer.support.PropHelper.commaArray; +import static pro.fessional.wings.silencer.support.PropHelper.invalid; /** * @author trydofor @@ -88,92 +102,165 @@ public class TinyMailServiceImpl implements TinyMailService, InitializingBean { protected TinyMailServiceProp tinyMailServiceProp; @Setter(onMethod_ = { @Autowired }) protected ResourceLoader resourceLoader; - @Setter(onMethod_ = { @Autowired(required = false) }) - protected List statusHooks; + @Setter(onMethod_ = { @Autowired }) + protected ObjectProvider statusHookProvider; + @Setter(onMethod_ = { @Autowired }) + protected ObjectProvider lazyBeanProvider; - @Setter(onMethod_ = { @Autowired, @Qualifier(DEFAULT_TASK_SCHEDULER_BEAN_NAME) }) - private ThreadPoolTaskScheduler taskScheduler; + // init by afterPropertiesSet + protected ThreadPoolTaskScheduler taskScheduler; + protected volatile boolean isShutdown = false; + protected SingletonSupplier> lazyBeanHolder; + protected SingletonSupplier> statusHookHolder; - @SuppressWarnings("all") - private final PriorityBlockingQueue asyncMails = new PriorityBlockingQueue<>(); + protected final PriorityQueue asyncMailQueue = new PriorityQueue<>(); + protected final ConcurrentHashMap> asyncMailTask = new ConcurrentHashMap<>(); @Override public boolean send(@NotNull TinyMail message, boolean retry) { - final String conf = message.getConf(); - final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf); - AssertArgs.notNull(config, "mail conf={} not found", conf); - - final WinMailSender po = saveMailSender(config, message); - final TinyMailMessage mailMessage = makeMailMessage(config, po, message); - return doSyncSend(po, mailMessage, retry, true); + return doSend(true, message, retry) == Success; } @Override - public boolean post(@NotNull TinyMail message, boolean retry) { + public long post(@NotNull TinyMail message, boolean retry) { try { - return send(message, retry); + return doSend(true, message, retry); + } + catch (MailRetryException e) { + // noinspection StringConcatenationArgumentToLogCall + log.warn("fail to post tiny-mail, next-retry=" + e.getNextEpoch() + ", subject=" + message.getSubject(), e.getCause()); + return e.getNextEpoch(); } catch (Exception e) { - log.error("fail to post mail, subject=" + message.getSubject(), e); - return false; + // noinspection StringConcatenationArgumentToLogCall + log.error("fail to post tiny-mail, retry=" + retry + ", subject=" + message.getSubject(), e); + return ErrOther; } } @Override public long emit(@NotNull TinyMail message, boolean retry) { - final String conf = message.getConf(); - final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf); - AssertArgs.notNull(config, "mail conf={} not found", conf); - final WinMailSender po = saveMailSender(config, message); - final TinyMailMessage mailMessage = makeMailMessage(config, po, message); - return doAsyncFreshSend(po, mailMessage, retry, true); + try { + return doSend(false, message, retry); + } + catch (MailRetryException e) { + // noinspection StringConcatenationArgumentToLogCall + log.warn("fail to emit tiny-mail, next-retry=" + e.getNextEpoch() + ", subject=" + message.getSubject(), e.getCause()); + return e.getNextEpoch(); + } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.error("fail to emit tiny-mail, retry=" + retry + ", subject=" + message.getSubject(), e); + return ErrOther; + } } @Override public boolean send(long id, boolean retry, boolean check) { - final WinMailSender po = winMailSenderDao.fetchOneById(id); - if (po == null) { - log.warn("mail not found by id={}, skip send", id); - return false; + return doSend(true, id, retry, check) == Success; + } + + @Override + public long post(long id, boolean retry, boolean check) { + try { + return doSend(true, id, retry, check); } - final String conf = po.getMailConf(); - final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf); - if (config == null) { - log.warn("mail conf={} not found", conf); - return false; + catch (MailRetryException e) { + // noinspection StringConcatenationArgumentToLogCall + log.warn("fail to post tiny-mail, next-retry=" + e.getNextEpoch() + ", id=" + id, e.getCause()); + return e.getNextEpoch(); + } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.error("fail to post tiny-mail, retry=" + retry + ", id=" + id, e); + return ErrOther; } - - final TinyMailMessage mailMessage = makeMailMessage(config, po, null); - return doSyncSend(po, mailMessage, retry, check); } @Override - public boolean post(long id, boolean retry, boolean check) { + public long emit(long id, boolean retry, boolean check) { try { - return send(id, retry, check); + return doSend(false, id, retry, check); + } + catch (MailRetryException e) { + // noinspection StringConcatenationArgumentToLogCall + log.warn("fail to emit tiny-mail, next-retry=" + e.getNextEpoch() + ", id=" + id, e.getCause()); + return e.getNextEpoch(); } catch (Exception e) { - log.error("fail to post mail, id=" + id, e); - return false; + // noinspection StringConcatenationArgumentToLogCall + log.error("fail to emit tiny-mail, retry=" + retry + ", id=" + id, e); + return ErrOther; } } @Override - public long emit(long id, boolean retry, boolean check) { - final WinMailSender po = winMailSenderDao.fetchOneById(id); - if (po == null) { - log.warn("mail not found by id={}, skip emit", id); - return -1; + public void afterPropertiesSet() { + ThreadPoolTaskSchedulerBuilder builder = new ThreadPoolTaskSchedulerBuilder(); + TaskSchedulingProperties scheduler = tinyMailServiceProp.getScheduler(); + builder = builder.poolSize(scheduler.getPool().getSize()); + builder = builder.threadNamePrefix(scheduler.getThreadNamePrefix()); + TaskSchedulingProperties.Shutdown shutdown = scheduler.getShutdown(); + builder = builder.awaitTermination(shutdown.isAwaitTermination()); + builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); + + taskScheduler = TaskSchedulerHelper.Ttl(builder); + taskScheduler.initialize(); + isShutdown = false; + log.info("tiny-mail taskScheduler, prefix={}", taskScheduler.getThreadNamePrefix()); + + final long idle = tinyMailServiceProp.getBootScan().toMillis(); + if (idle > 0) { + log.info("tiny-mail schedule boot-scan after={} ms", idle); + var task = taskScheduler.schedule(this::scanIdle, Instant.ofEpochMilli(ThreadNow.millis() + idle)); + idleScanTask.set(task); + } + + statusHookHolder = SingletonSupplier.of(() -> statusHookProvider.orderedStream().toList()); + lazyBeanHolder = SingletonSupplier.of(() -> { + Map map = new HashMap<>(); + for (TinyMailLazy bean : lazyBeanProvider) { + TinyMailLazy old = map.put(bean.lazyBean(), bean); + if (old != null) { + log.error("lazy bean name existed, name={}, new-bean={}", old.lazyBean(), bean.getClass()); + } + } + return map.isEmpty() ? Collections.emptyMap() : map; + }); + } + + @EventListener(ContextClosedEvent.class) + public void destroy() { + isShutdown = true; + + if (taskScheduler != null) { + taskScheduler.shutdown(); + } + + int size = 0; + for (ScheduledFuture task : asyncMailTask.values()) { + task.cancel(false); + size++; + } + + if (size > 0) { + asyncMailTask.clear(); + log.info("cancel async mail for shutdown, size={}", size); + } + + ScheduledFuture task = idleScanTask.get(); + if (task != null) { + task.cancel(false); + log.info("cancel async scan mail for shutdown"); } - return doAsyncFreshSend(po, null, retry, check); } @Override @SuppressWarnings("DuplicatedCode") - public long save(@NotNull TinyMailPlain msg) { + public long save(@NotNull TinyMailPlain msg, boolean check) { final String conf = msg.getConf(); final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf); - AssertArgs.notNull(config, "mail conf={} not found", conf); + AssertArgs.notNull(config, "skip tiny-mail conf={} not found", conf); final WinMailSender po = new WinMailSender(); final boolean isNew = msg.getId() == null || msg.getId() <= 0; @@ -194,7 +281,7 @@ public long save(@NotNull TinyMailPlain msg) { } else { id = msg.getId(); - Null.notNull(msg.getNextSend(), po::setNextSend); + IfSetter.nonnull(po::setNextSend, msg.getNextSend()); } po.setId(id); @@ -208,21 +295,27 @@ public long save(@NotNull TinyMailPlain msg) { po.setMailReply(toString(msg.getReply(), config.getReply())); po.setMailSubj(msg.getSubject()); po.setMailText(msg.getContent()); - po.setMailHtml(BoxedCastUtil.orElse(msg.getHtml(), config.getHtml())); - po.setMailFile(toStringMap(msg.getAttachment())); + po.setMailHtml(EmptySugar.emptyOrElse(msg.getHtml(), config::getHtml)); + po.setMailFile(toString(msg.getAttachment())); po.setMailMark(msg.getMark()); + po.setMailDate(md); + po.setLazyBean(msg.getLazyBean()); + po.setLazyPara(msg.getLazyPara()); - Null.notNull(msg.getMaxFail(), po::setMaxFail); - Null.notNull(msg.getMaxDone(), po::setMaxDone); + // PropertyMapper + po.setMaxFail(EmptySugar.emptyOrElse(msg.getMaxFail(), tinyMailServiceProp::getMaxFail)); + po.setMaxDone(EmptySugar.emptyOrElse(msg.getMaxDone(), tinyMailServiceProp::getMaxDone)); - Null.notNull(msg.getRefType(), po::setRefType); - Null.notNull(msg.getRefKey1(), po::setRefKey1); - Null.notNull(msg.getRefKey2(), po::setRefKey2); + IfSetter.nonnull(po::setRefType, msg.getRefType()); + IfSetter.nonnull(po::setRefKey1, msg.getRefKey1()); + IfSetter.nonnull(po::setRefKey2, msg.getRefKey2()); - // try to check message format - final TinyMailMessage tms = makeMailMessage(config, po, null); - mailSenderManager.checkMessage(tms); + if (check) { + // try to check message format + final TinyMailMessage tms = makeMailMessage(config, po, null); + mailSenderManager.checkMessage(tms); + } journalService.commit(Jane.Insert, journal -> { if (isNew) { @@ -239,17 +332,72 @@ public long save(@NotNull TinyMailPlain msg) { } @Override - public int scan() { + public int scan(Long idle) { + if (idle == null) return scanSync(); + + idleScanMills.set(idle); + final ScheduledFuture task = idleScanTask.get(); + if (task != null) task.cancel(false); + + return scanIdle(); + } + + @NotNull + public ArrayList listAsyncMailQueue() { + synchronized (asyncMailQueue) { + return new ArrayList<>(asyncMailQueue); + } + } + + @NotNull + public TreeMap> listAsyncMailTask() { + return new TreeMap(asyncMailTask); + } + + protected final AtomicLong idleScanMills = new AtomicLong(-1); + protected final AtomicReference> idleScanTask = new AtomicReference<>(); + + protected int scanIdle() { + int size = -1; + try { + size = scanSync(); + } + catch (Exception e) { + log.error("fail to scanSync", e); + } + + long idle = idleScanMills.get(); + + if (idle < 0) { // reset to prop + idle = tinyMailServiceProp.getScanIdle().toMillis(); + idleScanMills.set(idle); + log.info("reset tiny-mail scan idle to prop={} ms", idle); + } + + if (idle > 0) { + var task = taskScheduler.schedule(this::scanIdle, Instant.ofEpochMilli(ThreadNow.millis() + idle)); + idleScanTask.set(task); + log.info("plan tiny-mail scan, idle={} ms", idle); + } + else { + idleScanTask.set(null); + log.info("stop tiny-mail scan, idle={} ms", idle); + } + + return size; + } + + protected int scanSync() { final long now = ThreadNow.millis(); final LocalDateTime min = DateLocaling.sysLdt(now - tinyMailServiceProp.getMaxNext().toMillis()); final LocalDateTime max = DateLocaling.sysLdt(now + tinyMailServiceProp.getTryNext().toMillis()); - log.info("scan misfire-mail to queue, min={}, max={}", min, max); + log.info("scan tiny-mail to queue, next-send min={}, max={}", min, max); final WinMailSenderTable t = winMailSenderDao.getTable(); - final List pos = winMailSenderDao + final List mails = winMailSenderDao .ctx() .selectFrom(t) - .where(t.NextSend.gt(min).and(t.NextSend.lt(max))) + .where(t.NextSend.ge(min).and(t.NextSend.lt(max))) .fetch() .into(WinMailSender.class) .stream() @@ -258,50 +406,39 @@ public int scan() { .toList(); // - final int size = pos.size(); - log.info("plan misfire-mail, size={}", size); + final int size = mails.size(); if (size > 0) { - asyncMails.addAll(pos); - taskScheduler.schedule(this::doAsyncBatchSend, Instant.ofEpochMilli(now)); + planAsyncMail(mails); } return size; } - @Override - public void afterPropertiesSet() { - final long bms = tinyMailServiceProp.getBootScan().toMillis(); - if (bms > 0) { - taskScheduler.schedule(this::scan, Instant.ofEpochMilli(ThreadNow.millis() + bms)); - } - } - - private TinyMailMessage makeMailMessage(@NotNull TinyMailConfig config, @NotNull WinMailSender po, @Nullable TinyMail msg) { + protected TinyMailMessage makeMailMessage(@NotNull TinyMailConfig config, @NotNull WinMailSender po, @Nullable TinyMail msg) { final TinyMailMessage message = new TinyMailMessage(); - message.adopt(config); + TinyMailConfig.ConfSetter.toAny(message, config); message.setBizId(po.getId()); if (msg == null) { message.setFrom(po.getMailFrom()); - message.setTo(arrayOrNull(po.getMailTo(), true)); - message.setCc(arrayOrNull(po.getMailCc(), true)); - message.setBcc(arrayOrNull(po.getMailBcc(), true)); - message.setReply(toStrOrNull(po.getMailReply())); + message.setTo(commaArray(po.getMailTo())); + message.setCc(commaArray(po.getMailCc())); + message.setBcc(commaArray(po.getMailBcc())); + message.setReply(EmptySugar.emptyToNull(po.getMailReply())); message.setHtml(po.getMailHtml()); message.setSubject(po.getMailSubj()); message.setContent(po.getMailText()); - final Map files = toResource(po.getMailFile()); + final Map files = resourceString(po.getMailFile()); if (!files.isEmpty()) { message.setAttachment(files); } - message.setBizMark(toStrOrNull(po.getMailMark())); + message.setBizMark(EmptySugar.emptyToNull(po.getMailMark())); } else { - if (msg.getFrom() != null) message.setFrom(msg.getFrom()); - if (msg.getTo() != null) message.setTo(msg.getTo()); - if (msg.getCc() != null) message.setCc(msg.getCc()); - if (msg.getBcc() != null) message.setBcc(msg.getBcc()); - if (msg.getReply() != null) message.setReply(msg.getReply()); - if (msg.getHtml() != null) message.setHtml(msg.getHtml()); + IfSetter.nonnull(message::setFrom, msg.getFrom()); + IfSetter.nonnull(message::setTo, msg.getTo()); + IfSetter.nonnull(message::setCc, msg.getCc()); + IfSetter.nonnull(message::setReply, msg.getReply()); + IfSetter.nonnull(message::setHtml, msg.getHtml()); message.setSubject(msg.getSubject()); message.setContent(msg.getContent()); message.setAttachment(msg.getAttachment()); @@ -311,7 +448,7 @@ private TinyMailMessage makeMailMessage(@NotNull TinyMailConfig config, @NotNull return message; } - private WinMailSender saveMailSender(@NotNull TinyMailConfig config, @NotNull TinyMail msg) { + protected WinMailSender saveMailSender(@NotNull TinyMailConfig config, @NotNull TinyMail msg) { final WinMailSender po = new WinMailSender(); final long id = lightIdService.getId(winMailSenderDao.getTable()); po.setId(id); @@ -327,19 +464,21 @@ private WinMailSender saveMailSender(@NotNull TinyMailConfig config, @NotNull Ti po.setMailReply(toString(msg.getReply(), config.getReply())); po.setMailSubj(msg.getSubject()); po.setMailText(msg.getContent()); - po.setMailHtml(BoxedCastUtil.orElse(msg.getHtml(), config.getHtml())); - po.setMailFile(toString(msg.getAttachment())); + po.setMailHtml(EmptySugar.emptyOrElse(msg.getHtml(), config::getHtml)); + po.setMailFile(stringResource(msg.getAttachment())); po.setMailMark(msg.getMark()); final LocalDateTime md = msg.getDate(); po.setMailDate(md); + po.setLazyBean(msg.getLazyBean()); + po.setLazyPara(msg.getLazyPara()); - po.setMaxFail(BoxedCastUtil.orElse(msg.getMaxFail(), tinyMailServiceProp.getMaxFail())); - po.setMaxDone(BoxedCastUtil.orElse(msg.getMaxDone(), tinyMailServiceProp.getMaxDone())); + po.setMaxFail(EmptySugar.emptyOrElse(msg.getMaxFail(), tinyMailServiceProp::getMaxFail)); + po.setMaxDone(EmptySugar.emptyOrElse(msg.getMaxDone(), tinyMailServiceProp::getMaxDone)); - Null.notNull(msg.getRefType(), po::setRefType); - Null.notNull(msg.getRefKey1(), po::setRefKey1); - Null.notNull(msg.getRefKey2(), po::setRefKey2); + IfSetter.nonnull(po::setRefType, msg.getRefType()); + IfSetter.nonnull(po::setRefKey1, msg.getRefKey1()); + IfSetter.nonnull(po::setRefKey2, msg.getRefKey2()); // Optimist lock po.setNextLock(0); @@ -357,11 +496,24 @@ private WinMailSender saveMailSender(@NotNull TinyMailConfig config, @NotNull Ti return po; } - private boolean notMatchProp(WinMailSender po) { + @Nullable + protected TinyMailLazy findLazyBean(String name) { + return lazyBeanHolder.obtain().get(name); + } + + @NotNull + protected List findStatusHook() { + return statusHookHolder.obtain(); + } + + /** + * should skip sending, condition not match + */ + protected boolean notMatchProp(@NotNull WinMailSender po) { if (tinyMailServiceProp.isOnlyApp()) { final String ma = po.getMailApps(); if (StringUtils.isNotEmpty(ma) && !appName.equalsIgnoreCase(ma)) { - log.debug("skip only send app-mail app={}, id={}", appName, po.getId()); + log.debug("skip only send app tiny-mail app={}, id={}", appName, po.getId()); return true; } } @@ -370,34 +522,48 @@ private boolean notMatchProp(WinMailSender po) { if (StringUtils.isNotEmpty(mrs)) { final RunMode rmd = RuntimeMode.getRunMode(); if (rmd == RunMode.Nothing) { - log.debug("skip only send run-mail, run={}, id={}", mrs, po.getId()); + log.debug("skip only send run tiny-mail, run={}, id={}", mrs, po.getId()); return true; } - if (!RuntimeMode.hasRunMode(arrayOrNull(mrs, true))) { - log.debug("skip only send run-mail, run={}, cur={}, id={}", mrs, rmd, po.getId()); + if (!RuntimeMode.voteRunMode(mrs)) { + log.debug("skip only send run tiny-mail, run={}, cur={}, id={}", mrs, rmd, po.getId()); return true; } } } - final int maxDone = BoxedCastUtil.orElse(po.getMaxDone(), 0) > 0 ? po.getMaxDone() : tinyMailServiceProp.getMaxDone(); - final int sumDone = BoxedCastUtil.orElse(po.getSumDone(), 0); + final int poDone = BoxedCastUtil.orZero(po.getMaxDone()); + final int maxDone = poDone > 0 ? poDone : tinyMailServiceProp.getMaxDone(); + final int sumDone = BoxedCastUtil.orZero(po.getSumDone()); if (sumDone >= maxDone) { - log.debug("skip max-send, max={}, sum={}, id={}", maxDone, sumDone, po.getId()); + log.debug("skip max-send tiny-mail, max={}, sum={}, id={}", maxDone, sumDone, po.getId()); return true; } - final int maxFail = BoxedCastUtil.orElse(po.getMaxFail(), 0) > 0 ? po.getMaxFail() : tinyMailServiceProp.getMaxFail(); - final int sumFail = BoxedCastUtil.orElse(po.getSumFail(), 0); + final int poFail = BoxedCastUtil.orZero(po.getMaxFail()); + final int maxFail = poFail > 0 ? poFail : tinyMailServiceProp.getMaxFail(); + final int sumFail = BoxedCastUtil.orZero(po.getSumFail()); if (sumFail >= maxFail) { - log.debug("skip max-fail, max={}, sum={}, id={}", maxFail, sumFail, po.getId()); + log.debug("skip max-fail tiny-mail, max={}, sum={}, id={}", maxFail, sumFail, po.getId()); return true; } + if (po.getMailText() == null) { + String bn = po.getLazyBean(); + var bean = findLazyBean(bn); + if (bean == null) { + log.error("stop lazy tiny-mail, not found bean={}, id={}", bn, po.getId()); + return true; + } + } + return false; } - private boolean notNextLock(WinMailSender po, long now) { + /** + * should skip sending, others do it + */ + protected boolean notNextLock(@NotNull WinMailSender po, long now) { final WinMailSenderTable t = winMailSenderDao.getTable(); final int rc = winMailSenderDao .ctx() @@ -408,94 +574,148 @@ private boolean notNextLock(WinMailSender po, long now) { .execute(); if (rc <= 0) { - log.debug("skip not-next-lock mail, id={}", po.getId()); + log.debug("skip not-next-lock tiny-mail, id={}", po.getId()); return true; } return false; } - private void saveStatusAndRetry(@NotNull WinMailSender po, TinyMailMessage message, long cost, long now, Exception exception, boolean retry, boolean check, boolean rethrow) { + protected long doSend(boolean sync, @NotNull TinyMail message, boolean retry) { + final String conf = message.getConf(); + final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf); + AssertArgs.notNull(config, "skip tiny-mail conf={} not found", conf); + + final WinMailSender po = saveMailSender(config, message); + + if (isShutdown) { + log.warn("save but skip tiny-mail for shutdwon, subject={}", message.getSubject()); + return ErrOther; + } + + final TinyMailMessage mailMessage = makeMailMessage(config, po, message); + if (sync) { + return doSyncSend(po, mailMessage, retry, true); + } + else { + return doAsyncSend(po, mailMessage, retry, true); + } + } + + protected long doSend(boolean sync, long id, boolean retry, boolean check) { + if (isShutdown) { + log.warn("doSend skip tiny-mail for shutdwon, id={}", id); + return ErrOther; + } + + final WinMailSender po = winMailSenderDao.fetchOneById(id); + AssertArgs.notNull(po, "skip tiny-mail not found by id={}", id); + + final String conf = po.getMailConf(); + final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf); + AssertArgs.notNull(config, "skip tiny-mail conf={} not found, id={}", conf, id); + + final TinyMailMessage mailMessage = makeMailMessage(config, po, null); + if (sync) { + return doSyncSend(po, mailMessage, retry, check); + } + else { + return doAsyncSend(po, mailMessage, retry, check); + } + } + + /** + * next if done lt max or exception and retry + */ + protected long saveSendResult(@NotNull WinMailSender po, long cost, long exit, Exception error, boolean retry) { long nextSend = -1; - boolean notHookStop = true; + final Long id = po.getId(); try { final WinMailSenderTable t = winMailSenderDao.getTable(); final Map setter = new HashMap<>(); - if (exception == null) { + if (error == null) { setter.put(t.LastFail, null); - setter.put(t.LastDone, DateLocaling.sysLdt(now)); + setter.put(t.LastDone, DateLocaling.sysLdt(exit)); setter.put(t.LastCost, cost); - if (po.getSumDone() + 1 >= tinyMailServiceProp.getMaxDone()) { - setter.put(t.NextSend, EmptyValue.DATE_TIME); - log.debug("done mail by max-send id={}, subject={}", po.getId(), po.getMailSubj()); + final int poDone = BoxedCastUtil.orZero(po.getMaxDone()); + final int maxDone = poDone > 0 ? poDone : tinyMailServiceProp.getMaxDone(); + if (po.getSumDone() + 1 >= maxDone) { + log.debug("done tiny-mail by max-send id={}, subject={}", id, po.getMailSubj()); } else { - nextSend = now + tinyMailServiceProp.getTryNext().toMillis(); - setter.put(t.NextSend, DateLocaling.sysLdt(nextSend)); - log.debug("next done-mail id={}, subject={}", po.getId(), po.getMailSubj()); + nextSend = exit + tinyMailServiceProp.getTryNext().toMillis(); + log.debug("next done tiny-mail id={}, subject={}", id, po.getMailSubj()); } setter.put(t.SumSend, t.SumSend.add(1)); setter.put(t.SumDone, t.SumDone.add(1)); } else { - setter.put(t.LastFail, ThrowableUtil.toString(exception)); + setter.put(t.LastFail, ThrowableUtil.toString(error)); setter.put(t.LastDone, EmptyValue.DATE_TIME); setter.put(t.LastCost, cost); - final int maxFail = BoxedCastUtil.orElse(po.getMaxFail(), 0) > 0 ? po.getMaxFail() : tinyMailServiceProp.getMaxFail(); + final int poFail = BoxedCastUtil.orZero(po.getMaxFail()); + final int maxFail = poFail > 0 ? poFail : tinyMailServiceProp.getMaxFail(); if (po.getSumFail() + 1 >= maxFail) { - setter.put(t.NextSend, EmptyValue.DATE_TIME); - log.debug("done mail by max-fail id={}, subject={}", po.getId(), po.getMailSubj()); + log.debug("done tiny-mail by max-fail id={}, subject={}", id, po.getMailSubj()); } else if (retry) { - if (exception instanceof MailWaitException mwe) { + if (error instanceof MailStopException) { + // noinspection StringConcatenationArgumentToLogCall + log.warn("stop tiny-mail by stop-exception, id=" + id, error); + } + else if (error instanceof MailWaitException mwe) { if (mwe.isStopRetry()) { - setter.put(t.NextSend, EmptyValue.DATE_TIME); - log.error("stop stop-retry mail, id=" + po.getId(), exception); + // noinspection StringConcatenationArgumentToLogCall + log.error("stop tiny-mail by stop-retry, id=" + id, error); } else { nextSend = mwe.getWaitEpoch(); } } - else if (exception instanceof MailParseException || exception instanceof MessagingException) { - setter.put(t.NextSend, EmptyValue.DATE_TIME); - log.error("failed to parse, stop mail, id=" + po.getId(), exception); + else if (error instanceof MailParseException || error instanceof MessagingException) { + // noinspection StringConcatenationArgumentToLogCall + log.error("stop tiny-mail by failed parse, id=" + id, error); } else { - nextSend = now + tinyMailServiceProp.getTryNext().toMillis(); - } - - if (nextSend > 0) { - setter.put(t.NextSend, DateLocaling.sysLdt(nextSend)); - log.debug("next fail-mail id={}, subject={}", po.getId(), po.getMailSubj()); + nextSend = exit + tinyMailServiceProp.getTryNext().toMillis(); } } else { - setter.put(t.NextSend, EmptyValue.DATE_TIME); - log.error("stop not-retry mail, id=" + po.getId(), exception); + // noinspection StringConcatenationArgumentToLogCall + log.error("stop tiny-mail by not-retry, id=" + id, error); } + setter.put(t.SumSend, t.SumSend.add(1)); setter.put(t.SumFail, t.SumFail.add(1)); } - if (statusHooks != null) { - for (StatusHook sh : statusHooks) { - try { - if (sh.stop(po, cost, exception)) { - notHookStop = false; - } - } - catch (Exception e) { - log.error("should NOT throw in hook, hook-class=" + sh.getClass().getName(), e); + boolean hookStop = false; + + for (StatusHook sh : findStatusHook()) { + try { + if (sh.stop(po, cost, error)) { + hookStop = true; } } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.error("should NOT throw in hook, hook-class=" + sh.getClass().getName(), e); + } + } + + if (hookStop) { + log.debug("stop tiny-mail by hook, id={}", id); } - if (!notHookStop) { + if (nextSend > 0) { + setter.put(t.NextSend, DateLocaling.sysLdt(nextSend)); + log.debug("next tiny-mail id={}, subject={}", id, po.getMailSubj()); + } + else { setter.put(t.NextSend, EmptyValue.DATE_TIME); - log.debug("hook stop mail, id={}", po.getId()); } journalService.commit(Jane.Update, journal -> { @@ -504,183 +724,349 @@ else if (exception instanceof MailParseException || exception instanceof Messagi winMailSenderDao.ctx() .update(t) .set(setter) - .where(t.Id.eq(po.getId())) + .where(t.Id.eq(id)) .execute(); }); } catch (Exception e) { - log.error("failed to save mail status, id=" + po.getId() + ", subject=" + po.getMailSubj(), e); - nextSend = now + tinyMailServiceProp.getTryNext().toMillis(); + // noinspection StringConcatenationArgumentToLogCall + log.error("failed to save tiny-mail status, id=" + id + ", subject=" + po.getMailSubj(), e); + nextSend = exit + tinyMailServiceProp.getTryNext().toMillis(); } - if (exception == null) { - if (notHookStop && nextSend > 0) { - asyncMails.add(new AsyncMail(po.getId(), nextSend, retry, check, null, message)); - taskScheduler.schedule(this::doAsyncBatchSend, Instant.ofEpochMilli(nextSend)); - log.debug("schedule done-mail send, id={}, subject={}", po.getId(), po.getMailSubj()); - } + return nextSend; + } + + protected void editLazyMail(@NotNull WinMailSender po, @NotNull @Param.Out TinyMailMessage message) { + // nonnull means no need to edit + if (message.getContent() != null) return; + + final Long id = po.getId(); + final String bn = po.getLazyBean(); + var bean = findLazyBean(bn); + if (bean == null) { + throw new MailStopException("tiny-mail lazy-edit, not-found bean=" + bn + ", id=" + id); } - else { - if (notHookStop && retry && nextSend > 0 && nextSend - now < tinyMailServiceProp.getMaxNext().toMillis()) { - asyncMails.add(new AsyncMail(po.getId(), nextSend, retry, check, null, message)); - taskScheduler.schedule(this::doAsyncBatchSend, Instant.ofEpochMilli(nextSend)); - log.debug("schedule fail-mail send, id=" + po.getId() + ", subject=" + po.getMailSubj()); - } - else { - if (rethrow) { - if (exception instanceof RuntimeException) { - throw (RuntimeException) exception; - } - else { - throw new MailSendException("failed mail, id=" + po.getId() + ", subject=" + po.getMailSubj(), exception); - } - } - else { - log.debug("no rethrow or retry mail, id=" + po.getId() + ", subject=" + po.getMailSubj()); - } - } + + var edit = bean.lazyEdit(po.getLazyPara()); + if (edit == null) { + throw new MailStopException("tiny-mail lazy-edit, edit is null, id=" + id); + } + + final WinMailSenderTable t = winMailSenderDao.getTable(); + final Map setter = new HashMap<>(); + + Boolean html = edit.getHtml(); + if (html != null) { + setter.put(t.MailHtml, html); + message.setHtml(html); + } + + String subj = edit.getSubject(); + if (subj != null) { + setter.put(t.MailSubj, subj); + message.setSubject(subj); } + + String text = edit.getContent(); + if (text != null) { + setter.put(t.MailText, text); + message.setContent(text); + } + + Map file = edit.getAttachment(); + if (file != null && !file.isEmpty()) { + setter.put(t.MailFile, stringResource(file)); + message.setAttachment(file); + } + + if (setter.isEmpty()) { + throw new MailStopException("tiny-mail lazy-edit, edit is empty, id=" + id); + } + + log.debug("lazy-edit tiny-mail, id={}", id); + + journalService.commit(Jane.Lazify, journal -> { + setter.put(t.CommitId, journal.getCommitId()); + setter.put(t.ModifyDt, journal.getCommitDt()); + winMailSenderDao.ctx() + .update(t) + .set(setter) + .where(t.Id.eq(id)) + .execute(); + }); } - private boolean doSyncSend(@NotNull WinMailSender po, TinyMailMessage mailMessage, boolean retry, boolean check) { + private long doSyncSend(@NotNull WinMailSender po, @NotNull TinyMailMessage message, boolean retry, boolean check) { + if (isShutdown) { + log.warn("doSyncSend skip tiny-mail for shutdwon, id={}", po.getId()); + return ErrOther; + } + final long start; if (check) { - if (notMatchProp(po)) { - return false; - } + // condition not match + if (notMatchProp(po)) return ErrCheck; start = ThreadNow.millis(); - if (notNextLock(po, start)) { - return false; - } + // others do it + if (notNextLock(po, start)) return ErrCheck; } else { start = ThreadNow.millis(); } - Exception exception = null; + final Long id = po.getId(); try { - mailSenderManager.singleSend(mailMessage); + editLazyMail(po, message); + } + catch (Exception err) { + // noinspection StringConcatenationArgumentToLogCall + log.error("stop tiny-mail for lazy-edit error, id=" + id, err); + saveSendResult(po, 0, start, err, false); // no retry is lazy fail + return ErrCheck; + } + + Exception error = null; + final long next; + try { + mailSenderManager.singleSend(message); } catch (Exception e) { - exception = e; + error = e; } finally { - final long now = ThreadNow.millis(); - saveStatusAndRetry(po, mailMessage, now - start, now, exception, retry, check, true); + final long end = ThreadNow.millis(); + next = saveSendResult(po, end - start, end, error, retry); + } + + if (next > 0) { + planAsyncMail(new AsyncMail(id, next, retry, check, null, message)); + log.debug("plan tiny-mail next-send, err={}, id={}, subject={}", error == null, id, po.getMailSubj()); + if (error != null) throw new MailRetryException(next, error); } - return exception == null; + if (error == null) return Success; + + // noinspection ConstantValue + if (error instanceof RuntimeException re) { + throw re; // rethrow exception + } + else { + throw new MailSendException("failed tiny-mail, id=" + id + ", subject=" + po.getMailSubj(), error); + } } - private long doAsyncFreshSend(@NotNull WinMailSender po, TinyMailMessage message, boolean retry, boolean check) { - if (check && notMatchProp(po)) return -1; + private long doAsyncSend(@NotNull WinMailSender po, @NotNull TinyMailMessage message, boolean retry, boolean check) { + if (isShutdown) { + log.warn("doAsyncSend skip tiny-mail for shutdwon, id={}", po.getId()); + return ErrOther; + } - final LocalDateTime md = po.getNextSend(); - final long mds = md == null ? 0 : DateLocaling.sysEpoch(md); - final long now = ThreadNow.millis(); + // condition not match + if (check && notMatchProp(po)) return ErrCheck; + + final long start = ThreadNow.millis(); final Long id = po.getId(); - final long nxt; - if (mds > now) { - nxt = mds; - log.debug("plan async date={} id={}", md, id); + try { + editLazyMail(po, message); + } + catch (Exception err) { + // noinspection StringConcatenationArgumentToLogCall + log.error("stop tiny-mail for lazy-edit error, id=" + id, err); + saveSendResult(po, 0, start, err, false); // no retry is lazy fail + return ErrCheck; + } + + final LocalDateTime ns = po.getNextSend(); + final long nsm = ns == null ? 0 : DateLocaling.sysEpoch(ns); + + long next; + if (nsm > start) { + next = nsm; + log.debug("plan async tiny-mail date={} id={}", ns, id); } else { - nxt = now; - log.debug("plan async date=now id={}", id); + next = start; + log.debug("plan async tiny-mail date=now id={}", id); } - // check format - mailSenderManager.checkMessage(message); + planAsyncMail(new AsyncMail(id, next, retry, check, po, message)); + return next; + } - asyncMails.add(new AsyncMail(id, nxt, retry, check, po, message)); - taskScheduler.schedule(this::doAsyncBatchSend, Instant.ofEpochMilli(nxt)); - return nxt; + private void planAsyncMail(@NotNull AsyncMail mail) { + log.debug("plan async tiny-mail, id={}", mail.id); + synchronized (asyncMailQueue) { + asyncMailQueue.removeIf(it -> it.id == mail.id); + asyncMailQueue.add(mail); + } + planAsyncMail(mail.next); } - private void doAsyncBatchSend() { - final long start = ThreadNow.millis(); - final int bz = tinyMailServiceProp.getBatchSize(); + private void planAsyncMail(@NotNull Collection mails) { + if (mails.isEmpty()) return; - try { - final AtomicInteger count = new AtomicInteger(bz > 0 ? bz : 1); - final HashMap mails = new HashMap<>(count.get()); - asyncMails.removeIf(it -> { - if (count.get() <= 0) return false; - if (it.next > start) { - return false; - } - else { - mails.put(it.id, it); - count.decrementAndGet(); - return true; - } - }); + final Set ids = new HashSet<>(); + long next = -1; + for (AsyncMail m : mails) { + ids.add(m.id); + if (next <= 0) { + next = m.next; + } + else if (m.next < next) { + next = m.next; + } + } - if (mails.isEmpty()) return; + log.debug("plan async tiny-mail, size={}", ids.size()); + synchronized (asyncMailQueue) { + asyncMailQueue.removeIf(it -> ids.contains(it.id)); + asyncMailQueue.addAll(mails); + } - final Map freshPo = new HashMap<>(); - final List dirtyIds = new ArrayList<>(); - for (AsyncMail am : mails.values()) { - if (am.fresher == null) { - dirtyIds.add(am.id); + planAsyncMail(next); + } + + private void planAsyncMail(long next) { + if (next <= 0) { + log.debug("plan async tiny-mail, skip={}", next); + return; + } + + if (log.isDebugEnabled()) { + log.debug("plan async tiny-mail, next={}", DateLocaling.sysLdt(next)); + } + + final long nxt = (next / 1_000L + 1) * 1_000L; // ceiling to second + asyncMailTask.computeIfAbsent(nxt, k -> + taskScheduler.schedule(() -> { + try { + sendAsyncMail(); } - else { - freshPo.put(am.id, am.fresher); + finally { + asyncMailTask.remove(k); } - } + }, Instant.ofEpochMilli(nxt)) + ); + } - if (!dirtyIds.isEmpty()) { - final WinMailSenderTable t = winMailSenderDao.getTable(); - winMailSenderDao - .ctx() - .selectFrom(t) - .where(t.Id.in(dirtyIds)) - .fetchInto(WinMailSender.class) - .forEach(po -> freshPo.put(po.getId(), po)); - } + private void sendAsyncMail() { + final long start = ThreadNow.millis(); + final int count = Math.max(tinyMailServiceProp.getBatchSize(), 1); + final HashMap mails = new HashMap<>(count); + final HashMap freshPo = new HashMap<>(count); + final ArrayList dirtyIds = new ArrayList<>(count); + final ArrayList messages = new ArrayList<>(count); + final ArrayList nexts = new ArrayList<>(count); - final List messages = new ArrayList<>(freshPo.size()); - for (WinMailSender po : freshPo.values()) { - final AsyncMail am = mails.get(po.getId()); + try { + while (true) { + mails.clear(); + freshPo.clear(); + dirtyIds.clear(); + messages.clear(); + nexts.clear(); + + // get TopN by prioriy, remove dupli + synchronized (asyncMailQueue) { + asyncMailQueue.removeIf(it -> { + if (it.next > start) return false; + if (mails.size() >= count) return mails.containsKey(it.id); + + mails.put(it.id, it); + return true; + }); + } - if (am != null && am.check && (notMatchProp(po) || notNextLock(po, start))) { - continue; + if (mails.isEmpty()) return; + + for (AsyncMail am : mails.values()) { + if (am.fresher == null) { + dirtyIds.add(am.id); + } + else { + freshPo.put(am.id, am.fresher); + } } - if (am != null && am.message != null) { - messages.add(am.message); + if (!dirtyIds.isEmpty()) { + final WinMailSenderTable t = winMailSenderDao.getTable(); + // mails >= freshPo + dirtyIds(db may less) + winMailSenderDao + .ctx() + .selectFrom(t) + .where(t.Id.in(dirtyIds)) + .fetchInto(WinMailSender.class) + .forEach(po -> freshPo.put(po.getId(), po)); } - else { - final String conf = po.getMailConf(); - final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf); - if (config == null) { - log.warn("mail conf={} not found", conf); + + for (WinMailSender po : freshPo.values()) { + final Long id = po.getId(); + final AsyncMail am = mails.get(id); + if (am == null) continue; // never null + + if (am.check && (notMatchProp(po) || notNextLock(po, start))) { + continue; + } + + final TinyMailMessage msg; + if (am.message != null) { + msg = am.message; } else { - messages.add(makeMailMessage(config, po, null)); + final String conf = po.getMailConf(); + final TinyMailConfig config = mailConfigProvider.bynamedConfig(conf); + if (config == null) { + log.warn("tiny-mail conf={} not found, id={}", conf, id); + continue; + } + else { + msg = makeMailMessage(config, po, null); + } + } + + try { + editLazyMail(po, msg); + messages.add(msg); + } + catch (Exception err) { + // noinspection StringConcatenationArgumentToLogCall + log.error("stop tiny-mail for lazy-edit error, id=" + id, err); + saveSendResult(po, 0, start, err, false); // no retry is lazy fail } } - } - final List results = mailSenderManager.batchSend(messages); - for (BatchResult result : results) { - final TinyMailMessage msg = result.getTinyMessage(); - final WinMailSender po = freshPo.get(msg.getBizId()); // must not null - final AsyncMail am = mails.get(po.getId()); - saveStatusAndRetry(po, msg, result.getCostMillis(), result.getDoneMillis(), result.getException(), am != null && am.retry, am != null && am.check, false); + final List brs = mailSenderManager.batchSend(messages); + + for (BatchResult br : brs) { + final TinyMailMessage msg = br.getTinyMessage(); + final WinMailSender po = freshPo.get(msg.getBizId()); // must not null + final AsyncMail am = mails.get(po.getId()); + if (am == null) continue; // never null + + long nxt = saveSendResult(po, br.getCostMillis(), br.getExitMillis(), br.getException(), am.retry); + if (nxt > 0) { + nexts.add(new AsyncMail(po.getId(), nxt, am.retry, am.check, null, msg)); + } + } + + planAsyncMail(nexts); } } finally { - final int size = asyncMails.size(); - if (size > 0) { - final long next = start + tinyMailServiceProp.getTryNext().toMillis(); - taskScheduler.schedule(this::doAsyncBatchSend, Instant.ofEpochMilli(next)); - if (size > tinyMailServiceProp.getWarnSize()) { - log.warn("plan next war-size={}, idle={}", size, tinyMailServiceProp.getTryNext()); + final int ws = tinyMailServiceProp.getWarnSize(); + final int qs; + synchronized (asyncMailQueue) { + qs = asyncMailQueue.size(); + } + if (qs > 0) { + planAsyncMail(start + tinyMailServiceProp.getTryNext().toMillis()); + if (qs > ws) { + log.warn("plan tiny-mail queue-size={}, idle={}", qs, tinyMailServiceProp.getTryNext()); } else { - log.debug("plan next size={}, idle={}", size, tinyMailServiceProp.getTryNext()); + log.debug("plan tiny-mail queue-size={}, idle={}", qs, tinyMailServiceProp.getTryNext()); } } } @@ -694,7 +1080,7 @@ private String toString(String[] arr, String[] elz) { @Nullable private String toString(String str, String[] elz) { - return notValue(str) + return invalid(str) ? (elz == null || elz.length == 0 ? null : String.join(",", elz)) @@ -703,46 +1089,40 @@ private String toString(String str, String[] elz) { @Nullable private String toString(String str, String elz) { - return notValue(str) ? elz : str; + return invalid(str) ? elz : str; } - @SneakyThrows - @NotNull - private String toString(Map file) { - if (file == null || file.isEmpty()) return Null.Str; - Map nameUrl = new LinkedHashMap<>(file.size()); - for (Map.Entry en : file.entrySet()) { - nameUrl.put(en.getKey(), CommonPropHelper.toString(en.getValue())); - } - return JacksonHelper.string(nameUrl, true); - } - - @SneakyThrows - @NotNull - private String toStringMap(Map file) { - if (file == null || file.isEmpty()) return Null.Str; - return JacksonHelper.string(file, true); + @Nullable + private String toString(Map file) { + if (file == null || file.isEmpty()) return null; + return FastJsonHelper.string(file); } @Nullable - private String toStrOrNull(String str) { - return (str == null || str.isEmpty()) ? null : str; + private String stringResource(Map file) { + if (file == null || file.isEmpty()) return null; + + Map nameUrl = new LinkedHashMap<>(); + for (Map.Entry en : file.entrySet()) { + nameUrl.put(en.getKey(), PropHelper.stringResource(en.getValue())); + } + return toString(nameUrl); } @NotNull - private Map toResource(String map) { - if (map == null || map.isEmpty()) return Collections.emptyMap(); + private Map resourceString(String jsonMap) { + if (EmptySugar.asEmptyValue(jsonMap)) return Collections.emptyMap(); + final Map rst = new LinkedHashMap<>(); - final Iterator> node = JacksonHelper.object(map).fields(); - while (node.hasNext()) { - final Map.Entry en = node.next(); - rst.put(en.getKey(), resourceLoader.getResource(en.getValue().asText())); + Map map = FastJsonHelper.object(jsonMap, Map.class, String.class, String.class); + for (Map.Entry en : map.entrySet()) { + rst.put(en.getKey(), PropHelper.resourceString(en.getValue(), resourceLoader)); } return rst; } @Data - private static class AsyncMail implements Comparable { + public static class AsyncMail implements Comparable { private final long id; private final long next; private final boolean retry; @@ -760,6 +1140,7 @@ public int compareTo(@NotNull TinyMailServiceImpl.AsyncMail o) { public enum Jane { Insert, - Update + Update, + Lazify } } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/spring/bean/TinyMailConfiguration.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/spring/bean/TinyMailConfiguration.java index 7928c1d35..f832f175a 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/spring/bean/TinyMailConfiguration.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/spring/bean/TinyMailConfiguration.java @@ -2,12 +2,15 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jetbrains.annotations.NotNull; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.web.bind.annotation.RestController; +import pro.fessional.wings.silencer.runner.ApplicationReadyEventRunner; +import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.tiny.mail.controller.MailListController; import pro.fessional.wings.tiny.mail.database.TinyMailDatabase; @@ -15,10 +18,14 @@ import pro.fessional.wings.tiny.mail.sender.MailNotice; import pro.fessional.wings.tiny.mail.sender.MailSenderManager; import pro.fessional.wings.tiny.mail.sender.MailSenderProvider; +import pro.fessional.wings.tiny.mail.service.TinyMailLazy; import pro.fessional.wings.tiny.mail.service.TinyMailService; import pro.fessional.wings.tiny.mail.spring.prop.TinyMailConfigProp; import pro.fessional.wings.tiny.mail.spring.prop.TinyMailSenderProp; +import java.util.HashMap; +import java.util.Map; + /** * @author trydofor * @since 2022-08-03 @@ -77,4 +84,28 @@ public MailSenderProvider mailSenderProvider(JavaMailSender defaultSender) { log.info("TinyMail spring-bean mailSenderProvider"); return new MailSenderProvider(defaultSender); } + + /** + * Check if the bean name is duplicated + */ + @Bean + @ConditionalWingsEnabled + public ApplicationReadyEventRunner tinyMailLazyRunner(@NotNull Map lazyMap) { + log.info("TinyMail spring-runs tinyMailLazyRunner"); + return new ApplicationReadyEventRunner(WingsOrdered.Lv3Service, ignored -> { + Map map = new HashMap<>(); + for (var en : lazyMap.entrySet()) { + TinyMailLazy bean = en.getValue(); + TinyMailLazy old = map.put(bean.lazyBean(), bean); + if (old != null) { + throw new IllegalStateException( + "lazy bean name existed, name=" + old.lazyBean() + + ", new-bean-name=" + en.getKey() + + ", old-bean-class=" + old.getClass() + ); + } + } + log.info("tiny-mail TinyMailLazy beans, size=" + map.size()); + }); + } } diff --git a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/spring/prop/TinyMailServiceProp.java b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/spring/prop/TinyMailServiceProp.java index 8c7c8467f..c506bfff5 100644 --- a/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/spring/prop/TinyMailServiceProp.java +++ b/radiant/tiny-mail/src/main/java/pro/fessional/wings/tiny/mail/spring/prop/TinyMailServiceProp.java @@ -1,6 +1,7 @@ package pro.fessional.wings.tiny.mail.spring.prop; import lombok.Data; +import org.springframework.boot.autoconfigure.task.TaskSchedulingProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import java.time.Duration; @@ -59,17 +60,26 @@ public class TinyMailServiceProp { * * @see #Key$warnSize */ - private int warnSize = 1000; + private int warnSize = 50; public static final String Key$warnSize = Key + ".warn-size"; /** - * how long after start, scan for unsent mail, `0` for no scan. + * idle time after afterPropertiesSet to scan for unsent/misfired mail, `<=0` for disable. * * @see #Key$bootScan + * @see #Key$scanIdle */ private Duration bootScan = Duration.ofSeconds(60); public static final String Key$bootScan = Key + ".boot-scan"; + /** + * idle time to scan for unsent/misfired mail. `<=0` for disable. + * + * @see #Key$scanIdle + */ + private Duration scanIdle = Duration.ofMinutes(5); + public static final String Key$scanIdle = Key + ".scan-idle"; + /** * whether to send emails from this app only. * @@ -85,4 +95,11 @@ public class TinyMailServiceProp { */ private boolean onlyRun = true; public static final String Key$onlyRun = Key + ".only-run"; + + /** + * @see TaskSchedulingProperties + * @see #Key$scheduler + */ + private TaskSchedulingProperties scheduler = null; + public static final String Key$scheduler = Key + ".scheduler"; } diff --git a/radiant/tiny-mail/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/radiant/tiny-mail/src/main/resources/META-INF/additional-spring-configuration-metadata.json index d307a8a41..b6913714e 100644 --- a/radiant/tiny-mail/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/radiant/tiny-mail/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,10 +1,19 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.controller"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.database.autogen.tables.daos"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.service.impl"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.conf.TinyMailAutoConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.controller.MailListController", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.mail.mvc-list for short."}, + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.controller.MailSendController", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.mail.mvc-send for short."}, + + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.database.autogen.tables.daos.WinMailSenderDao", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.service.impl.TinyMailListServiceImpl", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.service.impl.TinyMailServiceImpl", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.bean.TinyMailConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.bean.TinyMailConfiguration$DaoServScan", "type": "java.lang.Boolean"}, @@ -13,12 +22,9 @@ {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.bean.TinyMailConfiguration.mailNotice", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.bean.TinyMailConfiguration.mailSenderManager", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.bean.TinyMailConfiguration.mailSenderProvider", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.bean.TinyMailConfiguration.tinyMailLazyRunner", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.mail.controller.MailListController", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.mail.mvc-list for short."}, - {"name": "wings.enabled.pro.fessional.wings.tiny.mail.controller.MailSendController", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.mail.mvc-send for short."}, - - {"name": "wings.enabled.pro.fessional.wings.tiny.mail.service.impl.TinyMailListServiceImpl", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.mail.service.impl.TinyMailServiceImpl", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.tiny.mail.spring.conf.TinyMailAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/radiant/tiny-mail/src/main/resources/wings-conf/wings-tinymail-service-79.properties b/radiant/tiny-mail/src/main/resources/wings-conf/wings-tinymail-service-79.properties index ec095542d..61797385c 100644 --- a/radiant/tiny-mail/src/main/resources/wings-conf/wings-tinymail-service-79.properties +++ b/radiant/tiny-mail/src/main/resources/wings-conf/wings-tinymail-service-79.properties @@ -10,9 +10,17 @@ wings.tiny.mail.service.try-next=1m wings.tiny.mail.service.batch-size=10 ## if this capacity is exceeded, log it as Warn. wings.tiny.mail.service.warn-size=50 -## how long after start, scan for unsent mail, `0` for no scan. +## idle time after afterPropertiesSet to scan for unsent/misfired mail, `<=0` for disable wings.tiny.mail.service.boot-scan=60s +## idle time to scan for unsent/misfired mail. `<=0` for disable. +wings.tiny.mail.service.scan-idle=5m ## whether to send emails from this app only. wings.tiny.mail.service.only-app=false ## whether to send emails from this RumMode only. wings.tiny.mail.service.only-run=true + +## TaskSchedulingProperties +wings.tiny.mail.service.scheduler.pool.size=2 +wings.tiny.mail.service.scheduler.shutdown.await-termination=false +#wings.tiny.mail.service.scheduler.shutdown.await-termination-period=15s +wings.tiny.mail.service.scheduler.thread-name-prefix=mail- diff --git a/radiant/tiny-mail/src/main/resources/wings-flywave/branch/somefix/06-lazy-mail/2021-10-26u06-lazy-mail.sql b/radiant/tiny-mail/src/main/resources/wings-flywave/branch/somefix/06-lazy-mail/2021-10-26u06-lazy-mail.sql new file mode 100644 index 000000000..b020230d6 --- /dev/null +++ b/radiant/tiny-mail/src/main/resources/wings-flywave/branch/somefix/06-lazy-mail/2021-10-26u06-lazy-mail.sql @@ -0,0 +1,5 @@ +ALTER TABLE `win_mail_sender` + DROP COLUMN `lazy_bean`, + DROP COLUMN `lazy_para`; + +-- CALL FLYWAVE('2021-10-26u06-lazy-mail.sql'); \ No newline at end of file diff --git a/radiant/tiny-mail/src/main/resources/wings-flywave/branch/somefix/06-lazy-mail/2021-10-26v06-lazy-mail.sql b/radiant/tiny-mail/src/main/resources/wings-flywave/branch/somefix/06-lazy-mail/2021-10-26v06-lazy-mail.sql new file mode 100644 index 000000000..1d278fe05 --- /dev/null +++ b/radiant/tiny-mail/src/main/resources/wings-flywave/branch/somefix/06-lazy-mail/2021-10-26v06-lazy-mail.sql @@ -0,0 +1,5 @@ +ALTER TABLE `win_mail_sender` + ADD COLUMN `lazy_bean` VARCHAR(300) NOT NULL DEFAULT '' COMMENT 'lazy bean to edit mail if mail_text is null' AFTER `mail_date`, + ADD COLUMN `lazy_para` TEXT NULL COMMENT 'lazy para of lazy bean' AFTER `lazy_bean`; + +-- CALL FLYWAVE('2021-10-26v06-lazy-mail.sql'); \ No newline at end of file diff --git a/radiant/tiny-mail/src/main/resources/wings-flywave/master/07-mail/2020-10-27u01-tiny_mail.sql b/radiant/tiny-mail/src/main/resources/wings-flywave/master/07-mail/2020-10-27u01-tiny_mail.sql index 7da5da9a4..3f6ae752c 100644 --- a/radiant/tiny-mail/src/main/resources/wings-flywave/master/07-mail/2020-10-27u01-tiny_mail.sql +++ b/radiant/tiny-mail/src/main/resources/wings-flywave/master/07-mail/2020-10-27u01-tiny_mail.sql @@ -1,2 +1,3 @@ -DROP TABLE IF EXISTS `win_mail_sender`; -- 124/Mail Sending; +DROP TABLE IF EXISTS `win_mail_sender`; -- 143/Mail Sending; +-- CALL FLYWAVE('2020-10-27u01-tiny_mail.sql'); \ No newline at end of file diff --git a/radiant/tiny-mail/src/main/resources/wings-flywave/master/07-mail/2020-10-27v01-tiny_mail.sql b/radiant/tiny-mail/src/main/resources/wings-flywave/master/07-mail/2020-10-27v01-tiny_mail.sql index b7cfb5b77..89754fe81 100644 --- a/radiant/tiny-mail/src/main/resources/wings-flywave/master/07-mail/2020-10-27v01-tiny_mail.sql +++ b/radiant/tiny-mail/src/main/resources/wings-flywave/master/07-mail/2020-10-27v01-tiny_mail.sql @@ -1,37 +1,39 @@ CREATE TABLE `win_mail_sender` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key/mail_id', - `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', - `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime(sys)', - `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', - `mail_apps` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'belong to applications, comma-separated, default spring.application.name', - `mail_runs` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'RunMode(product|test|develop|local), comma-separated case-insensitive, default all', - `mail_conf` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'config name, default', - `mail_from` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'mail from (sender)', - `mail_to` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'mail to, comma-separated', - `mail_cc` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'mail cc, comma-separated', - `mail_bcc` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'mail bcc, comma-separated', - `mail_reply` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'mail reply', - `mail_subj` VARCHAR(400) NOT NULL DEFAULT '' COMMENT 'mail subject', - `mail_text` TEXT NULL DEFAULT NULL COMMENT 'mail content', - `mail_html` TINYINT(1) NOT NULL DEFAULT '1' COMMENT 'whether HTML email', - `mail_file` VARCHAR(9000) NOT NULL DEFAULT '' COMMENT 'attachment name and path map, json format', - `mail_mark` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'space-separated business key', - `mail_date` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'scheduled mail send (sys)', - `last_send` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous send (sys)', - `last_fail` TEXT NULL DEFAULT NULL COMMENT 'previous fail info', - `last_done` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous success (sys)', - `last_cost` INT(11) NOT NULL DEFAULT '0' COMMENT 'mills of previous send cost', - `next_send` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'next send datetime (sys)', - `next_lock` INT(11) NOT NULL DEFAULT '0' COMMENT 'optimistic lock of sending', - `sum_send` INT(11) NOT NULL DEFAULT '0' COMMENT 'total count of send', - `sum_fail` INT(11) NOT NULL DEFAULT '0' COMMENT 'total count of fail', - `sum_done` INT(11) NOT NULL DEFAULT '0' COMMENT 'total count of success', - `max_fail` INT(11) NOT NULL DEFAULT '0' COMMENT 'max count of fail, 0 means use the config', - `max_done` INT(11) NOT NULL DEFAULT '0' COMMENT 'max count of success, 0 means use the config', - `ref_type` INT(11) NOT NULL DEFAULT '0' COMMENT 'ref type to mark key1, key2 use', - `ref_key1` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'ref key1, generally the primary key', - `ref_key2` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'ref key2, generally the composite data', + `id` BIGINT NOT NULL COMMENT 'primary key/mail_id', + `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', + `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime(sys)', + `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', + `mail_apps` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'belong to applications, comma-separated, default spring.application.name', + `mail_runs` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'RunMode(product|test|develop|local), comma-separated case-insensitive, default all', + `mail_conf` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'config name, default', + `mail_from` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'mail from (sender)', + `mail_to` VARCHAR(900) NOT NULL DEFAULT '' COMMENT 'mail to, comma-separated', + `mail_cc` VARCHAR(900) NOT NULL DEFAULT '' COMMENT 'mail cc, comma-separated', + `mail_bcc` VARCHAR(900) NOT NULL DEFAULT '' COMMENT 'mail bcc, comma-separated', + `mail_reply` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'mail reply', + `mail_subj` VARCHAR(400) NOT NULL DEFAULT '' COMMENT 'mail subject', + `mail_text` TEXT NULL COMMENT 'mail content', + `mail_html` BOOLEAN NOT NULL DEFAULT '1' COMMENT 'whether HTML email', + `mail_file` TEXT NULL COMMENT 'attachment name and path map, json format', + `mail_mark` VARCHAR(900) NOT NULL DEFAULT '' COMMENT 'business key to lookup', + `mail_date` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'scheduled mail send (sys)', + `lazy_bean` VARCHAR(300) NOT NULL DEFAULT '' COMMENT 'lazy bean to edit mail if mail_text is null', + `lazy_para` TEXT NULL COMMENT 'lazy para of lazy bean', + `last_send` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous send (sys)', + `last_fail` TEXT NULL COMMENT 'previous fail info', + `last_done` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous success (sys)', + `last_cost` INT NOT NULL DEFAULT '0' COMMENT 'mills of previous send cost', + `next_send` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'next send datetime (sys)', + `next_lock` INT NOT NULL DEFAULT '0' COMMENT 'optimistic lock of sending', + `sum_send` INT NOT NULL DEFAULT '0' COMMENT 'total count of send', + `sum_fail` INT NOT NULL DEFAULT '0' COMMENT 'total count of fail', + `sum_done` INT NOT NULL DEFAULT '0' COMMENT 'total count of success', + `max_fail` INT NOT NULL DEFAULT '0' COMMENT 'max count of fail, 0 means use the config', + `max_done` INT NOT NULL DEFAULT '0' COMMENT 'max count of success, 0 means use the config', + `ref_type` INT NOT NULL DEFAULT '0' COMMENT 'ref type indicate key1, key2 usage', + `ref_key1` BIGINT NOT NULL DEFAULT '0' COMMENT 'ref key1, generally the primary key', + `ref_key2` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'ref key2, generally the composite data', PRIMARY KEY (`id`), INDEX ix_next_send (`next_send`), INDEX ix_sum_done (`sum_done`), @@ -40,4 +42,6 @@ CREATE TABLE `win_mail_sender` ( INDEX ix_ref_key1 (`ref_key1`), INDEX ix_ref_key2 (`ref_key2`) ) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4 COMMENT ='124/Mail Sending'; + DEFAULT CHARSET = utf8mb4 COMMENT ='143/Mail Sending'; + +-- CALL FLYWAVE('2020-10-27v01-tiny_mail.sql'); \ No newline at end of file diff --git a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/conf/TestStatusHookConfiguration.java b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/conf/TestStatusHookConfiguration.java index e3b53edc5..fc618c783 100644 --- a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/conf/TestStatusHookConfiguration.java +++ b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/conf/TestStatusHookConfiguration.java @@ -1,11 +1,13 @@ package pro.fessional.wings.tiny.app.conf; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import pro.fessional.wings.slardar.notice.DingTalkNotice; +import pro.fessional.wings.tiny.app.service.TestMailSenderManager; +import pro.fessional.wings.tiny.mail.sender.MailSenderManager; +import pro.fessional.wings.tiny.mail.sender.MailSenderProvider; import pro.fessional.wings.tiny.mail.service.TinyMailService; +import pro.fessional.wings.tiny.mail.spring.prop.TinyMailSenderProp; /** * @author trydofor @@ -16,13 +18,16 @@ public class TestStatusHookConfiguration { @Bean - public TinyMailService.StatusHook gmailStatusHook(@Autowired(required = false) DingTalkNotice notice) { + public TinyMailService.StatusHook gmailStatusHook() { return (po, cost, exception) -> { - if (notice != null) { - notice.send("hook mail subj=" + po.getMailSubj() + ", cost=" + cost, po.getMailText()); - } - log.info("hook mail subj=" + po.getMailSubj() + ", cost" + cost, exception); - return exception != null; + log.info("hook mail subj=" + po.getMailSubj() + ", cost=" + cost, exception); + return false; }; } + + @Bean + public MailSenderManager mailSenderManager(TinyMailSenderProp senderProp, MailSenderProvider senderProvider) { + log.info("TinyMail spring-bean mailSenderManager"); + return new TestMailSenderManager(senderProp, senderProvider); + } } diff --git a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/service/TestMailSenderManager.java b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/service/TestMailSenderManager.java new file mode 100644 index 000000000..7f1df90dc --- /dev/null +++ b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/service/TestMailSenderManager.java @@ -0,0 +1,65 @@ +package pro.fessional.wings.tiny.app.service; + +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.mail.MailParseException; +import pro.fessional.mirana.time.ThreadNow; +import pro.fessional.wings.tiny.mail.sender.MailSenderManager; +import pro.fessional.wings.tiny.mail.sender.MailSenderProvider; +import pro.fessional.wings.tiny.mail.sender.MailWaitException; +import pro.fessional.wings.tiny.mail.sender.TinyMailMessage; +import pro.fessional.wings.tiny.mail.spring.prop.TinyMailSenderProp; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +/** + * @author trydofor + * @since 2024-07-11 + */ +@Slf4j +public class TestMailSenderManager extends MailSenderManager { + + private final HashSet exception1st = new HashSet<>(); + + public TestMailSenderManager(TinyMailSenderProp senderProp, MailSenderProvider senderProvider) { + super(senderProp, senderProvider); + } + + @Override + public void singleSend(@NotNull TinyMailMessage message, long maxWait, @Nullable MimeMessagePrepareHelper preparer) { + super.singleSend(message, maxWait, preparer); + + String text = message.getContent(); + if (text.contains("AlwaysRuntimeException")) { + throw new RuntimeException("Mock " + text); + } + + if (exception1st.add(message.getBizId())) { + if (text.contains("MailWaitException")) { + throw new MailWaitException(ThreadNow.millis() + 5_000, false, false, new IllegalStateException("Mock " + text)); + } + if (text.contains("MailParseException")) { + throw new MailParseException("Mock " + text); + } + if (text.contains("RuntimeException")) { + throw new RuntimeException("Mock " + text); + } + } + } + + @Override + public List batchSend(Collection messages, long maxWait, @Nullable MimeMessagePrepareHelper preparer) { + List results = super.batchSend(messages, maxWait, preparer); + + for (BatchResult result : results) { + String text = result.getTinyMessage().getContent(); + if (text.contains("AlwaysRuntimeException")) { + result.setException(new RuntimeException("Mock " + text)); + } + } + return results; + } +} diff --git a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/service/TestTinyMailLazy.java b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/service/TestTinyMailLazy.java new file mode 100644 index 000000000..bf5c02c93 --- /dev/null +++ b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/app/service/TestTinyMailLazy.java @@ -0,0 +1,49 @@ +package pro.fessional.wings.tiny.app.service; + +import lombok.Setter; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import pro.fessional.wings.slardar.fastjson.FastJsonHelper; +import pro.fessional.wings.tiny.mail.service.TinyMail; +import pro.fessional.wings.tiny.mail.service.TinyMailLazy; +import pro.fessional.wings.tiny.mail.service.TinyMailService; + +import java.util.HashSet; + +/** + * @author trydofor + * @since 2024-07-12 + */ +@Service +public class TestTinyMailLazy implements TinyMailLazy { + + private final HashSet exception1st = new HashSet<>(); + + @Setter(onMethod_ = { @Autowired }) + protected TinyMailService tinyMailService; + + /** + * Testing:: + * Requested bean is currently in creation: + * Is there an unresolvable circular reference? + */ + public void send(TinyMail mail) { + tinyMailService.send(mail, true); + } + + @Override + public @Nullable Edit lazyEdit(@Nullable String para) { + if (para == null) return null; + String txt = FastJsonHelper.object(para, String.class); + if (exception1st.add(txt)) { + if (para.contains("RuntimeException")) { + throw new RuntimeException("Mock " + txt); + } + } + + Edit edit = new Edit(); + edit.setContent(txt); + return edit; + } +} diff --git a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/sender/MailSenderManagerTest.java b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/sender/MailSenderManagerTest.java index f56bad44e..6093b322b 100644 --- a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/sender/MailSenderManagerTest.java +++ b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/sender/MailSenderManagerTest.java @@ -18,17 +18,17 @@ * @since 2023-01-06 */ @SpringBootTest(properties = { - "wings.tiny.mail.service.boot-scan=0", - "logging.level.root=INFO", + "wings.tiny.mail.service.boot-scan=0", + "logging.level.root=INFO", }) @Slf4j public class MailSenderManagerTest { - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) protected MailSenderManager mailSenderManager; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) protected MailConfigProvider mailConfigProvider; /** @@ -61,7 +61,7 @@ public void timeLoopAndBatch() { try (final StopWatch.Watch ignored = stopWatch.start("single")) { for (int i = 0; i < size; i++) { TinyMailMessage message = new TinyMailMessage(); - message.adopt(config); + TinyMailConfig.ConfSetter.toAny(message, config); String text = "test single tiny mail " + i; message.setSubject(TestingMailUtil.dryrun(text, dryrun)); message.setContent(text); @@ -76,7 +76,7 @@ public void timeLoopAndBatch() { List messages = new ArrayList<>(size); for (int i = 0; i < size; i++) { TinyMailMessage message = new TinyMailMessage(); - message.adopt(config); + TinyMailConfig.ConfSetter.toAny(message, config); String text = "test batch tiny mail " + i; message.setSubject(TestingMailUtil.dryrun(text, dryrun)); message.setContent(text); @@ -109,7 +109,7 @@ public void batchMailDryrun() { List messages = new ArrayList<>(); for (int i = 0; i < 2; i++) { TinyMailMessage message = new TinyMailMessage(); - message.adopt(config); + TinyMailConfig.ConfSetter.toAny(message, config); message.setSubject(TestingMailUtil.dryrun("test batch tiny mail " + i)); message.setContent("test batch tiny mail " + i); messages.add(message); diff --git a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/ResourceMapTest.java b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/ResourceMapTest.java index bb0e013d9..6369ca7d3 100644 --- a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/ResourceMapTest.java +++ b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/ResourceMapTest.java @@ -9,7 +9,7 @@ import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; -import pro.fessional.wings.silencer.spring.help.CommonPropHelper; +import pro.fessional.wings.silencer.support.PropHelper; import java.io.IOException; import java.util.Iterator; @@ -36,7 +36,7 @@ public void jsonResourceSerializer() throws IOException { Map urls = new LinkedHashMap<>(); for (Map.Entry en : res1.entrySet()) { - urls.put(en.getKey(), CommonPropHelper.toString(en.getValue())); + urls.put(en.getKey(), PropHelper.stringResource(en.getValue())); } final String json = objectMapper.writeValueAsString(urls); Map res2 = new LinkedHashMap<>(); diff --git a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/TinyMailServiceDbTest.java b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/TinyMailServiceDbTest.java new file mode 100644 index 000000000..b68d54feb --- /dev/null +++ b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/TinyMailServiceDbTest.java @@ -0,0 +1,263 @@ +package pro.fessional.wings.tiny.mail.service; + +import io.qameta.allure.TmsLink; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.mail.MailProperties; +import org.springframework.boot.test.context.SpringBootTest; +import pro.fessional.mirana.time.Sleep; +import pro.fessional.mirana.time.ThreadNow; +import pro.fessional.wings.faceless.convention.EmptySugar; +import pro.fessional.wings.tiny.mail.TestingMailUtil; +import pro.fessional.wings.tiny.mail.database.autogen.tables.daos.WinMailSenderDao; +import pro.fessional.wings.tiny.mail.database.autogen.tables.pojos.WinMailSender; +import pro.fessional.wings.tiny.mail.sender.MailRetryException; +import pro.fessional.wings.tiny.mail.service.impl.TinyMailServiceImpl; +import pro.fessional.wings.tiny.mail.spring.prop.TinyMailServiceProp; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ScheduledFuture; + +/** + * @author trydofor + * @since 2023-01-10 + */ +@SpringBootTest(properties = { + "wings.tiny.mail.service.boot-scan=0", + "wings.tiny.mail.service.scan-idle=0", + "wings.tiny.mail.service.try-next=3s", + "wings.tiny.mail.service.batch-size=2", + "wings.tiny.mail.service.warn-size=5", + "logging.level.pro.fessional.wings.tiny.mail=debug", +}) +@Slf4j +class TinyMailServiceDbTest { + + @Setter(onMethod_ = { @Autowired }) + protected TinyMailServiceImpl tinyMailService; + + @Setter(onMethod_ = { @Autowired }) + protected MailProperties mailProperties; + + @Setter(onMethod_ = { @Autowired }) + protected TinyMailServiceProp tinyMailServiceProp; + + @Setter(onMethod_ = { @Autowired }) + protected WinMailSenderDao winMailSenderDao; + + @Setter(onMethod_ = { @Autowired }) + protected TinyMailLazy tinyMailLazy; + + @Test + @TmsLink("C15017") + void mockData() { + bootScan(); + sendError(); + } + + void bootScan() { + String subject = TestingMailUtil.dryrun("TinyMailService bootScan", mailProperties); + TinyMailPlain mail0 = new TinyMailPlain(); + mail0.setSubject(subject); + mail0.setContent("boot scan"); + long id = tinyMailService.save(mail0); + Assertions.assertTrue(id > 0); + // before boot scan + WinMailSender po0 = winMailSenderDao.fetchOneById(id); + Assertions.assertTrue(EmptySugar.nonEmptyValue(po0.getNextSend())); + Assertions.assertTrue(EmptySugar.asEmptyValue(po0.getLastSend())); + Assertions.assertTrue(EmptySugar.asEmptyValue(po0.getLastDone())); + Assertions.assertTrue(StringUtils.isBlank(po0.getLastFail())); + Assertions.assertEquals(0, po0.getSumFail()); + Assertions.assertEquals(0, po0.getSumSend()); + Assertions.assertEquals(0, po0.getSumDone()); + + // wait boot scan and send + tinyMailService.scan(); // scan now + long ms = 5_000; + Sleep.ignoreInterrupt(ms); + log.info("after boot scan, slept={}", ms); + WinMailSender po1 = winMailSenderDao.fetchOneById(id); + Assertions.assertTrue(EmptySugar.asEmptyValue(po1.getNextSend())); + Assertions.assertTrue(EmptySugar.nonEmptyValue(po1.getLastSend())); + Assertions.assertTrue(EmptySugar.nonEmptyValue(po1.getLastDone())); + Assertions.assertTrue(StringUtils.isBlank(po0.getLastFail())); + Assertions.assertEquals(0, po1.getSumFail()); + Assertions.assertEquals(1, po1.getSumSend()); + Assertions.assertEquals(1, po1.getSumDone()); + } + + void sendError() { + + final String subj = TestingMailUtil.dryrun("TinyMailService", mailProperties); + int time = 5; + final ArrayList ids = new ArrayList<>(time); + final Set awlNg = new HashSet<>(); + final Set lzyNg = new HashSet<>(); + final Set txtNg = new HashSet<>(); + + for (int i = 0; i < time; i++) { + final TinyMailPlain ml = new TinyMailPlain(); + + final long id; + if (i % 5 == 0) { + ml.setSubject(subj + " sendError AlwaysRuntimeException " + i); + ml.setContent("text AlwaysRuntimeException " + i); + id = tinyMailService.save(ml); + awlNg.add(id); + } + else if (i % 5 == 1) { // lazy ng + ml.setSubject(subj + " sendError lazy RuntimeException " + i); + ml.setMailLazy(tinyMailLazy, "lazy RuntimeException " + i); + id = tinyMailService.save(ml); + lzyNg.add(id); + } + else if (i % 5 == 2) { // text ng + ml.setSubject(subj + " sendError text RuntimeException " + i); + ml.setContent("text RuntimeException " + i); + id = tinyMailService.save(ml); + txtNg.add(id); + } + else if (i % 5 == 3) { // lzy ok + ml.setSubject(subj + " sendError lazy " + i); + ml.setMailLazy(tinyMailLazy, "lazy " + i); + id = tinyMailService.save(ml); + } + else { // text ok + ml.setSubject(subj + " sendError text " + i); + ml.setContent("text " + i); + id = tinyMailService.save(ml); + } + + ids.add(id); + + try { + boolean rc = tinyMailService.send(id, true, true); + if (lzyNg.contains(id)) { + Assertions.assertFalse(rc); + } + else if (awlNg.contains(id) || txtNg.contains(id)) { + Assertions.fail(); + } + else { + Assertions.assertTrue(rc); + } + } + catch (MailRetryException e) { + Assertions.assertTrue(e.getCause().getMessage().contains("Mock")); + } + + WinMailSender po = winMailSenderDao.fetchOneById(id); + + if (lzyNg.contains(id)) { + Assertions.assertTrue(EmptySugar.asEmptyValue(po.getNextSend())); // stop next + Assertions.assertTrue(StringUtils.isNotBlank(po.getLastFail())); + Assertions.assertTrue(StringUtils.isBlank(po.getMailText())); + Assertions.assertEquals(1, po.getSumFail()); + Assertions.assertEquals(1, po.getSumSend()); + Assertions.assertEquals(0, po.getSumDone()); + } + else if (awlNg.contains(id) || txtNg.contains(id)) { + Assertions.assertTrue(po.getNextSend().isAfter(ThreadNow.localDateTime())); + Assertions.assertTrue(StringUtils.isNotBlank(po.getLastFail())); + Assertions.assertEquals(1, po.getSumFail()); + Assertions.assertEquals(1, po.getSumSend()); + Assertions.assertEquals(0, po.getSumDone()); + } + else { + Assertions.assertTrue(EmptySugar.asEmptyValue(po.getNextSend())); + Assertions.assertTrue(StringUtils.isBlank(po.getLastFail())); + Assertions.assertEquals(0, po.getSumFail()); + Assertions.assertEquals(1, po.getSumSend()); + Assertions.assertEquals(1, po.getSumDone()); + } + } + + long ms = tinyMailServiceProp.getTryNext().toMillis() * 4; + Sleep.ignoreInterrupt(ms); + log.info("after try next, slept={}", ms); + + List pos = winMailSenderDao.fetchById(ids); + int maxFail = tinyMailServiceProp.getMaxFail(); + for (WinMailSender po : pos) { + Assertions.assertTrue(EmptySugar.asEmptyValue(po.getNextSend())); + Assertions.assertTrue(EmptySugar.nonEmptyValue(po.getLastSend())); + + Long id = po.getId(); + if (awlNg.contains(id)) { + Assertions.assertEquals(maxFail, po.getSumSend()); + Assertions.assertEquals(maxFail, po.getSumFail()); + Assertions.assertEquals(0, po.getSumDone()); + Assertions.assertTrue(StringUtils.isNotBlank(po.getLastFail())); + } + else if (lzyNg.contains(id)) { + Assertions.assertEquals(1, po.getSumSend()); + Assertions.assertEquals(1, po.getSumFail()); + Assertions.assertEquals(0, po.getSumDone()); + Assertions.assertTrue(StringUtils.isBlank(po.getMailText())); + Assertions.assertTrue(StringUtils.isNotBlank(po.getLastFail())); + } + else if (txtNg.contains(id)) { + Assertions.assertEquals(2, po.getSumSend()); + Assertions.assertEquals(1, po.getSumFail()); + Assertions.assertEquals(1, po.getSumDone()); + Assertions.assertTrue(StringUtils.isBlank(po.getLastFail())); + } + else { + Assertions.assertEquals(1, po.getSumSend()); + Assertions.assertEquals(0, po.getSumFail()); + Assertions.assertEquals(1, po.getSumDone()); + Assertions.assertTrue(StringUtils.isBlank(po.getLastFail())); + } + } + + Sleep.ignoreInterrupt(2_000L); + ArrayList queue = tinyMailService.listAsyncMailQueue(); + Assertions.assertTrue(queue.isEmpty()); + TreeMap> sched = tinyMailService.listAsyncMailTask(); + Assertions.assertTrue(sched.isEmpty()); + + // retry as if it was fixed, but 1st send error + for (Long id : lzyNg) { + long rc = tinyMailService.post(id, false, true); + Assertions.assertEquals(TinyMailService.ErrOther, rc); + } + + // retry, no 1st error + for (Long id : lzyNg) { + long rc = tinyMailService.post(id, false, true); + Assertions.assertEquals(TinyMailService.Success, rc); + } + + // retry with check + for (Long id : lzyNg) { + long rc = tinyMailService.post(id, false, true); + Assertions.assertEquals(TinyMailService.ErrCheck, rc); + } + + // force to send without check + for (Long id : lzyNg) { + long rc = tinyMailService.post(id, false, false); + Assertions.assertEquals(TinyMailService.Success, rc); + } + + // [DRYRUN]TinyMailService sendError lazy RuntimeException 1 + // force to send, exceed max fail/done + List pvz = winMailSenderDao.fetchById(lzyNg); + for (WinMailSender po : pvz) { + Assertions.assertEquals(4, po.getSumSend()); + Assertions.assertEquals(2, po.getSumFail()); + Assertions.assertEquals(2, po.getSumDone()); + Assertions.assertTrue(StringUtils.isNotBlank(po.getMailText())); + Assertions.assertTrue(StringUtils.isBlank(po.getLastFail())); + } + } +} diff --git a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/TinyMailServiceTest.java b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/TinyMailServiceTest.java index e39c8c7ac..fd24892d7 100644 --- a/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/TinyMailServiceTest.java +++ b/radiant/tiny-mail/src/test/java/pro/fessional/wings/tiny/mail/service/TinyMailServiceTest.java @@ -20,15 +20,16 @@ * @since 2023-01-10 */ @SpringBootTest(properties = { - "wings.tiny.mail.service.boot-scan=0", + "wings.tiny.mail.service.scan-idle=0", + "wings.tiny.mail.service.boot-scan=0", }) @Slf4j class TinyMailServiceTest { - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) protected TinyMailService tinyMailService; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) protected MailProperties mailProperties; @Test diff --git a/radiant/tiny-mail/src/test/resources/application.properties b/radiant/tiny-mail/src/test/resources/application.properties index 0dfb08897..ffef63d5b 100644 --- a/radiant/tiny-mail/src/test/resources/application.properties +++ b/radiant/tiny-mail/src/test/resources/application.properties @@ -22,3 +22,5 @@ wings.tiny.mail.sender.err-auth=60s wings.faceless.flywave.auto-init=true wings.faceless.flywave.fit.tiny-mail.revi=2019_0512_01L, 2019_0520_01L, 2020_1027_01L +wings.enabled.pro.fessional.wings.tiny.mail.spring.bean.TinyMailConfiguration.mailSenderManager=false + diff --git a/radiant/tiny-task/pom.xml b/radiant/tiny-task/pom.xml index 04282d17a..4628e0ca8 100644 --- a/radiant/tiny-task/pom.xml +++ b/radiant/tiny-task/pom.xml @@ -18,21 +18,21 @@ pro.fessional.wings - slardar + faceless-jooq pro.fessional.wings - slardar-webmvc - true + slardar pro.fessional.wings - faceless-flywave + slardar-webmvc true + pro.fessional.wings - faceless-codegen + faceless-flywave true diff --git a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/WinTaskDefineTable.java b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/WinTaskDefineTable.java index 750871153..88b4dc469 100644 --- a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/WinTaskDefineTable.java +++ b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/WinTaskDefineTable.java @@ -4,12 +4,6 @@ package pro.fessional.wings.tiny.task.database.autogen.tables; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.processing.Generated; - import org.jetbrains.annotations.NotNull; import org.jooq.Condition; import org.jooq.Field; @@ -23,7 +17,6 @@ import org.jooq.impl.Internal; import org.jooq.impl.SQLDataType; import org.jooq.impl.TableImpl; - import pro.fessional.wings.faceless.convention.EmptyValue; import pro.fessional.wings.faceless.database.jooq.WingsJournalTable; import pro.fessional.wings.faceless.service.journal.JournalService; @@ -31,6 +24,11 @@ import pro.fessional.wings.tiny.task.database.autogen.DefaultSchemaTinyTask; import pro.fessional.wings.tiny.task.database.autogen.tables.records.WinTaskDefineRecord; +import javax.annotation.processing.Generated; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + /** * The table wings.win_task_define. @@ -39,7 +37,7 @@ value = { "https://www.jooq.org", "jOOQ version:3.18.9", - "schema version:2020102701" + "schema version:2020102801" }, comments = "This class is generated by jOOQ" ) @@ -185,12 +183,12 @@ public Class getRecordType() { /** * The column win_task_define.timing_miss. */ - public final TableField TimingMiss = createField(DSL.name("timing_miss"), SQLDataType.INTEGER.nullable(false).defaultValue(DSL.inline("0", SQLDataType.INTEGER)), this, ""); + public final TableField TimingMiss = createField(DSL.name("timing_miss"), SQLDataType.BIGINT.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BIGINT)), this, ""); /** * The column win_task_define.timing_beat. */ - public final TableField TimingBeat = createField(DSL.name("timing_beat"), SQLDataType.INTEGER.nullable(false).defaultValue(DSL.inline("0", SQLDataType.INTEGER)), this, ""); + public final TableField TimingBeat = createField(DSL.name("timing_beat"), SQLDataType.BIGINT.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BIGINT)), this, ""); /** * The column win_task_define.during_from. diff --git a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/daos/WinTaskDefineDao.java b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/daos/WinTaskDefineDao.java index 9eedfe84b..29f92e8b4 100644 --- a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/daos/WinTaskDefineDao.java +++ b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/daos/WinTaskDefineDao.java @@ -4,23 +4,21 @@ package pro.fessional.wings.tiny.task.database.autogen.tables.daos; -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -import javax.annotation.processing.Generated; - import org.jooq.Configuration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; - import pro.fessional.wings.faceless.database.jooq.WingsJooqDaoJournalImpl; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.tiny.task.database.autogen.tables.WinTaskDefineTable; import pro.fessional.wings.tiny.task.database.autogen.tables.pojos.WinTaskDefine; import pro.fessional.wings.tiny.task.database.autogen.tables.records.WinTaskDefineRecord; +import javax.annotation.processing.Generated; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + /** * The table wings.win_task_define. @@ -29,7 +27,7 @@ value = { "https://www.jooq.org", "jOOQ version:3.18.9", - "schema version:2020102701" + "schema version:2020102801" }, comments = "This class is generated by jOOQ" ) @@ -878,32 +876,32 @@ public List fetchByTimingTuneLive(Collection v * Fetch records that have timing_miss BETWEEN lowerInclusive AND * upperInclusive */ - public List fetchRangeOfTimingMiss(Integer lowerInclusive, Integer upperInclusive) { + public List fetchRangeOfTimingMiss(Long lowerInclusive, Long upperInclusive) { return fetchRange(WinTaskDefineTable.WinTaskDefine.TimingMiss, lowerInclusive, upperInclusive); } - public List fetchRangeOfTimingMissLive(Integer lowerInclusive, Integer upperInclusive) { + public List fetchRangeOfTimingMissLive(Long lowerInclusive, Long upperInclusive) { return fetchRangeLive(WinTaskDefineTable.WinTaskDefine.TimingMiss, lowerInclusive, upperInclusive); } /** * Fetch records that have timing_miss IN (values) */ - public List fetchByTimingMiss(Integer... values) { + public List fetchByTimingMiss(Long... values) { return fetch(WinTaskDefineTable.WinTaskDefine.TimingMiss, values); } - public List fetchByTimingMiss(Collection values) { + public List fetchByTimingMiss(Collection values) { return fetch(WinTaskDefineTable.WinTaskDefine.TimingMiss, values); } - public List fetchByTimingMissLive(Integer... values) { + public List fetchByTimingMissLive(Long... values) { return fetchLive(WinTaskDefineTable.WinTaskDefine.TimingMiss, values); } - public List fetchByTimingMissLive(Collection values) { + public List fetchByTimingMissLive(Collection values) { return fetchLive(WinTaskDefineTable.WinTaskDefine.TimingMiss, values); } @@ -911,32 +909,32 @@ public List fetchByTimingMissLive(Collection v * Fetch records that have timing_beat BETWEEN lowerInclusive AND * upperInclusive */ - public List fetchRangeOfTimingBeat(Integer lowerInclusive, Integer upperInclusive) { + public List fetchRangeOfTimingBeat(Long lowerInclusive, Long upperInclusive) { return fetchRange(WinTaskDefineTable.WinTaskDefine.TimingBeat, lowerInclusive, upperInclusive); } - public List fetchRangeOfTimingBeatLive(Integer lowerInclusive, Integer upperInclusive) { + public List fetchRangeOfTimingBeatLive(Long lowerInclusive, Long upperInclusive) { return fetchRangeLive(WinTaskDefineTable.WinTaskDefine.TimingBeat, lowerInclusive, upperInclusive); } /** * Fetch records that have timing_beat IN (values) */ - public List fetchByTimingBeat(Integer... values) { + public List fetchByTimingBeat(Long... values) { return fetch(WinTaskDefineTable.WinTaskDefine.TimingBeat, values); } - public List fetchByTimingBeat(Collection values) { + public List fetchByTimingBeat(Collection values) { return fetch(WinTaskDefineTable.WinTaskDefine.TimingBeat, values); } - public List fetchByTimingBeatLive(Integer... values) { + public List fetchByTimingBeatLive(Long... values) { return fetchLive(WinTaskDefineTable.WinTaskDefine.TimingBeat, values); } - public List fetchByTimingBeatLive(Collection values) { + public List fetchByTimingBeatLive(Collection values) { return fetchLive(WinTaskDefineTable.WinTaskDefine.TimingBeat, values); } diff --git a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/interfaces/IWinTaskDefine.java b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/interfaces/IWinTaskDefine.java index 623b300ca..6a5e0d552 100644 --- a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/interfaces/IWinTaskDefine.java +++ b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/interfaces/IWinTaskDefine.java @@ -4,12 +4,11 @@ package pro.fessional.wings.tiny.task.database.autogen.tables.interfaces; -import java.io.Serializable; -import java.time.LocalDateTime; +import pro.fessional.wings.faceless.service.journal.JournalAware; import javax.annotation.processing.Generated; - -import pro.fessional.wings.faceless.service.journal.JournalAware; +import java.io.Serializable; +import java.time.LocalDateTime; /** @@ -19,7 +18,7 @@ value = { "https://www.jooq.org", "jOOQ version:3.18.9", - "schema version:2020102701" + "schema version:2020102801" }, comments = "This class is generated by jOOQ" ) @@ -269,22 +268,22 @@ public interface IWinTaskDefine extends JournalAware, Serializable { /** * Setter for win_task_define.timing_miss. */ - public void setTimingMiss(Integer value); + public void setTimingMiss(Long value); /** * Getter for win_task_define.timing_miss. */ - public Integer getTimingMiss(); + public Long getTimingMiss(); /** * Setter for win_task_define.timing_beat. */ - public void setTimingBeat(Integer value); + public void setTimingBeat(Long value); /** * Getter for win_task_define.timing_beat. */ - public Integer getTimingBeat(); + public Long getTimingBeat(); /** * Setter for win_task_define.during_from. diff --git a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/pojos/WinTaskDefine.java b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/pojos/WinTaskDefine.java index d097f11c9..a023c0b3e 100644 --- a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/pojos/WinTaskDefine.java +++ b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/pojos/WinTaskDefine.java @@ -4,16 +4,15 @@ package pro.fessional.wings.tiny.task.database.autogen.tables.pojos; +import pro.fessional.wings.tiny.task.database.autogen.tables.interfaces.IWinTaskDefine; + +import javax.annotation.processing.Generated; import java.beans.Transient; import java.time.LocalDateTime; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.UnaryOperator; -import javax.annotation.processing.Generated; - -import pro.fessional.wings.tiny.task.database.autogen.tables.interfaces.IWinTaskDefine; - /** * The table wings.win_task_define. @@ -22,7 +21,7 @@ value = { "https://www.jooq.org", "jOOQ version:3.18.9", - "schema version:2020102701" + "schema version:2020102801" }, comments = "This class is generated by jOOQ" ) @@ -55,8 +54,8 @@ public class WinTaskDefine implements IWinTaskDefine { private Integer timingIdle; private Integer timingRate; private Integer timingTune; - private Integer timingMiss; - private Integer timingBeat; + private Long timingMiss; + private Long timingBeat; private String duringFrom; private String duringStop; private Integer duringExec; @@ -146,8 +145,8 @@ public WinTaskDefine( Integer timingIdle, Integer timingRate, Integer timingTune, - Integer timingMiss, - Integer timingBeat, + Long timingMiss, + Long timingBeat, String duringFrom, String duringStop, Integer duringExec, @@ -2133,7 +2132,7 @@ public void setTimingTuneIf(UnaryOperator timingTune) { * Getter for win_task_define.timing_miss. */ @Override - public Integer getTimingMiss() { + public Long getTimingMiss() { return this.timingMiss; } @@ -2141,38 +2140,38 @@ public Integer getTimingMiss() { * Setter for win_task_define.timing_miss. */ @Override - public void setTimingMiss(Integer timingMiss) { + public void setTimingMiss(Long timingMiss) { this.timingMiss = timingMiss; } @Transient - public void setTimingMissIf(Integer timingMiss, boolean bool) { + public void setTimingMissIf(Long timingMiss, boolean bool) { if (bool) { this.timingMiss = timingMiss; } } @Transient - public void setTimingMissIf(Supplier timingMiss, boolean bool) { + public void setTimingMissIf(Supplier timingMiss, boolean bool) { if (bool) { this.timingMiss = timingMiss.get(); } } @Transient - public void setTimingMissIf(Integer timingMiss, Predicate bool) { + public void setTimingMissIf(Long timingMiss, Predicate bool) { if (bool.test(timingMiss)) { this.timingMiss = timingMiss; } } @Transient - public void setTimingMissIf(Integer timingMiss, Predicate bool, Supplier... timingMisss) { + public void setTimingMissIf(Long timingMiss, Predicate bool, Supplier... timingMisss) { if (bool.test(timingMiss)) { this.timingMiss = timingMiss; return; } - for (Supplier supplier : timingMisss) { + for (Supplier supplier : timingMisss) { timingMiss = supplier.get(); if (bool.test(timingMiss)) { this.timingMiss = timingMiss; @@ -2182,19 +2181,19 @@ public void setTimingMissIf(Integer timingMiss, Predicate bool, Supplie } @Transient - public void setTimingMissIfNot(Integer timingMiss, Predicate bool) { + public void setTimingMissIfNot(Long timingMiss, Predicate bool) { if (!bool.test(timingMiss)) { this.timingMiss = timingMiss; } } @Transient - public void setTimingMissIfNot(Integer timingMiss, Predicate bool, Supplier... timingMisss) { + public void setTimingMissIfNot(Long timingMiss, Predicate bool, Supplier... timingMisss) { if (!bool.test(timingMiss)) { this.timingMiss = timingMiss; return; } - for (Supplier supplier : timingMisss) { + for (Supplier supplier : timingMisss) { timingMiss = supplier.get(); if (!bool.test(timingMiss)) { this.timingMiss = timingMiss; @@ -2204,7 +2203,7 @@ public void setTimingMissIfNot(Integer timingMiss, Predicate bool, Supp } @Transient - public void setTimingMissIf(UnaryOperator timingMiss) { + public void setTimingMissIf(UnaryOperator timingMiss) { this.timingMiss = timingMiss.apply(this.timingMiss); } @@ -2213,7 +2212,7 @@ public void setTimingMissIf(UnaryOperator timingMiss) { * Getter for win_task_define.timing_beat. */ @Override - public Integer getTimingBeat() { + public Long getTimingBeat() { return this.timingBeat; } @@ -2221,38 +2220,38 @@ public Integer getTimingBeat() { * Setter for win_task_define.timing_beat. */ @Override - public void setTimingBeat(Integer timingBeat) { + public void setTimingBeat(Long timingBeat) { this.timingBeat = timingBeat; } @Transient - public void setTimingBeatIf(Integer timingBeat, boolean bool) { + public void setTimingBeatIf(Long timingBeat, boolean bool) { if (bool) { this.timingBeat = timingBeat; } } @Transient - public void setTimingBeatIf(Supplier timingBeat, boolean bool) { + public void setTimingBeatIf(Supplier timingBeat, boolean bool) { if (bool) { this.timingBeat = timingBeat.get(); } } @Transient - public void setTimingBeatIf(Integer timingBeat, Predicate bool) { + public void setTimingBeatIf(Long timingBeat, Predicate bool) { if (bool.test(timingBeat)) { this.timingBeat = timingBeat; } } @Transient - public void setTimingBeatIf(Integer timingBeat, Predicate bool, Supplier... timingBeats) { + public void setTimingBeatIf(Long timingBeat, Predicate bool, Supplier... timingBeats) { if (bool.test(timingBeat)) { this.timingBeat = timingBeat; return; } - for (Supplier supplier : timingBeats) { + for (Supplier supplier : timingBeats) { timingBeat = supplier.get(); if (bool.test(timingBeat)) { this.timingBeat = timingBeat; @@ -2262,19 +2261,19 @@ public void setTimingBeatIf(Integer timingBeat, Predicate bool, Supplie } @Transient - public void setTimingBeatIfNot(Integer timingBeat, Predicate bool) { + public void setTimingBeatIfNot(Long timingBeat, Predicate bool) { if (!bool.test(timingBeat)) { this.timingBeat = timingBeat; } } @Transient - public void setTimingBeatIfNot(Integer timingBeat, Predicate bool, Supplier... timingBeats) { + public void setTimingBeatIfNot(Long timingBeat, Predicate bool, Supplier... timingBeats) { if (!bool.test(timingBeat)) { this.timingBeat = timingBeat; return; } - for (Supplier supplier : timingBeats) { + for (Supplier supplier : timingBeats) { timingBeat = supplier.get(); if (!bool.test(timingBeat)) { this.timingBeat = timingBeat; @@ -2284,7 +2283,7 @@ public void setTimingBeatIfNot(Integer timingBeat, Predicate bool, Supp } @Transient - public void setTimingBeatIf(UnaryOperator timingBeat) { + public void setTimingBeatIf(UnaryOperator timingBeat) { this.timingBeat = timingBeat.apply(this.timingBeat); } diff --git a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/records/WinTaskDefineRecord.java b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/records/WinTaskDefineRecord.java index a960bb87d..2b77e4ae4 100644 --- a/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/records/WinTaskDefineRecord.java +++ b/radiant/tiny-task/src/main/java-gen/pro/fessional/wings/tiny/task/database/autogen/tables/records/WinTaskDefineRecord.java @@ -4,17 +4,15 @@ package pro.fessional.wings.tiny.task.database.autogen.tables.records; -import java.time.LocalDateTime; - -import javax.annotation.processing.Generated; - import org.jooq.Record1; import org.jooq.impl.UpdatableRecordImpl; - import pro.fessional.wings.tiny.task.database.autogen.tables.WinTaskDefineTable; import pro.fessional.wings.tiny.task.database.autogen.tables.interfaces.IWinTaskDefine; import pro.fessional.wings.tiny.task.database.autogen.tables.pojos.WinTaskDefine; +import javax.annotation.processing.Generated; +import java.time.LocalDateTime; + /** * The table wings.win_task_define. @@ -23,7 +21,7 @@ value = { "https://www.jooq.org", "jOOQ version:3.18.9", - "schema version:2020102701" + "schema version:2020102801" }, comments = "This class is generated by jOOQ" ) @@ -420,7 +418,7 @@ public Integer getTimingTune() { * Setter for win_task_define.timing_miss. */ @Override - public void setTimingMiss(Integer value) { + public void setTimingMiss(Long value) { set(24, value); } @@ -428,15 +426,15 @@ public void setTimingMiss(Integer value) { * Getter for win_task_define.timing_miss. */ @Override - public Integer getTimingMiss() { - return (Integer) get(24); + public Long getTimingMiss() { + return (Long) get(24); } /** * Setter for win_task_define.timing_beat. */ @Override - public void setTimingBeat(Integer value) { + public void setTimingBeat(Long value) { set(25, value); } @@ -444,8 +442,8 @@ public void setTimingBeat(Integer value) { * Getter for win_task_define.timing_beat. */ @Override - public Integer getTimingBeat() { - return (Integer) get(25); + public Long getTimingBeat() { + return (Long) get(25); } /** @@ -784,7 +782,7 @@ public WinTaskDefineRecord() { /** * Create a detached, initialised WinTaskDefineRecord */ - public WinTaskDefineRecord(Long id, LocalDateTime createDt, LocalDateTime modifyDt, LocalDateTime deleteDt, Long commitId, String propkey, Boolean enabled, Boolean autorun, Integer version, String taskerBean, String taskerPara, String taskerName, Boolean taskerFast, String taskerApps, String taskerRuns, String noticeBean, String noticeWhen, String noticeConf, String timingZone, String timingType, String timingCron, Integer timingIdle, Integer timingRate, Integer timingTune, Integer timingMiss, Integer timingBeat, String duringFrom, String duringStop, Integer duringExec, Integer duringFail, Integer duringDone, Integer duringBoot, Integer resultKeep, LocalDateTime lastExec, LocalDateTime lastExit, Boolean lastFail, LocalDateTime nextExec, Integer nextLock, Integer durFail, Integer sumExec, Integer sumFail, Integer sumDone) { + public WinTaskDefineRecord(Long id, LocalDateTime createDt, LocalDateTime modifyDt, LocalDateTime deleteDt, Long commitId, String propkey, Boolean enabled, Boolean autorun, Integer version, String taskerBean, String taskerPara, String taskerName, Boolean taskerFast, String taskerApps, String taskerRuns, String noticeBean, String noticeWhen, String noticeConf, String timingZone, String timingType, String timingCron, Integer timingIdle, Integer timingRate, Integer timingTune, Long timingMiss, Long timingBeat, String duringFrom, String duringStop, Integer duringExec, Integer duringFail, Integer duringDone, Integer duringBoot, Integer resultKeep, LocalDateTime lastExec, LocalDateTime lastExit, Boolean lastFail, LocalDateTime nextExec, Integer nextLock, Integer durFail, Integer sumExec, Integer sumFail, Integer sumDone) { super(WinTaskDefineTable.WinTaskDefine); setId(id); diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/TinyTasker.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/TinyTasker.java index f5b447d2c..88df57a45 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/TinyTasker.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/TinyTasker.java @@ -77,6 +77,16 @@ */ int tune() default 0; + /** + *
+     * Within how many seconds of a misfire, execution is required.
+     * * `<0` - disable execution
+     * * `0` - execute if N0 < now <= N0 + (N1-N0) * 25% < N1
+     * * `>0` - execute if N1 < now <= N1 + miss
+     * 
+ */ + int miss() default 0; + /** * Adding to a SpringBean can be auto config by Wings at startup. */ diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/conf/TaskerProp.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/conf/TaskerProp.java index 0edfd40c3..7ba2c3d09 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/conf/TaskerProp.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/conf/TaskerProp.java @@ -60,7 +60,7 @@ public boolean notTaskerApps() { } /** - * RunMode(product|test|develop|local), Comma separated, ignore case, default all, + * RunMode(product|test|develop|local), `!test`,`-test` means not test, Comma separated, ignore case, default all, * use Default config if null or empty. * * @see pro.fessional.wings.silencer.modulate.RunMode @@ -179,21 +179,29 @@ public boolean notTimingTune() { } /** - * Within how many seconds of a misfire, execution is required, - * 0 means no execution. not use Default config. + *
+     * Within how many seconds of a misfire, execution is required, not use Default config.
+     * * `<0` - execute as `0` if now + miss * 1000 >= 0
+     * * `0` - execute if N0 < now <= N0 + (N1-N0) * 25% < N1
+     * * `>0` - execute if N1 < now <= N1 + miss * 1000
+     * 
*/ - protected int timingMiss = 0; + protected long timingMiss = 0; + + public boolean notTimingMiss() { + return timingMiss == 0; + } /** *
      * the interval seconds of heartbeat and health-check, not use Default config.
      * it is considered as an exception if the last_exec is more than 2 heartbeats away from now.
-     * * `<0` - disable check
-     * * `0` - auto calculate, when cron, calc next_exec from last_exec, others, max rate and idle
+     * * `<0` - calculate as `0` if now + beat * 1000 >= 0
+     * * `0` - calculate, when cron, calc next_exec from last_exec, others, max rate and idle
      * * `>0` - fixed positive seconds
      * 
*/ - protected int timingBeat = 0; + protected long timingBeat = 0; /** * schedule start datetime at timingZone, in yyyy-MM-dd HH:mm:ss format, * 0 means disable, not use Default config. diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/exec/ExecHolder.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/exec/ExecHolder.java index 5fb158387..4deff97bf 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/exec/ExecHolder.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/exec/ExecHolder.java @@ -3,6 +3,7 @@ import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -22,7 +23,7 @@ public static NoticeExec getNotice(@NotNull String bean, @NotNull Function!null") - public static NoticeExec getNotice(String bean, boolean nonnull) { + public static NoticeExec getNotice(@Nullable String bean, boolean nonnull) { final NoticeExec exec = StringUtils.isEmpty(bean) ? null : Notice.get(bean); if (nonnull && exec == null) { throw new IllegalStateException("notice not found, bean=" + bean); @@ -30,17 +31,29 @@ public static NoticeExec getNotice(String bean, boolean nonnull) { return exec; } + @Nullable + public static NoticeExec delNotice(@NotNull String bean) { + return Notice.remove(bean); + } + + @NotNull public static TaskerExec getTasker(@NotNull String prop, @NotNull Function exec) { return Tasker.computeIfAbsent(prop, exec); } @Contract("_,true->!null") - public static TaskerExec getTasker(String prop, boolean nonnull) { + public static TaskerExec getTasker(@Nullable String prop, boolean nonnull) { final TaskerExec exec = StringUtils.isEmpty(prop) ? null : Tasker.get(prop); if (nonnull && exec == null) { throw new IllegalStateException("tasker not found, prop=" + prop); } return exec; } + + @Nullable + public static TaskerExec delTasker(@NotNull String prop) { + return Tasker.remove(prop); + } + } diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/exec/NoticeExec.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/exec/NoticeExec.java index 22016f0b4..e16e55f86 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/exec/NoticeExec.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/schedule/exec/NoticeExec.java @@ -53,6 +53,7 @@ public void postNotice(String config, String subject, String content) { beanObject.post(conf, subject, content); } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.warn("failed to post notice, subject=" + subject + ", content=" + content, e); } } diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/TinyTaskConfService.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/TinyTaskConfService.java index 0be41b3b8..6517a82cf 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/TinyTaskConfService.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/TinyTaskConfService.java @@ -25,8 +25,8 @@ public interface TinyTaskConfService { *
      * Configure the TinyTasker annotated and enabled method , return the taskId.
      * - throw exception if property not exist
-     * - save to database if not exist in database
-     * - save to database, if exist in database, but higher version
+     * - insert to database if not exist in database
+     * - update to database, if exist and matched and lower version
      * - otherwise no operation
      * 
*/ @@ -72,5 +72,9 @@ class Conf { private final String key; private final boolean enabled; private final boolean autorun; + /** + * matched app and run + */ + private final boolean matched; } } diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/TinyTaskService.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/TinyTaskService.java index e4974533f..eab6380ce 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/TinyTaskService.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/TinyTaskService.java @@ -5,6 +5,7 @@ import org.jetbrains.annotations.Nullable; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import pro.fessional.mirana.best.AssertArgs; import pro.fessional.mirana.func.Lam; import pro.fessional.mirana.time.ThreadNow; @@ -106,6 +107,7 @@ default ScheduledFuture execute(boolean fast, Trigger trigger, @NotNull Runna * @see ThreadPoolTaskScheduler#schedule(Runnable, Trigger) */ default Task schedule(@NotNull Lam.Ref lambdaRefer, @Nullable Object taskerPara) { + AssertArgs.notNull(lambdaRefer.object, "schedule object is null"); return schedule(lambdaRefer.object, lambdaRefer.method, taskerPara); } diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskBeatServiceImpl.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskBeatServiceImpl.java index 2e96ac80e..18cfb7d35 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskBeatServiceImpl.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskBeatServiceImpl.java @@ -13,6 +13,7 @@ import pro.fessional.mirana.time.DateLocaling; import pro.fessional.mirana.time.ThreadNow; import pro.fessional.wings.faceless.convention.EmptySugar; +import pro.fessional.wings.silencer.modulate.RuntimeMode; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.tiny.task.database.autogen.tables.WinTaskDefineTable; import pro.fessional.wings.tiny.task.database.autogen.tables.WinTaskResultTable; @@ -62,7 +63,7 @@ public int cleanResult() { .from(tr) .fetchInto(Long.class); if (tid.isEmpty()) { - log.debug("no task result to clean"); + log.debug("no tiny-task result to clean"); return 0; } @@ -81,7 +82,7 @@ public int cleanResult() { .collect(Collectors.toList()); if (cond.isEmpty()) { - log.debug("no task condition to clean"); + log.debug("no tiny-task condition to clean"); return 0; } @@ -90,7 +91,7 @@ public int cleanResult() { .delete(tr) .where(DSL.or(cond)) .execute(); - log.info("clean task result, count={}", rc); + log.info("clean tiny-task result, count={}", rc); return rc; } @@ -102,22 +103,29 @@ public String checkHealth() { final WinTaskDefineTable td = winTaskDefineDao.getTable(); List tks = winTaskDefineDao .ctx() - .select(td.Id, td.TaskerName, td.LastExec, + .select(td.Id, td.TaskerName, td.LastExec, td.TaskerRuns, td.TimingBeat, td.TimingRate, td.TimingIdle, td.TimingTune, td.TimingCron, td.TimingZone) .from(td) - .where(td.Enabled.eq(Boolean.TRUE).and(td.TimingBeat.ge(0))) + .where(td.Enabled.eq(Boolean.TRUE)) .fetch() .into(WinTaskDefine.class); final StringBuilder mis = warmed ? new StringBuilder() : null; for (WinTaskDefine r : tks) { - log.debug("check health task id={}, name={}", r.getId(), r.getTaskerName()); + log.debug("check health tiny-task id={}, name={}", r.getId(), r.getTaskerName()); + + // check runs, same database must be same run mode + final String runs = r.getTaskerRuns(); + if (StringUtils.isNotBlank(runs) && !RuntimeMode.voteRunMode(runs)) { + continue; + } + // coordinate to system timezone long beat = calcBeatMills(r, now); if (beat <= 0) continue; if (beat < now) { - log.info("misfired task id={}, name={}", r.getId(), r.getTaskerName()); + log.info("misfired tiny-task id={}, name={}", r.getId(), r.getTaskerName()); if (mis != null) { mis.append(r.getId()).append('@').append(r.getTaskerName()).append('\n'); } @@ -125,10 +133,12 @@ public String checkHealth() { } warmed = true; - return mis == null || mis.isEmpty() ? null : "misfired task id@name\n" + mis; + return mis == null || mis.isEmpty() ? null + : "misfired tiny-task id@name\n" + + mis + "\nUPDATE win_task_define SET timing_beat = -UNIX_TIMESTAMP(now() + INTERVAL 1 hour ) WHERE id IN (...); to skip checking for 1 hour"; } - private long calcBeatMills(WinTaskDefine td, long now) { + protected long calcBeatMills(WinTaskDefine td, long now) { // no previous LocalDateTime lastExec = td.getLastExec(); if (EmptySugar.asEmptyValue(lastExec)) { @@ -136,7 +146,7 @@ private long calcBeatMills(WinTaskDefine td, long now) { } final long beat = td.getTimingBeat(); - if (beat < 0) return beat; + if (beat < 0 && now + beat * 1000 < 0) return beat; final long lastExecSys = DateLocaling.sysEpoch(lastExec); if (beat > 0) return lastExecSys + beat * 1000L; @@ -154,7 +164,10 @@ private long calcBeatMills(WinTaskDefine td, long now) { final CronExpression cronExpr = CronExpression.parse(cron); for (int i = 0; i < beatTimes; i++) { - beatZdt = cronExpr.next(beatZdt); + ZonedDateTime nxt = cronExpr.next(beatZdt); + if (nxt == null) break; + + beatZdt = nxt; } // then convert to instance diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskConfServiceImpl.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskConfServiceImpl.java index 3aec8e18d..f002d9abc 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskConfServiceImpl.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskConfServiceImpl.java @@ -3,6 +3,7 @@ import lombok.Setter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,6 +22,8 @@ import pro.fessional.mirana.data.Diff; import pro.fessional.wings.faceless.service.journal.JournalService; import pro.fessional.wings.faceless.service.lightid.LightIdService; +import pro.fessional.wings.silencer.modulate.RunMode; +import pro.fessional.wings.silencer.modulate.RuntimeMode; import pro.fessional.wings.silencer.notice.SmallNotice; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.tiny.task.database.autogen.tables.WinTaskDefineTable; @@ -47,6 +50,7 @@ import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.springframework.core.MethodIntrospector.selectMethods; import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation; +import static pro.fessional.wings.silencer.support.PropHelper.commaArray; /** * @author trydofor @@ -105,6 +109,7 @@ private TaskerProp property(@NotNull String key, @NotNull TinyTasker anno) { rp.setTimingIdle(anno.idle()); rp.setTimingRate(anno.rate()); rp.setTimingTune(anno.tune()); + rp.setTimingMiss(anno.miss()); log.debug("no prop, use annotation, key={}", key); } else { @@ -113,6 +118,7 @@ private TaskerProp property(@NotNull String key, @NotNull TinyTasker anno) { rp.setTimingIdle(pp.notTimingIdle() ? anno.idle() : pp.getTimingIdle()); rp.setTimingRate(pp.notTimingRate() ? anno.rate() : pp.getTimingRate()); rp.setTimingTune(pp.notTimingTune() ? anno.tune() : pp.getTimingTune()); + rp.setTimingMiss(pp.notTimingMiss() ? anno.miss() : pp.getTimingMiss()); } // not default @@ -125,7 +131,7 @@ private TaskerProp property(@NotNull String key, @NotNull TinyTasker anno) { rp.setTaskerName(pp.getTaskerName()); rp.setTaskerFast(pp.isTaskerFast()); // - rp.setTimingMiss(pp.getTimingMiss()); + rp.setTimingBeat(pp.getTimingBeat()); rp.setDuringFrom(pp.getDuringFrom()); rp.setDuringStop(pp.getDuringStop()); @@ -154,7 +160,7 @@ private TaskerProp property(@NotNull String key, @NotNull TinyTasker anno) { public TaskerProp database(long id, boolean nonnull) { final TaskerProp conf = fetchProp(TaskerProp.class, t -> t.Id.eq(id)); if (conf == null && nonnull) { - throw new IllegalArgumentException("database tasker is null, id=" + id); + throw new IllegalArgumentException("database tiny-task is null, id=" + id); } return conf; } @@ -171,7 +177,7 @@ public TaskerProp property(long id, boolean nonnull) { .fetchOne(); if (r2 == null || isEmpty(r2.value1()) || isEmpty(r2.value2())) { if (nonnull) { - throw new IllegalArgumentException("database tasker is null, id=" + id); + throw new IllegalArgumentException("database tiny-task is null, id=" + id); } else { return null; @@ -188,10 +194,10 @@ public TaskerProp property(long id, boolean nonnull) { @NotNull public LinkedHashMap> diffProp(long id) { final WinTaskDefine po = fetchProp(WinTaskDefine.class, t -> t.Id.eq(id)); - AssertArgs.notNull(po, "database tasker is null, id={}", id); + AssertArgs.notNull(po, "database tiny-task is null, id={}", id); final TinyTasker anno = referAnno(po.getPropkey(), po.getTaskerBean()); - AssertArgs.notNull(anno, "database without TinyTasker, id={}", id); + AssertArgs.notNull(anno, "database without tiny-task, id={}", id); final TaskerProp prop = property(po.getPropkey(), anno); return diff(po, prop); @@ -229,7 +235,7 @@ public boolean replace(long id, TaskerProp prop) { private Conf config(@NotNull Class claz, @NotNull Object bean, @NotNull Method method, @Nullable Object para) { final TinyTasker anno = method.getAnnotation(TinyTasker.class); if (anno == null) { - throw new IllegalStateException("need @TinyTasker, tasker method=" + method.getName() + ", class=" + claz.getName()); + throw new IllegalStateException("need @TinyTasker, tiny-task method=" + method.getName() + ", class=" + claz.getName()); } final String entry = TaskerHelper.tokenize(claz, method.getName()); @@ -243,7 +249,7 @@ private Conf config(@NotNull Class claz, @NotNull Object bean, @NotNull Metho ); } - log.info("find tiny task, prop={}, entry={}", key, entry); + log.info("find tiny-task, prop={}, entry={}", key, entry); if (isEmpty(prop.getTaskerName())) { prop.setTaskerName(claz.getSimpleName() + TaskerHelper.MethodPrefix + method.getName()); @@ -262,12 +268,14 @@ private Conf config(@NotNull Class claz, @NotNull Object bean, @NotNull Metho final long id; final boolean enabled; final boolean autorun; + final boolean matched; final String noticeBean; final WinTaskDefine po = fetchProp(WinTaskDefine.class, t -> t.TaskerBean.eq(entry)); if (po == null) { id = insertProp(prop, key); enabled = prop.isEnabled(); autorun = prop.isAutorun(); + matched = true; noticeBean = prop.getNoticeBean(); log.info("insert prop to database, version={}, id={}", prop.getVersion(), id); } @@ -275,8 +283,10 @@ private Conf config(@NotNull Class claz, @NotNull Object bean, @NotNull Metho id = po.getId(); enabled = BoxedCastUtil.orTrue(po.getEnabled()); autorun = BoxedCastUtil.orTrue(po.getAutorun()); + matched = hasApps(po.getTaskerApps()) && hasRuns(po.getTaskerRuns()); noticeBean = po.getNoticeBean(); - log.debug("find database config, version={}, id={}", prop.getVersion(), id); + log.debug("find database config, version={}, id={}, matched={}", prop.getVersion(), id, matched); + // diff final LinkedHashMap> df = diff(po, prop); if (!df.isEmpty()) { @@ -288,12 +298,18 @@ private Conf config(@NotNull Class claz, @NotNull Object bean, @NotNull Metho sb.append(", pp=").append(en.getValue().getV2()); sb.append('\n'); } - if (po.getVersion() <= prop.getVersion()) { + + if (matched && po.getVersion() <= prop.getVersion()) { updateProp(prop, key, id); - log.info("update prop to database, prop={}, diff={}", key, sb); + log.warn("update prop to database(low-ver), prop={}, diff={}", key, sb); } else { - log.warn("diff from prop and database, prop={}, diff={}", key, sb); + if (matched) { + log.warn("diff from prop(low-ver) and database, prop={}, diff={}", key, sb); + } + else { + log.info("diff from prop and database but not matched, prop={}, diff={}", key, sb); + } } } } @@ -306,7 +322,8 @@ private Conf config(@NotNull Class claz, @NotNull Object bean, @NotNull Metho ", entry2=" + poKey.getTaskerBean()); } - if (noticeBean != null && !noticeBean.isEmpty()) { + // keep bean and notice even if not matched to launch task bewteen the cluster + if (StringUtils.isNotEmpty(noticeBean)) { ExecHolder.getNotice(noticeBean, k -> { try { final Class> cz = (Class>) ClassUtils.forName(noticeBean, null); @@ -314,20 +331,34 @@ private Conf config(@NotNull Class claz, @NotNull Object bean, @NotNull Metho return new NoticeExec<>(nb); } catch (ClassNotFoundException e) { + // noinspection StringConcatenationArgumentToLogCall log.error("failed to init notice bean=" + noticeBean, e); throw new IllegalArgumentException(e); } }); } - return new Conf(id, key, enabled, autorun); + return new Conf(id, key, enabled, autorun, matched); + } + + private boolean hasApps(String apps) { + if (StringUtils.isEmpty(apps)) return true; + for (String s : commaArray(apps)) { + if (s.trim().equals(appName)) return true; + } + return false; + } + + private boolean hasRuns(String runs) { + if (StringUtils.isEmpty(runs)) return true; + return !RuntimeMode.isRunMode(RunMode.Nothing) && RuntimeMode.voteRunMode(runs); } private long insertProp(TaskerProp prop, String key) { return journalService.submit(Jane.Insert, journal -> { final WinTaskDefineTable t = winTaskDefineDao.getTable(); final long id = lightIdService.getId(t); - final WinTaskDefine po = genWinTaskDefine(prop, key); + final WinTaskDefine po = genWinTaskDefine(prop, key, appName); po.setId(id); po.setNextLock(0); journal.create(po); @@ -338,7 +369,7 @@ private long insertProp(TaskerProp prop, String key) { private boolean updateProp(TaskerProp prop, String key, long id) { return journalService.submit(Jane.Update, journal -> { - WinTaskDefine po = genWinTaskDefine(prop, key); + WinTaskDefine po = genWinTaskDefine(prop, key, null); po.setId(id); journal.modify(po); final int rc = winTaskDefineDao.update(po, true); @@ -347,7 +378,7 @@ private boolean updateProp(TaskerProp prop, String key, long id) { } @NotNull - private WinTaskDefine genWinTaskDefine(TaskerProp prop, String key) { + private WinTaskDefine genWinTaskDefine(TaskerProp prop, String key, String apps) { WinTaskDefine wtd = new WinTaskDefine(); wtd.setEnabled(prop.isEnabled()); @@ -360,8 +391,8 @@ private WinTaskDefine genWinTaskDefine(TaskerProp prop, String key) { wtd.setTaskerName(prop.getTaskerName()); wtd.setTaskerFast(prop.isTaskerFast()); - final String apps = prop.getTaskerApps(); - wtd.setTaskerApps(isEmpty(apps) ? appName : apps); + final String ta = prop.getTaskerApps(); + wtd.setTaskerApps(isEmpty(ta) ? apps : ta); wtd.setTaskerRuns(prop.getTaskerRuns()); wtd.setNoticeBean(prop.getNoticeBean()); @@ -374,8 +405,8 @@ private WinTaskDefine genWinTaskDefine(TaskerProp prop, String key) { wtd.setTimingIdle(prop.getTimingIdle()); wtd.setTimingRate(prop.getTimingRate()); wtd.setTimingTune(prop.getTimingTune()); - wtd.setTimingMiss(prop.getTimingMiss()); + wtd.setTimingBeat(prop.getTimingBeat()); wtd.setDuringFrom(prop.getDuringFrom()); wtd.setDuringStop(prop.getDuringStop()); diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskExecServiceImpl.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskExecServiceImpl.java index 8168df3f4..bf1440533 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskExecServiceImpl.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskExecServiceImpl.java @@ -4,8 +4,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jooq.Field; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.support.CronTrigger; import org.springframework.scheduling.support.PeriodicTrigger; @@ -13,6 +16,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import pro.fessional.mirana.cast.BoxedCastUtil; +import pro.fessional.mirana.data.U; import pro.fessional.mirana.lock.JvmStaticGlobalLock; import pro.fessional.mirana.pain.ThrowableUtil; import pro.fessional.mirana.stat.JvmStat; @@ -57,7 +61,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.arrayOrNull; +import static pro.fessional.wings.silencer.support.PropHelper.commaArray; import static pro.fessional.wings.tiny.task.schedule.exec.NoticeExec.WhenDone; import static pro.fessional.wings.tiny.task.schedule.exec.NoticeExec.WhenExec; import static pro.fessional.wings.tiny.task.schedule.exec.NoticeExec.WhenFail; @@ -70,13 +74,15 @@ @Service @ConditionalWingsEnabled @Slf4j -public class TinyTaskExecServiceImpl implements TinyTaskExecService { +public class TinyTaskExecServiceImpl implements TinyTaskExecService, InitializingBean { - protected static final ConcurrentHashMap> Handle = new ConcurrentHashMap<>(); + protected static final ConcurrentHashMap, Long, String>> Handle = new ConcurrentHashMap<>(); protected static final ConcurrentHashMap Cancel = new ConcurrentHashMap<>(); protected static final ConcurrentHashMap Booted = new ConcurrentHashMap<>(); protected static final ConcurrentHashMap Untune = new ConcurrentHashMap<>(); + private final AtomicInteger noticeCounter = new AtomicInteger(0); + @Setter(onMethod_ = { @Value("${spring.application.name}") }) protected String appName; @@ -95,6 +101,29 @@ public class TinyTaskExecServiceImpl implements TinyTaskExecService { @Setter(onMethod_ = { @Autowired }) protected TinyTaskExecProp execProp; + protected volatile boolean isShutdownFast = false; + protected volatile boolean isShutdownSlow = false; + + @Override + public void afterPropertiesSet() { + isShutdownFast = false; + isShutdownSlow = false; + } + + @EventListener(ContextClosedEvent.class) + public void destroy() { + log.info("tiny-task shutdown for ContextClosedEvent"); + isShutdownFast = true; + isShutdownSlow = true; + for (var en : Handle.entrySet()) { + var tvl = en.getValue(); + var task = tvl.one(); + log.info("try to cancal tiny-task for shutdown, id={}, key={}, next_sys={}", en.getKey(), tvl.three(), DateLocaling.sysLdt(tvl.two())); + task.cancel(false); + } + Handle.clear(); + } + @Override public boolean launch(long id) { Cancel.remove(id); @@ -103,14 +132,31 @@ public boolean launch(long id) { @Override public boolean force(long id) { + if (isShutdownFast && isShutdownSlow) { + log.warn("cancel tiny-task for shutdown all on force, id={}", id); + return false; + } + final WinTaskDefine td = winTaskDefineDao.fetchOneById(id); if (td == null) { - log.info("skip task for not found, id={}", id); + log.info("skip tiny-task for not found on force, id={}", id); return false; } final boolean fast = BoxedCastUtil.orTrue(td.getTaskerFast()); - final var scheduler = fast ? TaskSchedulerHelper.Fast() : TaskSchedulerHelper.Scheduled(); + if (fast && isShutdownFast || !fast && isShutdownSlow) { + log.warn("cancal tiny-task for shutdown on force, fast={}, id={}", fast, id); + return false; + } + + final var scheduler = TaskSchedulerHelper.Scheduler(fast); + if (scheduler.getScheduledExecutor().isShutdown()) { + log.warn("cancal tiny-task for Executor shutdown on force, fast={}, id={}", fast, id); + if (fast) isShutdownFast = true; + else isShutdownSlow = true; + return false; + } + scheduler.schedule(() -> { long execTms = ThreadNow.millis(); long doneTms = -1; @@ -118,7 +164,7 @@ public boolean force(long id) { final String taskerInfo = td.getPropkey() + " force"; final String noticeConf = td.getNoticeConf(); - String taskMsg = "force task id=" + id; + String taskMsg = "force tiny-task id=" + id; NoticeExec notice = null; Set ntcWhen = Collections.emptySet(); try { @@ -128,17 +174,17 @@ public boolean force(long id) { if (notice != null) ntcWhen = noticeWhen(td.getNoticeWhen()); postNotice(notice, noticeConf, ntcWhen, taskerInfo, taskMsg, execTms, WhenExec); - log.debug("task force exec, id={}", id); + log.debug("tiny-task force exec, id={}", id); final Object result; if (execProp.isDryrun()) { final long slp = Sleep.ignoreInterrupt(10, 2000); result = "dryrun and sleep " + slp; - log.info("task force done, dryrun and sleep {} ms, id={}", slp, id); + log.info("tiny-task force done, dryrun and sleep {} ms, id={}", slp, id); } else { result = tasker.invoke(td.getTaskerPara(), true); - log.info("task force done, id={}", id); + log.info("tiny-task force done, id={}", id); } // doneTms = ThreadNow.millis(); @@ -146,7 +192,8 @@ public boolean force(long id) { postNotice(notice, noticeConf, ntcWhen, taskerInfo, taskMsg, doneTms, WhenFeed, WhenDone); } catch (Exception e) { - log.error("task force fail, id=" + id, e); + // noinspection StringConcatenationArgumentToLogCall + log.error("tiny-task force fail, id=" + id, e); failTms = ThreadNow.millis(); taskMsg = ThrowableUtil.toString(e); postNotice(notice, noticeConf, ntcWhen, taskerInfo, taskMsg, failTms, WhenFail); @@ -156,26 +203,28 @@ public boolean force(long id) { saveResult(id, td.getPropkey(), execTms, failTms, doneTms, taskMsg, td.getDurFail()); } catch (Exception e) { - log.error("failed to save result, id=" + id, e); + // noinspection StringConcatenationArgumentToLogCall + log.error("failed to save tiny-task result, id=" + id, e); } } }, Instant.ofEpochMilli(ThreadNow.millis())); + return true; } @Override public boolean cancel(long id) { Cancel.put(id, Boolean.TRUE); - final ScheduledFuture ft = Handle.get(id); - if (ft == null) { + final var tvl = Handle.get(id); + if (tvl == null) { log.info("cancel not found, id={}", id); return true; } - final boolean r = ft.cancel(false); + final boolean r = tvl.one().cancel(false); if (r) { Handle.remove(id); } - log.info("cancel success={}, id={}", r, id); + log.info("cancel success, id={}, key={}, next_sys={}", id, tvl.three(), DateLocaling.sysLdt(tvl.two())); return r; } @@ -190,47 +239,58 @@ public Set running() { } private boolean relaunch(long id) { + if (isShutdownFast && isShutdownSlow) { + log.warn("cancel tiny-task for shutdown all on relaunch, id={}", id); + return false; + } + final Lock lock = JvmStaticGlobalLock.get(id); try { lock.lock(); if (Handle.containsKey(id)) { - log.info("skip task for launching, id={}", id); + log.info("skip tiny-task on launching, id={}", id); return false; } final WinTaskDefine td = winTaskDefineDao.fetchOneById(id); if (td == null) { - log.info("skip task for not found, id={}", id); + log.info("skip tiny-task for not found on relaunch id={}", id); return false; } - if (notEnable(td.getEnabled(), id) - || notApps(td.getTaskerApps(), id) - || notRuns(td.getTaskerRuns(), id)) { + final String key = td.getPropkey(); + if (notEnable(td.getEnabled(), id, key) + || notApps(td.getTaskerApps(), id, key) + || notRuns(td.getTaskerRuns(), id, key)) { return false; } - final long next = calcNextExec(td); + final long next = calcNextExec(td, ThreadNow.millis()); if (next < 0) return false; // temp save before schedule to avoid kill saveNextExec(next, td); - final String key = td.getPropkey(); final boolean fast = BoxedCastUtil.orTrue(td.getTaskerFast()); - final var taskScheduler = fast ? TaskSchedulerHelper.Fast() : TaskSchedulerHelper.Scheduled(); + if (fast && isShutdownFast || !fast && isShutdownSlow) { + log.warn("cancal tiny-task for shutdown on relaunch, fast={}, id={}", fast, id); + return false; + } - if (taskScheduler.getScheduledExecutor().isShutdown()) { - log.error("TaskScheduler={} is shutdown, id={}, prop={}", fast, id, key); + final var scheduler = TaskSchedulerHelper.Scheduler(fast); + if (scheduler.getScheduledExecutor().isShutdown()) { + log.warn("cancal tiny-task for Executor shutdown on relaunch, fast={}, id={}, prop={}", fast, id, key); + if (fast) isShutdownFast = true; + else isShutdownSlow = true; return false; } - log.info("prepare task id={}, prop={}", id, key); - final ScheduledFuture handle = taskScheduler.schedule(() -> { + log.info("prepare tiny-task id={}, prop={}", id, key); + final ScheduledFuture handle = scheduler.schedule(() -> { long execTms = ThreadNow.millis(); try { if (notNextLock(td, execTms)) { - log.warn("skip task for Not nextLock, should manually check and launch it, id={}, prop={}", id, key); + log.warn("skip tiny-task for Not nextLock, should manually check and launch it, id={}, prop={}", id, key); Handle.remove(id); return; } @@ -246,7 +306,7 @@ private boolean relaunch(long id) { final String taskerInfo = key + " launch"; final String noticeConf = td.getNoticeConf(); - String exitMsg = "relaunch task id=" + id; + String exitMsg = "relaunch tiny-task key=" + key; NoticeExec notice = null; Set ntcWhen = Collections.emptySet(); try { @@ -256,17 +316,17 @@ private boolean relaunch(long id) { if (notice != null) ntcWhen = noticeWhen(td.getNoticeWhen()); postNotice(notice, noticeConf, ntcWhen, taskerInfo, exitMsg, execTms, WhenExec); - log.info("task exec, id={}, prop={}", id, key); + log.info("tiny-task exec, id={}, prop={}", id, key); final Object result; if (execProp.isDryrun()) { final long slp = Sleep.ignoreInterrupt(10, 2000); result = "dryrun and sleep " + slp; - log.info("task done, dryrun and sleep {} ms, id={}, prop={}", slp, id, key); + log.info("tiny-task done, dryrun and sleep {} ms, id={}, prop={}", slp, id, key); } else { result = tasker.invoke(td.getTaskerPara(), true); - log.info("task done, id={}, prop={}", id, key); + log.info("tiny-task done, id={}, prop={}", id, key); } // doneTms = ThreadNow.millis(); @@ -275,7 +335,8 @@ private boolean relaunch(long id) { } catch (Exception e) { Throwable c = ThrowableUtil.cause(e, 1); - log.error("task fail, id=" + id + ", prop=" + key, c); + // noinspection StringConcatenationArgumentToLogCall + log.error("tiny-task fail, id=" + id + ", prop=" + key, c); failTms = ThreadNow.millis(); exitMsg = ThrowableUtil.toString(c); postNotice(notice, noticeConf, ntcWhen, taskerInfo, exitMsg, failTms, WhenFail); @@ -286,7 +347,8 @@ private boolean relaunch(long id) { saveResult(id, key, execTms, failTms, doneTms, exitMsg, td.getDurFail()); } catch (Exception e) { - log.error("failed to save result, id=" + id + ", prop=" + key, e); + // noinspection StringConcatenationArgumentToLogCall + log.error("failed to save tiny-task result, id=" + id + ", prop=" + key, e); } if (canRelaunch(id, doneTms, failTms, td)) { // canceled @@ -296,7 +358,7 @@ private boolean relaunch(long id) { }, Instant.ofEpochMilli(next)); // - Handle.put(id, handle); + Handle.put(id, U.of(handle, next, key)); return true; } finally { @@ -305,71 +367,82 @@ private boolean relaunch(long id) { } // - private boolean notEnable(Boolean b, long id) { + private boolean notEnable(Boolean b, long id, String key) { if (BoxedCastUtil.orTrue(b)) { return false; } - log.info("skip task for not enabled, id={}", id); + log.info("skip tiny-task for not enabled, id={}, prop={}", id, key); return true; } - private boolean notApps(String apps, long id) { + private boolean notApps(String apps, long id, String key) { if (StringUtils.isEmpty(apps)) return false; - for (String s : arrayOrNull(apps, true)) { + for (String s : commaArray(apps)) { if (s.trim().equals(appName)) return false; } - log.info("skip task for not apps={}, cur={}, id={}", apps, appName, id); + + log.info("skip tiny-task for not apps={}, cur={}, id={}, prop={}", apps, appName, id, key); return true; } - private boolean notRuns(String runs, long id) { + private boolean notRuns(String runs, long id, String key) { if (StringUtils.isEmpty(runs)) return false; final RunMode rmd = RuntimeMode.getRunMode(); if (rmd == RunMode.Nothing) { - log.info("skip task for not runs={}, cur is null, id={}", runs, id); + log.info("skip tiny-task for not runs={}, cur is Nothing, id={}, prop={}", runs, id, key); return true; } - if (!RuntimeMode.hasRunMode(arrayOrNull(runs, true))) { - log.info("skip task for not runs={}, cur={}, id={}", runs, rmd, id); + if (!RuntimeMode.voteRunMode(runs)) { + log.info("skip tiny-task for not runs={}, cur={}, id={}, prop={}", runs, rmd, id, key); return true; } - - return false; + else { + return false; + } } private Set noticeWhen(String nw) { if (nw == null || nw.isEmpty()) return Collections.emptySet(); Set rs = new HashSet<>(); - for (String s : arrayOrNull(nw, true)) { + for (String s : commaArray(nw)) { rs.add(s.trim().toLowerCase()); } return rs; } - private String stringResult(Object result) { + protected String stringResult(Object result) { return JacksonHelper.string(result); } - private void postNotice(NoticeExec ntc, String cnf, Set whs, String sub, String msg, long ms, String... wh) { + protected void postNotice(NoticeExec ntc, String cnf, Set whs, String sub, String msg, long ms, String... wh) { if (ntc == null) return; - final String zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(ms), ThreadNow.sysZoneId()).toString(); + + String key = null; + boolean rtn = StringUtils.isNotEmpty(msg); for (String w : wh) { - if (whs.contains(w)) { - if (w.equals(WhenFeed)) { - if (!execProp.isDryrun() && StringUtils.isNotEmpty(msg)) { - ntc.postNotice(cnf, sub + " " + w.toUpperCase(), zdt + "\n\n" + msg); - return; - } - } - else { - ntc.postNotice(cnf, sub + " " + w.toUpperCase(), msg == null ? zdt : zdt + "\n\n" + msg); - return; + if (!whs.contains(w)) continue; + if (w.equals(WhenFeed)) { + if (rtn && !execProp.isDryrun()) { + key = w; + break; } } + else { + key = w; + break; + } } + + if (key == null) return; + + String sb = execProp.getNoticePrefix() + " " + sub + " " + key + " #" + noticeCounter.incrementAndGet(); + String bd = appName + "\n" + ZonedDateTime.ofInstant(Instant.ofEpochMilli(ms), ThreadNow.sysZoneId()); + if (rtn) bd = bd + "\n\n" + msg; + + ntc.postNotice(cnf, sb, bd); } private boolean notNextLock(WinTaskDefine td, long now) { @@ -466,26 +539,26 @@ private boolean canRelaunch(long id, long doneTms, long failTms, WinTaskDefine t final int duringExec = td.getDuringExec(); final int sumExec = td.getSumExec(); if (duringExec > 0 && duringExec <= sumExec + 1) { - log.info("remove task for duringExec={}, sumExec={}, id={}, name={}", duringExec, sumExec, id, td.getTaskerName()); + log.info("remove tiny-task for duringExec={}, sumExec={}, id={}, prop={}", duringExec, sumExec, id, td.getPropkey()); return false; } final int duringDone = td.getDuringDone(); final int sumDone = td.getSumDone(); if (duringDone > 0 && duringDone <= (doneTms < 0 ? sumDone : sumDone + 1)) { - log.info("remove task for duringDone={}, sumDone={}, id={}, name={}", duringDone, sumDone, id, td.getTaskerName()); + log.info("remove tiny-task for duringDone={}, sumDone={}, id={}, prop={}", duringDone, sumDone, id, td.getPropkey()); return false; } final int duringFail = td.getDuringFail(); final int durFail = td.getDurFail(); if (duringFail > 0 && duringFail <= (failTms < 0 ? durFail : durFail + 1)) { - log.info("remove task for duringFail={}, durFail={}, id={}, name={}", duringFail, durFail, id, td.getTaskerName()); + log.info("remove tiny-task for duringFail={}, durFail={}, id={}, prop={}", duringFail, durFail, id, td.getPropkey()); return false; } if (Cancel.containsKey(id)) { // canceled - log.info("remove task for canceled, id={}, name={}", id); + log.info("remove tiny-task for canceled, id={}, prop={}", id, td.getPropkey()); return false; } @@ -493,7 +566,7 @@ private boolean canRelaunch(long id, long doneTms, long failTms, WinTaskDefine t if (duringBoot > 0) { final int bct = Booted.compute(id, (ignored, v) -> v == null ? 1 : v + 1); if (bct >= duringBoot) { - log.info("remove task for duringBoot={}, id={}, name={}", bct, id, td.getTaskerName()); + log.info("remove tiny-task for duringBoot={}, id={}, prop={}", bct, id, td.getPropkey()); return false; } } @@ -501,11 +574,10 @@ private boolean canRelaunch(long id, long doneTms, long failTms, WinTaskDefine t return true; } - private long calcNextExec(WinTaskDefine td) { + protected long calcNextExec(WinTaskDefine td, long now) { final String zid = td.getTimingZone(); final ZoneId zone = StringUtils.isEmpty(zid) ? ThreadNow.sysZoneId() : ZoneId.of(zid); - final long now = ThreadNow.millis(); if (notRanged(td, zone, now)) return -1; final Long id = td.getId(); @@ -513,38 +585,46 @@ private long calcNextExec(WinTaskDefine td) { final long timingMiss = td.getTimingMiss() * 1000L; // Planned, program was killed before execution ends final long nextMs = DateLocaling.sysEpoch(td.getNextExec()); - if (nextMs + timingMiss >= now) { - log.info("launch misfire task, id={}, name={}", id, td.getTaskerName()); + if (timingMiss >= 0 && nextMs + timingMiss >= now) { + log.info("launch misfire tiny-task, id={}, prop={}", id, td.getPropkey()); return nextMs; } + final boolean zeroMiss = timingMiss <= 0 && timingMiss + now >= 0; + final Trigger trigger = makeTrigger(td, zone); final SimpleTriggerContext context = makeContext(td, zone, now); long nextExec = -1; + long n0 = -1; while (true) { Instant next = trigger.nextExecution(context); if (next == null) { - log.info("skip task for trigger not fire, id={}, name={}", id, td.getTaskerName()); + log.info("skip tiny-task for trigger not fire, id={}, prop={}", id, td.getPropkey()); break; } - final long nxt = next.toEpochMilli(); - if (nxt < now) { - if (timingMiss > 0 && nxt + timingMiss >= now) { - log.info("launch task for misfire={}, id={}, name={}", next, id, td.getTaskerName()); - nextExec = nxt; - break; + final long n1 = next.toEpochMilli(); + if (n1 >= now) { + if (zeroMiss && n0 > 0 && now <= n0 + ((n1 - n0) * 25 / 100)) { + log.info("launch tiny-task for miss=0, next={}, id={}, prop={}", next, id, td.getPropkey()); + nextExec = n0; } else { - context.update(next, next, next); + log.info("launch tiny-task for next={}, id={}, prop={}", next, id, td.getPropkey()); + nextExec = n1; } + break; } - else { - log.info("launch task for next={}, id={}, name={}", next, id, td.getTaskerName()); - nextExec = nxt; + + if (timingMiss > 0 && now <= n1 + timingMiss) { + log.info("launch tiny-task for miss={}, next={}, id={}, prop={}", timingMiss, next, id, td.getPropkey()); + nextExec = n1; break; } + + context.update(next, next, next); + n0 = n1; } if (nextExec < 0) return -1; @@ -586,13 +666,13 @@ private SimpleTriggerContext makeContext(WinTaskDefine td, ZoneId zone, long now private Trigger makeTrigger(WinTaskDefine td, ZoneId zone) { final String cron = td.getTimingCron(); if (StringUtils.isNotEmpty(cron)) { - log.info("use trigger cron={}, id={}, name={}", cron, td.getId(), td.getTaskerName()); + log.info("use trigger cron={}, id={}, prop={}", cron, td.getId(), td.getPropkey()); return new CronTrigger(cron, zone); } final int idle = td.getTimingIdle(); if (idle > 0) { - log.info("use trigger idle={}, id={}, name={}", idle, td.getId(), td.getTaskerName()); + log.info("use trigger idle={}, id={}, prop={}", idle, td.getId(), td.getPropkey()); PeriodicTrigger trg = new PeriodicTrigger(Duration.ofSeconds(idle)); trg.setFixedRate(false); return trg; @@ -600,7 +680,7 @@ private Trigger makeTrigger(WinTaskDefine td, ZoneId zone) { final int rate = td.getTimingRate(); if (rate > 0) { - log.info("use trigger rate={}, id={}, name={}", rate, td.getId(), td.getTaskerName()); + log.info("use trigger rate={}, id={}, prop={}", rate, td.getId(), td.getPropkey()); PeriodicTrigger trg = new PeriodicTrigger(Duration.ofSeconds(rate)); trg.setFixedRate(true); return trg; @@ -615,7 +695,7 @@ private boolean notRanged(WinTaskDefine td, ZoneId zone, long now) { final LocalDateTime ldt = DateParser.parseDateTime(duringFrom); final long ms = DateLocaling.useEpoch(ldt, zone); if (ms > now) { - log.info("skip task for duringFrom={}, id={}, name={}", duringFrom, td.getId(), td.getTaskerName()); + log.info("skip tiny-task for duringFrom={}, id={}, prop={}", duringFrom, td.getId(), td.getPropkey()); return true; } } @@ -625,26 +705,26 @@ private boolean notRanged(WinTaskDefine td, ZoneId zone, long now) { final LocalDateTime ldt = DateParser.parseDateTime(duringStop); final long ms = DateLocaling.useEpoch(ldt, zone); if (ms < now) { - log.info("skip task for duringStop={}, id={}, name={}", duringStop, td.getId(), td.getTaskerName()); + log.info("skip tiny-task for duringStop={}, id={}, prop={}", duringStop, td.getId(), td.getPropkey()); return true; } } final int duringExec = td.getDuringExec(); if (duringExec > 0 && duringExec <= td.getSumExec()) { - log.info("skip task for duringExec={}, sumExec={}, id={}, name={}", duringExec, td.getSumExec(), td.getId(), td.getTaskerName()); + log.info("skip tiny-task for duringExec={}, sumExec={}, id={}, prop={}", duringExec, td.getSumExec(), td.getId(), td.getPropkey()); return true; } final int duringDone = td.getDuringDone(); if (duringDone > 0 && duringDone <= td.getSumDone()) { - log.info("skip task for duringDone={}, sumDone={}, id={}, name={}", duringDone, td.getSumDone(), td.getId(), td.getTaskerName()); + log.info("skip tiny-task for duringDone={}, sumDone={}, id={}, prop={}", duringDone, td.getSumDone(), td.getId(), td.getPropkey()); return true; } final int duringFail = td.getDuringFail(); if (duringFail > 0 && duringFail <= td.getDurFail()) { - log.info("skip task for duringFail={}, durFail={}, id={}, name={}", duringFail, td.getDurFail(), td.getId(), td.getTaskerName()); + log.info("skip tiny-task for duringFail={}, durFail={}, id={}, prop={}", duringFail, td.getDurFail(), td.getId(), td.getPropkey()); return true; } diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskServiceImpl.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskServiceImpl.java index e34f82d7d..673a68d30 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskServiceImpl.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskServiceImpl.java @@ -36,7 +36,7 @@ public class TinyTaskServiceImpl implements TinyTaskService { @Override @NotNull public ThreadPoolTaskScheduler referScheduler(boolean fast) { - return fast ? TaskSchedulerHelper.Fast() : TaskSchedulerHelper.Scheduled(); + return TaskSchedulerHelper.Scheduler(fast); } @Override @@ -45,13 +45,13 @@ public Set schedule(@NotNull Object taskerBean) { final Set conf = tinyTaskConfService.config(taskerBean); final Set rst = new HashSet<>(); for (Conf cnf : conf) { - if (cnf.isEnabled() && cnf.isAutorun()) { + if (cnf.isEnabled() && cnf.isAutorun() && cnf.isMatched()) { final boolean cd = tinyTaskExecService.launch(cnf.getId()); - log.info("schedule task {}, scheduled={}", cnf, cd); + log.info("schedule bean tiny-task {}, scheduled={}", cnf, cd); rst.add(new Task(cnf.getId(), cnf.getKey(), cd)); } else { - log.info("skip task {}", cnf); + log.info("skip bean tiny-task {}", cnf); } } return rst; @@ -63,11 +63,11 @@ public Task schedule(@NotNull Object taskerBean, @NotNull Method taskerCall, @Nu final boolean cd; if (cnf.isEnabled()) { cd = tinyTaskExecService.launch(cnf.getId()); - log.info("schedule task {}, scheduled={}", cnf, cd); + log.info("schedule method tiny-task {}, scheduled={}", cnf, cd); } else { cd = false; - log.info("skip task {}", cnf); + log.info("skip method tiny-task {}", cnf); } return new Task(cnf.getId(), cnf.getKey(), cd); diff --git a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/spring/prop/TinyTaskExecProp.java b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/spring/prop/TinyTaskExecProp.java index d9fc6b1f8..b32a8dd5e 100644 --- a/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/spring/prop/TinyTaskExecProp.java +++ b/radiant/tiny-task/src/main/java/pro/fessional/wings/tiny/task/spring/prop/TinyTaskExecProp.java @@ -23,4 +23,12 @@ public class TinyTaskExecProp { */ private boolean dryrun = false; public static final String Key$dryrun = Key + ".dryrun"; + + /** + * prefix of notice subject + * + * @see #Key$noticePrefix + */ + private String noticePrefix = "tiny-task"; + public static final String Key$noticePrefix = Key + ".notice-prefix"; } diff --git a/radiant/tiny-task/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/radiant/tiny-task/src/main/resources/META-INF/additional-spring-configuration-metadata.json index e5a15204b..86dacad9e 100644 --- a/radiant/tiny-task/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/radiant/tiny-task/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,29 +1,30 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.tiny.task.controller"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.database.autogen.tables.daos"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.service.impl"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.conf.TinyTaskAutoConfiguration", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration$DaoServScan", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration$MvcRestScan", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration.tinyTaskerAutoRunner", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.task.autorun for short."}, - - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration.mailNotice", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration.mailSenderManager", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration.mailSenderProvider", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.task.controller.TaskConfController", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.task.mvc-conf for short."}, {"name": "wings.enabled.pro.fessional.wings.tiny.task.controller.TaskExecController", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.task.mvc-exec for short."}, {"name": "wings.enabled.pro.fessional.wings.tiny.task.controller.TaskListController", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.task.mvc-list for short."}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.database.autogen.tables.daos.WinTaskDefineDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.database.autogen.tables.daos.WinTaskResultDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.service.impl.TinyTaskBeatServiceImpl", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.tiny.task.service.impl.TinyTaskConfServiceImpl", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.tiny.task.service.impl.TinyTaskExecServiceImpl", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.tiny.task.service.impl.TinyTaskListServiceImpl", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.tiny.task.service.impl.TinyTaskServiceImpl", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.tiny.task.service.impl.TinyTaskServiceImpl", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration$DaoServScan", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.bean.TinyTaskConfiguration.tinyTaskerAutoRunner", "type": "java.lang.Boolean", "description": "wings.enabled.tiny.task.autorun for short."}, + + {"name": "wings.enabled.pro.fessional.wings.tiny.task.spring.conf.TinyTaskAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-beat-79.properties b/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-beat-79.properties index 6d2c783c2..f96b980f7 100644 --- a/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-beat-79.properties +++ b/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-beat-79.properties @@ -1,5 +1,5 @@ -## Clean the result at system timezone 02:01:00 -wings.tiny.task.define[TinyTaskCleanResult].timing-cron=0 1 2 * * * -## Check health status every 300 seconds of idle time. -wings.tiny.task.define[TinyTaskCheckHealth].timing-idle=300 +## Clean the result at system timezone 13:11:07 +wings.tiny.task.define[TinyTaskCleanResult].timing-cron=7 11 13 * * * +## Check health status every 347 seconds of idle time. +wings.tiny.task.define[TinyTaskCheckHealth].timing-idle=347 wings.tiny.task.define[TinyTaskCheckHealth].notice-when=fail,feed diff --git a/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-define-79.properties b/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-define-79.properties index 4260a14d9..69393f7a8 100644 --- a/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-define-79.properties +++ b/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-define-79.properties @@ -27,7 +27,7 @@ ## use Default config if null or empty. wings.tiny.task.define[default].tasker-apps=${spring.application.name} -## RunMode(product|test|develop|local), Comma separated, ignore case, default all, +## RunMode(product|test|develop|local), `!test`,`-test` means not test, Comma separated, ignore case, default all, ## use Default config if null or empty. wings.tiny.task.define[default].tasker-runs= @@ -70,14 +70,16 @@ wings.tiny.task.define[default].timing-type=cron ## * cron - each time #wings.tiny.task.define[default].timing-tune=0 -## Within how many seconds of a misfire, execution is required, -## 0 means no execution. not use Default config. +## Within how many seconds of a misfire, execution is required, not use Default config. +## * `<0` - execute as `0` if now + miss * 1000 >= 0 +## * `0` - execute if N0 < now <= N0 + (N1-N0) * 25% < N1 +## * `>0` - execute if N1 < now <= N1 + miss * 1000 #wings.tiny.task.define[default].timing-miss=0 ## the interval seconds of heartbeat and health-check, not use Default config. ## it is considered as an exception if the last_exec is more than 2 heartbeats away from now. -## * `<0` - disable check -## * `0` - auto calculate, when cron, calc next_exec from last_exec, others, max rate and idle +## * `<0` - calculate as `0` if now + beat * 1000 >= 0 +## * `0` - calculate, when cron, calc next_exec from last_exec, others, max rate and idle ## * `>0` - fixed positive seconds #wings.tiny.task.define[default].timing-beat=0 diff --git a/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-exec-79.properties b/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-exec-79.properties index 9473b119f..88061796a 100644 --- a/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-exec-79.properties +++ b/radiant/tiny-task/src/main/resources/wings-conf/wings-tinytask-exec-79.properties @@ -1,2 +1,4 @@ ## whether to dry run, log only without realy exec the task. wings.tiny.task.exec.dryrun=false +## prefix of notice subject +wings.tiny.task.exec.notice-prefix=tiny-task diff --git a/radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-v32-130/2021-10-26u02-task-tune.sql b/radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-task-tune/2021-10-26u02-task-tune.sql similarity index 60% rename from radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-v32-130/2021-10-26u02-task-tune.sql rename to radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-task-tune/2021-10-26u02-task-tune.sql index b2eec67fa..e0c9545bb 100644 --- a/radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-v32-130/2021-10-26u02-task-tune.sql +++ b/radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-task-tune/2021-10-26u02-task-tune.sql @@ -1,4 +1,4 @@ --- win_task_define +-- https://github.com/trydofor/professional-wings/issues/256 UPDATE `win_task_define` SET timing_tune = last_fail WHERE TRUE; @@ -6,6 +6,8 @@ WHERE TRUE; ALTER TABLE `win_task_define` DROP COLUMN `last_fail`, CHANGE COLUMN `last_exit` `last_done` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous success (sys)', + CHANGE COLUMN `timing_miss` `timing_miss` INT NOT NULL DEFAULT '0' COMMENT 'within how many seconds of a misfire', + CHANGE COLUMN `timing_beat` `timing_beat` INT NOT NULL DEFAULT '0' COMMENT 'interval seconds of heartbeat, default auto calc', ADD COLUMN `last_fail` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous fail (sys)' AFTER last_exec; UPDATE `win_task_define` @@ -29,3 +31,10 @@ WHERE exit_fail = 1; ALTER TABLE `win_task_result` DROP COLUMN `exit_fail`; + +-- https://github.com/trydofor/professional-wings/issues/284 +ALTER TABLE `win_task_define` + CHANGE COLUMN `timing_miss` `timing_miss` INT NOT NULL DEFAULT '0' COMMENT 'within how many seconds of a misfire', + CHANGE COLUMN `timing_beat` `timing_beat` INT NOT NULL DEFAULT '0' COMMENT 'interval seconds of heartbeat, default auto calc'; + +-- CALL FLYWAVE('2021-10-26u02-task-tune.sql'); \ No newline at end of file diff --git a/radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-v32-130/2021-10-26v02-task-tune.sql b/radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-task-tune/2021-10-26v02-task-tune.sql similarity index 57% rename from radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-v32-130/2021-10-26v02-task-tune.sql rename to radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-task-tune/2021-10-26v02-task-tune.sql index 8b0d17c74..be1e4ead8 100644 --- a/radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-v32-130/2021-10-26v02-task-tune.sql +++ b/radiant/tiny-task/src/main/resources/wings-flywave/branch/somefix/03-task-tune/2021-10-26v02-task-tune.sql @@ -1,6 +1,6 @@ --- win_task_define +-- https://github.com/trydofor/professional-wings/issues/256 ALTER TABLE `win_task_define` - ADD COLUMN `timing_tune` INT(11) NOT NULL DEFAULT '0' COMMENT 'execute before or after tune (seconds)' AFTER `timing_rate`, + ADD COLUMN `timing_tune` INT NOT NULL DEFAULT '0' COMMENT 'execute before or after tune (seconds)' AFTER `timing_rate`, CHANGE COLUMN `last_done` `last_exit` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous done/fail (sys)'; UPDATE `win_task_define` @@ -10,7 +10,7 @@ WHERE last_fail > '2000-01-01'; ALTER TABLE `win_task_define` DROP COLUMN `last_fail`, - ADD COLUMN `last_fail` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'whether previous fail' AFTER last_exit; + ADD COLUMN `last_fail` BOOLEAN NOT NULL DEFAULT '0' COMMENT 'whether previous fail' AFTER last_exit; UPDATE `win_task_define` SET last_fail = timing_tune, @@ -21,7 +21,7 @@ WHERE timing_tune = 1; ALTER TABLE `win_task_result` ADD COLUMN `task_key` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'conf file key, auto-generated' AFTER `task_id`, CHANGE COLUMN `task_msg` `exit_data` TEXT NULL DEFAULT NULL COMMENT 'return (json) or exception (stacktrace)', - ADD COLUMN `exit_fail` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'whether fail' AFTER `exit_data`, + ADD COLUMN `exit_fail` BOOLEAN NOT NULL DEFAULT '0' COMMENT 'whether fail' AFTER `exit_data`, CHANGE COLUMN `time_done` `time_exit` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'datetime of done/fail (sys)'; UPDATE `win_task_result` @@ -35,3 +35,10 @@ WHERE time_fail > '2000-01-01'; ALTER TABLE `win_task_result` DROP COLUMN `time_fail`; + +-- https://github.com/trydofor/professional-wings/issues/284 +ALTER TABLE `win_task_define` + CHANGE COLUMN `timing_miss` `timing_miss` BIGINT NOT NULL DEFAULT '0' COMMENT 'within how many seconds of a misfire, default auto calc', + CHANGE COLUMN `timing_beat` `timing_beat` BIGINT NOT NULL DEFAULT '0' COMMENT 'interval seconds of heartbeat, default auto calc'; + +-- CALL FLYWAVE('2021-10-26v02-task-tune.sql'); \ No newline at end of file diff --git a/radiant/tiny-task/src/main/resources/wings-flywave/master/06-task/2020-10-26u01-tiny_task.sql b/radiant/tiny-task/src/main/resources/wings-flywave/master/06-task/2020-10-26u01-tiny_task.sql index 8ec767b31..345e8749f 100644 --- a/radiant/tiny-task/src/main/resources/wings-flywave/master/06-task/2020-10-26u01-tiny_task.sql +++ b/radiant/tiny-task/src/main/resources/wings-flywave/master/06-task/2020-10-26u01-tiny_task.sql @@ -1,3 +1,4 @@ -DROP TABLE IF EXISTS `win_task_define`; -- 120/Task Define; -DROP TABLE IF EXISTS `win_task_result`; -- 122/Task Result; +DROP TABLE IF EXISTS `win_task_define`; -- 141/Task Define; +DROP TABLE IF EXISTS `win_task_result`; -- 142/Task Result; +-- CALL FLYWAVE('2020-10-26u01-tiny_task.sql'); \ No newline at end of file diff --git a/radiant/tiny-task/src/main/resources/wings-flywave/master/06-task/2020-10-26v01-tiny_task.sql b/radiant/tiny-task/src/main/resources/wings-flywave/master/06-task/2020-10-26v01-tiny_task.sql index 06e0dce68..eaab3f58d 100644 --- a/radiant/tiny-task/src/main/resources/wings-flywave/master/06-task/2020-10-26v01-tiny_task.sql +++ b/radiant/tiny-task/src/main/resources/wings-flywave/master/06-task/2020-10-26v01-tiny_task.sql @@ -1,17 +1,17 @@ CREATE TABLE `win_task_define` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key/task_id', + `id` BIGINT NOT NULL COMMENT 'primary key/task_id', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime(sys)', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', `propkey` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'conf file key, auto-generated', - `enabled` TINYINT(1) NOT NULL DEFAULT '1' COMMENT 'whether to register and execute', - `autorun` TINYINT(1) NOT NULL DEFAULT '1' COMMENT 'whether to auto register and start', - `version` INT(11) NOT NULL DEFAULT '0' COMMENT 'version number, higher overrides lower one', + `enabled` BOOLEAN NOT NULL DEFAULT '1' COMMENT 'whether to register and execute', + `autorun` BOOLEAN NOT NULL DEFAULT '1' COMMENT 'whether to auto register and start', + `version` INT NOT NULL DEFAULT '0' COMMENT 'version number, higher overrides lower one', `tasker_bean` VARCHAR(300) NOT NULL DEFAULT '' COMMENT 'beans annotated by TinyTasker, formatted as Class#method', - `tasker_para` TEXT NULL DEFAULT NULL COMMENT 'parameters of the task, object array in json format', + `tasker_para` TEXT NULL COMMENT 'parameters of the task, object array in json format', `tasker_name` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'task name, used for notice and log, shortClassName#method', - `tasker_fast` TINYINT(1) NOT NULL DEFAULT '1' COMMENT 'whether light task, fast execution, completed in seconds', + `tasker_fast` BOOLEAN NOT NULL DEFAULT '1' COMMENT 'whether light task, fast execution, completed in seconds', `tasker_apps` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'belong to applications, comma-separated, default spring.application.name', `tasker_runs` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'RunMode(product|test|develop|local), comma-separated case-insensitive', `notice_bean` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'notice bean, SmallNotice type, fullpath of Class', @@ -20,44 +20,46 @@ CREATE TABLE `win_task_define` ( `timing_zone` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'timezone of scheduling , default system timezone', `timing_type` VARCHAR(100) NOT NULL DEFAULT 'cron' COMMENT 'scheduling expression type', `timing_cron` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'scheduling expression content', - `timing_idle` INT(11) NOT NULL DEFAULT '0' COMMENT 'fixed idle interval (seconds)', - `timing_rate` INT(11) NOT NULL DEFAULT '0' COMMENT 'fixed frequency interval (seconds)', - `timing_tune` INT(11) NOT NULL DEFAULT '0' COMMENT 'execute before or after tune (seconds)', - `timing_miss` INT(11) NOT NULL DEFAULT '0' COMMENT 'within how many seconds of a misfire', - `timing_beat` INT(11) NOT NULL DEFAULT '0' COMMENT 'interval seconds of heartbeat', + `timing_idle` INT NOT NULL DEFAULT '0' COMMENT 'fixed idle interval (seconds)', + `timing_rate` INT NOT NULL DEFAULT '0' COMMENT 'fixed frequency interval (seconds)', + `timing_tune` INT NOT NULL DEFAULT '0' COMMENT 'execute before or after tune (seconds)', + `timing_miss` BIGINT NOT NULL DEFAULT '0' COMMENT 'within how many seconds of a misfire, default auto calc', + `timing_beat` BIGINT NOT NULL DEFAULT '0' COMMENT 'interval seconds of heartbeat, default auto calc', `during_from` VARCHAR(20) NOT NULL DEFAULT '' COMMENT 'schedule start datetime at timingZone, yyyy-MM-dd HH:mm:ss', `during_stop` VARCHAR(20) NOT NULL DEFAULT '' COMMENT 'schedule stop datetime at timingZone, yyyy-MM-dd HH:mm:ss', - `during_exec` INT(11) NOT NULL DEFAULT '0' COMMENT 'stop schedule after how many total executions', - `during_fail` INT(11) NOT NULL DEFAULT '0' COMMENT 'stop schedule after how many consecutive failures', - `during_done` INT(11) NOT NULL DEFAULT '0' COMMENT 'stop schedule after how many successful executions', - `during_boot` INT(11) NOT NULL DEFAULT '0' COMMENT 'recount each time the app is started, and stop schedule after how many successful executions', - `result_keep` INT(11) NOT NULL DEFAULT '60' COMMENT 'how many days to save the execution results', + `during_exec` INT NOT NULL DEFAULT '0' COMMENT 'stop schedule after how many total executions', + `during_fail` INT NOT NULL DEFAULT '0' COMMENT 'stop schedule after how many consecutive failures', + `during_done` INT NOT NULL DEFAULT '0' COMMENT 'stop schedule after how many successful executions', + `during_boot` INT NOT NULL DEFAULT '0' COMMENT 'recount each time the app is started, and stop schedule after how many successful executions', + `result_keep` INT NOT NULL DEFAULT '60' COMMENT 'how many days to save the execution results', `last_exec` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous exec (sys)', `last_exit` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'previous done/fail (sys)', - `last_fail` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'whether previous fail', + `last_fail` BOOLEAN NOT NULL DEFAULT '0' COMMENT 'whether previous fail', `next_exec` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'next exec (sys), default stop', - `next_lock` INT(11) NOT NULL DEFAULT '0' COMMENT 'optimistic lock of exec', - `dur_fail` INT(11) NOT NULL DEFAULT '0' COMMENT 'total count of consecutive fail', - `sum_exec` INT(11) NOT NULL DEFAULT '0' COMMENT 'total count of exec', - `sum_fail` INT(11) NOT NULL DEFAULT '0' COMMENT 'total count of fail', - `sum_done` INT(11) NOT NULL DEFAULT '0' COMMENT 'total count of done', + `next_lock` INT NOT NULL DEFAULT '0' COMMENT 'optimistic lock of exec', + `dur_fail` INT NOT NULL DEFAULT '0' COMMENT 'total count of consecutive fail', + `sum_exec` INT NOT NULL DEFAULT '0' COMMENT 'total count of exec', + `sum_fail` INT NOT NULL DEFAULT '0' COMMENT 'total count of fail', + `sum_done` INT NOT NULL DEFAULT '0' COMMENT 'total count of done', PRIMARY KEY (`id`), UNIQUE INDEX uq_tasker_bean (`tasker_bean`) ) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4 COMMENT ='120/Task Define'; + DEFAULT CHARSET = utf8mb4 COMMENT ='141/Task Define'; CREATE TABLE `win_task_result` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', - `task_id` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'task id, win_task_define.id', + `id` BIGINT NOT NULL COMMENT 'primary key', + `task_id` BIGINT NOT NULL DEFAULT '0' COMMENT 'task id, win_task_define.id', `task_key` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'conf file key, auto-generated', `task_app` VARCHAR(300) NOT NULL DEFAULT '' COMMENT 'belong to applications, comma-separated', - `task_pid` INT(11) NOT NULL DEFAULT '0' COMMENT 'belong to jvm pid', - `exit_data` TEXT NULL DEFAULT NULL COMMENT 'return (json) or exception (stacktrace)', - `exit_fail` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'whether fail', + `task_pid` INT NOT NULL DEFAULT '0' COMMENT 'belong to jvm pid', + `exit_data` TEXT NULL COMMENT 'return (json) or exception (stacktrace)', + `exit_fail` BOOLEAN NOT NULL DEFAULT '0' COMMENT 'whether fail', `time_exec` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'datetime of exec (sys)', `time_exit` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'datetime of done/fail (sys)', - `time_cost` INT(11) NOT NULL DEFAULT '0' COMMENT 'mills cost of task', + `time_cost` INT NOT NULL DEFAULT '0' COMMENT 'mills cost of task', PRIMARY KEY (`id`), INDEX ix_task_id (`task_id`) ) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4 COMMENT ='122/Task Result'; + DEFAULT CHARSET = utf8mb4 COMMENT ='142/Task Result'; + +-- CALL FLYWAVE('2020-10-26v01-tiny_task.sql'); \ No newline at end of file diff --git a/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/other/ExecutorServiceTest.java b/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/other/ExecutorServiceTest.java index 9110285cd..2d1159140 100644 --- a/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/other/ExecutorServiceTest.java +++ b/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/other/ExecutorServiceTest.java @@ -2,14 +2,13 @@ import io.qameta.allure.TmsLink; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import pro.fessional.mirana.time.Sleep; import pro.fessional.wings.slardar.async.TaskSchedulerHelper; import pro.fessional.wings.testing.silencer.TestingLoggerAssert; -import java.time.Instant; import java.util.concurrent.ScheduledFuture; /** @@ -17,8 +16,8 @@ * @since 2022-12-13 */ @SpringBootTest(properties = { - "logging.level.root=DEBUG", // AssertionLogger - "spring.task.scheduling.shutdown.await-termination-period=2s", + "logging.level.root=DEBUG", // AssertionLogger + "spring.task.scheduling.shutdown.await-termination-period=2s", }) @Slf4j class ExecutorServiceTest { @@ -38,54 +37,53 @@ void cancelSchedule() { al.rule(".. cancel", event -> event.getFormattedMessage().contains(".. cancel=true")); al.start(); - final ThreadPoolTaskScheduler scheduler = TaskSchedulerHelper.Scheduled(); - final ScheduledFuture f1 = scheduler.schedule(() -> log.info("-1 run={}", System.currentTimeMillis()), - Instant.ofEpochMilli(System.currentTimeMillis() - 1000)); + final ScheduledFuture f1 = TaskSchedulerHelper.Scheduled(-1000L, () -> log.info("-1 run={}", System.currentTimeMillis())); Sleep.ignoreInterrupt(500); log.info("-1 cancel={}", f1.cancel(false)); - final ScheduledFuture f2 = scheduler.schedule(() -> log.info("=0 run={}", System.currentTimeMillis()), - Instant.ofEpochMilli(System.currentTimeMillis())); + final ScheduledFuture f2 = TaskSchedulerHelper.Scheduled(0L, () -> log.info("=0 run={}", System.currentTimeMillis())); Sleep.ignoreInterrupt(500); log.info("=0 cancel={}", f2.cancel(false)); - final ScheduledFuture f3 = scheduler.schedule(() -> log.info("+1 run={}", System.currentTimeMillis()), - Instant.ofEpochMilli(System.currentTimeMillis() + 1000)); + final ScheduledFuture f3 = TaskSchedulerHelper.Scheduled(1000L, () -> log.info("+1 run={}", System.currentTimeMillis())); Sleep.ignoreInterrupt(500); log.info("+1 cancel={}", f3.cancel(false)); - final ScheduledFuture f4 = scheduler.schedule(() -> { - for (int i = 0; i < 10; i++) { - log.info("== run={}", i); - Sleep.ignoreInterrupt(100); - } - }, - Instant.ofEpochMilli(System.currentTimeMillis())); + final ScheduledFuture f4 = TaskSchedulerHelper.Scheduled(0L, () -> { + for (int i = 0; i < 10; i++) { + log.info("== run={}", i); + Sleep.ignoreInterrupt(100); + } + }); Sleep.ignoreInterrupt(500); log.info("== cancel={}", f4.cancel(false)); - final ScheduledFuture f5 = scheduler.schedule(() -> { - for (int i = 0; i < 10; i++) { - log.info(".. run={}", i); - try { - Thread.sleep(100); - } - catch (InterruptedException e) { - break; - } - } - }, - Instant.ofEpochMilli(System.currentTimeMillis())); + final ScheduledFuture f5 = TaskSchedulerHelper.Scheduled(0L, () -> { + for (int i = 0; i < 10; i++) { + log.info(".. run={}", i); + try { + Thread.sleep(100); + } + catch (InterruptedException e) { + break; + } + } + }); + + int size = TaskSchedulerHelper.runningSize(); + Assertions.assertTrue(size >= 1, "size=" + size); + Sleep.ignoreInterrupt(500); log.info(".. cancel={}", f5.cancel(true)); Sleep.ignoreInterrupt(2000); log.info("== done="); - al.stop(); al.assertCount(1); al.uninstall(); + + Assertions.assertEquals(0, TaskSchedulerHelper.runningSize()); } } diff --git a/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/TinyTaskExecServiceTest.java b/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/TinyTaskExecServiceTest.java index b046e7d6c..cb042ab2c 100644 --- a/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/TinyTaskExecServiceTest.java +++ b/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/TinyTaskExecServiceTest.java @@ -80,8 +80,8 @@ private void check(PageResult result, int size, int span) { long spaned = 0; if (prevExec != null) { spaned = Duration.between(timeExec, prevExec).abs().toSeconds() + 2; - log.info("WinTaskResult, id={}, exec={}, exit={}, fail={}, span={}", it.getTaskId(), timeExec, it.getTimeExit(), it.getExitFail(), spaned); - Assertions.assertTrue(spaned >= span); + log.info("WinTaskResult, id={}, exec={}, exit={}, fail={}, spaned={}", it.getTaskId(), timeExec, it.getTimeExit(), it.getExitFail(), spaned); + Assertions.assertTrue(spaned >= span, "spaned=" + spaned + " vs span=" + span); } else { log.info("WinTaskResult, id={}, exec={}, exit={}, fail={}", it.getTaskId(), timeExec, it.getTimeExit(), it.getExitFail()); diff --git a/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskBeatServiceImplTest.java b/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskBeatServiceImplTest.java new file mode 100644 index 000000000..0df13b1cc --- /dev/null +++ b/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskBeatServiceImplTest.java @@ -0,0 +1,51 @@ +package pro.fessional.wings.tiny.task.service.impl; + +import io.qameta.allure.TmsLink; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import pro.fessional.mirana.time.DateLocaling; +import pro.fessional.mirana.time.ThreadNow; +import pro.fessional.wings.tiny.task.database.autogen.tables.pojos.WinTaskDefine; + +import java.time.LocalDateTime; + +/** + * @author trydofor + * @since 2024-08-02 + */ +class TinyTaskBeatServiceImplTest { + + @Test + @TmsLink("C15020") + void calcBeatMills() { + TinyTaskBeatServiceImpl bs = new TinyTaskBeatServiceImpl(); + WinTaskDefine td = new WinTaskDefine(); + td.setLastExec(LocalDateTime.parse("2024-07-31T02:01:00")); + td.setTimingBeat(0L); + td.setTimingCron("0 1 2 * * *"); + td.setTimingZone(""); + long now = ThreadNow.millis(); + long next = bs.calcBeatMills(td, now); + LocalDateTime ldt = DateLocaling.sysLdt(next); + LocalDateTime nxt2 = LocalDateTime.parse("2024-08-02T02:00:00"); + Assertions.assertTrue(ldt.isAfter(nxt2), ldt + " > " + nxt2); + + td.setLastExec(DateLocaling.sysLdt(now)); + td.setTimingRate(60); + td.setTimingIdle(50); + td.setTimingCron(""); + long nr2 = bs.calcBeatMills(td, now); + long df = (nr2 - now) / 1000; + Assertions.assertTrue(df > 120 - 2, "s=" + df); + + long nb3 = -now/1000 - 10; + td.setTimingBeat(nb3); + long nr3 = bs.calcBeatMills(td, now); + Assertions.assertEquals(nb3, nr3); + + long nb4 = -now/1000 + 10; + td.setTimingBeat(nb4); + long nr4 = bs.calcBeatMills(td, now); + Assertions.assertEquals(nr2, nr4); // same as nr2 + } +} \ No newline at end of file diff --git a/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskExecServiceImplTest.java b/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskExecServiceImplTest.java new file mode 100644 index 000000000..98895477b --- /dev/null +++ b/radiant/tiny-task/src/test/java/pro/fessional/wings/tiny/task/service/impl/TinyTaskExecServiceImplTest.java @@ -0,0 +1,127 @@ +package pro.fessional.wings.tiny.task.service.impl; + +import io.qameta.allure.TmsLink; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import pro.fessional.mirana.time.DateLocaling; +import pro.fessional.wings.faceless.convention.EmptyValue; +import pro.fessional.wings.tiny.task.database.autogen.tables.pojos.WinTaskDefine; + +import java.time.LocalDateTime; + +/** + * @author trydofor + * @since 2024-08-03 + */ +class TinyTaskExecServiceImplTest { + + @Test + @TmsLink("C15021") + void calcNextExec() { + TinyTaskExecServiceImpl impl = new TinyTaskExecServiceImpl(); + WinTaskDefine td = new WinTaskDefine(); + td.setId(1L); + td.setDuringExec(0); + td.setDuringFail(0); + td.setDuringDone(0); + + td.setTimingMiss(0L); + td.setTimingTune(0); + LocalDateTime last = LocalDateTime.of(2024, 8, 1, 8, 1); + td.setLastExec(last); + td.setLastExit(last.withSecond(30)); + + + td.setTimingCron("0 21 * * * *"); + td.setTimingIdle(0); + td.setTimingRate(0); + td.setTimingZone(""); + + { + // killed before next + td.setNextExec(last.withHour(9)); // 2024-08-01 09:01 + long now = DateLocaling.sysEpoch(last.withMinute(20)); // 2024-08-01 08:20 + long next = impl.calcNextExec(td, now); + LocalDateTime nxt = DateLocaling.sysLdt(next); + Assertions.assertEquals(LocalDateTime.of(2024, 8, 1, 9, 1), nxt); + } + { + // empty next + td.setNextExec(EmptyValue.DATE_TIME); + long now = DateLocaling.sysEpoch(last.withMinute(20)); // 2024-08-01 08:20 + long next = impl.calcNextExec(td, now); + LocalDateTime nxt = DateLocaling.sysLdt(next); + Assertions.assertEquals(LocalDateTime.of(2024, 8, 1, 8, 21), nxt); + } + { + // 08:01___mis9___mis10___mis11 + //N0= 10:21 + //Now + //Nx= 10:36 = 10:21 + :15 (25% N1-N0) + //N1= 11:21 + td.setNextExec(last.withHour(9)); // 2024-08-01 09:01 + long now = DateLocaling.sysEpoch(last.withHour(10).withMinute(36)); // 2024-08-02 11:01 + long next = impl.calcNextExec(td, now); + LocalDateTime nxt = DateLocaling.sysLdt(next); + Assertions.assertEquals(LocalDateTime.of(2024, 8, 1, 10, 21), nxt); + } + { + // 08:01___mis9___mis10___mis11 + //N0= 10:21 + //Nx= 10:36 = 10:21 + :15 (25% N1-N0) + //Now + //N1= 11:21 + td.setNextExec(last.withHour(9)); // 2024-08-01 09:01 + long now = DateLocaling.sysEpoch(last.withHour(10).withMinute(37)); // 2024-08-02 11:01 + long next = impl.calcNextExec(td, now); + LocalDateTime nxt = DateLocaling.sysLdt(next); + Assertions.assertEquals(LocalDateTime.of(2024, 8, 1, 11, 21), nxt); + } + + td.setTimingCron(""); + td.setTimingIdle(0); + td.setTimingRate(3600); + td.setTimingZone(""); + + { + // killed before next + td.setNextExec(last.withHour(9)); // 2024-08-01 09:01 + long now = DateLocaling.sysEpoch(last.withMinute(20)); // 2024-08-01 08:20 + long next = impl.calcNextExec(td, now); + LocalDateTime nxt = DateLocaling.sysLdt(next); + Assertions.assertEquals(LocalDateTime.of(2024, 8, 1, 9, 1), nxt); + } + { + // empty next + td.setNextExec(EmptyValue.DATE_TIME); + long now = DateLocaling.sysEpoch(last.withMinute(20)); // 2024-08-01 08:20 + long next = impl.calcNextExec(td, now); + LocalDateTime nxt = DateLocaling.sysLdt(next); + Assertions.assertEquals(LocalDateTime.of(2024, 8, 1, 9, 1), nxt); + } + { + // 08:01___mis9___mis10___mis11 + //N0= 10:01 + //Now + //Nx= 10:16 = 10:01 + :15 (25% N1-N0) + //N1= 11:01 + td.setNextExec(last.withHour(9)); // 2024-08-01 09:01 + long now = DateLocaling.sysEpoch(last.withHour(10).withMinute(16)); // 2024-08-02 11:01 + long next = impl.calcNextExec(td, now); + LocalDateTime nxt = DateLocaling.sysLdt(next); + Assertions.assertEquals(LocalDateTime.of(2024, 8, 1, 10, 1), nxt); + } + { + // 08:01___mis9___mis10___mis11 + //N0= 10:01 + //Nx= 10:16 = 10:01 + :15 (25% N1-N0) + //Now + //N1= 11:01 + td.setNextExec(last.withHour(9)); // 2024-08-01 09:01 + long now = DateLocaling.sysEpoch(last.withHour(10).withMinute(17)); // 2024-08-02 11:01 + long next = impl.calcNextExec(td, now); + LocalDateTime nxt = DateLocaling.sysLdt(next); + Assertions.assertEquals(LocalDateTime.of(2024, 8, 1, 11, 1), nxt); + } + } +} \ No newline at end of file diff --git a/readme-zh.md b/readme-zh.md index 22d52d301..d0b1c9f9b 100644 --- a/readme-zh.md +++ b/readme-zh.md @@ -37,7 +37,7 @@ ```bash ## ① 获取源码,成功后进入项目目录 git clone --depth 1 https://github.com/\ -trydofor/pro.fessional.wings.git +trydofor/professional-wings.git ## ② 安装依赖,可跳过,支持java8编译 # asdf shell java temurin-8.0.412+8 git submodule update --remote --init diff --git a/readme.md b/readme.md index c4a808482..82abec8f4 100644 --- a/readme.md +++ b/readme.md @@ -37,7 +37,7 @@ ```bash ## (1) get source code git clone --depth 1 https://github.com/\ -trydofor/pro.fessional.wings.git +trydofor/professional-wings.git ## (2) install dependency using java8 # asdf shell java temurin-8.0.412+8 git submodule update --remote --init diff --git a/wings/faceless-codegen/src/main/java/pro/fessional/wings/faceless/jooqgen/WingsJavaGenerator.java b/wings/faceless-codegen/src/main/java/pro/fessional/wings/faceless/jooqgen/WingsJavaGenerator.java index deb75adab..ff67a767a 100644 --- a/wings/faceless-codegen/src/main/java/pro/fessional/wings/faceless/jooqgen/WingsJavaGenerator.java +++ b/wings/faceless-codegen/src/main/java/pro/fessional/wings/faceless/jooqgen/WingsJavaGenerator.java @@ -8,6 +8,8 @@ import org.jooq.impl.DAOImpl; import org.jooq.meta.CatalogDefinition; import org.jooq.meta.ColumnDefinition; +import org.jooq.meta.DataTypeDefinition; +import org.jooq.meta.Database; import org.jooq.meta.Definition; import org.jooq.meta.SchemaDefinition; import org.jooq.meta.TableDefinition; @@ -64,6 +66,13 @@ public class WingsJavaGenerator extends JavaGenerator { private GeneratorStrategy proxyStrategy = null; + @Override + protected String getJavaTypeReference(Database db, DataTypeDefinition type, JavaWriter out) { + return super.getJavaTypeReference(db, type, out); + // https://github.com/jOOQ/jOOQ/issues/16853 + // columnTypeRef = columnTypeRef.replace("_utf8mb4\\'\\'","('')"); + } + @Override public GeneratorStrategy getStrategy() { final GeneratorStrategy wrapper = super.getStrategy(); @@ -249,6 +258,7 @@ else if (colType.contains("int")) { // 🦁<<< } + @Override // Confirm the replacement code and diff it public void generateDao(TableDefinition table, JavaWriter out) { super.generateDao(table, out); diff --git a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/RevisionFitness.java b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/RevisionFitness.java index 71190796c..d349fe2d8 100644 --- a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/RevisionFitness.java +++ b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/RevisionFitness.java @@ -16,6 +16,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import java.util.TreeSet; /** * @author trydofor @@ -33,7 +34,7 @@ public void addFit(Fit fit, String msg) { if (fit == null) return; final Act act = fit.getLost(); if (act == null || act == Act.SKIP) { - log.info("skip fit {}", msg); + log.info("skip fit-revi {}", msg); return; } @@ -45,11 +46,11 @@ public void addFit(Fit fit, String msg) { final Set revi = fit.getRevi(); for (String str : revi) { Long r = AnyIntegerUtil.obj64(str); - reviAct.computeIfAbsent(r, ignored -> new HashSet<>()).add(act); - reviMsg.computeIfAbsent(r, ignored -> new HashSet<>()).add(msg); + reviAct.computeIfAbsent(r, ignored -> new TreeSet<>()).add(act); + reviMsg.computeIfAbsent(r, ignored -> new TreeSet<>()).add(msg); } - log.info("found fit {}. `wings.faceless.flywave.fit[{}].lost=SKIP` to skip", revi, msg); + log.info("found fit-revi {}. `wings.faceless.flywave.fit[{}].lost=SKIP` to skip", revi, msg); } public void addFits(Map fits) { @@ -72,7 +73,6 @@ private void applyRevision(SchemaRevisionManager manager, TreeMap for (Set at : revi.values()) { if (!autoInit && at.contains(Act.EXEC)) { throw new IllegalStateException(""" - Wings `flywave revision` do NOT exist, and Auto Init is dangerous, you can, 1.stop checker: `wings.faceless.flywave.checker=false` 2.revision fitness do NOT contain `EXEC` @@ -89,10 +89,10 @@ private void applyRevision(SchemaRevisionManager manager, TreeMap final Set ts = en.getValue(); final Set ms = reviMsg.get(rv); if (ts.contains(Act.WARN)) { - log.warn("Wings Revision Lost revi={}. Manual={}", rv, manual(ms)); + log.warn("Wings Revision Lost fit-revi={}. Manual={}", rv, manual(ms)); } if (ts.contains(Act.FAIL)) { - log.error("Wings Revision Lost revi={}. Manual={}", rv, manual(ms)); + log.error("Wings Revision Lost fit-revi={}. Manual={}", rv, manual(ms)); failed = true; } if (ts.contains(Act.EXEC)) { @@ -101,7 +101,7 @@ private void applyRevision(SchemaRevisionManager manager, TreeMap } if (failed) { - throw new IllegalStateException("Wings Revision Lost revi need FAIL"); + throw new IllegalStateException("Wings Revision Lost fit-revi need FAIL"); } if (exec.isEmpty()) { return; @@ -116,7 +116,7 @@ private void applyRevision(SchemaRevisionManager manager, TreeMap final Set ms = en.getValue(); final RevisionSql sql = scan.get(rv); if (sql == null) { - log.error("Wings Revision Lost And Failed to Scan. revi={} by={}", rv, ms); + log.error("Wings Revision Lost And Failed to Scan. fit-revi={} by={}", rv, ms); errors = true; } } @@ -134,12 +134,12 @@ private void applyRevision(SchemaRevisionManager manager, TreeMap if (rv == WingsRevision.V00_19_0512_01_Schema.revision()) { TreeMap init = new TreeMap<>(); init.put(rv, sql); - log.info("Wings Revision force to init revi={}, cid={}, by={}", rv, cid, ms); + log.info("Wings Revision force to init fit-revi={}, cid={}, by={}", rv, cid, ms); manager.checkAndInitSql(init, cid, true); } else { manager.forceUpdateSql(sql, cid); - log.info("Wings Revision force to apply revi={}, cid={}, by={}", rv, cid, ms); + log.info("Wings Revision force to apply fit-revi={}, cid={}, by={}", rv, cid, ms); manager.forceApplyBreak(rv, cid, true, null); } } @@ -198,13 +198,13 @@ private TreeMap> checkUnapply(SchemaRevisionManager manager) { if (reviStatus == null || reviStatus.isEmpty()) { if (unInit) { - log.warn("Wings Revision UnInit all-revi"); + log.warn("Wings Revision UnInit all fit-revi"); final TreeMap> map = new TreeMap<>(reviAct); map.put(UnInit, Set.of(Act.WARN)); return map; } else { - log.info("Wings Revision Unapply all-revi"); + log.info("Wings Revision Unapply all fit-revi"); return reviAct; } } @@ -228,7 +228,7 @@ private TreeMap> checkUnapply(SchemaRevisionManager manager) { throw new IllegalStateException("Wings Revision Diff Schemas Found:" + diffWarn); } else { - log.warn("Wings Revision Diff Schemas Found:" + diffWarn); + log.warn("Wings Revision Diff Schemas Found:{}", diffWarn); } } @@ -236,13 +236,17 @@ private TreeMap> checkUnapply(SchemaRevisionManager manager) { for (Map.Entry> revi : reviAct.entrySet()) { final Long rv = revi.getKey(); final Status st = reviStatus.get(rv); - if (st != Status.Applied) { + if (st == Status.Applied) { + log.info("found fit-revi={} had applied", rv); + } + else { map.put(rv, revi.getValue()); - log.info("Wings Revision Unapply revi={}, status={}", rv, st); + Set keys = reviMsg.get(rv); + log.info("found fit-revi={} not applied, status={}", rv, st); } } if (unInit) { - log.warn("Wings Revision UnInit all-revi"); + log.warn("Wings Revision UnInit all fit-revi"); map.put(UnInit, Set.of(Act.WARN)); return map; } diff --git a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/WingsRevision.java b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/WingsRevision.java index ee85643d4..446d9fbde 100644 --- a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/WingsRevision.java +++ b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/WingsRevision.java @@ -13,16 +13,24 @@ public enum WingsRevision implements RevisionRegister { V00_19_0512_02_Fix227(2019_0512_02L, "fix v227", "branch/somefix/01-v227-fix", "wings/faceless-flywave/src/main/resources/wings-flywave"), V01_19_0520_01_IdLog(2019_0520_01L, "lightId and journal", "master/01-light", "wings/faceless/src/main/resources/wings-flywave"), V01_19_0521_01_EnumI18n(2019_0521_01L, "enum and i18n", "branch/feature/01-enum-i18n", "wings/faceless/src/main/resources/wings-flywave"), + V03_20_1023_01_AuthEnum(2020_1023_01L, "auth enum", "master/03-enum", "wings/warlock/src/main/resources/wings-flywave"), V04_20_1024_01_UserLogin(2020_1024_01L, "user auth login", "master/04-auth", "wings/warlock/src/main/resources/wings-flywave"), V04_20_1024_02_RolePermit(2020_1024_02L, "user role permit", "master/04-auth", "wings/warlock/src/main/resources/wings-flywave"), V05_20_1025_01_ConfRuntime(2020_1025_01L, "runtime config", "master/05-conf", "wings/warlock/src/main/resources/wings-flywave"), V06_20_1026_01_TinyTask(2020_1026_01L, "tiny task", "master/06-task", "radiant/tiny-task/src/main/resources/wings-flywave"), V07_20_1027_01_TinyMail(2020_1027_01L, "tiny mail", "master/07-mail", "radiant/tiny-mail/src/main/resources/wings-flywave"), + V08_20_1028_01_TinyGrow(2020_1028_01L, "tiny grow", "master/08-grow", "radiant/tiny-grow/src/main/resources/wings-flywave"), + + // upgrade V01_21_0918_01_FixAuthn(2021_0918_01L, "fix authn", "branch/somefix/01-authn-fix", "wings/warlock/src/main/resources/wings-flywave"), V02_21_1220_01_Fix242(2021_1220_01L, "fix v242.201", "branch/somefix/02-v242-201", "wings/faceless-flywave/src/main/resources/wings-flywave"), - V03_21_1026_02_Fix32130(2021_1026_02L, "fix v3.2.130", "branch/somefix/03-v32-130", "radiant/tiny-task/src/main/resources/wings-flywave"), + V03_21_1026_02_FixTaskTune(2021_1026_02L, "fix task tune", "branch/somefix/03-task-tune", "radiant/tiny-task/src/main/resources/wings-flywave"), + V04_21_1026_03_FixConfSize(2021_1026_03L, "fix conf size", "branch/somefix/04-conf-size", "wings/warlock/src/main/resources/wings-flywave"), + V05_21_1026_05_FixJournal(2021_1026_05L, "fix journal elapse", "branch/somefix/05-journal-elapse", "wings/faceless/src/main/resources/wings-flywave"), + V06_21_1026_06_FixLazyMail(2021_1026_06L, "fix lazy mail", "branch/somefix/06-lazy-mail", "radiant/tiny-mail/src/main/resources/wings-flywave"), + // testing V90_22_0601_01_TestSchema(2022_0601_01L, "test v1 schema", "master", "wings/testing-faceless/src/main/resources/wings-flywave/"), V90_22_0601_02_TestRecord(2022_0601_02L, "test v2 record", "master", "wings/testing-faceless/src/main/resources/wings-flywave/"), V91_22_0222_01_ExampleInit(2022_0222_01L, "example demo", "master/00-init", "example/winx-common/src/main/resources/wings-flywave/"), diff --git a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/spring/bean/FlywaveConfiguration.java b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/spring/bean/FlywaveConfiguration.java index cf425b18a..3871a8f9f 100644 --- a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/spring/bean/FlywaveConfiguration.java +++ b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/spring/bean/FlywaveConfiguration.java @@ -104,6 +104,18 @@ public SchemaFulldumpManager schemaFulldumpManager( return new SchemaFulldumpManager(statementParser, schemaDefinitionLoader); } + @Bean + @ConditionalWingsEnabled + public MysqlDefinitionLoader schemaDefinitionLoader(FlywaveSqlProp conf) { + if ("mysql".equalsIgnoreCase(conf.getDialect())) { + log.info("FacelessFlywave spring-bean schemaDefinitionLoader"); + return new MysqlDefinitionLoader(); + } + else { + throw new IllegalArgumentException("only support mysql"); + } + } + @Bean @ConditionalWingsEnabled public MySqlStatementParser sqlStatementParser(FlywaveSqlProp conf) { @@ -141,18 +153,6 @@ public SqlSegmentProcessor sqlSegmentProcessor(FlywaveSqlProp conf) { } } - @Bean - @ConditionalWingsEnabled - public MysqlDefinitionLoader schemaDefinitionLoader(FlywaveSqlProp conf) { - if ("mysql".equalsIgnoreCase(conf.getDialect())) { - log.info("FacelessFlywave spring-bean schemaDefinitionLoader"); - return new MysqlDefinitionLoader(); - } - else { - throw new IllegalArgumentException("only support mysql"); - } - } - @Bean @ConditionalWingsEnabled(abs = FlywaveFitProp.Key$checker) public ApplicationRunnerOrdered revisionCheckerRunner(DefaultRevisionManager manager, FlywaveFitProp prop) { diff --git a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/spring/prop/FlywaveVerProp.java b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/spring/prop/FlywaveVerProp.java index c7d91ead8..07e3a3a76 100644 --- a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/spring/prop/FlywaveVerProp.java +++ b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/spring/prop/FlywaveVerProp.java @@ -31,7 +31,7 @@ public class FlywaveVerProp { * @see #Key$schemaVersionTable */ private String schemaVersionTable = "sys_schema_version"; - public static final String Key$schemaVersionTable = Key + ".schema-version-table"; + public static final String Key$schemaVersionTable = Key + ".schema-version-table"; // refer by database checker /** * table name of journal. diff --git a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/util/FlywaveRevisionScanner.java b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/util/FlywaveRevisionScanner.java index 917d091d9..10d5d5759 100644 --- a/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/util/FlywaveRevisionScanner.java +++ b/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/util/FlywaveRevisionScanner.java @@ -235,7 +235,7 @@ public static void scan(SortedMap result, String path) { try { PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources(path); - log.info("[FlywaveRevisionScanner]🐝 scanned " + resources.length + " resources in path=" + path); + log.info("[FlywaveRevisionScanner]🐝 scanned {} resources in path={}", resources.length, path); Pattern reviRegex = Pattern.compile("([-_0-9]{8,})([uv])([0-9]{2,})[^/]*\\.sql$", Pattern.CASE_INSENSITIVE); Charset utf8 = StandardCharsets.UTF_8; @@ -245,7 +245,7 @@ public static void scan(SortedMap result, String path) { file = res.getURL().getPath(); Matcher m = reviRegex.matcher(file); if (!m.find()) { - log.info("[FlywaveRevisionScanner]🐝 skip unsupported resource=" + file); + log.info("[FlywaveRevisionScanner]🐝 skip unsupported resource={}", file); continue; } boolean undo = "u".equalsIgnoreCase(m.group(2)); @@ -266,11 +266,11 @@ public static void scan(SortedMap result, String path) { if (undo) { final String ou = d.getUndoPath(); if (EmptySugar.asEmptyValue(ou)) { - log.info("[FlywaveRevisionScanner]🐝 scan " + revi + " undo↓ resource=" + file); + log.info("[FlywaveRevisionScanner]🐝 scan {} undo↓ resource={}", revi, file); } else { rplRevi.add(revi); - log.warn("[FlywaveRevisionScanner]🐝 replace " + revi + " undo↓ new=" + file + ", old=" + ou); + log.warn("[FlywaveRevisionScanner]🐝 replace {} undo↓ new={}, old={}", revi, file, ou); } d.setUndoPath(file); d.setUndoText(text); @@ -278,20 +278,20 @@ public static void scan(SortedMap result, String path) { else { final String ou = d.getUptoPath(); if (EmptySugar.asEmptyValue(ou)) { - log.info("[FlywaveRevisionScanner]🐝 scan " + revi + " upto↑ resource=" + file); + log.info("[FlywaveRevisionScanner]🐝 scan {} upto↑ resource={}", revi, file); } else { rplRevi.add(revi); - log.warn("[FlywaveRevisionScanner]🐝 replace " + revi + " upto↑ new=" + file + ", old=" + ou); + log.warn("[FlywaveRevisionScanner]🐝 replace {} upto↑ new={}, old={}", revi, file, ou); } d.setUptoPath(file); d.setUptoText(text); } } - log.info("[FlywaveRevisionScanner]🐝 scanned revisions new=" + newRevi.size() + ", replace=" + rplRevi.size()); + log.info("[FlywaveRevisionScanner]🐝 scanned revisions new={}, replace={}", newRevi.size(), rplRevi.size()); } catch (Exception e) { - throw new IllegalStateException("failed to scan path = " + path + ", file=" + file, e); + throw new IllegalStateException("failed to scan path=" + path + ", file=" + file, e); } } @@ -466,7 +466,7 @@ public Helper replace(long from, long to, Function mod) { modify("replace " + from + " to " + to + " with sql", to, it -> { it.setUptoText(mod.apply(it.getUptoText())); it.setUndoText(mod.apply(it.getUndoText())); - log.info("[FlywaveRevisionScanner]🐝 replace revi from=" + from + " to=" + to + " with sql text"); + log.info("[FlywaveRevisionScanner]🐝 replace revi from={}, to={} with sql text", from, to); }); } return this; @@ -621,12 +621,12 @@ public SortedMap scan() { final RevisionSql old = result.remove(ov); if (old == null) { - throw new IllegalStateException("failed to replace not-exist from=" + ov + " to=" + nv); + throw new IllegalStateException("failed to replace not-exist from=" + ov + ", to=" + nv); } final RevisionSql tor = result.put(nv, old); if (tor != null) { - log.info("[FlywaveRevisionScanner]🐝 replace revi from=" + ov + " to=" + nv + ", exist=" + tor); + log.info("[FlywaveRevisionScanner]🐝 replace revi from={}, to={}, exist={}", ov, nv, tor); } } @@ -637,15 +637,15 @@ public SortedMap scan() { if (ent.getKey().test(it.getKey())) { final String info = ent.getValue(); if (info != null && !info.isEmpty()) { - log.info("[FlywaveRevisionScanner]🐝 include " + it.getKey() + " by " + info); + log.info("[FlywaveRevisionScanner]🐝 include {} by {}", it.getKey(), info); } else { - log.info("[FlywaveRevisionScanner]🐝 include " + it.getKey()); + log.info("[FlywaveRevisionScanner]🐝 include {}", it.getKey()); } return false; } } - log.info("[FlywaveRevisionScanner]🐝 remove " + it.getKey() + " by include filter unmatched"); + log.info("[FlywaveRevisionScanner]🐝 remove {} by include filter unmatched", it.getKey()); return true; }); } @@ -657,10 +657,10 @@ public SortedMap scan() { if (ent.getKey().test(it.getKey())) { final String info = ent.getValue(); if (info == null || info.isEmpty()) { - log.info("[FlywaveRevisionScanner]🐝 remove " + it.getKey() + " by exclude filter matched"); + log.info("[FlywaveRevisionScanner]🐝 remove {} by exclude filter matched", it.getKey()); } else { - log.info("[FlywaveRevisionScanner]🐝 remove " + it.getKey() + " by " + info); + log.info("[FlywaveRevisionScanner]🐝 remove {} by {}", it.getKey(), info); } return true; } @@ -671,7 +671,7 @@ public SortedMap scan() { // modifier for (Map.Entry, String> mod : modifier.entrySet()) { - log.info("[FlywaveRevisionScanner]🐝 modify RevisionSql by " + mod.getValue()); + log.info("[FlywaveRevisionScanner]🐝 modify RevisionSql by {}", mod.getValue()); final BiConsumer fn = mod.getKey(); for (Map.Entry ent : result.entrySet()) { fn.accept(ent.getKey(), ent.getValue()); diff --git a/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/SchemaJournalManager.kt b/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/SchemaJournalManager.kt index 6d3cc0b64..5c05625c6 100644 --- a/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/SchemaJournalManager.kt +++ b/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/SchemaJournalManager.kt @@ -327,8 +327,15 @@ class SchemaJournalManager( val isInsert = "insert".equals(event, true) val isUpdate = "update".equals(event, true) val isDelete = "delete".equals(event, true) - val selectSql = when { - isInsert -> { + + val logDate = if (enable) { + "NOW(3)" + } else { + "'1000-01-01 00:00:00.000'" + } + + val (selectSql, updateSql) = when { + isInsert -> Pair( """ SELECT ddl_instbl ddl_tbl, @@ -336,9 +343,16 @@ class SchemaJournalManager( log_insert apply_dt FROM $schemaJournalTable WHERE table_name = ? + """.trimIndent(), + """ + UPDATE $schemaJournalTable SET + log_insert = $logDate, + commit_id = ? + WHERE table_name = ? """.trimIndent() - } - isUpdate -> { + ) + + isUpdate -> Pair( """ SELECT ddl_updtbl ddl_tbl, @@ -346,9 +360,16 @@ class SchemaJournalManager( log_update apply_dt FROM $schemaJournalTable WHERE table_name = ? + """.trimIndent(), + """ + UPDATE $schemaJournalTable SET + log_update = $logDate, + commit_id = ? + WHERE table_name = ? """.trimIndent() - } - isDelete -> { + ) + + isDelete -> Pair( """ SELECT ddl_deltbl ddl_tbl, @@ -356,47 +377,16 @@ class SchemaJournalManager( log_delete apply_dt FROM $schemaJournalTable WHERE table_name = ? - """.trimIndent() - } - else -> { - throw RuntimeException("unsupported event $event") - } - } - - val logDate = if (enable) { - "NOW(3)" - } else { - "'1000-01-01 00:00:00.000'" - } - - val updateSql = when { - isInsert -> { - """ - UPDATE $schemaJournalTable SET - log_insert = $logDate, - commit_id = ? - WHERE table_name = ? - """.trimIndent() - } - isUpdate -> { - """ - UPDATE $schemaJournalTable SET - log_update = $logDate, - commit_id = ? - WHERE table_name = ? - """.trimIndent() - } - isDelete -> { + """.trimIndent(), """ UPDATE $schemaJournalTable SET log_delete = $logDate, commit_id = ? WHERE table_name = ? """.trimIndent() - } - else -> { - throw RuntimeException("unsupported event $event") - } + ) + + else -> throw RuntimeException("unsupported event $event") } val model = HashMap() diff --git a/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/impl/DefaultRevisionManager.kt b/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/impl/DefaultRevisionManager.kt index cc3ad4195..2a1b6d93b 100644 --- a/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/impl/DefaultRevisionManager.kt +++ b/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/impl/DefaultRevisionManager.kt @@ -154,7 +154,7 @@ class DefaultRevisionManager( // check and handle boundary if (isUptoSql) { // revision is from low to high, inconsistent and non-exist are important - if (reviText.last.first != revision) { + if (reviText.last().first != revision) { interactive.log(WARN, here, "skip the diff upgrade end point, db-revi=$plainRevi, to-revi=$revision, db=$plainName") continue } @@ -164,7 +164,7 @@ class DefaultRevisionManager( continue } } else { // revision is from high to low - if (reviText.last.first != revision) { + if (reviText.last().first != revision) { interactive.log(WARN, here, "skip the diff downgrade end point, db-revi=$plainRevi, to-revi=$revision, db=$plainName") continue } @@ -364,17 +364,17 @@ class DefaultRevisionManager( continue } - val reviSql = applySqls.first + val reviSql = applySqls.first() val notAppd = isUnapply(reviSql.second) val msgAly = applyMessage(reviSql.second) if (isUpto && !notAppd) { - interactive.log(ERROR, here, "skip, $msgAly upto, need force to undo first, revi=$revision, isUpto=$isUpto, db=$plainName") + interactive.log(ERROR, here, "skip, $msgAly upto, need force to undo first, revi=$revision, isUpto=true, db=$plainName") continue } if (!isUpto && notAppd) { - interactive.log(ERROR, here, "skip, not $msgAly undo, revi=$revision, isUpto=$isUpto, db=$plainName") + interactive.log(ERROR, here, "skip, not $msgAly undo, revi=$revision, isUpto=false, db=$plainName") continue } diff --git a/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/impl/MySqlStatementParser.kt b/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/impl/MySqlStatementParser.kt index 512bed891..7a19a7be7 100644 --- a/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/impl/MySqlStatementParser.kt +++ b/wings/faceless-flywave/src/main/kotlin/pro/fessional/wings/faceless/flywave/impl/MySqlStatementParser.kt @@ -4,6 +4,7 @@ import org.slf4j.LoggerFactory import pro.fessional.mirana.bits.Bytes import pro.fessional.mirana.data.Null import pro.fessional.mirana.time.DateFormatter +import pro.fessional.wings.faceless.database.helper.JdbcTemplateHelper import pro.fessional.wings.faceless.flywave.SqlStatementParser import java.time.LocalDate import java.time.LocalDateTime @@ -94,7 +95,7 @@ class MySqlStatementParser : SqlStatementParser { } override fun parseTypeAndTable(sql: String): SqlStatementParser.SqlType { - if(sql.startsWith("SELECT ",true)) { + if (sql.startsWith("SELECT ", true)) { return SqlStatementParser.SqlType.Other } @@ -124,18 +125,7 @@ class MySqlStatementParser : SqlStatementParser { return SqlStatementParser.SqlType.Other } - override fun safeName(str: String): String { - val i1 = str.indexOf('`') - if (i1 >= 0) { - val i2 = str.lastIndexOf('`') - return if (i1 == 0 && i2 == str.length - 1) { - str - } else { - "`${str.replaceAfter("`", "")}`" - } - } - return "`$str`" - } + override fun safeName(str: String): String = JdbcTemplateHelper.safeName(str) // https://dev.mysql.com/doc/refman/8.0/en/string-literals.html#character-escape-sequences override fun safeValue(obj: Any?): String { diff --git a/wings/faceless-flywave/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/faceless-flywave/src/main/resources/META-INF/additional-spring-configuration-metadata.json index de91c5cb6..26a8b6b7c 100644 --- a/wings/faceless-flywave/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/faceless-flywave/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,13 +1,9 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf"} ], "properties": [ - {"name": "java.awt.headless", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf.FlywaveAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration.revisionCheckerRunner", "type": "java.lang.Boolean", "description": "wings.faceless.flywave.checker for short."}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration.schemaDefinitionLoader", "type": "java.lang.Boolean"}, @@ -15,7 +11,10 @@ {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration.schemaJournalManager", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration.schemaShardingManager", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration.schemaVersionManger", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration.sqlSegmentProcessor", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration.sqlSegmentProcessor", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FlywaveConfiguration.sqlStatementParser", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf.FlywaveAutoConfiguration", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.faceless.flywave for short."} ], "hints": [] } \ No newline at end of file diff --git a/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/01-v227-fix/2019-05-12u02-version-add-column.sql b/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/01-v227-fix/2019-05-12u02-version-add-column.sql index 74359138c..5585fb7d8 100644 --- a/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/01-v227-fix/2019-05-12u02-version-add-column.sql +++ b/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/01-v227-fix/2019-05-12u02-version-add-column.sql @@ -1,3 +1,5 @@ ALTER TABLE `sys_schema_version` DROP COLUMN `comments`, CHANGE COLUMN `apply_dt` `apply_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'applied datetime' AFTER `undo_sql`; + +-- CALL FLYWAVE('2019-05-12u02-version-add-column.sql'); \ No newline at end of file diff --git a/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/01-v227-fix/2019-05-12v02-version-add-column.sql b/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/01-v227-fix/2019-05-12v02-version-add-column.sql index 1fbbbf78d..4b57bb438 100644 --- a/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/01-v227-fix/2019-05-12v02-version-add-column.sql +++ b/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/01-v227-fix/2019-05-12v02-version-add-column.sql @@ -2,3 +2,5 @@ ALTER TABLE `sys_schema_version` CHANGE COLUMN `apply_dt` `apply_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'applied datetime' AFTER `commit_id`, ADD COLUMN `comments` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'sql path' AFTER `apply_dt`; + +-- CALL FLYWAVE('2019-05-12v02-version-add-column.sql'); \ No newline at end of file diff --git a/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/02-v242-201/2021-12-20u01-journal-trg-insert.sql b/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/02-v242-201/2021-12-20u01-journal-trg-insert.sql index 548422d4b..7cb069f99 100644 --- a/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/02-v242-201/2021-12-20u01-journal-trg-insert.sql +++ b/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/02-v242-201/2021-12-20u01-journal-trg-insert.sql @@ -2,3 +2,5 @@ ALTER TABLE `sys_schema_journal` DROP COLUMN `ddl_instrg`, DROP COLUMN `ddl_instbl`, DROP COLUMN `log_insert`; + +-- CALL FLYWAVE('2021-12-20u01-journal-trg-insert.sql'); \ No newline at end of file diff --git a/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/02-v242-201/2021-12-20v01-journal-trg-insert.sql b/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/02-v242-201/2021-12-20v01-journal-trg-insert.sql index 3258eb571..b66db753e 100644 --- a/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/02-v242-201/2021-12-20v01-journal-trg-insert.sql +++ b/wings/faceless-flywave/src/main/resources/wings-flywave/branch/somefix/02-v242-201/2021-12-20v01-journal-trg-insert.sql @@ -2,4 +2,6 @@ ALTER TABLE `sys_schema_journal` ADD COLUMN `ddl_instbl` TEXT NOT NULL COMMENT 'trace DDL of insert' AFTER `commit_id`, ADD COLUMN `ddl_instrg` TEXT NOT NULL COMMENT 'trigger DDL of insert' AFTER `ddl_instbl`, - ADD COLUMN `log_insert` DATETIME(3) NOT NULL DEFAULT '1000-01-01 00:00:00.000' COMMENT 'applied datetime of insert' AFTER `ddl_deltrg`; + ADD COLUMN `log_insert` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'applied datetime of insert' AFTER `ddl_deltrg`; + +-- CALL FLYWAVE('2021-12-20v01-journal-trg-insert.sql'); \ No newline at end of file diff --git a/wings/faceless-flywave/src/main/resources/wings-flywave/manual-revision-procedure.sql b/wings/faceless-flywave/src/main/resources/wings-flywave/manual-revision-procedure.sql new file mode 100644 index 000000000..cc2cf4048 --- /dev/null +++ b/wings/faceless-flywave/src/main/resources/wings-flywave/manual-revision-procedure.sql @@ -0,0 +1,21 @@ +-- drop +DROP PROCEDURE IF EXISTS FLYWAVE; + +-- create +DELIMITER $$ +CREATE PROCEDURE FLYWAVE(IN filename VARCHAR(50)) +BEGIN + DECLARE revi VARCHAR(20); + SET revi = REGEXP_REPLACE(REGEXP_SUBSTR(filename, '[-_0-9]{8,}[uv][0-9]{2,}', 1, 1, 'i'), '[^0-9]', ''); + IF REGEXP_LIKE(filename, '[-_0-9]{8,}[v][0-9]{2,}','i') = 1 THEN + INSERT INTO `sys_schema_version` (`revision`, `apply_dt`, `comments`, `commit_id`, `upto_sql`, `undo_sql`) + VALUES (revi, NOW(3), filename, 0, '', '') + ON DUPLICATE KEY UPDATE `apply_dt` = NOW(3); + ELSE + UPDATE `sys_schema_version` + SET `apply_dt` = '1000-01-01', + `modify_dt`= NOW(3) + WHERE `revision` = revi; + END IF; +END$$ +DELIMITER ; diff --git a/wings/faceless-flywave/src/main/resources/wings-flywave/master/00-init/2019-05-12v01-version-journal.sql b/wings/faceless-flywave/src/main/resources/wings-flywave/master/00-init/2019-05-12v01-version-journal.sql index e2dcc405b..595d2400d 100644 --- a/wings/faceless-flywave/src/main/resources/wings-flywave/master/00-init/2019-05-12v01-version-journal.sql +++ b/wings/faceless-flywave/src/main/resources/wings-flywave/master/00-init/2019-05-12v01-version-journal.sql @@ -2,11 +2,11 @@ -- CREATE DATABASE `wings` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; CREATE TABLE `sys_schema_version` ( - `revision` BIGINT(20) NOT NULL COMMENT 'version + build', + `revision` BIGINT NOT NULL COMMENT 'version + build', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime', - `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01 00:00:00.000' ON UPDATE NOW(3) COMMENT 'modified datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', - `apply_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01 00:00:00.000' COMMENT 'applied datetime', + `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', + `apply_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'applied datetime', `comments` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'sql path', `upto_sql` TEXT NOT NULL COMMENT 'upgrade script', `undo_sql` TEXT NOT NULL COMMENT 'downgrade script', @@ -17,26 +17,26 @@ CREATE TABLE `sys_schema_version` ( CREATE TABLE `sys_schema_journal` ( `table_name` VARCHAR(100) NOT NULL COMMENT 'plain table name', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime', - `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01 00:00:00.000' ON UPDATE NOW(3) COMMENT 'modified datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', + `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', `ddl_instbl` TEXT NOT NULL COMMENT 'trace DDL of insert', `ddl_instrg` TEXT NOT NULL COMMENT 'trigger DDL of insert', `ddl_updtbl` TEXT NOT NULL COMMENT 'trace DDL of update', `ddl_updtrg` TEXT NOT NULL COMMENT 'trigger DDL of update', `ddl_deltbl` TEXT NOT NULL COMMENT 'trace DDL of delete', `ddl_deltrg` TEXT NOT NULL COMMENT 'trigger DDL of delete', - `log_insert` DATETIME(3) NOT NULL DEFAULT '1000-01-01 00:00:00.000' COMMENT 'applied datetime of insert', - `log_update` DATETIME(3) NOT NULL DEFAULT '1000-01-01 00:00:00.000' COMMENT 'applied datetime of update', - `log_delete` DATETIME(3) NOT NULL DEFAULT '1000-01-01 00:00:00.000' COMMENT 'applied datetime of delete', + `log_insert` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'applied datetime of insert', + `log_update` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'applied datetime of update', + `log_delete` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'applied datetime of delete', PRIMARY KEY (`table_name`) USING BTREE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='102/Table Trigger'; --- sys_schema_version@plain -INSERT IGNORE INTO `sys_schema_version` (`revision`, `commit_id`, `upto_sql`, `undo_sql`, `apply_dt`) -VALUES (2019051201, 0, '', '', NOW(3)); - -- sys_schema_journal@plain REPLACE INTO `sys_schema_journal` (`table_name`, `commit_id`, `ddl_instbl`, `ddl_instrg`, `ddl_updtbl`, `ddl_updtrg`, `ddl_deltbl`, `ddl_deltrg`) VALUES ('sys_schema_journal', 0, '', '', '', '', '', ''), ('sys_schema_version', 0, '', '', '', '', '', ''); + +-- sys_schema_version@plain +INSERT IGNORE INTO `sys_schema_version` (`revision`, `apply_dt`, `commit_id`,`comments`, `upto_sql`, `undo_sql`) +VALUES (2019051201, NOW(3), 0, 'master/00-init/2019-05-12v01-version-journal.sql', '', ''); diff --git a/wings/faceless-flywave/src/test/java/pro/fessional/wings/faceless/util/FlywaveRevisionScannerTest.java b/wings/faceless-flywave/src/test/java/pro/fessional/wings/faceless/util/FlywaveRevisionScannerTest.java index c950b936a..084dce0a1 100644 --- a/wings/faceless-flywave/src/test/java/pro/fessional/wings/faceless/util/FlywaveRevisionScannerTest.java +++ b/wings/faceless-flywave/src/test/java/pro/fessional/wings/faceless/util/FlywaveRevisionScannerTest.java @@ -37,8 +37,8 @@ public void flywaveBranchPath() { @TmsLink("C12029") public void flywaveCommentInfo() { assertEquals("master/2022-0601_01-test.sql", FlywaveRevisionScanner.commentInfo( - "/Users/trydofor/Workspace/github.com/pro.fessional.wings/wings/faceless/src/test/resources/wings-flywave/master/2022-0601u01-test.sql", - "/Users/trydofor/Workspace/github.com/pro.fessional.wings/wings/faceless/src/test/resources/wings-flywave/master/2022-0601v01-test.sql" + "github.com/professional-wings/wings/faceless/src/test/resources/wings-flywave/master/2022-0601u01-test.sql", + "github.com/professional-wings/wings/faceless/src/test/resources/wings-flywave/master/2022-0601v01-test.sql" )); } diff --git a/wings/faceless-jooq/src/main/java/pro/fessional/wings/faceless/database/jooq/listener/TableCudListener.java b/wings/faceless-jooq/src/main/java/pro/fessional/wings/faceless/database/jooq/listener/TableCudListener.java index 9a8e1970a..30207d704 100644 --- a/wings/faceless-jooq/src/main/java/pro/fessional/wings/faceless/database/jooq/listener/TableCudListener.java +++ b/wings/faceless-jooq/src/main/java/pro/fessional/wings/faceless/database/jooq/listener/TableCudListener.java @@ -82,8 +82,9 @@ public void clauseStart(VisitContext context) { final String clz = scn(context.queryPart()); final Clause clause = context.clause(); if (clause == Clause.INSERT || clause == Clause.UPDATE || clause == Clause.DELETE) { - log.warn(">>> clauseStart Clause=" + clause + ", Query=" + clz - , new DebugException("debug for call stack")); + // noinspection StringConcatenationArgumentToLogCall + log.warn(">>> clauseStart Clause=" + clause + ", Query=" + clz, + new DebugException("debug for call stack")); } else { log.warn(">>> clauseStart Clause={}, Query={}", clause, clz); @@ -135,7 +136,7 @@ public void clauseEnd(VisitContext context) { log.warn("<<< clauseEnd Clause={}, Query={}\n\n", clause, clz); } else { - log.warn(">>> clauseStart Clause={}, Query={}", clause, clz); + log.warn(">>> clauseEnd Clause={}, Query={}", clause, clz); } } diff --git a/wings/faceless-jooq/src/main/java/pro/fessional/wings/faceless/spring/prop/FacelessJooqCudProp.java b/wings/faceless-jooq/src/main/java/pro/fessional/wings/faceless/spring/prop/FacelessJooqCudProp.java index abf283321..b8da0bbef 100644 --- a/wings/faceless-jooq/src/main/java/pro/fessional/wings/faceless/spring/prop/FacelessJooqCudProp.java +++ b/wings/faceless-jooq/src/main/java/pro/fessional/wings/faceless/spring/prop/FacelessJooqCudProp.java @@ -11,7 +11,7 @@ import java.util.Map; import java.util.Set; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.DisabledValue; +import static pro.fessional.wings.silencer.support.PropHelper.DisabledValue; /** * CUD listener settings for jooq. @@ -56,7 +56,7 @@ public class FacelessJooqCudProp { * Listening tables and their fields. `empty` means no fields are recorded, `-` means this table is ignored. * CUD listens to tables and fields, both tables and fields are case-sensitive. * - * @see pro.fessional.wings.silencer.spring.help.CommonPropHelper#DisabledValue + * @see pro.fessional.wings.silencer.support.PropHelper#DisabledValue * @see #Key$table */ private Map> table = Collections.emptyMap(); @@ -78,7 +78,7 @@ public void setTable(@NotNull Map> table) { Map> temp = new LinkedHashMap<>(); for (Map.Entry> en : table.entrySet()) { if (en.getValue().contains(DisabledValue)) { - log.info("remove disable value for table={}", en.getKey()); + log.info("ignore disable value for table={}", en.getKey()); } else { temp.put(en.getKey(), en.getValue()); diff --git a/wings/faceless-jooq/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/faceless-jooq/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 2b122b95d..20ce2781a 100644 --- a/wings/faceless-jooq/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/faceless-jooq/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,11 +1,9 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf.FacelessJooqAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessJooqConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessJooqConfiguration.jooqAutoQualifyFieldListener", "type": "java.lang.Boolean", "description": "wings.faceless.jooq.conf.auto-qualify for short."}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessJooqConfiguration.jooqWingsConfigCustomizer", "type": "java.lang.Boolean"}, @@ -13,7 +11,9 @@ {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessJooqCudConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessJooqCudConfiguration$CudListenerBean", "type": "java.lang.Boolean", "description": "wings.faceless.jooq.conf.listen-cud for short."}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessJooqCudConfiguration$JournalDiffWired", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessJooqCudConfiguration.jooqJournalDeleteListener", "type": "java.lang.Boolean", "description": "wings.faceless.jooq.conf.journal-delete for short."} + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessJooqCudConfiguration.jooqJournalDeleteListener", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.faceless.jooq.conf.journal-delete for short."}, + + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf.FacelessJooqAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/SysConstantEnumTable.java b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/SysConstantEnumTable.java index c23156e6f..27e149f45 100644 --- a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/SysConstantEnumTable.java +++ b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/SysConstantEnumTable.java @@ -29,13 +29,13 @@ /** - * The table wings.sys_constant_enum. + * The table wings_faceless.sys_constant_enum. */ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", - "schema version:2020102701" + "jOOQ version:3.18.9", + "schema version:2022060102" }, comments = "This class is generated by jOOQ" ) @@ -184,7 +184,6 @@ public SelectField mapping(Function5 SelectField mapping(Class toType, Function5 from) { return convertFrom(toType, Records.mapping(from)); } - /** * alias asK5 diff --git a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/SysStandardI18nTable.java b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/SysStandardI18nTable.java index d84142e49..f3139f14e 100644 --- a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/SysStandardI18nTable.java +++ b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/SysStandardI18nTable.java @@ -29,13 +29,13 @@ /** - * The table wings.sys_standard_i18n. + * The table wings_faceless.sys_standard_i18n. */ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", - "schema version:2020102701" + "jOOQ version:3.18.9", + "schema version:2022060102" }, comments = "This class is generated by jOOQ" ) @@ -184,7 +184,6 @@ public SelectField mapping(Function5 SelectField mapping(Class toType, Function5 from) { return convertFrom(toType, Records.mapping(from)); } - /** * alias asM5 diff --git a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstNormalTableTable.java b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstNormalTableTable.java index da0746116..2d95e8656 100644 --- a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstNormalTableTable.java +++ b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstNormalTableTable.java @@ -41,13 +41,13 @@ /** - * The table wings.tst_normal_table. + * The table wings_faceless.tst_normal_table. */ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", - "schema version:2020102701" + "jOOQ version:3.18.9", + "schema version:2022060102" }, comments = "This class is generated by jOOQ" ) @@ -245,7 +245,6 @@ public SelectField mapping(Class toType, Function13delete_dt condition diff --git a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstShardingTable.java b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstShardingTable.java index 8b8895659..df9521c6f 100644 --- a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstShardingTable.java +++ b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstShardingTable.java @@ -38,13 +38,13 @@ /** - * The table wings.tst_sharding. + * The table wings_faceless.tst_sharding. */ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", - "schema version:2020102701" + "jOOQ version:3.18.9", + "schema version:2022060102" }, comments = "This class is generated by jOOQ" ) @@ -217,7 +217,6 @@ public SelectField mapping(Class toType, Function8delete_dt condition diff --git a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/sample/TestJooqDslAndDaoSample.java b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/sample/TestJooqDslAndDaoSample.java index ea72b859a..20f1a4f63 100644 --- a/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/sample/TestJooqDslAndDaoSample.java +++ b/wings/faceless-jooq/src/test/java/pro/fessional/wings/faceless/sample/TestJooqDslAndDaoSample.java @@ -50,9 +50,9 @@ @SpringBootTest(properties = { - "logging.level.root=DEBUG", // AssertionLogger - "wings.faceless.jooq.conf.auto-qualify=true", - "wings.faceless.jooq.conf.render-table=ALWAYS", + "logging.level.root=DEBUG", // AssertionLogger + "wings.faceless.jooq.conf.auto-qualify=true", + "wings.faceless.jooq.conf.render-table=ALWAYS", // "wings.faceless.jooq.conf.auto-qualify=false", // "wings.faceless.jooq.conf.render-table=WHEN_MULTIPLE_TABLES", }) @@ -61,16 +61,16 @@ @Slf4j public class TestJooqDslAndDaoSample { - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private SchemaRevisionManager schemaRevisionManager; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private TestingDatabaseHelper testingDatabaseHelper; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private TstShardingDao tstShardingDao; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private FacelessJooqConfProp prop; @Test @@ -87,8 +87,8 @@ public void test0Init() { public void test1Dao() { final TestingLoggerAssert al = TestingLoggerAssert.install(); final Pattern alias = prop.isAutoQualify() - ? Pattern.compile("from `tst_sharding` as `(\\w+)` where \\(`\\1`.`id` > \\? and `\\1`.`commit_id` < \\?\\)") - : Pattern.compile("from `tst_sharding` as `(\\w+)` where \\(`id` > \\? and `commit_id` < \\?\\)"); + ? Pattern.compile("from `tst_sharding` as `(\\w+)` where \\(`\\1`.`id` > \\? and `\\1`.`commit_id` < \\?\\)") + : Pattern.compile("from `tst_sharding` as `(\\w+)` where \\(`id` > \\? and `commit_id` < \\?\\)"); al.rule("alias-count", event -> alias.matcher(event.getFormattedMessage()).find()); al.rule("table-select", event -> event.getFormattedMessage().contains("from `tst_sharding` where (`id` > ? and `commit_id` < ?)")); al.rule("table-update1", event -> event.getFormattedMessage().contains("update `tst_sharding` set `commit_id` = (`id` + ?), `login_info` = ? where `id` = ?")); @@ -106,10 +106,10 @@ public void test1Dao() { log.info("============count {}, ft2'size={}", i, ft1.size()); // testcaseNotice("select `id`, `commit_id` from `tst_sharding` where (`id` > ? and `commit_id` < ?) order by `id` desc limit ? offset ?"); final var ft2 = tstShardingDao.fetch((t, w) -> w - .where(t.Id.gt(1L).and(t.CommitId.lt(200L))) - .select(t.Id, t.CommitId) - .order(t.Id.desc()) - .limit(2) + .where(t.Id.gt(1L).and(t.CommitId.lt(200L))) + .select(t.Id, t.CommitId) + .order(t.Id.desc()) + .limit(2) ); log.info("============count {}, ft2'size={}", i, ft2.size()); @@ -163,24 +163,24 @@ public void test2Dsl() { final var t1 = TstShardingTable.TstSharding.as("t1"); final var t2 = TstShardingTable.TstSharding.as("t2"); String j1 = dsl - .select(t1.Id, t2.CommitId) - .from(t1, t2) - .where(t1.Id.eq(t2.CommitId)) - .getSQL(); + .select(t1.Id, t2.CommitId) + .from(t1, t2) + .where(t1.Id.eq(t2.CommitId)) + .getSQL(); String j2 = dsl - .select(t1.Id) - .from(t1) - .where(t1.CommitId.in(dsl.select(t2.CommitId).from(t2).where(t2.Id.eq(t1.Id)))) - .getSQL(); + .select(t1.Id) + .from(t1) + .where(t1.CommitId.in(dsl.select(t2.CommitId).from(t2).where(t2.Id.eq(t1.Id)))) + .getSQL(); String j3 = dsl - .select(t1.Id, t2.Id) - .from(t1) - .join(t2) - .on(t1.Id.eq(t2.Id).and(t1.CommitId.eq(t2.CommitId))) - .where(t1.Id.eq(1L)) - .getSQL(); + .select(t1.Id, t2.Id) + .from(t1) + .join(t2) + .on(t1.Id.eq(t2.Id).and(t1.CommitId.eq(t2.CommitId))) + .where(t1.Id.eq(1L)) + .getSQL(); log.info(j1); log.info(j2); @@ -193,7 +193,7 @@ public void test3Journal() { testcaseNotice("Journal Feature"); final var now = LocalDateTime.now(); - final var journal = new Journal(1L, now, "", "", "", ""); + final var journal = new Journal(1L, now, 0, System.currentTimeMillis(), "", "", "", ""); final var s1 = new HashMap<>(); final var t = TstShardingTable.TstSharding; @@ -296,8 +296,8 @@ public void test5DiffDao() { Assertions.assertEquals(t.getName(), d3.getTable()); Assertions.assertEquals(Arrays.asList(t.Id.getName(), t.LoginInfo.getName(), t.OtherInfo.getName()), d3.getColumn()); Assertions.assertEquals(Arrays.asList( - id, "login by diff update", "other by diff insert", - id + 1, "login by diff update", "other by diff insert" + id, "login by diff update", "other by diff insert", + id + 1, "login by diff update", "other by diff insert" ), d3.getValue1()); Assertions.assertTrue(d3.getValue2().isEmpty()); } @@ -333,12 +333,12 @@ public void test5DiffDsl() { Assertions.assertEquals(Arrays.asList(id, now, DATE_TIME, DATE_TIME, id, "login by diff insert", "other by diff insert", ZH_CN), d0.getValue2()); final JournalDiff d2 = JournalDiffHelper.diffUpdate(t, query, () -> - dsl.update(t) - .set(t.CommitId, t.CommitId.add(1)) - .set(t.LoginInfo, "login by diff update") - .set(t.ModifyDt, now) - .where(t.Id.eq(id)) - .execute()); + dsl.update(t) + .set(t.CommitId, t.CommitId.add(1)) + .set(t.LoginInfo, "login by diff update") + .set(t.ModifyDt, now) + .where(t.Id.eq(id)) + .execute()); log.warn("diffUpdate2={}", d2); Assertions.assertNotNull(d2); Assertions.assertEquals(1, d2.getCount()); diff --git a/wings/faceless-shard/pom.xml b/wings/faceless-shard/pom.xml index c0b8c984d..7479e9e42 100644 --- a/wings/faceless-shard/pom.xml +++ b/wings/faceless-shard/pom.xml @@ -21,21 +21,10 @@ pro.fessional.wings faceless
- - - org.glassfish.jaxb - jaxb-runtime - 2.3.8 - - - org.yaml - snakeyaml - 1.33 - org.apache.shardingsphere - shardingsphere-jdbc-core + shardingsphere-jdbc diff --git a/wings/faceless-shard/src/main/java/pro/fessional/wings/faceless/spring/bean/FacelessShardingSphereConfiguration.java b/wings/faceless-shard/src/main/java/pro/fessional/wings/faceless/spring/bean/FacelessShardingSphereConfiguration.java index 160916f93..ecbfe00c5 100644 --- a/wings/faceless-shard/src/main/java/pro/fessional/wings/faceless/spring/bean/FacelessShardingSphereConfiguration.java +++ b/wings/faceless-shard/src/main/java/pro/fessional/wings/faceless/spring/bean/FacelessShardingSphereConfiguration.java @@ -2,7 +2,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereURLManager; +import org.apache.shardingsphere.driver.ShardingSphereDriver; +import org.apache.shardingsphere.infra.url.core.ShardingSphereURL; +import org.apache.shardingsphere.infra.url.core.ShardingSphereURLLoadEngine; import org.apache.shardingsphere.infra.util.yaml.YamlEngine; import org.apache.shardingsphere.infra.yaml.config.pojo.YamlRootConfiguration; import org.apache.shardingsphere.infra.yaml.config.swapper.resource.YamlDataSourceConfigurationSwapper; @@ -20,6 +22,7 @@ /** * Config sharding datasource to DataSourceContext * + * @see ShardingSphereDriver * @author trydofor */ @@ -31,12 +34,16 @@ public class FacelessShardingSphereConfiguration { @Bean @ConditionalWingsEnabled public DataSourceContext.Customizer shardingDataSourceContext(@Value("${spring.datasource.url}") String jdbcUrl) throws Exception { - if (!jdbcUrl.startsWith("jdbc:shardingsphere:")) { + String urlPrefix = "jdbc:shardingsphere:"; + if (!jdbcUrl.startsWith(urlPrefix)) { log.info("FacelessShard skip shardingSphereCustomizer jdbcUrl=" + jdbcUrl); return ignored -> false; } - final byte[] yamlBytes = ShardingSphereURLManager.getContent(jdbcUrl, "jdbc:shardingsphere:"); + ShardingSphereURL shardingUrl = ShardingSphereURL.parse(jdbcUrl.substring(urlPrefix.length())); + ShardingSphereURLLoadEngine engine = new ShardingSphereURLLoadEngine(shardingUrl); + + final byte[] yamlBytes = engine.loadContent(); YamlRootConfiguration rootConfig = YamlEngine.unmarshal(yamlBytes, YamlRootConfiguration.class); final YamlDataSourceConfigurationSwapper configurationSwapper = new YamlDataSourceConfigurationSwapper(); final Map dsMap = configurationSwapper.swapToDataSources(rootConfig.getDataSources()); diff --git a/wings/faceless-shard/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/faceless-shard/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 60d3a2328..2fcc0ca72 100644 --- a/wings/faceless-shard/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/faceless-shard/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,14 +1,14 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf.FacelessShardAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessShardingSphereConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessShardingSphereConfiguration.shardingDataSourceContext", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessShardingSphereConfiguration.writeRouteOnlyAround", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessShardingSphereConfiguration.writeRouteOnlyAround", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf.FacelessShardAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/faceless-shard/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstNormalTableTable.java b/wings/faceless-shard/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstNormalTableTable.java index 328a324fe..b13be9699 100644 --- a/wings/faceless-shard/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstNormalTableTable.java +++ b/wings/faceless-shard/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstNormalTableTable.java @@ -39,13 +39,13 @@ /** - * The table wings.tst_normal_table. + * The table wings_faceless.tst_normal_table. */ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", - "schema version:2020102701" + "jOOQ version:3.18.9", + "schema version:2022060102" }, comments = "This class is generated by jOOQ" ) @@ -243,7 +243,6 @@ public SelectField mapping(Class toType, Function13delete_dt condition diff --git a/wings/faceless-shard/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstShardingTable.java b/wings/faceless-shard/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstShardingTable.java index 481d472d2..f4ade0c27 100644 --- a/wings/faceless-shard/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstShardingTable.java +++ b/wings/faceless-shard/src/test/java/pro/fessional/wings/faceless/app/database/autogen/tables/TstShardingTable.java @@ -36,13 +36,13 @@ /** - * The table wings.tst_sharding. + * The table wings_faceless.tst_sharding. */ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", - "schema version:2020102701" + "jOOQ version:3.18.9", + "schema version:2022060102" }, comments = "This class is generated by jOOQ" ) @@ -215,7 +215,6 @@ public SelectField mapping(Class toType, Function8delete_dt condition diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/convention/EmptySugar.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/convention/EmptySugar.java index e33bed889..8a9a5772c 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/convention/EmptySugar.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/convention/EmptySugar.java @@ -1,5 +1,7 @@ package pro.fessional.wings.faceless.convention; +import org.jetbrains.annotations.Contract; + import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; @@ -7,6 +9,7 @@ import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; +import java.util.function.Supplier; /** * `isXxx/notXxx` for exact comparison, `asXxx/nonXxx` for range comparison @@ -147,7 +150,7 @@ public static boolean notEmptyValue(OffsetDateTime v) { // ///////////////////// public static boolean asEmptyValue(String v) { - return v == null || v.trim().isEmpty(); + return v == null || v.isBlank(); } public static boolean asEmptyValue(Integer v) { @@ -433,4 +436,134 @@ public static OffsetDateTime emptyToNull(OffsetDateTime v) { public static Boolean emptyToNull(Boolean v) { return nullToTrue(v) ? null : v; } + + @Contract("_,!null->!null") + public static String emptyOrElse(String v, String elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static Integer emptyOrElse(Integer v, Integer elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static Long emptyOrElse(Long v, Long elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static Double emptyOrElse(Double v, Double elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static Float emptyOrElse(Float v, Float elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static BigDecimal emptyOrElse(BigDecimal v, BigDecimal elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static BigInteger emptyOrElse(BigInteger v, BigInteger elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static LocalDate emptyOrElse(LocalDate v, LocalDate elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static LocalTime emptyOrElse(LocalTime v, LocalTime elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static LocalDateTime emptyOrElse(LocalDateTime v, LocalDateTime elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static ZonedDateTime emptyOrElse(ZonedDateTime v, ZonedDateTime elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static OffsetDateTime emptyOrElse(OffsetDateTime v, OffsetDateTime elz) { + return asEmptyValue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static Boolean emptyOrElse(Boolean v, Boolean elz) { + return nullToTrue(v) ? elz : v; + } + + @Contract("_,!null->!null") + public static String emptyOrElse(String v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static Integer emptyOrElse(Integer v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static Long emptyOrElse(Long v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static Double emptyOrElse(Double v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static Float emptyOrElse(Float v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static BigDecimal emptyOrElse(BigDecimal v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static BigInteger emptyOrElse(BigInteger v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static LocalDate emptyOrElse(LocalDate v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static LocalTime emptyOrElse(LocalTime v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static LocalDateTime emptyOrElse(LocalDateTime v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static ZonedDateTime emptyOrElse(ZonedDateTime v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static OffsetDateTime emptyOrElse(OffsetDateTime v, Supplier elz) { + return asEmptyValue(v) ? elz.get() : v; + } + + @Contract("_,!null->!null") + public static Boolean emptyOrElse(Boolean v, Supplier elz) { + return nullToTrue(v) ? elz.get() : v; + } } diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/DatabaseChecker.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/DatabaseChecker.java index 93e908214..fcd678bcf 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/DatabaseChecker.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/DatabaseChecker.java @@ -2,13 +2,16 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.JdbcUtils; +import pro.fessional.mirana.best.DummyBlock; import pro.fessional.mirana.time.DateFormatter; import pro.fessional.mirana.time.DateParser; import pro.fessional.wings.faceless.database.DataSourceContext; +import pro.fessional.wings.silencer.spring.help.ApplicationContextHelper; import javax.sql.DataSource; import java.sql.Connection; @@ -113,7 +116,9 @@ public static void timezone(DataSource ds, int off, boolean fail) { sb.append("\njvm-zone-offset=").append(ZoneOffset.ofTotalSeconds(jvOff)); if (Math.abs(jvOff) <= Math.abs(off) && Math.abs(dbOff) <= Math.abs(off)) { - log.info(sb.substring(1).replace("\n", ", ")); + for (String s : sb.substring(1).split("\n")) { + log.info(s); + } return; } @@ -137,18 +142,29 @@ public static void timezone(DataSource ds, int off, boolean fail) { } /** - * output the database version in the log + * output the database version in the log, return flywave revision if found */ - public static long version(DataSource ds) { final JdbcTemplate tmpl = new JdbcTemplate(ds); + log.info("jdbcurl={}", DataSourceContext.extractUrl(ds)); final String ver = isH2(ds) ? "H2VERSION()" : "VERSION()"; tmpl.query("SELECT " + ver + " FROM dual", rs -> { - log.info("{}={}, primary={}", ver, rs.getString(1), DataSourceContext.extractUrl(ds)); + log.info("database {}={}", ver, rs.getString(1)); }); - String sql = "SELECT MAX(revision) FROM sys_schema_version WHERE apply_dt > '1111-11-11'"; + String reviTable = null; + try { + reviTable = ApplicationContextHelper.getProperties("wings.faceless.flywave.ver.schema-version-table"); + } + catch (Exception e) { + DummyBlock.ignore(e); + } + if (StringUtils.isBlank(reviTable)) { + reviTable = "sys_schema_version"; + } + + String sql = "SELECT MAX(revision) FROM " + JdbcTemplateHelper.safeName(reviTable) + " WHERE apply_dt > '1111-11-11'"; Long rev = null; try { rev = tmpl.query(sql, JdbcTemplateHelper.FirstLongOrNull); diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/JdbcTemplateHelper.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/JdbcTemplateHelper.java index e347558da..cf3290272 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/JdbcTemplateHelper.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/JdbcTemplateHelper.java @@ -76,7 +76,18 @@ public static String safeWhere(String where) { * in mysql (not ANSI_QUOTES), return `table` */ @NotNull - protected static Function Quotes = (String name) -> "`" + name + "`"; + protected static Function Quotes = (String name) -> { + int pos0 = name.indexOf('`'); + if (pos0 >= 0) { + int pos1 = name.lastIndexOf('`'); + if(pos0 == 0 && pos1 == name.length() -1) { + int pos = name.indexOf('`', pos0 + 1, pos1); + if(pos < 0) return name; + } + name = name.replace("`",""); + } + return "`" + name + "`"; + }; public static void initSafeTable(JdbcTemplate tmpl) { tmpl.query(ShowTableSql, rs -> { diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/TransactionHelper.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/TransactionHelper.java index 60052e472..6f261635f 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/TransactionHelper.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/helper/TransactionHelper.java @@ -97,7 +97,7 @@ public static TransactionDefinition definition(@Nullable Propagation propagation /** * Propagation(REQUIRED), Isolation(DEFAULT) and timeout(-1) */ - @Contract("_->param1") + @Contract("_,_,_,_->param1") public static T definition(@NotNull T tpl, @Nullable Propagation propagation, @Nullable Isolation isolation, int timeoutSeconds) { tpl.setPropagationBehavior(propagation == null ? Propagation.REQUIRED.value() : propagation.value()); tpl.setIsolationLevel(isolation == null ? Isolation.DEFAULT.value() : isolation.value()); diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/modify/commitjournal/CommitJournalModify.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/modify/commitjournal/CommitJournalModify.java index 7714a8947..a98c2fd7a 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/modify/commitjournal/CommitJournalModify.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/modify/commitjournal/CommitJournalModify.java @@ -7,5 +7,14 @@ * @since 2019-09-12 */ public interface CommitJournalModify { + + /** + * insert new journal + */ int insert(JournalService.Journal journal); + + /** + * update elapse mills by id + */ + int elapse(long mills, long id); } diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/modify/commitjournal/impl/CommitJournalModifyJdbc.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/modify/commitjournal/impl/CommitJournalModifyJdbc.java index cf8abc860..71e1616f7 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/modify/commitjournal/impl/CommitJournalModifyJdbc.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/modify/commitjournal/impl/CommitJournalModifyJdbc.java @@ -16,16 +16,23 @@ public class CommitJournalModifyJdbc implements CommitJournalModify { private final JdbcTemplate template; - private static final String INS_SQL = "INSERT INTO sys_commit_journal (id, create_dt, event_name, target_key, login_info, other_info) VALUES (?,?,?,?,?,?)"; + private static final String INS_SQL = "INSERT INTO sys_commit_journal (id, create_dt, parent_id, event_name, target_key, login_info, other_info) VALUES (?,?,?,?,?,?,?)"; + private static final String ELS_SQL = "UPDATE sys_commit_journal SET elapse_ms = ? WHERE id = ?"; @Override public int insert(JournalService.Journal vo) { return template.update(INS_SQL, vo.getId(), vo.getCommitDt(), + vo.getParentId(), nullToEmpty(vo.getEventName()), nullToEmpty(vo.getTargetKey()), nullToEmpty(vo.getLoginInfo()), nullToEmpty(vo.getOtherInfo())); } + + @Override + public int elapse(long mills, long id) { + return template.update(ELS_SQL, mills, id); + } } diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/select/lightsequence/impl/LightSequenceSelectJdbc.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/select/lightsequence/impl/LightSequenceSelectJdbc.java index 85c4f9a2c..31ad8256b 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/select/lightsequence/impl/LightSequenceSelectJdbc.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/database/manual/single/select/lightsequence/impl/LightSequenceSelectJdbc.java @@ -103,6 +103,7 @@ private NameNextStep checkTableAndAdjust(NextStep step, String name) { } } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.warn("LightSequence failed to load dbMax, name=" + name, e); return Long.MIN_VALUE; } diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/service/journal/JournalService.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/service/journal/JournalService.java index 95907d8bc..162a1e976 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/service/journal/JournalService.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/service/journal/JournalService.java @@ -27,6 +27,8 @@ public interface JournalService { class Journal { private final long commitId; private final LocalDateTime commitDt; + private final long parentId; + private final long commitMs; private final String eventName; private final String targetKey; private final String loginInfo; @@ -78,9 +80,30 @@ public long getId() { } } + /** + * set and return elapse mills of the journal, can ignore error. + * SHOULD use the default connection without an explicit transaction. + * NOTE: submit/commit auto elapse, but create not. + */ + long elapse(@NotNull Journal journal); + + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventName event name + * @param loginInfo login info ,eg. userId, ip + * @param targetKey key/id of target + * @param otherInfo other info of operation + * @return Journal without the context + */ + @NotNull + Journal create(long parentId, @NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo); + /** * Submit the operation (event) with journal and return some result. - * Should with Transactional Propagation.REQUIRES_NEW + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * * @param eventName event name * @param loginInfo login info ,eg. userId, ip @@ -93,6 +116,7 @@ public long getId() { /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * * @param eventName event name * @param loginInfo login info ,eg. userId, ip @@ -109,8 +133,28 @@ default Journal commit(@NotNull String eventName, @Nullable String loginInfo, @N }); } + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventClass use Class.getName as eventName + * @param loginInfo login info ,eg. userId, ip + * @param targetKey key/id of target + * @param otherInfo other info of operation + * @return Journal without the context + */ + @NotNull + default Journal create(long parentId, @NotNull Class eventClass, @Nullable String loginInfo, @Nullable Object targetKey, @Nullable Object otherInfo) { + String lgn = loginInfo == null ? EmptyValue.VARCHAR : loginInfo; + String key = targetKey == null ? EmptyValue.VARCHAR : String.valueOf(targetKey); + String oth = otherInfo == null ? EmptyValue.VARCHAR : String.valueOf(otherInfo); + return create(parentId, eventClass.getName(), lgn, key, oth); + } + /** * Submit the operation (event) with journal and return some result. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to create targetKey/OtherInfo in Json * * @param eventClass use Class.getName as eventName @@ -130,6 +174,7 @@ default R submit(@NotNull Class eventClass, @Nullable String loginInfo, @ /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to create targetKey/OtherInfo in Json * * @param eventClass use Class.getName as eventName @@ -147,8 +192,24 @@ default Journal commit(@NotNull Class eventClass, @Nullable String loginInfo, }); } + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventClass use Class.getName as eventName + * @param targetKey key/id of target + * @param otherInfo other info of operation + * @return Journal without the context + */ + @NotNull + default Journal create(long parentId, @NotNull Class eventClass, @Nullable Object targetKey, @Nullable Object otherInfo) { + return create(parentId, eventClass, null, targetKey, otherInfo); + } + /** * Submit the operation (event) with journal and return some result. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventClass use Class.getName as eventName @@ -164,6 +225,7 @@ default R submit(@NotNull Class eventClass, @Nullable Object targetKey, @ /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventClass use Class.getName as eventName @@ -177,8 +239,23 @@ default Journal commit(@NotNull Class eventClass, @Nullable Object targetKey, return commit(eventClass, null, targetKey, otherInfo, commitSet); } + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventClass use Class.getName as eventName + * @param targetKey key/id of target + * @return Journal without the context + */ + @NotNull + default Journal create(long parentId, @NotNull Class eventClass, @Nullable Object targetKey) { + return create(parentId, eventClass, null, targetKey, null); + } + /** * Submit the operation (event) with journal and return some result. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventClass use Class.getName as eventName @@ -193,6 +270,7 @@ default R submit(@NotNull Class eventClass, @Nullable Object targetKey, @ /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventClass use Class.getName as eventName @@ -205,8 +283,22 @@ default Journal commit(@NotNull Class eventClass, @Nullable Object targetKey, return commit(eventClass, null, targetKey, null, commitSet); } + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventClass use Class.getName as eventName + * @return Journal without the context + */ + @NotNull + default Journal create(long parentId, @NotNull Class eventClass) { + return create(parentId, eventClass, null, null, null); + } + /** * Submit the operation (event) with journal and return some result. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventClass use Class.getName as eventName @@ -220,6 +312,7 @@ default R submit(@NotNull Class eventClass, @NotNull Function /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventClass use Class.getName as eventName @@ -231,8 +324,29 @@ default Journal commit(@NotNull Class eventClass, @NotNull Consumer return commit(eventClass, null, null, null, commitSet); } + + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventEnum convert enum with EnumConvertor + * @param loginInfo login info ,eg. userId, ip + * @param targetKey key/id of target + * @param otherInfo other info of operation + * @return Journal without the context + */ + @NotNull + default Journal create(long parentId, @NotNull Enum eventEnum, @Nullable String loginInfo, @Nullable Object targetKey, @Nullable Object otherInfo) { + String lgn = loginInfo == null ? EmptyValue.VARCHAR : loginInfo; + String key = targetKey == null ? EmptyValue.VARCHAR : String.valueOf(targetKey); + String oth = otherInfo == null ? EmptyValue.VARCHAR : String.valueOf(otherInfo); + return create(parentId, EnumConvertor.enum2Str(eventEnum), lgn, key, oth); + } + /** * Submit the operation (event) with journal and return some result. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to create targetKey/OtherInfo in Json * * @param eventEnum convert enum with EnumConvertor @@ -252,6 +366,7 @@ default R submit(@NotNull Enum eventEnum, @Nullable String loginInfo, @Nu /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to create targetKey/OtherInfo in Json * * @param eventEnum convert enum with EnumConvertor @@ -269,8 +384,24 @@ default Journal commit(@NotNull Enum eventEnum, @Nullable String loginInfo, @ }); } + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventEnum convert enum with EnumConvertor + * @param targetKey key/id of target + * @param otherInfo other info of operation + * @return Journal without the context + */ + @NotNull + default Journal create(long parentId, @NotNull Enum eventEnum, @Nullable Object targetKey, @Nullable Object otherInfo) { + return create(parentId, eventEnum, null, targetKey, otherInfo); + } + /** * Submit the operation (event) with journal and return some result. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventEnum convert enum with EnumConvertor @@ -286,6 +417,7 @@ default R submit(@NotNull Enum eventEnum, @Nullable Object targetKey, @Nu /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventEnum convert enum with EnumConvertor @@ -299,8 +431,23 @@ default Journal commit(@NotNull Enum eventEnum, @Nullable Object targetKey, @ return commit(eventEnum, null, targetKey, otherInfo, commitSet); } + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventEnum convert enum with EnumConvertor + * @param targetKey key/id of target + * @return Journal without the context + */ + @NotNull + default Journal create(long parentId, @NotNull Enum eventEnum, @Nullable Object targetKey) { + return create(parentId, eventEnum, null, targetKey); + } + /** * Submit the operation (event) with journal and return some result. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventEnum convert enum with EnumConvertor @@ -315,6 +462,7 @@ default R submit(@NotNull Enum eventEnum, @Nullable Object targetKey, @No /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventEnum convert enum with EnumConvertor @@ -327,8 +475,23 @@ default Journal commit(@NotNull Enum eventEnum, @Nullable Object targetKey, @ return commit(eventEnum, null, targetKey, null, commitSet); } + + /** + * create new Journal without the context. + * SHOULD create journal in REQUIRES_NEW. + * + * @param parentId parent id, default 0; + * @param eventEnum convert enum with EnumConvertor + * @return Journal without the context + */ + @NotNull + default Journal create(long parentId, @NotNull Enum eventEnum) { + return create(parentId, eventEnum, null, null); + } + /** * Submit the operation (event) with journal and return some result. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventEnum convert enum with EnumConvertor @@ -342,6 +505,7 @@ default R submit(@NotNull Enum eventEnum, @NotNull Function c /** * Commit the operation (event) with journal and return the journal. + * SHOULD create journal in REQUIRES_NEW, but not affect commitSet * It is recommended to `Override` to get loginInfo in TerminalContext/SecurityContext * * @param eventEnum convert enum with EnumConvertor diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/service/journal/impl/DefaultJournalService.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/service/journal/impl/DefaultJournalService.java index 61df27aa1..a3caca288 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/service/journal/impl/DefaultJournalService.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/service/journal/impl/DefaultJournalService.java @@ -3,9 +3,12 @@ import com.alibaba.ttl.TransmittableThreadLocal; import lombok.RequiredArgsConstructor; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.transaction.annotation.Propagation; +import pro.fessional.mirana.time.DateLocaling; import pro.fessional.mirana.time.ThreadNow; import pro.fessional.wings.faceless.convention.EmptyValue; import pro.fessional.wings.faceless.database.helper.DaoAssert; @@ -15,7 +18,7 @@ import pro.fessional.wings.faceless.service.lightid.BlockIdProvider; import pro.fessional.wings.faceless.service.lightid.LightIdService; -import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; /** @@ -23,12 +26,14 @@ * @since 2019-09-11 */ @RequiredArgsConstructor +@Slf4j public class DefaultJournalService implements JournalService { public static final String SEQ_JOURNAL = "sys_commit_journal"; /** no leak, for try-finally */ private final TransmittableThreadLocal context = new TransmittableThreadLocal<>(); + private final LightIdService lightIdService; private final BlockIdProvider blockIdProvider; private final CommitJournalModify journalModify; @@ -37,44 +42,126 @@ public class DefaultJournalService implements JournalService { private Propagation propagation = Propagation.REQUIRES_NEW; /** - * programmatic Propagation.REQUIRES_NEW + * create journal by dummyLightId getAndIncrement + */ + @Setter + private AtomicLong dummyLightId = null; + + /** + *
+     * create new journal if the existing to alive,
+     * * negative - use the old
+     * * zero - new one every time
+     * * positive - new one if older
+     * 
+ */ + @Setter + private int aliveSecond = 300; + + /** + * For internal debugging purposes, insert records with a new transaction (REQUIRES_NEW) if not using a dummy connection. */ @NotNull - @Override - public R submit(@NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo, @NotNull Function commitSet) { - // not null - R result = TransactionHelper.template(propagation).execute(ignore -> - execute(eventName, loginInfo, targetKey, otherInfo, commitSet) + @ApiStatus.Internal + public Journal create(long parentId, AtomicLong dummyId, long nowUtc, @NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo) { + if (nowUtc <= 0) nowUtc = ThreadNow.millis(); + + final long id = dummyId == null + ? lightIdService.getId(SEQ_JOURNAL, blockIdProvider.getBlockId()) + : dummyId.getAndIncrement(); + + final Journal journal = new Journal(id, DateLocaling.sysLdt(nowUtc), parentId, nowUtc, eventName, + targetKey == null ? EmptyValue.VARCHAR : targetKey, + loginInfo == null ? EmptyValue.VARCHAR : loginInfo, + otherInfo == null ? EmptyValue.VARCHAR : otherInfo ); - return Objects.requireNonNull(result); + + if (dummyId == null) { + TransactionHelper.template(propagation).executeWithoutResult(ignore -> { + int rc = journalModify.insert(journal); + DaoAssert.assertEq1(rc, "failed to insert Journal={}", journal); + }); + } + else { + log.warn("dummyLightId id={}", id); + } + + return journal; } + /** + * For internal debugging purposes, insert records with a new transaction (REQUIRES_NEW) if not using a dummy connection. + * However, update the elapsed time using the default connection without an explicit transaction. + */ @NotNull - private R execute(@NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo, @NotNull Function commitSet) { - final Journal commit = context.get(); - if (commit == null) { - long id = lightIdService.getId(SEQ_JOURNAL, blockIdProvider.getBlockId()); - - Journal journal = new Journal(id, ThreadNow.localDateTime(), eventName, - targetKey == null ? EmptyValue.VARCHAR : targetKey, - loginInfo == null ? EmptyValue.VARCHAR : loginInfo, - otherInfo == null ? EmptyValue.VARCHAR : otherInfo - ); - - int rc = journalModify.insert(journal); - DaoAssert.assertEq1(rc, "failed to insert Journal={}", journal); - - // Who created, who destroy - context.set(journal); - try { - return commitSet.apply(journal); + @ApiStatus.Internal + public R submit(int aliveSd, AtomicLong dummyId, @NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo, @NotNull Function commitSet) { + long now = 0; + long pid = 0; + final Journal oldOne = context.get(); + if (oldOne != null) { + if (aliveSd < 0) { + return commitSet.apply(oldOne); } - finally { - context.remove(); + else if (aliveSd > 0) { + now = ThreadNow.millis(); + long live = (now - oldOne.getCommitMs()) / 1000; + if (live <= aliveSd) { + return commitSet.apply(oldOne); + } + else { + pid = oldOne.getId(); + log.warn("renew timeout journal id={}, for alive={}, but live={}", pid, aliveSd, live); + } } } - else { - return commitSet.apply(commit); + + final Journal newOne = create(pid, dummyId, now, eventName, loginInfo, targetKey, otherInfo); + // Who created, who destroy + context.set(newOne); + try { + return commitSet.apply(newOne); } + finally { + context.remove(); + elapse(newOne); + } + } + + /** + * For internal debugging purposes, using the default connection without an explicit transaction, can ignore error. + */ + @ApiStatus.Internal + public long elapse(AtomicLong dummyId, @NotNull Journal journal) { + final long cost = ThreadNow.millis() - journal.getCommitMs(); + + if (dummyId == null) { + try { + // using the default connection without an explicit transaction. + journalModify.elapse(cost, journal.getId()); + } + catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall + log.warn("fail to update elapse=" + cost + ", id=" + journal.getId(), e); + } + } + return cost; + } + + @Override + public long elapse(@NotNull Journal journal) { + return elapse(dummyLightId, journal); + } + + @Override + @NotNull + public Journal create(long parentId, @NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo) { + return create(parentId, dummyLightId, 0, eventName, loginInfo, targetKey, otherInfo); + } + + @NotNull + @Override + public R submit(@NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo, @NotNull Function commitSet) { + return submit(aliveSecond, dummyLightId, eventName, loginInfo, targetKey, otherInfo, commitSet); } } diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/spring/bean/FacelessConfiguration.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/spring/bean/FacelessConfiguration.java index d58c78b0b..31ed50c89 100644 --- a/wings/faceless/src/main/java/pro/fessional/wings/faceless/spring/bean/FacelessConfiguration.java +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/spring/bean/FacelessConfiguration.java @@ -17,6 +17,7 @@ import pro.fessional.wings.faceless.service.lightid.LightIdService; import pro.fessional.wings.faceless.service.wini18n.impl.StandardI18nServiceJdbc; import pro.fessional.wings.faceless.spring.prop.FacelessEnabledProp; +import pro.fessional.wings.faceless.spring.prop.JournalProp; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import javax.sql.DataSource; @@ -48,10 +49,10 @@ public DataSourceContext dataSourceContext(DataSource current, List if (md) break; } - log.info("Faceless🦄 database-current-url=" + ctx.cacheJdbcUrl(ctx.getCurrent())); + log.info("Faceless🦄 database Current-jdbcurl=" + ctx.cacheJdbcUrl(ctx.getCurrent())); Map backends = ctx.getBackends(); for (Map.Entry e : backends.entrySet()) { - log.info("Faceless🦄 initSafeTable database-" + e.getKey() + "-url=" + ctx.cacheJdbcUrl(e.getValue())); + log.info("Faceless🦄 initSafeTable database " + e.getKey() + "-jdbcurl=" + ctx.cacheJdbcUrl(e.getValue())); JdbcTemplateHelper.initSafeTable(new JdbcTemplate(e.getValue())); } @@ -81,8 +82,11 @@ public CommitJournalModifyJdbc commitJournalModify(JdbcTemplate jdbcTemplate) { @Bean @ConditionalWingsEnabled(abs = FacelessEnabledProp.Key$simpleJournal) - public DefaultJournalService journalService(LightIdService lightIdService, BlockIdProvider blockIdProvider, CommitJournalModify journalModify) { - log.info("Faceless spring-bean journalService"); - return new DefaultJournalService(lightIdService, blockIdProvider, journalModify); + public DefaultJournalService journalService(JournalProp journalProp, LightIdService lightIdService, BlockIdProvider blockIdProvider, CommitJournalModify journalModify) { + DefaultJournalService bean = new DefaultJournalService(lightIdService, blockIdProvider, journalModify); + bean.setPropagation(journalProp.getPropagation()); + bean.setAliveSecond(journalProp.getAlive()); + log.info("Faceless spring-bean journalService, propagation=" + journalProp.getPropagation() + ", alive=" + journalProp.getAlive()); + return bean; } } diff --git a/wings/faceless/src/main/java/pro/fessional/wings/faceless/spring/prop/JournalProp.java b/wings/faceless/src/main/java/pro/fessional/wings/faceless/spring/prop/JournalProp.java new file mode 100644 index 000000000..3e59d30b1 --- /dev/null +++ b/wings/faceless/src/main/java/pro/fessional/wings/faceless/spring/prop/JournalProp.java @@ -0,0 +1,36 @@ +package pro.fessional.wings.faceless.spring.prop; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.transaction.annotation.Propagation; + +/** + * @author trydofor + * @since 2024-07-05 + */ +@Data +@ConfigurationProperties(JournalProp.Key) +public class JournalProp { + public static final String Key = "wings.faceless.journal"; + + /** + * transaction to create new Journal + * + * @see #Key$propagation + */ + private Propagation propagation = Propagation.REQUIRES_NEW; + public static final String Key$propagation = Key + ".propagation"; + + /** + *
+     * create new journal if the existing is older than alive,
+     * * negative - use the old
+     * * zero - new one every time
+     * * positive - new one if older
+     * 
+ * + * @see #Key$alive + */ + private int alive = 300; + public static final String Key$alive = Key + ".alive"; +} diff --git a/wings/faceless/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/faceless/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 9e52c6d2b..63e6a78cf 100644 --- a/wings/faceless/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/faceless/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,11 +1,9 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf.FacelessAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessConfiguration.commitJournalModify", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessConfiguration.dataSourceContext", "type": "java.lang.Boolean"}, @@ -21,7 +19,9 @@ {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessLightIdConfiguration.lightIdLoader", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessLightIdConfiguration.lightIdService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessLightIdConfiguration.lightSequenceModify", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessLightIdConfiguration.lightSequenceSelect", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.bean.FacelessLightIdConfiguration.lightSequenceSelect", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.faceless.spring.conf.FacelessAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/faceless/src/main/resources/wings-conf/wings-journal-79.properties b/wings/faceless/src/main/resources/wings-conf/wings-journal-79.properties new file mode 100644 index 000000000..2114b111a --- /dev/null +++ b/wings/faceless/src/main/resources/wings-conf/wings-journal-79.properties @@ -0,0 +1,8 @@ +## transaction to create new Journal +wings.faceless.journal.propagation=REQUIRES_NEW + +## create new journal if the existing is older than alive seconds, +## - `<0` - use the old +## - `0` - new one every time +## - `>0` - new one if older +wings.faceless.journal.alive=300 diff --git a/wings/faceless/src/main/resources/wings-flywave/branch/feature/01-enum-i18n/2019-05-21u01-enum-i18n.sql b/wings/faceless/src/main/resources/wings-flywave/branch/feature/01-enum-i18n/2019-05-21u01-enum-i18n.sql index 881b470ca..9b331ea08 100644 --- a/wings/faceless/src/main/resources/wings-flywave/branch/feature/01-enum-i18n/2019-05-21u01-enum-i18n.sql +++ b/wings/faceless/src/main/resources/wings-flywave/branch/feature/01-enum-i18n/2019-05-21u01-enum-i18n.sql @@ -1,2 +1,4 @@ DROP TABLE IF EXISTS `sys_constant_enum`; -- 105/Enum and Const; DROP TABLE IF EXISTS `sys_standard_i18n`; -- 106/I18n Message; + +-- CALL FLYWAVE('2019-05-21u01-enum-i18n.sql'); \ No newline at end of file diff --git a/wings/faceless/src/main/resources/wings-flywave/branch/feature/01-enum-i18n/2019-05-21v01-enum-i18n.sql b/wings/faceless/src/main/resources/wings-flywave/branch/feature/01-enum-i18n/2019-05-21v01-enum-i18n.sql index 5bc729d34..4dc6de256 100644 --- a/wings/faceless/src/main/resources/wings-flywave/branch/feature/01-enum-i18n/2019-05-21v01-enum-i18n.sql +++ b/wings/faceless/src/main/resources/wings-flywave/branch/feature/01-enum-i18n/2019-05-21v01-enum-i18n.sql @@ -1,5 +1,5 @@ CREATE TABLE `sys_constant_enum` ( - `id` INT(11) NOT NULL COMMENT 'id: 9+ digits for dynamic, 8 digits for static, 3-2-2 (table-column-value) segments, SUPER end with 00', + `id` INT NOT NULL COMMENT 'id: 9+ digits for dynamic, 8 digits for static, 3-2-2 (table-column-value) segments, SUPER end with 00', `type` VARCHAR(100) NOT NULL COMMENT 'enum group: same type for same enum, auto Pascal naming', `code` VARCHAR(100) NOT NULL COMMENT 'enum name: Fixed [code|id] for SUPER, external key, coding friendly', `hint` VARCHAR(100) NOT NULL COMMENT 'display message', @@ -179,3 +179,5 @@ VALUES ('sys_constant_enum', 'hint', 'standard_language.zh_CN', 'zh_CN', '简体 ('sys_constant_enum', 'hint', 'standard_language.en_US', 'en_US', 'English(US)'), ('sys_constant_enum', 'hint', 'standard_language.en_US', 'zh_CN', '美国英语'); + +-- CALL FLYWAVE('2019-05-21v01-enum-i18n.sql'); \ No newline at end of file diff --git a/wings/faceless/src/main/resources/wings-flywave/branch/somefix/05-journal-elapse/2021-10-26u05-journal-elapse.sql b/wings/faceless/src/main/resources/wings-flywave/branch/somefix/05-journal-elapse/2021-10-26u05-journal-elapse.sql new file mode 100644 index 000000000..43ad7e4b5 --- /dev/null +++ b/wings/faceless/src/main/resources/wings-flywave/branch/somefix/05-journal-elapse/2021-10-26u05-journal-elapse.sql @@ -0,0 +1,5 @@ +ALTER TABLE `sys_commit_journal` + DROP COLUMN `parent_id`, + DROP COLUMN `elapse_ms`; + +-- CALL FLYWAVE('2021-10-26u05-journal-elapse.sql'); \ No newline at end of file diff --git a/wings/faceless/src/main/resources/wings-flywave/branch/somefix/05-journal-elapse/2021-10-26v05-journal-elapse.sql b/wings/faceless/src/main/resources/wings-flywave/branch/somefix/05-journal-elapse/2021-10-26v05-journal-elapse.sql new file mode 100644 index 000000000..df8cd4d0a --- /dev/null +++ b/wings/faceless/src/main/resources/wings-flywave/branch/somefix/05-journal-elapse/2021-10-26v05-journal-elapse.sql @@ -0,0 +1,5 @@ +ALTER TABLE `sys_commit_journal` + ADD COLUMN `parent_id` BIGINT NOT NULL DEFAULT '0' COMMENT 'parent id if renew' AFTER `create_dt`, + ADD COLUMN `elapse_ms` BIGINT NOT NULL DEFAULT '0' COMMENT 'elapse mills' AFTER `parent_id`; + +-- CALL FLYWAVE('2021-10-26v05-journal-elapse.sql'); \ No newline at end of file diff --git a/wings/faceless/src/main/resources/wings-flywave/master/01-light/2019-05-20u01-light-commit.sql b/wings/faceless/src/main/resources/wings-flywave/master/01-light/2019-05-20u01-light-commit.sql index 48983d538..a6f162b48 100644 --- a/wings/faceless/src/main/resources/wings-flywave/master/01-light/2019-05-20u01-light-commit.sql +++ b/wings/faceless/src/main/resources/wings-flywave/master/01-light/2019-05-20u01-light-commit.sql @@ -1,3 +1,5 @@ -- ask@danger DROP TABLE IF EXISTS `sys_light_sequence`; -- 103/Sequence Generation; DROP TABLE IF EXISTS `sys_commit_journal`; -- 104/Data Changeset; + +-- CALL FLYWAVE('2019-05-20u01-light-commit.sql'); \ No newline at end of file diff --git a/wings/faceless/src/main/resources/wings-flywave/master/01-light/2019-05-20v01-light-commit.sql b/wings/faceless/src/main/resources/wings-flywave/master/01-light/2019-05-20v01-light-commit.sql index e8dda7ad1..58adecd21 100644 --- a/wings/faceless/src/main/resources/wings-flywave/master/01-light/2019-05-20v01-light-commit.sql +++ b/wings/faceless/src/main/resources/wings-flywave/master/01-light/2019-05-20v01-light-commit.sql @@ -1,20 +1,22 @@ CREATE TABLE `sys_light_sequence` ( `seq_name` VARCHAR(100) NOT NULL COMMENT 'sequence name', - `block_id` INT(11) NOT NULL DEFAULT 0 COMMENT 'block', - `next_val` BIGINT(20) NOT NULL DEFAULT '1' COMMENT 'next value', - `step_val` INT(11) NOT NULL DEFAULT '100' COMMENT 'step of increment', + `block_id` INT NOT NULL DEFAULT 0 COMMENT 'block', + `next_val` BIGINT NOT NULL DEFAULT '1' COMMENT 'next value', + `step_val` INT NOT NULL DEFAULT '100' COMMENT 'step of increment', `comments` VARCHAR(200) NOT NULL COMMENT 'comments', PRIMARY KEY (`seq_name`, `block_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='103/Sequence Generation'; CREATE TABLE `sys_commit_journal` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', + `id` BIGINT NOT NULL COMMENT 'primary key', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime', + `parent_id` BIGINT NOT NULL DEFAULT '0' COMMENT 'parent id if renew', + `elapse_ms` BIGINT NOT NULL DEFAULT '0' COMMENT 'elapse mills', `event_name` VARCHAR(200) NOT NULL COMMENT 'event name', `target_key` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'target data', - `login_info` TEXT COMMENT 'login info: agent, terminal', - `other_info` TEXT COMMENT 'other info: biz index data', + `login_info` TEXT NULL COMMENT 'login info: agent, terminal', + `other_info` TEXT NULL COMMENT 'other info: biz index data', PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='104/Data Changeset'; @@ -27,3 +29,5 @@ VALUES ('singleton_lightid_blockid', 0, 10000000, 100, 'default block_id'), -- sys_commit_journal@plain REPLACE INTO `sys_commit_journal` (`id`, `event_name`) VALUES (0, 'system_manual_init'); + +-- CALL FLYWAVE('2019-05-20v01-light-commit.sql'); \ No newline at end of file diff --git a/wings/faceless/src/test/java/pro/fessional/wings/faceless/database/helper/JdbcTemplateHelperTest.java b/wings/faceless/src/test/java/pro/fessional/wings/faceless/database/helper/JdbcTemplateHelperTest.java index 997ead8f4..c174f4eee 100644 --- a/wings/faceless/src/test/java/pro/fessional/wings/faceless/database/helper/JdbcTemplateHelperTest.java +++ b/wings/faceless/src/test/java/pro/fessional/wings/faceless/database/helper/JdbcTemplateHelperTest.java @@ -25,6 +25,10 @@ class JdbcTemplateHelperTest { @Test @TmsLink("12148") void safeTable() { + assertEquals("`sys_light_sequence`",JdbcTemplateHelper.safeName("sys_light_sequence")); + assertEquals("`sys_light_sequence`",JdbcTemplateHelper.safeName("`sys_light_sequence`")); + assertEquals("`sys_light_sequence`",JdbcTemplateHelper.safeName("`sys_light``_sequence`")); + // init the context assertNotNull(sourceContext); diff --git a/wings/faceless/src/test/java/pro/fessional/wings/faceless/service/journal/impl/DefaultJournalServiceTest.java b/wings/faceless/src/test/java/pro/fessional/wings/faceless/service/journal/impl/DefaultJournalServiceTest.java index ccf7c07c1..69e5fe786 100644 --- a/wings/faceless/src/test/java/pro/fessional/wings/faceless/service/journal/impl/DefaultJournalServiceTest.java +++ b/wings/faceless/src/test/java/pro/fessional/wings/faceless/service/journal/impl/DefaultJournalServiceTest.java @@ -11,6 +11,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Propagation; +import pro.fessional.mirana.time.Sleep; import pro.fessional.wings.faceless.database.helper.TransactionHelper; import pro.fessional.wings.faceless.service.journal.JournalService.Journal; @@ -23,7 +24,8 @@ */ @SpringBootTest(properties = { "logging.level.org.springframework.jdbc=DEBUG", - "logging.level.org.springframework.transaction=DEBUG" + "logging.level.org.springframework.transaction=DEBUG", + "wings.faceless.journal.alive=1" }) @Slf4j class DefaultJournalServiceTest { @@ -53,7 +55,7 @@ void commit() { // default required new TransactionStatus status = TransactionHelper.begin(); journalService.commit(CallLevel.L1, j1 -> { - journalService.commit(CallLevel.L1, j2 -> { + journalService.commit(CallLevel.L2, j2 -> { journalService.commit(CallLevel.L3, journalRef::set); }); }); @@ -66,21 +68,39 @@ void commit() { Long id = jdbcTemplate.queryForObject("SELECT id FROM sys_commit_journal WHERE id = ?", Long.class, jn.getId()); Assertions.assertEquals(jn.getId(), id); + Assertions.assertEquals(0, jn.getParentId()); } { journalService.setPropagation(Propagation.REQUIRED); + try { + TransactionStatus status = TransactionHelper.begin(); + journalService.commit(CallLevel.L1, j1 -> { + journalService.commit(CallLevel.L1, j2 -> { + journalService.commit(CallLevel.L3, journalRef::set); + }); + }); + TransactionHelper.rollback(status); + Journal jn = journalRef.get(); + List id = jdbcTemplate.queryForList("SELECT id FROM sys_commit_journal WHERE id = ?", Long.class, jn.getId()); + Assertions.assertTrue(id.isEmpty()); + Assertions.assertEquals(0, jn.getParentId()); + } + finally { + journalService.setPropagation(Propagation.REQUIRES_NEW); + } + } - TransactionStatus status = TransactionHelper.begin(); + { journalService.commit(CallLevel.L1, j1 -> { journalService.commit(CallLevel.L1, j2 -> { + Sleep.ignoreInterrupt(2500); journalService.commit(CallLevel.L3, journalRef::set); }); }); - TransactionHelper.rollback(status); + Journal jn = journalRef.get(); - List id = jdbcTemplate.queryForList("SELECT id FROM sys_commit_journal WHERE id = ?", Long.class, jn.getId()); - Assertions.assertTrue(id.isEmpty()); + Assertions.assertTrue(jn.getParentId() != 0); } } } \ No newline at end of file diff --git a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/common/Splitter.java b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/common/Splitter.java deleted file mode 100644 index cf23afb57..000000000 --- a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/common/Splitter.java +++ /dev/null @@ -1,104 +0,0 @@ -package pro.fessional.wings.silencer.common; - -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; - -/** - * @author trydofor - * @since 2023-06-13 - */ -public class Splitter { - - /** - * split string by separator - * - * @param sep separator - * @param str string - * @return list of string - * @see com.google.common.base.Splitter - */ - @NotNull - public static List list(@NotNull String sep, String str) { - if (str == null || str.isEmpty()) return Collections.emptyList(); - return com.google.common.base.Splitter.on(sep).splitToList(str); - } - - /** - * split string by separator - * - * @param sep separator - * @param str string - * @param max max items - * @return list of string - * @see com.google.common.base.Splitter - */ - @NotNull - public static List list(@NotNull String sep, String str, int max) { - if (str == null || str.isEmpty()) return Collections.emptyList(); - return com.google.common.base.Splitter.on(sep).limit(max).splitToList(str); - } - - /** - * split string by separator and convert to T - * - * @param sep separator - * @param str string - * @param fun convert string to T - * @return list of T - * @see com.google.common.base.Splitter - */ - @NotNull - public static List list(@NotNull String sep, String str, @NotNull Function fun) { - if (str == null || str.isEmpty()) return Collections.emptyList(); - final Iterable iter = com.google.common.base.Splitter.on(sep).split(str); - List result = new ArrayList<>(); - for (String s : iter) { - result.add(fun.apply(s)); - } - return Collections.unmodifiableList(result); - } - - /** - * split string by separator and convert to T - * - * @param sep separator - * @param str string - * @param max max items - * @param fun convert string to T - * @return list of T - * @see com.google.common.base.Splitter - */ - @NotNull - public static List list(@NotNull String sep, String str, int max, @NotNull Function fun) { - if (str == null || str.isEmpty()) return Collections.emptyList(); - final Iterable iter = com.google.common.base.Splitter.on(sep).limit(max).split(str); - List result = new ArrayList<>(); - for (String s : iter) { - result.add(fun.apply(s)); - } - return Collections.unmodifiableList(result); - } - - /** - * count items split by separator - *
-     * 0 - if str is empty
-     * 1 - if str does not contain sep
-     * n+1 - if str contains n sep
-     * 
- * - * @param sep separator - * @param str string - * @return count of items - * @see StringUtils#countMatches(CharSequence, CharSequence) - */ - public static int count(@NotNull String sep, String str) { - if (str == null || str.isEmpty()) return 0; - return StringUtils.countMatches(str, sep) + 1; - } -} diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazy.java b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazy.java similarity index 100% rename from wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazy.java rename to wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazy.java diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyAware.java b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyAware.java similarity index 100% rename from wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyAware.java rename to wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyAware.java diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyPostProcessor.java b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyPostProcessor.java similarity index 100% rename from wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyPostProcessor.java rename to wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyPostProcessor.java diff --git a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/modulate/RuntimeMode.java b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/modulate/RuntimeMode.java index a0464340b..a0e8fce89 100644 --- a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/modulate/RuntimeMode.java +++ b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/modulate/RuntimeMode.java @@ -2,7 +2,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import pro.fessional.mirana.cond.StaticFlag; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; /** @@ -11,32 +13,23 @@ */ public class RuntimeMode { - private static final AtomicReference unitTest = new AtomicReference<>(null); + private static volatile @NotNull RunMode runMode = RunMode.Nothing; + private static volatile @NotNull ApiMode apiMode = ApiMode.Nothing; - protected RuntimeMode(@Nullable RunMode run, @Nullable ApiMode api) { - if (run != null) runMode = run; - if (api != null) apiMode = api; - } + private static final ConcurrentHashMap runVote = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap apiVote = new ConcurrentHashMap<>(); - public static boolean isUnitTest() { - Boolean b = unitTest.get(); - if (b == null) { - b = false; - for (StackTraceElement el : new RuntimeException().getStackTrace()) { - if (el.getClassName().startsWith("org.junit.")) { - b = true; - break; - } - } - unitTest.set(b); + protected RuntimeMode(@Nullable RunMode run, @Nullable ApiMode api) { + if (run != null) { + runMode = run; + runVote.clear(); + } + if (api != null) { + apiMode = api; + apiVote.clear(); } - return b; } - @NotNull - private static RunMode runMode = RunMode.Nothing; - - @NotNull public static RunMode getRunMode() { return runMode; @@ -56,7 +49,7 @@ public static boolean hasRunMode(RunMode... modes) { public static boolean isRunMode(CharSequence mode) { if (mode == null) return false; - return contains(mode.toString(), runMode.name()); + return StaticFlag.vote(runMode.name(), mode.toString()) > 0; } public static boolean hasRunMode(CharSequence... modes) { @@ -66,8 +59,9 @@ public static boolean hasRunMode(CharSequence... modes) { return false; } - @NotNull - private static ApiMode apiMode = ApiMode.Nothing; + public static boolean voteRunMode(String votes) { + return runVote.computeIfAbsent(votes, k -> StaticFlag.hasVote(runMode.name(), k)); + } @NotNull public static ApiMode getApiMode() { @@ -88,7 +82,7 @@ public static boolean hasApiMode(ApiMode... modes) { public static boolean isApiMode(CharSequence mode) { if (mode == null) return false; - return contains(mode.toString(), apiMode.name()); + return StaticFlag.vote(apiMode.name(), mode.toString()) > 0; } public static boolean hasApiMode(CharSequence... modes) { @@ -98,22 +92,27 @@ public static boolean hasApiMode(CharSequence... modes) { return false; } - private static boolean contains(String tstr, String ostr) { - final int tlen = tstr.length(); - int toff = 0; - for (int i = 0; i < tlen; i++) { - if (Character.isJavaIdentifierPart(tstr.charAt(i))) { - toff = i; - break; - } - } + public static boolean voteApiMode(String votes) { + return apiVote.computeIfAbsent(votes, k -> StaticFlag.hasVote(apiMode.name(), k)); + } - final int olen = ostr.length(); - if (tstr.regionMatches(true, toff, ostr, 0, olen)) { - final int idx = toff + olen; - return idx == tlen || !Character.isJavaIdentifierPart(tstr.charAt(idx)); - } + // // - return false; + private static final AtomicReference unitTest = new AtomicReference<>(null); + + public static boolean isUnitTest() { + Boolean b = unitTest.get(); + if (b == null) { + b = false; + for (StackTraceElement el : new RuntimeException().getStackTrace()) { + if (el.getClassName().startsWith("org.junit.")) { + b = true; + break; + } + } + unitTest.set(b); + } + return b; } + } diff --git a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerCurseConfiguration.java b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerCurseConfiguration.java index f4fb28ec9..f291720c4 100644 --- a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerCurseConfiguration.java +++ b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerCurseConfiguration.java @@ -8,21 +8,23 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.info.GitProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import pro.fessional.wings.silencer.enhance.ThisLazyPostProcessor; import pro.fessional.wings.silencer.modulate.RuntimeMode; import pro.fessional.wings.silencer.runner.ApplicationInspectRunner; import pro.fessional.wings.silencer.runner.ApplicationReadyEventRunner; import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.silencer.spring.help.ApplicationContextHelper; -import pro.fessional.wings.silencer.spring.help.VersionInfoHelper; import pro.fessional.wings.silencer.spring.prop.SilencerAutoLogProp; import pro.fessional.wings.silencer.spring.prop.SilencerEnabledProp; import pro.fessional.wings.silencer.spring.prop.SilencerRuntimeProp; +import pro.fessional.wings.silencer.support.InspectHelper; import java.util.ArrayList; import java.util.HashSet; @@ -82,19 +84,20 @@ public ApplicationInspectRunner auditPropRunner() { public ApplicationInspectRunner infoGitJvmRunner(ApplicationContext context) { log.info("Silencer spring-runs infoGitJvmRunner"); return new ApplicationInspectRunner(WingsOrdered.Lv1Config, args -> { - - - log.info("jvm-name=" + VersionInfoHelper.jvmName()); - log.info("jvm-version=" + VersionInfoHelper.jvmVersion()); - log.info("jvm-vendor=" + VersionInfoHelper.jvmVendor()); - var git = context.getBean(GitProperties.class); - log.info("git-branch=" + VersionInfoHelper.branch(git)); - log.info("git-id=" + VersionInfoHelper.commitId(git)); - log.info("git-time=" + VersionInfoHelper.commitDateTime(git)); - log.info("git-build=" + VersionInfoHelper.buildDateTime(git)); - log.info("git-version=" + VersionInfoHelper.buildVersion(git)); - log.info("git-message=" + VersionInfoHelper.commitMessage(git)); + log.info("git-branch=" + InspectHelper.branch(git)); + log.info("git-id=" + InspectHelper.commitId(git)); + log.info("git-time=" + InspectHelper.commitDateTime(git)); + log.info("git-build=" + InspectHelper.buildDateTime(git)); + log.info("git-version=" + InspectHelper.buildVersion(git)); + log.info("git-message=" + InspectHelper.commitMessage(git)); + + log.info("jvm-name=" + InspectHelper.jvmName()); + log.info("jvm-version=" + InspectHelper.jvmVersion()); + log.info("jvm-vendor=" + InspectHelper.jvmVendor()); + + log.info("app-ApiMode=" + RuntimeMode.getApiMode()); + log.info("app-RunMode=" + RuntimeMode.getRunMode()); }); } @@ -154,9 +157,15 @@ else if (exists.contains(name)) { @Bean @ConditionalWingsEnabled - public RuntimeMode runtimeMode(SilencerRuntimeProp prop) { + public static BeanPostProcessor thisLazyAwarePostProcessor() { + log.info("Silencer spring-auto thisLazyAwarePostProcessor"); + return new ThisLazyPostProcessor(); + } - log.info("Silencer spring-auto runtimeMode"); + @Bean + @ConditionalWingsEnabled + public RuntimeMode runtimeMode(SilencerRuntimeProp prop) { + log.info("Silencer spring-auto RuntimeMode, runMode=" + prop.getRunMode() + ", apiMode=" + prop.getApiMode()); return new RuntimeMode(prop.getRunMode(), prop.getApiMode()) {}; } } diff --git a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/CommonPropHelper.java b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/CommonPropHelper.java deleted file mode 100644 index a4bc93825..000000000 --- a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/CommonPropHelper.java +++ /dev/null @@ -1,110 +0,0 @@ -package pro.fessional.wings.silencer.spring.help; - -import lombok.SneakyThrows; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.util.ResourceUtils; -import org.springframework.util.StringUtils; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; - -import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX; - -/** - * @author trydofor - * @since 2022-06-17 - */ -public class CommonPropHelper { - - public static final String DisabledValue = "-"; - public static final String MaskingValue = "*****"; - - public static boolean notValue(String str) { - return str == null || str.isEmpty() || DisabledValue.equals(str) || MaskingValue.equals(str); - } - - public static boolean hasValue(String value) { - return !notValue(value); - } - - @NotNull - public static LinkedHashSet onlyValue(Collection values) { - if (values == null) values = Collections.emptyList(); - LinkedHashSet set = new LinkedHashSet<>(values); - set.removeIf(CommonPropHelper::notValue); - return set; - } - - @NotNull - public static LinkedHashMap onlyValue(Map values) { - if (values == null) values = Collections.emptyMap(); - LinkedHashMap map = new LinkedHashMap<>(); - for (Map.Entry en : values.entrySet()) { - final String value = en.getValue(); - if (hasValue(value)) { - map.put(en.getKey(), value); - } - } - return map; - } - - /** - * Use 'classpath:' format for ClassPathResource and getURL().toExternalForm() for the rest - * - * @see ResourceUtils - */ - @SneakyThrows @NotNull - public static String toString(@NotNull Resource resource) { - if (resource instanceof ClassPathResource) { - final String path = ((ClassPathResource) resource).getPath(); - return CLASSPATH_URL_PREFIX + path; - } - return resource.getURL().toExternalForm(); - } - - /** - * if this.value is invalid, then use that.value by key matches. - * - * @param thiz this map - * @param that that map - */ - public static void mergeNotValue(@NotNull Map thiz, @Nullable Map that) { - if (that == null || that.isEmpty()) return; - - if (thiz.isEmpty()) { - thiz.putAll(that); - } - else { - for (Map.Entry en : thiz.entrySet()) { - final String v = en.getValue(); - if (notValue(v)) { - final String tv = that.get(en.getKey()); - en.setValue(tv); - } - } - } - } - - /** - * comma-separated values to StringArray - * - * @see StringUtils#commaDelimitedListToStringArray(String) - */ - @Contract("_,true->!null") - public static String[] arrayOrNull(String str, boolean nonnull) { - final String[] arr = StringUtils.commaDelimitedListToStringArray(str); - if (nonnull) { - return arr; - } - else { - return arr.length == 0 ? null : arr; - } - } -} diff --git a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/watch/Watches.java b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/watch/Watches.java index bfd5b4e48..3047b7313 100644 --- a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/watch/Watches.java +++ b/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/watch/Watches.java @@ -5,8 +5,11 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import pro.fessional.mirana.best.DummyBlock; import pro.fessional.mirana.time.StopWatch; +import java.util.function.BiConsumer; + /** * Performance Monitoring * @@ -57,16 +60,38 @@ public static StopWatch.Watch current(String name) { /** * Release the current timer and returns whether all timers have finished. - * When all timings are finished, clear the log if clean, write to log if token != null. + * When all timings are finished, clear the watches if clean. + */ + public static boolean release(boolean clean) { + return release(clean, null, WatchHandler); + } + + /** + * Release the current timer and returns whether all timers have finished. + * When all timings are finished, handle the StopWatch if token is not null, clear the watches if clean. */ - public static boolean release(boolean clean, String token) { + public static boolean release(boolean clean, @Nullable String token) { + return release(clean, token, WatchHandler); + } + + /** + * Release the current timer and returns whether all timers have finished. + * When all timings are finished, handle the StopWatch if token is not null, clear the watches if clean. + */ + public static boolean release(boolean clean, @Nullable String token, @NotNull BiConsumer handle) { StopWatch watch = StopWatches.get(); if (watch == null || watch.isRunning()) return false; StopWatches.remove(); if (token != null) { - logging(token, watch); + try { + handle.accept(token, watch); + } + catch (Exception e) { + DummyBlock.ignore(e); + } } + if (clean) { watch.clear(); } @@ -74,9 +99,16 @@ public static boolean release(boolean clean, String token) { } /** - * output info to the log with token at Warn level + * to handle the StopWatch when timings are finished and token is not null. + * should handle the StopWatch before its clean. + * + * @param handler token and StopWatch are not null */ - public static void logging(String token, StopWatch watch) { - log.warn("Watching {} {}", token, watch); + public static void setWatchHandler(BiConsumer handler) { + WatchHandler = handler; } + + private static volatile BiConsumer WatchHandler = (token, watch) -> { + log.warn("Watching {} {}", token, watch); + }; } diff --git a/wings/silencer-curse/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/silencer-curse/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 9f7823986..82146fc77 100644 --- a/wings/silencer-curse/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/silencer-curse/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,15 +1,15 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.conf.SilencerCurseAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerCurseConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerCurseConfiguration.auditPropRunner", "type": "java.lang.Boolean", "description": "wings.enabled.silencer.audit-prop for short."}, + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerCurseConfiguration.auditPropRunner", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.silencer.audit-prop for short."}, + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerCurseConfiguration.infoGitJvmRunner", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerCurseConfiguration.muteConsoleRunner", "type": "java.lang.Boolean", "description": "wings.enabled.silencer.mute-console for short."}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerCurseConfiguration.runtimeMode", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerCurseConfiguration.thisLazyAwarePostProcessor", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerEncryptConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerEncryptConfiguration.aes256", "type": "java.lang.Boolean"}, @@ -20,7 +20,9 @@ {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerTweakConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerTweakConfiguration$ClockWired", "type": "java.lang.Boolean", "description": "wings.enabled.silencer.tweak-clock for short."}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerTweakConfiguration$LogbackWired", "type": "java.lang.Boolean", "description": "wings.enabled.silencer.tweak-logback for short."}, - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerTweakConfiguration$StackWired", "type": "java.lang.Boolean", "description": "wings.enabled.silencer.tweak-stack for short."} + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerTweakConfiguration$StackWired", "type": "java.lang.Boolean", "description": "wings.enabled.silencer.tweak-stack for short."}, + + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.conf.SilencerCurseAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/TestSilencerCurseApplication.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/TestSilencerCurseApplication.java index 27197d7c1..c1e50ee85 100644 --- a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/TestSilencerCurseApplication.java +++ b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/TestSilencerCurseApplication.java @@ -2,15 +2,57 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import pro.fessional.wings.silencer.app.conf.TestMergingProp; + +import java.util.concurrent.atomic.AtomicInteger; /** + * #01, 244ms, (spring.factories): ApplicationStartingEvent + * #02, 2209ms, (spring.factories): ApplicationEnvironmentPreparedEvent + * #03, 2758ms, (spring.factories): ApplicationContextInitializedEvent + * #04, 2809ms, (spring.factories): ApplicationPreparedEvent + * #05, 3365ms, @PostConstruct: TestSpringOrderService + * #06, 3365ms, @Override: InitializingBean TestSpringOrderService + * #07, 3381ms, (constructor): can inject para, autoconf=true + * #08, 3384ms, @Autowired: testAutowired1 can inject para, autoconf=true + * #09, 3384ms, @PostConstruct: postConstruct1 + * #10, 3384ms, @PostConstruct: postConstruct2 + * #11, 3385ms, @Override: InitializingBean TestSpringOrderConfiguration + * #12, 3385ms, @Bean: testBean1 can inject para, autoconf=true + * #13, 3386ms, @Bean: testBean2 can inject para, autoconf=true + * #14, 3657ms, (spring.factories): ContextRefreshedEvent + * #15, 3662ms, (spring.factories): ApplicationStartedEvent + * #16, 3664ms, @EventListener: ApplicationStartedEvent + * #17, 3665ms, (spring.factories): AvailabilityChangeEvent + * #18, 3669ms, CommandLineRunner: CommandLineRunner1 + * #19, 3673ms, CommandLineRunner: CommandLineRunner2 + * #20, 3674ms, (spring.factories): ApplicationReadyEvent + * #21, 3675ms, @EventListener: ApplicationReadyEvent + * #22, 3676ms, (spring.factories): AvailabilityChangeEvent + * #23, 3678ms, Jvm ShutdownHook + * #24, 3679ms, (spring.factories): ContextClosedEvent + * #25, 3679ms, @EventListener: ContextClosedEvent + * #26, 3682ms, @PreDestroy: TestSpringOrderConfiguration + * #27, 3682ms, @Override: DisposableBean TestSpringOrderConfiguration + * #28, 3682ms, @PreDestroy: TestSpringOrderService + * #29, 3682ms, @Override: DisposableBean TestSpringOrderService + * * @author trydofor * @since 2019-07-20 */ @SpringBootApplication +@EnableConfigurationProperties(TestMergingProp.class) public class TestSilencerCurseApplication { + private static final AtomicInteger seq = new AtomicInteger(0); + private static final long start = System.currentTimeMillis(); + + public static void log(String msg) { + System.err.printf(">>>>> #%02d, %4dms, %s\n", seq.incrementAndGet(), (System.currentTimeMillis() - start), msg); + } + public interface InnerFace { } @@ -19,8 +61,8 @@ public InnerFace innerFace() { return new InnerFace() {}; } - public static void main(String[] args) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> log("Jvm ShutdownHook"))); SpringApplication.run(TestSilencerCurseApplication.class, args); } } diff --git a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestApplicationEventLogger.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestApplicationEventLogger.java index ab6568956..2921c6681 100644 --- a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestApplicationEventLogger.java +++ b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestApplicationEventLogger.java @@ -1,9 +1,8 @@ package pro.fessional.wings.silencer.app.conf; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; +import pro.fessional.wings.silencer.app.TestSilencerCurseApplication; /** * @author trydofor @@ -11,10 +10,8 @@ */ public class TestApplicationEventLogger implements ApplicationListener { - private static final Log log = LogFactory.getLog(TestApplicationEventLogger.class); - @Override public void onApplicationEvent(ApplicationEvent event) { - log.info(">>>>> " + event.getClass().getSimpleName() + "(spring.factories) timestamp=" + event.getTimestamp()); + TestSilencerCurseApplication.log("(spring.factories): " + event.getClass().getSimpleName()); } } diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/conf/TestMergingProp.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestMergingProp.java similarity index 100% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/app/conf/TestMergingProp.java rename to wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestMergingProp.java diff --git a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestSpringOrderConfiguration.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestSpringOrderConfiguration.java index 7c3cf214d..600968d1b 100644 --- a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestSpringOrderConfiguration.java +++ b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/conf/TestSpringOrderConfiguration.java @@ -1,8 +1,8 @@ package pro.fessional.wings.silencer.app.conf; import jakarta.annotation.PostConstruct; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import jakarta.annotation.PreDestroy; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; @@ -10,39 +10,21 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; +import pro.fessional.wings.silencer.app.TestSilencerCurseApplication; import pro.fessional.wings.silencer.other.CollectionInjectTest; import pro.fessional.wings.silencer.spring.prop.SilencerEnabledProp; /** - * ApplicationEnvironmentPreparedEvent(spring.factories) - * ApplicationContextInitializedEvent(spring.factories) - * ApplicationPreparedEvent(spring.factories) - * constructor - * testAutowired1 - * postConstruct1 - * postConstruct2 - * afterPropertiesSet - * testBean1 - * testBean2 - * ContextRefreshedEvent(spring.factories) - * ApplicationStartedEvent - * CommandLineRunner1 - * CommandLineRunner2 - * ApplicationReadyEvent - * ContextClosedEvent(spring.factories) - * * @author trydofor * @since 2022-11-02 */ @Configuration(proxyBeanMethods = false) -public class TestSpringOrderConfiguration implements InitializingBean { - private static final Log log = LogFactory.getLog(TestSpringOrderConfiguration.class); +public class TestSpringOrderConfiguration implements InitializingBean, DisposableBean { public TestSpringOrderConfiguration(SilencerEnabledProp prop) { - log.info(">>>>> constructor can inject parameter Autoconf=" + prop.isAutoconf()); + TestSilencerCurseApplication.log("(constructor): can inject para, autoconf=" + prop.isAutoconf()); } - @Bean public CollectionInjectTest.Dto dto2() { return new CollectionInjectTest.Dto(2); @@ -50,38 +32,48 @@ public CollectionInjectTest.Dto dto2() { @Bean public CommandLineRunner testBean1(SilencerEnabledProp prop) { - log.info(">>>>> testBean1 can inject parameter autoconf=" + prop.isAutoconf()); - return ignored -> log.info(">>>>> CommandLineRunner1 " + prop.isAutoconf()); + TestSilencerCurseApplication.log("@Bean: testBean1 can inject para, autoconf=" + prop.isAutoconf()); + return ignored -> TestSilencerCurseApplication.log("CommandLineRunner: CommandLineRunner1 "); } @PostConstruct public void postConstruct1() { - log.info(">>>>> postConstruct1 can NOT inject parameter"); + TestSilencerCurseApplication.log("@PostConstruct: postConstruct1"); } @Autowired public void testAutowired1(SilencerEnabledProp prop) { - log.info(">>>>> testAutowired1 can inject parameter autoconf=" + prop.isAutoconf()); + TestSilencerCurseApplication.log("@Autowired: testAutowired1 can inject para, autoconf=" + prop.isAutoconf()); } @PostConstruct public void postConstruct2() { - log.info(">>>>> postConstruct2"); + TestSilencerCurseApplication.log("@PostConstruct: postConstruct2"); } @Bean public CommandLineRunner testBean2(SilencerEnabledProp prop) { - log.info(">>>>> testBean2 autoconf=" + prop.isAutoconf()); - return ignored -> log.info(">>>>> CommandLineRunner2 autoconf=" + prop.isAutoconf()); + TestSilencerCurseApplication.log("@Bean: testBean2 can inject para, autoconf=" + prop.isAutoconf()); + return ignored -> TestSilencerCurseApplication.log("CommandLineRunner: CommandLineRunner2"); } @EventListener public void testApplicationReadyEvent(SpringApplicationEvent event) { - log.info(">>>>> " + event.getClass().getSimpleName() + " timestamp=" + event.getTimestamp()); + TestSilencerCurseApplication.log("@EventListener: " + event.getClass().getSimpleName()); } @Override public void afterPropertiesSet() { - log.info(">>>>> afterPropertiesSet"); + TestSilencerCurseApplication.log("@Override: InitializingBean TestSpringOrderConfiguration"); + } + + @PreDestroy + public void preDestroy() { + TestSilencerCurseApplication.log("@PreDestroy: TestSpringOrderConfiguration"); + } + + @Override + public void destroy() { + TestSilencerCurseApplication.log("@Override: DisposableBean TestSpringOrderConfiguration"); } } diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/TestThisLazyService.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/TestThisLazyService.java similarity index 100% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/TestThisLazyService.java rename to wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/TestThisLazyService.java diff --git a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestSpringOrderService.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestSpringOrderService.java new file mode 100644 index 000000000..a10e793c9 --- /dev/null +++ b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestSpringOrderService.java @@ -0,0 +1,43 @@ +package pro.fessional.wings.silencer.app.service.impl; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import pro.fessional.wings.silencer.app.TestSilencerCurseApplication; + +/** + * @author trydofor + * @since 2024-08-02 + */ +@Service +public class TestSpringOrderService implements InitializingBean, DisposableBean { + + @EventListener + public void testApplicationReadyEvent(ContextClosedEvent event) { + TestSilencerCurseApplication.log("@EventListener: " + event.getClass().getSimpleName()); + } + + @PostConstruct + public void PostConstruct() { + TestSilencerCurseApplication.log("@PostConstruct: TestSpringOrderService"); + } + + @Override + public void afterPropertiesSet() { + TestSilencerCurseApplication.log("@Override: InitializingBean TestSpringOrderService"); + } + + @PreDestroy + public void preDestroy() { + TestSilencerCurseApplication.log("@PreDestroy: TestSpringOrderService"); + } + + @Override + public void destroy() { + TestSilencerCurseApplication.log("@Override: DisposableBean TestSpringOrderService"); + } +} diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceEnhanced.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceEnhanced.java similarity index 100% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceEnhanced.java rename to wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceEnhanced.java diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceItself.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceItself.java similarity index 100% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceItself.java rename to wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceItself.java diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceProxy.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceProxy.java similarity index 100% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceProxy.java rename to wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestThisLazyServiceProxy.java diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/ThisLazyCglibTest.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/enhance/ThisLazyCglibTest.java similarity index 100% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/ThisLazyCglibTest.java rename to wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/enhance/ThisLazyCglibTest.java diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/ThisLazyProxyTest.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/enhance/ThisLazyProxyTest.java similarity index 100% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/ThisLazyProxyTest.java rename to wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/enhance/ThisLazyProxyTest.java diff --git a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/modulate/RuntimeModeTest.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/modulate/RuntimeModeTest.java index 9618fb663..152c03795 100644 --- a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/modulate/RuntimeModeTest.java +++ b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/modulate/RuntimeModeTest.java @@ -15,8 +15,7 @@ public class RuntimeModeTest { @Test @TmsLink("C11011") void isRunMode() { - final RunMode rm = RuntimeMode.getRunMode(); - Assertions.assertEquals(RunMode.Local, rm); + Assertions.assertEquals(RunMode.Local, RuntimeMode.getRunMode()); Assertions.assertFalse(RuntimeMode.isRunMode("")); Assertions.assertFalse(RuntimeMode.isRunMode("localx")); Assertions.assertTrue(RuntimeMode.isRunMode("local")); @@ -24,17 +23,43 @@ void isRunMode() { Assertions.assertTrue(RuntimeMode.isRunMode(" local ")); Assertions.assertTrue(RuntimeMode.isRunMode(" local, ")); Assertions.assertTrue(RuntimeMode.isRunMode(" local, ")); + + Assertions.assertTrue(RuntimeMode.voteRunMode(" local, ")); + Assertions.assertTrue(RuntimeMode.voteRunMode(" !develop, ")); + Assertions.assertTrue(RuntimeMode.voteRunMode(" !develop,!test, ")); + Assertions.assertFalse(RuntimeMode.voteRunMode(" !local, ")); + + new RuntimeMode(RunMode.Test, null) {}; + Assertions.assertEquals(RunMode.Test, RuntimeMode.getRunMode()); + Assertions.assertFalse(RuntimeMode.isRunMode("")); + Assertions.assertFalse(RuntimeMode.isRunMode("testx")); + Assertions.assertTrue(RuntimeMode.isRunMode("test")); + Assertions.assertTrue(RuntimeMode.isRunMode("test ")); + Assertions.assertTrue(RuntimeMode.isRunMode(" test ")); + Assertions.assertTrue(RuntimeMode.isRunMode(" test, ")); + Assertions.assertTrue(RuntimeMode.isRunMode(" test, ")); + + Assertions.assertFalse(RuntimeMode.voteRunMode("")); + Assertions.assertTrue(RuntimeMode.voteRunMode(" test, ")); + Assertions.assertTrue(RuntimeMode.voteRunMode(" !local, ")); + Assertions.assertFalse(RuntimeMode.voteRunMode(" !develop, !test, ")); + Assertions.assertFalse(RuntimeMode.voteRunMode(" !test, ")); + Assertions.assertFalse(RuntimeMode.voteRunMode("!test")); } @Test @TmsLink("C11012") void isApiMode() { - final ApiMode am = RuntimeMode.getApiMode(); - Assertions.assertEquals(ApiMode.Nothing, am); + Assertions.assertEquals(ApiMode.Nothing, RuntimeMode.getApiMode()); Assertions.assertTrue(RuntimeMode.isApiMode("nothing")); Assertions.assertTrue(RuntimeMode.isApiMode("nothing ")); Assertions.assertTrue(RuntimeMode.isApiMode(" nothing ")); Assertions.assertTrue(RuntimeMode.isApiMode(" nothing, ")); Assertions.assertTrue(RuntimeMode.isApiMode(" nothing, ")); + + Assertions.assertTrue(RuntimeMode.voteApiMode(" nothing, ")); + Assertions.assertTrue(RuntimeMode.voteApiMode(" !online, ")); + Assertions.assertTrue(RuntimeMode.voteApiMode(" !online,!sandbox ")); + Assertions.assertFalse(RuntimeMode.voteApiMode(" !nothing ")); } } diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsSilencerMergeTest.java b/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsSilencerMergeTest.java similarity index 100% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsSilencerMergeTest.java rename to wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsSilencerMergeTest.java diff --git a/wings/silencer/src/test/resources/wings-conf/wings-merge-a.properties b/wings/silencer-curse/src/test/resources/wings-conf/wings-merge-a.properties similarity index 100% rename from wings/silencer/src/test/resources/wings-conf/wings-merge-a.properties rename to wings/silencer-curse/src/test/resources/wings-conf/wings-merge-a.properties diff --git a/wings/silencer/src/test/resources/wings-conf/wings-merge-b.properties b/wings/silencer-curse/src/test/resources/wings-conf/wings-merge-b.properties similarity index 100% rename from wings/silencer/src/test/resources/wings-conf/wings-merge-b.properties rename to wings/silencer-curse/src/test/resources/wings-conf/wings-merge-b.properties diff --git a/wings/silencer/src/main/java/pro/fessional/wings/WhoAmI.java b/wings/silencer/src/main/java/pro/fessional/wings/WhoAmI.java index c6d7726bd..f3f1624b9 100644 --- a/wings/silencer/src/main/java/pro/fessional/wings/WhoAmI.java +++ b/wings/silencer/src/main/java/pro/fessional/wings/WhoAmI.java @@ -43,7 +43,7 @@ public static void main(String[] args) throws Exception { buff.append("\n\n## Detailed Description\n"); buff.append("\n\n## Possible Solution\n"); - String url = "https://github.com/trydofor/pro.fessional.wings/issues/new?body=" + + String url = "https://github.com/trydofor/professional-wings/issues/new?body=" + URLEncoder.encode(buff.toString(), StandardCharsets.UTF_8) .replace("+", "%20") .replace("*", "%2A"); diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/TypeSugar.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/TypeSugar.java deleted file mode 100644 index efd8bd97b..000000000 --- a/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/TypeSugar.java +++ /dev/null @@ -1,120 +0,0 @@ -package pro.fessional.wings.silencer.enhance; - -import org.jetbrains.annotations.NotNull; -import org.springframework.core.ResolvableType; -import org.springframework.core.convert.TypeDescriptor; -import pro.fessional.mirana.lock.ArrayKey; - -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.concurrent.ConcurrentHashMap; - -/** - * ResolvableType for Class with Generics with cache, - * 20 times faster than new ResolvableType, TypeDescriptor - * - * @author trydofor - * @since 2024-06-09 - */ -public class TypeSugar { - - public static final TypeDescriptor StringDescriptor = TypeDescriptor.valueOf(String.class); - public static final TypeDescriptor BooleanDescriptor = TypeDescriptor.valueOf(Boolean.class); - public static final TypeDescriptor IntegerDescriptor = TypeDescriptor.valueOf(Integer.class); - public static final TypeDescriptor LongDescriptor = TypeDescriptor.valueOf(Long.class); - public static final TypeDescriptor DoubleDescriptor = TypeDescriptor.valueOf(Double.class); - public static final TypeDescriptor FloatDescriptor = TypeDescriptor.valueOf(Float.class); - public static final TypeDescriptor BigDecimalDescriptor = TypeDescriptor.valueOf(BigDecimal.class); - - public static final TypeDescriptor LocalDateDescriptor = TypeDescriptor.valueOf(LocalDate.class); - public static final TypeDescriptor LocalTimeDescriptor = TypeDescriptor.valueOf(LocalTime.class); - public static final TypeDescriptor LocalDateTimeDescriptor = TypeDescriptor.valueOf(LocalDateTime.class); - public static final TypeDescriptor ZonedDateTimeDescriptor = TypeDescriptor.valueOf(ZonedDateTime.class); - public static final TypeDescriptor OffsetDateTimeDescriptor = TypeDescriptor.valueOf(OffsetDateTime.class); - public static final TypeDescriptor ZoneIdDescriptor = TypeDescriptor.valueOf(ZoneId.class); - - - // - private static final ConcurrentHashMap CacheResolvable = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap CacheDescriptor = new ConcurrentHashMap<>(); - - /** - * by cache - */ - @NotNull - public static TypeDescriptor describe(@NotNull Class clazz, Class... generics) { - Object key = generics == null || generics.length == 0 - ? clazz - : new ArrayKey(clazz, generics); - return CacheDescriptor.computeIfAbsent(key, ignore -> describeNew(clazz, generics)); - } - - /** - * by cache - */ - @NotNull - public static ResolvableType resolve(@NotNull Class clazz, Class... generics) { - Object key = generics == null || generics.length == 0 - ? clazz - : new ArrayKey(clazz, generics); - return CacheResolvable.computeIfAbsent(key, ignore -> resolveNew(clazz, generics)); - } - - /** - * by cache - */ - @NotNull - public static Type type(@NotNull Class clazz, Class... generics) { - return resolve(clazz, generics).getType(); - } - - /** - * no cache - */ - @NotNull - public static TypeDescriptor describeNew(@NotNull Class clazz, Class... generics) { - return new TypeDescriptor(resolveNew(clazz, generics), null, null); - } - - /** - * no cache - */ - @NotNull - public static ResolvableType resolveNew(@NotNull Class clazz, Class... generics) { - if (generics == null || generics.length == 0) return ResolvableType.forClass(clazz); - - final int rootCnt = clazz.getTypeParameters().length; - final ResolvableType[] rootArg = new ResolvableType[rootCnt]; - - int nextIdx = 0; - for (int ri = 0; ri < rootCnt; ri++) { - Class rt = generics[nextIdx++]; - int rc = rt.getTypeParameters().length; - nextIdx = resolve(rt, rootArg, ri, rc, generics, nextIdx); - } - - return ResolvableType.forClassWithGenerics(clazz, rootArg); - } - - private static int resolve(Class rootClz, ResolvableType[] rootArg, int rootIdx, int paraCnt, Class[] nextClz, int nextIdx) { - if (paraCnt <= 0) { - rootArg[rootIdx] = ResolvableType.forClass(rootClz); - } - else { - final ResolvableType[] args = new ResolvableType[paraCnt]; - for (int i = 0; i < paraCnt; i++) { - Class root = nextClz[nextIdx++]; - int c1 = root.getTypeParameters().length; - nextIdx = resolve(root, args, i, c1, nextClz, nextIdx); - } - rootArg[rootIdx] = ResolvableType.forClassWithGenerics(rootClz, args); - } - return nextIdx; - } -} diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerConfiguration.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerConfiguration.java index ba668531b..43468a0d9 100644 --- a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerConfiguration.java +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerConfiguration.java @@ -2,13 +2,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; -import pro.fessional.wings.silencer.enhance.ThisLazyPostProcessor; import pro.fessional.wings.silencer.message.MessageSourceHelper; import pro.fessional.wings.silencer.runner.ApplicationInspectRunner; import pro.fessional.wings.silencer.runner.ApplicationRunnerOrdered; @@ -38,14 +36,6 @@ public static WingsReorderProcessor wingsReorderProcessor() { return new WingsReorderProcessor(); } - - @Bean - @ConditionalWingsEnabled - public static BeanPostProcessor thisLazyAwarePostProcessor() { - log.info("Silencer spring-auto thisLazyAwarePostProcessor"); - return new ThisLazyPostProcessor(); - } - /** * @link Internationalization * @see org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerRunnerConfiguration.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerRunnerConfiguration.java index e5acf5a5f..73136a876 100644 --- a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerRunnerConfiguration.java +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/bean/SilencerRunnerConfiguration.java @@ -2,12 +2,14 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jetbrains.annotations.NotNull; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; -import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; import pro.fessional.wings.silencer.runner.ApplicationReadyEventRunner; import pro.fessional.wings.silencer.runner.ApplicationStartedEventRunner; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; @@ -27,11 +29,11 @@ public class SilencerRunnerConfiguration { private static final Log log = LogFactory.getLog(SilencerRunnerConfiguration.class); - @Configuration(proxyBeanMethods = false) + @Component @ConditionalWingsEnabled - public static class ReadyEvent { - @EventListener - public void on(ApplicationReadyEvent event) { + public static class ReadyEvent implements ApplicationListener { + @Override + public void onApplicationEvent(@NotNull ApplicationReadyEvent event) { final ConfigurableApplicationContext context = event.getApplicationContext(); final Map beans = context.getBeansOfType(ApplicationReadyEventRunner.class); if (beans.isEmpty()) { @@ -54,12 +56,12 @@ public void on(ApplicationReadyEvent event) { } } - @Configuration(proxyBeanMethods = false) + @Component @ConditionalWingsEnabled - public static class StartedEvent { - @EventListener - public void on(ApplicationStartedEvent startedEvent) { - final ConfigurableApplicationContext context = startedEvent.getApplicationContext(); + public static class StartedEvent implements ApplicationListener { + @Override + public void onApplicationEvent(@NotNull ApplicationStartedEvent event) { + final ConfigurableApplicationContext context = event.getApplicationContext(); final Map beans = context.getBeansOfType(ApplicationStartedEventRunner.class); if (beans.isEmpty()) { log.info("===>>> Silencer applicationStartedEventRunner empty"); diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/help/ApplicationContextHelper.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/help/ApplicationContextHelper.java index 0a75402c8..2e668152c 100644 --- a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/help/ApplicationContextHelper.java +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/help/ApplicationContextHelper.java @@ -5,6 +5,7 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.ResolvableType; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; @@ -14,6 +15,7 @@ import pro.fessional.mirana.best.Param; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -32,10 +34,19 @@ public class ApplicationContextHelper { private static ConfigurableApplicationContext context; private static ConfigurableEnvironment environment; + private static boolean helperPrepared = false; - protected ApplicationContextHelper(ConfigurableApplicationContext ctx) { + protected ApplicationContextHelper(@NotNull ConfigurableApplicationContext ctx) { context = Objects.requireNonNull(ctx); environment = Objects.requireNonNull(ctx.getEnvironment()); + helperPrepared = true; + } + + /** + * whether this helper is prepared + */ + public static boolean isPrepared() { + return helperPrepared; } /** @@ -173,6 +184,11 @@ public static ObjectProvider getBeanProvider(Class type) { return context.getBeanProvider(type); } + @NotNull + public static ObjectProvider getBeanProvider(ResolvableType type) { + return context.getBeanProvider(type); + } + @NotNull public static SingletonSupplier getSingletonSupplier(Class type) { return SingletonSupplier.of(() -> context.getBean(type)); @@ -183,6 +199,44 @@ public static SingletonSupplier getSingletonSupplier(Class type, Suppl return SingletonSupplier.of(() -> context.getBeanProvider(type).getIfAvailable(elze)); } + /** + * SingletonSupplier of ordered bean list + */ + @NotNull + public static SingletonSupplier> getSingletonSupplierOfList(Class type) { + return getSingletonSupplierOfList(type, Collections::emptyList); + } + + /** + * SingletonSupplier of ordered bean list, elze if empty + */ + @NotNull + public static SingletonSupplier> getSingletonSupplierOfList(Class type, Supplier> elze) { + return SingletonSupplier.of(() -> { + var lst = context.getBeanProvider(type).orderedStream().toList(); + return lst.isEmpty() ? elze.get() : lst; + }); + } + + /** + * SingletonSupplier of bean and its name map + */ + @NotNull + public static SingletonSupplier> getSingletonSupplierOfMap(Class type) { + return getSingletonSupplierOfMap(type, Collections::emptyMap); + } + + /** + * SingletonSupplier of bean and its name map, elze if empty + */ + @NotNull + public static SingletonSupplier> getSingletonSupplierOfMap(Class type, Supplier> elze) { + return SingletonSupplier.of(() -> { + var map = context.getBeansOfType(type); + return map.isEmpty() ? elze.get() : map; + }); + } + public static String getMessage(String code, Locale locale, Object... arg) { return context.getMessage(code, arg, locale); } diff --git a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/VersionInfoHelper.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/InspectHelper.java similarity index 86% rename from wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/VersionInfoHelper.java rename to wings/silencer/src/main/java/pro/fessional/wings/silencer/support/InspectHelper.java index eb8132a67..19d438e97 100644 --- a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/VersionInfoHelper.java +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/InspectHelper.java @@ -1,4 +1,4 @@ -package pro.fessional.wings.silencer.spring.help; +package pro.fessional.wings.silencer.support; import org.springframework.boot.info.GitProperties; @@ -6,14 +6,11 @@ import java.time.ZoneId; import java.time.ZonedDateTime; -import static pro.fessional.wings.silencer.datetime.DateTimePattern.FMT_DATE_10; -import static pro.fessional.wings.silencer.datetime.DateTimePattern.FMT_FULL_19Z; - /** * @author trydofor * @since 2024-06-03 */ -public class VersionInfoHelper { +public class InspectHelper { public static String jvmName() { return System.getProperty("java.vm.name"); @@ -67,12 +64,12 @@ public static String buildVersion(GitProperties git) { private static String toDatetime(Instant time) { if (time == null) return null; ZonedDateTime zdt = ZonedDateTime.ofInstant(time, ZoneId.systemDefault()); - return FMT_FULL_19Z.format(zdt); + return zdt.toString(); } private static String toDate(Instant time) { if (time == null) return null; ZonedDateTime zdt = ZonedDateTime.ofInstant(time, ZoneId.systemDefault()); - return FMT_DATE_10.format(zdt); + return zdt.toLocalDate().toString(); } } diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/MetaJsonMaker.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/MetaJsonMaker.java new file mode 100644 index 000000000..b38fef39b --- /dev/null +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/MetaJsonMaker.java @@ -0,0 +1,265 @@ +package pro.fessional.wings.silencer.support; + +import lombok.Data; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ScannedGenericBeanDefinition; +import org.springframework.core.io.Resource; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.core.type.classreading.MetadataReader; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * META-INF/additional-spring-configuration-metadata.json + * + * @author trydofor + * @since 2024-07-30 + */ +public class MetaJsonMaker { + + @Data + public static class Meta { + @NotNull + private final String abs; + @NotNull + private final String name; + @NotNull + private final String root; + @NotNull + private final String claz; + @NotNull + private final String pkg; + + private final boolean bool; + private final boolean conf; + } + + @Data + public static class Proj { + private final String root; + private final Set grp = new TreeSet<>(); + private final List meta = new ArrayList<>(); + } + + /** + * maven compile first + */ + @NotNull + public List scanMeta() throws Exception { + return scanMeta("pro.fessional.wings"); + } + + /** + * maven compile first + */ + @NotNull + public List scanMeta(@NotNull String pkg) throws Exception { + + var scanner = new ClassPathScanningCandidateComponentProvider(false) { + { + addIncludeFilter((mr, ignore) -> mr.getClassMetadata().getClassName().startsWith(pkg)); + } + @Override + protected boolean isCandidateComponent(@NotNull MetadataReader ignore) { + return true; + } + }; + + Set beans = scanner.findCandidateComponents(pkg); + if (beans.isEmpty()) return Collections.emptyList(); + + List result = new ArrayList<>(beans.size()); + + final String cwe = ConditionalWingsEnabled.class.getName(); + final String acn = AutoConfiguration.class.getName(); + final String ccn = Configuration.class.getName(); + final String cbn = Bean.class.getName(); + for (BeanDefinition bd : beans) { + if (!(bd instanceof ScannedGenericBeanDefinition gbd)) continue; + + final String name = gbd.getBeanClassName(); + if (name == null || !name.startsWith(pkg)) continue; + + final String pack = name.substring(0, name.lastIndexOf('.')); + + final Resource res = gbd.getResource(); + if(res == null || !res.isFile()) continue; + + final String tmp = res.getFile().getCanonicalPath(); + final String root = tmp.substring(0, tmp.indexOf("/target/classes")); + + final AnnotationMetadata amd = gbd.getMetadata(); + Map caa = amd.getAnnotationAttributes(cwe, true); + if (caa != null) { + boolean conf = !amd.hasEnclosingClass() && (amd.hasAnnotation(acn) || amd.hasAnnotation(ccn)); + Meta wm = new Meta((String) caa.get("abs"), name, root, name, pack, (Boolean) caa.get("value"), conf); + result.add(wm); + } + + for (var md : amd.getDeclaredMethods()) { + var ban = md.getAnnotationAttributes(cbn, true); + if (ban == null) continue; + + var maa = md.getAnnotationAttributes(cwe, true); + if (maa != null) { + String mdn = name + "." + md.getMethodName(); + Meta wm = new Meta((String) maa.get("abs"), mdn, root, name, pack, (Boolean) maa.get("value"), false); + result.add(wm); + } + } + } + + return result; + } + + @NotNull + public List projMeta(@NotNull List meta) { + if (meta.isEmpty()) return Collections.emptyList(); + Map prjs = new LinkedHashMap<>(); + for (Meta mt : meta) { + Proj prj = prjs.computeIfAbsent(mt.root, Proj::new); + prj.grp.add(mt.pkg); + prj.meta.add(mt); + } + + List list = new ArrayList<>(prjs.values()); + list.sort(Comparator.comparing(Proj::getRoot)); + for (Proj prj : list) { + prj.meta.sort(Comparator.comparing(Meta::getName)); + } + return list; + } + + /** + * configuration-metadata + */ + public void writeMeta(@NotNull List proj) throws IOException { + writeMeta(proj, "additional-spring-configuration-metadata.json"); + } + + /** + * configuration-metadata + */ + public void writeMeta(@NotNull List proj, String json) throws IOException { + int c = 1; + for (Proj prj : proj) { + String file = prj.root + "/src/main/resources/META-INF/" + json; + System.out.printf("\n%2d %s", c++, file); + + try (var fw = new FileWriter(file)) { + fw.write("{"); + + fw.write("\n \"groups\": ["); + int i = prj.grp.size() - 1; + for (String grp : prj.grp) { + fw.write("\n {\"name\": \"wings.enabled."); + fw.write(grp); + fw.write("\"}"); + if (i-- > 0) fw.write(","); + } + fw.write("\n ],"); + + fw.write("\n \"properties\": ["); + i = prj.meta.size() - 1; + Meta pre = null; + for (Meta mt : prj.meta) { + + if (pre != null && !mt.claz.equals(pre.claz)) { + if (mt.conf) { + fw.write('\n'); + } + else if (!mt.pkg.equals(pre.pkg)) { + fw.write('\n'); + } + } + pre = mt; + + if (mt.abs.isEmpty()) { + fw.write("\n {\"name\": \"wings.enabled."); + fw.write(mt.name); + fw.write("\", "); + if (!mt.bool) fw.write("\"defaultValue\": false, "); + fw.write("\"type\": \"java.lang.Boolean\"}"); + } + else { + fw.write("\n {\"name\": \"wings.enabled."); + fw.write(mt.name); + fw.write("\", "); + if (!mt.bool) fw.write("\"defaultValue\": false, "); + fw.write("\"type\": \"java.lang.Boolean\", \"description\": \""); + fw.write(mt.abs); + fw.write(" for short.\"}"); + } + if (i-- > 0) fw.write(","); + } + fw.write("\n ],"); + + fw.write("\n \"hints\": []"); + fw.write("\n}"); + } + } + } + + /** + * observe/docs/src/0-wings/0h-prop-index.md + */ + public void printMeta(@NotNull List proj) { + printMeta(new PrintWriter(System.out), proj, "pro.fessional.wings."); + } + + /** + * observe/docs/src/0-wings/0h-prop-index.md + */ + public void printMeta(@NotNull PrintWriter writer, @NotNull List proj, String omit) { + for (Proj prj : proj) { + File tmp = new File(prj.root); + writer.println("\n### " + tmp.getParentFile().getName() + "/" + tmp.getName()); + boolean uls = true; + String pkg = null; + String nms = null; + for (Meta mt : prj.meta) { + String pg = mt.pkg; + if (pkg == null || !pkg.equals(pg)) { + uls = true; + writer.println("\n#### " + pg.replace(omit, "")); + } + pkg = pg; + + String pd = mt.bool ? "" : " (false)"; + if (!mt.abs.isEmpty()) { + pd = pd + " = " + mt.abs; + } + String nm = mt.name.substring(pg.length() + 1); + if (nms != null && nm.contains(nms)) { + writer.println(" - " + nm.substring(nms.length()) + pd); + } + else { + if (uls) writer.println(); + uls = false; + writer.println("* ." + nm + pd); + nms = nm; + } + } + writer.flush(); + } + writer.println(); + writer.flush(); + } +} diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/PropHelper.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/PropHelper.java new file mode 100644 index 000000000..7eefa9df2 --- /dev/null +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/PropHelper.java @@ -0,0 +1,306 @@ +package pro.fessional.wings.silencer.support; + +import lombok.SneakyThrows; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.ResourceUtils; +import pro.fessional.mirana.data.Null; +import pro.fessional.wings.silencer.spring.help.ApplicationContextHelper; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import static org.springframework.boot.context.config.ConfigDataLocation.OPTIONAL_PREFIX; +import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX; + +/** + * @author trydofor + * @since 2022-06-17 + */ +public class PropHelper { + + public static final String DisabledValue = "-"; + public static final String MaskingValue = "*****"; + + /** + * invalid if null/blank/DisabledValue/MaskingValue + */ + public static boolean invalid(String str) { + return str == null || str.isBlank() || DisabledValue.equals(str) || MaskingValue.equals(str); + } + + /** + * true if not invalid + */ + public static boolean valid(String value) { + return !invalid(value); + } + + /** + * uniq and remove invalid + */ + @NotNull + public static LinkedHashSet onlyValid(Collection values) { + if (values == null) return new LinkedHashSet<>(); + + final LinkedHashSet set = new LinkedHashSet<>(values.size()); + for (String value : values) { + if (valid(value)) set.add(value); + } + + return set; + } + + /** + * remain the entry if its value is valid + */ + @NotNull + public static LinkedHashMap onlyValid(Map values) { + if (values == null) return new LinkedHashMap<>(); + + final LinkedHashMap map = new LinkedHashMap<>(values.size()); + for (Map.Entry en : values.entrySet()) { + final String value = en.getValue(); + if (valid(value)) { + map.put(en.getKey(), value); + } + } + return map; + } + + /** + * if this.value is invalid, then use that.value by key + * + * @param thiz this map + * @param that that map + */ + public static void mergeToInvalid(@NotNull Map thiz, @Nullable Map that) { + if (that == null || that.isEmpty()) return; + + if (thiz.isEmpty()) { + thiz.putAll(that); + } + else { + for (Map.Entry en : thiz.entrySet()) { + final String thisVal = en.getValue(); + if (invalid(thisVal)) { + final String thatVal = that.get(en.getKey()); + en.setValue(thatVal); + } + } + } + } + + /** + * Use 'classpath:' format for ClassPathResource and getURL().toExternalForm() for the rest + * + * @see ResourceUtils + */ + @SneakyThrows + @Contract("!null->!null") + public static String stringResource(Resource resource) { + if (resource == null) return null; + + if (resource instanceof ClassPathResource) { + final String path = ((ClassPathResource) resource).getPath(); + return CLASSPATH_URL_PREFIX + path; + } + return resource.getURL().toExternalForm(); + } + + /** + * make sure prefix `optional:` + */ + @NotNull + public static String prefixOptional(@NotNull String url) { + return url.startsWith(OPTIONAL_PREFIX) + ? url + : OPTIONAL_PREFIX + url; + } + + /** + * remove prefix `optional:`, return elz if no prefix + */ + @Contract("_,!null->!null") + public static String removeOptional(@NotNull String url, String elz) { + boolean ok = false; + while (url.startsWith(OPTIONAL_PREFIX)) { + url = url.substring(9); + ok = true; + } + return ok ? url : elz; + } + + /** + * remove prefix `optional:`, return the input url if no prefix + */ + @NotNull + public static String removeOptional(@NotNull String url) { + return removeOptional(url, url); + } + + /** + * `optional:` prefix will return null if exception. + * use ApplicationContext(if prepared) or DefaultResourceLoader as loader by default. + */ + @Nullable + public static Resource resourceString(String url) { + ResourceLoader resourceLoader = ApplicationContextHelper.isPrepared() ? + ApplicationContextHelper.getContext() : new DefaultResourceLoader(); + return resourceString(url, resourceLoader); + } + + /** + * `optional:` prefix will return null if exception + */ + @Nullable + public static Resource resourceString(String url, @NotNull ResourceLoader resourceLoader) { + if (url == null || url.isBlank()) return null; + + String u1 = removeOptional(url, null); + if (u1 != null) { + try { + return resourceLoader.getResource(u1); + } + catch (Exception e) { + return null; + } + } + else { + return resourceLoader.getResource(url); + } + } + + /** + * parse comma-delimited-list string, no strip item, no drop + */ + @Contract("!null -> !null") + public static String commaString(Object[] items) { + return delimitedString(items, ",", false, false); + } + + /** + * parse comma-delimited-list string, no strip item, no drop + */ + @Contract("!null -> !null") + public static String commaString(Collection items) { + return delimitedString(items, ",", false, false); + } + + @Contract("!null,_,_ -> !null") + public static String commaString(Object[] items, boolean strip, boolean drop) { + return delimitedString(items, ",", strip, drop); + } + + @Contract("!null,_,_ -> !null") + public static String commaString(Collection items, boolean strip, boolean drop) { + return delimitedString(items, ",", strip, drop); + } + + @Contract("!null,_,_,_ -> !null") + public static String delimitedString(Object[] items, String delimiter, boolean strip, boolean drop) { + Collection its = items == null ? null : Arrays.asList(items); + return delimitedString(its, delimiter, strip, drop); + } + + @Contract("!null,_,_,_ -> !null") + public static String delimitedString(Collection items, String delimiter, boolean strip, boolean drop) { + if (items == null) return null; + + StringBuilder sb = new StringBuilder(); + boolean empty = true; + for (Object obj : items) { + if (obj == null) { + if (!drop) { + empty = false; + sb.append(delimiter); + } + continue; + } + + String str = obj.toString(); + if (strip) str = str.strip(); + + if (!(drop && invalid(str))) { + empty = false; + sb.append(delimiter).append(str); + } + } + return empty ? Null.Str : sb.substring(delimiter.length()); + } + + /** + * parse comma-delimited-list string, strip item, drop invalid + */ + @NotNull + public static String[] commaArray(String commaString) { + return commaArray(commaString, true, true); + } + + /** + * parse comma-delimited-list string, whether to strip item, whether to drop invalid + */ + @NotNull + public static String[] commaArray(String commaString, boolean strip, boolean drop) { + List list = delimitedList(commaString, ",", strip, drop); + return list.toArray(Null.StrArr); + } + + /** + * parse comma-delimited-list string, strip item, drop invalid + */ + @NotNull + public static List commaList(String commaString) { + return commaList(commaString, true, true); + } + + /** + * parse comma-delimited-list string, whether to strip item, whether to drop invalid + */ + @NotNull + public static List commaList(String commaString, boolean strip, boolean drop) { + return delimitedList(commaString, ",", strip, drop); + } + + /** + * parse delimiter(comma if empty) delimited-list string, whether to strip item, whether to drop invalid + */ + @NotNull + public static List delimitedList(String delimitedString, String delimiter, boolean strip, boolean drop) { + if (delimitedString == null || delimitedString.isBlank() && drop) return Collections.emptyList(); + + if (delimiter == null || delimiter.isEmpty()) delimiter = ","; + final int len = delimiter.length(); + + List result = new ArrayList<>(); + int offset = 0; + int curIdx; + while ((curIdx = delimitedString.indexOf(delimiter, offset)) != -1) { + addValue(result, delimitedString.substring(offset, curIdx), strip, drop); + offset = curIdx + len; + + } + if (offset <= delimitedString.length()) { + addValue(result, delimitedString.substring(offset), strip, drop); + } + + return result; + } + + private static void addValue(List result, String str, boolean strip, boolean drop) { + if (strip) str = str.strip(); + if (drop && invalid(str)) return; + result.add(str); + } +} diff --git a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/SubclassSpringLoader.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/SubclassGather.java similarity index 95% rename from wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/SubclassSpringLoader.java rename to wings/silencer/src/main/java/pro/fessional/wings/silencer/support/SubclassGather.java index c047f1dc4..0aa231b68 100644 --- a/wings/silencer-curse/src/main/java/pro/fessional/wings/silencer/spring/help/SubclassSpringLoader.java +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/SubclassGather.java @@ -1,4 +1,4 @@ -package pro.fessional.wings.silencer.spring.help; +package pro.fessional.wings.silencer.support; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -24,12 +24,12 @@ * @author trydofor * @since 2020-07-04 */ -public class SubclassSpringLoader { +public class SubclassGather { private final ResourcePatternResolver resourcePatternResolver; private final MetadataReaderFactory metadataReaderFactory; - public SubclassSpringLoader(ResourceLoader resourceLoader) { + public SubclassGather(ResourceLoader resourceLoader) { resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); } diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/TypeSugar.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/TypeSugar.java new file mode 100644 index 000000000..200000a5a --- /dev/null +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/support/TypeSugar.java @@ -0,0 +1,275 @@ +package pro.fessional.wings.silencer.support; + +import jakarta.annotation.Nullable; +import lombok.SneakyThrows; +import org.jetbrains.annotations.NotNull; +import org.springframework.core.ResolvableType; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.util.ClassUtils; +import pro.fessional.mirana.data.Null; +import pro.fessional.mirana.lock.ArrayKey; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +/** + * ResolvableType for Class with Generics with cache, + * 20 times faster than new ResolvableType, TypeDescriptor + * + * @author trydofor + * @since 2024-06-09 + */ +public class TypeSugar { + + public static final TypeDescriptor StringDescriptor = TypeDescriptor.valueOf(String.class); + public static final TypeDescriptor BooleanDescriptor = TypeDescriptor.valueOf(Boolean.class); + public static final TypeDescriptor IntegerDescriptor = TypeDescriptor.valueOf(Integer.class); + public static final TypeDescriptor LongDescriptor = TypeDescriptor.valueOf(Long.class); + public static final TypeDescriptor DoubleDescriptor = TypeDescriptor.valueOf(Double.class); + public static final TypeDescriptor FloatDescriptor = TypeDescriptor.valueOf(Float.class); + public static final TypeDescriptor BigDecimalDescriptor = TypeDescriptor.valueOf(BigDecimal.class); + + public static final TypeDescriptor LocalDateDescriptor = TypeDescriptor.valueOf(LocalDate.class); + public static final TypeDescriptor LocalTimeDescriptor = TypeDescriptor.valueOf(LocalTime.class); + public static final TypeDescriptor LocalDateTimeDescriptor = TypeDescriptor.valueOf(LocalDateTime.class); + public static final TypeDescriptor ZonedDateTimeDescriptor = TypeDescriptor.valueOf(ZonedDateTime.class); + public static final TypeDescriptor OffsetDateTimeDescriptor = TypeDescriptor.valueOf(OffsetDateTime.class); + public static final TypeDescriptor ZoneIdDescriptor = TypeDescriptor.valueOf(ZoneId.class); + + + // + private static final ConcurrentHashMap CacheResolvable = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap CacheDescriptor = new ConcurrentHashMap<>(); + + /** + * by cache + */ + @NotNull + public static TypeDescriptor describe(@NotNull Class clazz, Class... generics) { + Object key = generics == null || generics.length == 0 + ? clazz + : new ArrayKey(clazz, generics); + return CacheDescriptor.computeIfAbsent(key, ignore -> describeNew(clazz, generics)); + } + + /** + * by cache + */ + @NotNull + public static ResolvableType resolve(@NotNull Class clazz, Class... generics) { + Object key = generics == null || generics.length == 0 + ? clazz + : new ArrayKey(clazz, generics); + return CacheResolvable.computeIfAbsent(key, ignore -> resolveNew(clazz, generics)); + } + + /** + * by cache + */ + @NotNull + public static Type type(@NotNull Class clazz, Class... generics) { + return resolve(clazz, generics).getType(); + } + + /** + * no cache + */ + @NotNull + public static TypeDescriptor describeNew(@NotNull Class clazz, Class... generics) { + return new TypeDescriptor(resolveNew(clazz, generics), null, null); + } + + /** + * no cache + */ + @NotNull + public static ResolvableType resolveNew(@NotNull Class clazz, Class... generics) { + final int rootCnt = clazz.getTypeParameters().length; + if (rootCnt == 0) return ResolvableType.forClass(clazz); + + final int paraCnt = generics == null ? 0 : generics.length; + final ResolvableType[] rootArg = new ResolvableType[rootCnt]; + if (paraCnt <= rootCnt) { + for (int i = 0; i < paraCnt; i++) { + rootArg[i] = ResolvableType.forClass(generics[i]); + } + for (int i = paraCnt; i < rootCnt; i++) { + rootArg[i] = ResolvableType.forClass(Object.class); + } + } + else { + int nextIdx = 0; + for (int ri = 0; ri < rootCnt; ri++) { + Class rt = generics[nextIdx++]; + int rc = rt.getTypeParameters().length; + nextIdx = resolve(rt, rootArg, ri, rc, generics, nextIdx); + } + } + + return ResolvableType.forClassWithGenerics(clazz, rootArg); + } + + private static final ConcurrentHashMap ShortLong = new ConcurrentHashMap<>(); + private static final Pattern TypeSplit = Pattern.compile("[,<> ]+"); + + public static boolean shorten(@NotNull Class claz) { + String v1 = ShortLong.putIfAbsent(claz.getSimpleName(), claz.getName()); + return v1 == null; + } + + static { + shorten(void.class); + shorten(boolean.class); + shorten(byte.class); + shorten(char.class); + shorten(short.class); + shorten(int.class); + shorten(long.class); + shorten(float.class); + shorten(double.class); + + shorten(Void.class); + shorten(String.class); + shorten(Object.class); + + shorten(Boolean.class); + shorten(Byte.class); + shorten(Character.class); + shorten(Short.class); + shorten(Integer.class); + shorten(Long.class); + shorten(Float.class); + shorten(Double.class); + shorten(BigDecimal.class); + shorten(BigInteger.class); + + shorten(Date.class); + shorten(ZoneId.class); + shorten(TimeZone.class); + shorten(Locale.class); + shorten(LocalDate.class); + shorten(LocalTime.class); + shorten(LocalDateTime.class); + shorten(ZonedDateTime.class); + shorten(OffsetDateTime.class); + shorten(Instant.class); + + shorten(Collection.class); + shorten(Optional.class); + shorten(List.class); + shorten(ArrayList.class); + shorten(LinkedList.class); + shorten(Map.class); + shorten(HashMap.class); + shorten(TreeMap.class); + shorten(LinkedHashMap.class); + shorten(Set.class); + shorten(HashSet.class); + shorten(TreeSet.class); + + shorten(ConcurrentHashMap.class); + } + + @NotNull + public static String outline(@NotNull ResolvableType type) { + return outline(type, true); + } + + @NotNull + public static String outline(@NotNull ResolvableType type, boolean shorten) { + String st = type.toString(); + st = st.replace("?", "Object"); + + if (shorten) { + for (Map.Entry en : ShortLong.entrySet()) { + st = st.replace(en.getValue(), en.getKey()); + } + } + + return st; + } + + @Nullable + public static ResolvableType resolve(String structs) { + if (structs == null) return null; + return CacheResolvable.computeIfAbsent(structs, ignore -> resolveNew(structs)); + } + + @SneakyThrows + @Nullable + public static ResolvableType resolveNew(String structs) { + if (structs == null || structs.isEmpty()) return null; + + // java.util.Map>, java.lang.String> + String[] pts = TypeSplit.split(structs.trim()); + if (pts.length == 0) return null; + + List> clz = new ArrayList<>(pts.length); + for (String str : pts) { + if (str.isEmpty()) continue; // should not happen + + // java.util.List + if (str.equals("?")) { + clz.add(Object.class); + continue; + } + + final String name; + if (str.endsWith("[]")) { + String sub = str.substring(0, str.length() - 2); + name = ShortLong.getOrDefault(sub, sub) + "[]"; + } + else { + name = ShortLong.getOrDefault(str, str); + } + clz.add(ClassUtils.forName(name, null)); + } + + final int size = clz.size(); + if (size == 0) return null; + if (size == 1) return resolve(clz.get(0)); + Class[] ots = clz.subList(1, size).toArray(Null.ClzArr); + return resolve(clz.get(0), ots); + } + + private static int resolve(Class rootClz, ResolvableType[] rootArg, int rootIdx, int paraCnt, Class[] nextClz, int nextIdx) { + if (paraCnt <= 0) { + rootArg[rootIdx] = ResolvableType.forClass(rootClz); + } + else { + final ResolvableType[] args = new ResolvableType[paraCnt]; + for (int i = 0; i < paraCnt; i++) { + Class root = nextClz[nextIdx++]; + int c1 = root.getTypeParameters().length; + nextIdx = resolve(root, args, i, c1, nextClz, nextIdx); + } + rootArg[rootIdx] = ResolvableType.forClassWithGenerics(rootClz, args); + } + return nextIdx; + } +} diff --git a/wings/silencer/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/silencer/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 588b4deb9..987e50a5d 100644 --- a/wings/silencer/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/silencer/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,18 +1,19 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.conf.SilencerAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerConfiguration.applicationInspectRunner", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerConfiguration.messageSourceHelper", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerConfiguration.wingsReorderProcessor", "type": "java.lang.Boolean", "description": "wings.enabled.silencer.bean-reorder for short."}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerRunnerConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerRunnerConfiguration$ReadyEvent", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerRunnerConfiguration$StartedEvent", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.bean.SilencerRunnerConfiguration$StartedEvent", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.silencer.spring.conf.SilencerAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/TestSilencerApplication.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/TestSilencerApplication.java index 770765d04..0877aff41 100644 --- a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/TestSilencerApplication.java +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/TestSilencerApplication.java @@ -2,15 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import pro.fessional.wings.silencer.app.conf.TestMergingProp; /** * @author trydofor * @since 2019-07-20 */ @SpringBootApplication -@EnableConfigurationProperties(TestMergingProp.class) public class TestSilencerApplication { public static void main(String[] args) { diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/TypeSugarTest.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/TypeSugarTest.java deleted file mode 100644 index d9ceea328..000000000 --- a/wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/TypeSugarTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package pro.fessional.wings.silencer.enhance; - -import io.qameta.allure.TmsLink; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.core.ResolvableType; -import org.springframework.core.convert.TypeDescriptor; - -import java.util.List; -import java.util.Map; - -/** - * @author trydofor - * @since 2024-06-09 - */ -public class TypeSugarTest { - - @Test - @TmsLink("C11035") - void test() { - - // Map - var a0 = ResolvableType.forClassWithGenerics(Map.class, - ResolvableType.forClass(String.class), - ResolvableType.forClassWithGenerics(List.class, Long[].class) - ); - var a1 = TypeSugar.resolve(Map.class, String.class, List.class, Long[].class); - - var a2 = TypeDescriptor.map(Map.class, - TypeDescriptor.valueOf(String.class), - TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)) - ); - var a3 = TypeSugar.describe(Map.class, String.class, List.class, Long[].class); - - Assertions.assertEquals(a0, a1); - Assertions.assertEquals(a2, a3); - - // Map,String> - var b0 = ResolvableType.forClassWithGenerics(Map.class, - ResolvableType.forClassWithGenerics(List.class, Long[].class), - ResolvableType.forClass(String.class) - ); - var b1 = TypeSugar.resolve(Map.class, List.class, Long[].class, String.class); - - var b2 = TypeDescriptor.map(Map.class, - TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)), - TypeDescriptor.valueOf(String.class) - ); - var b3 = TypeSugar.describe(Map.class, List.class, Long[].class, String.class); - - Assertions.assertEquals(b0, b1); - Assertions.assertEquals(b2, b3); - - // Map>,String> - var c0 = ResolvableType.forClassWithGenerics(Map.class, - ResolvableType.forClassWithGenerics(List.class, - ResolvableType.forClassWithGenerics(List.class, Long[].class) - ), - ResolvableType.forClass(String.class) - ); - var c1 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); - - var c2 = TypeDescriptor.map(Map.class, - TypeDescriptor.collection(List.class, - TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)) - ), - TypeDescriptor.valueOf(String.class) - ); - var c3 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); - - Assertions.assertEquals(c0, c1); - Assertions.assertEquals(c2, c3); - - var d0 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); - var d1 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); - var d2 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); - var d3 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); - Assertions.assertSame(d0, d1); - Assertions.assertSame(d2, d3); - } -} \ No newline at end of file diff --git a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/spring/help/VersionInfoHelperTest.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/InspectHelperTest.java similarity index 58% rename from wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/spring/help/VersionInfoHelperTest.java rename to wings/silencer/src/test/java/pro/fessional/wings/silencer/support/InspectHelperTest.java index 361dbc47e..d554a25e3 100644 --- a/wings/silencer-curse/src/test/java/pro/fessional/wings/silencer/spring/help/VersionInfoHelperTest.java +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/InspectHelperTest.java @@ -1,4 +1,4 @@ -package pro.fessional.wings.silencer.spring.help; +package pro.fessional.wings.silencer.support; import io.qameta.allure.TmsLink; import lombok.Setter; @@ -17,7 +17,7 @@ */ @SpringBootTest @Slf4j -class VersionInfoHelperTest { +class InspectHelperTest { @Setter(onMethod_ = { @Autowired }) private BuildProperties buildProperties; @@ -34,7 +34,7 @@ public void infoBuildAndGit() { log.info("git={}", gitProperties); Assertions.assertEquals("pro.fessional.wings",buildProperties.getGroup()); - Assertions.assertEquals("silencer-curse",buildProperties.getArtifact()); + Assertions.assertEquals("silencer",buildProperties.getArtifact()); Assertions.assertNotNull(gitProperties.getBranch()); // spring use git.commit.id, need commitIdGenerationMode=flat, but wings use full Assertions.assertNull(gitProperties.getCommitId()); @@ -42,17 +42,17 @@ public void infoBuildAndGit() { Assertions.assertNotNull(gitProperties.getCommitTime()); // safe and format - Assertions.assertNotNull(VersionInfoHelper.jvmName()); - Assertions.assertNotNull(VersionInfoHelper.jvmVersion()); - Assertions.assertNotNull(VersionInfoHelper.jvmVendor()); - Assertions.assertNotNull(VersionInfoHelper.commitIdShort(gitProperties)); - Assertions.assertNotNull(VersionInfoHelper.commitId(gitProperties)); - Assertions.assertNotNull(VersionInfoHelper.commitDate(gitProperties)); - Assertions.assertNotNull(VersionInfoHelper.commitDateTime(gitProperties)); - Assertions.assertNotNull(VersionInfoHelper.commitMessage(gitProperties)); - Assertions.assertNotNull(VersionInfoHelper.branch(gitProperties)); - Assertions.assertNotNull(VersionInfoHelper.buildDate(gitProperties)); - Assertions.assertNotNull(VersionInfoHelper.buildDateTime(gitProperties)); - Assertions.assertNotNull(VersionInfoHelper.buildVersion(gitProperties)); + Assertions.assertNotNull(InspectHelper.jvmName()); + Assertions.assertNotNull(InspectHelper.jvmVersion()); + Assertions.assertNotNull(InspectHelper.jvmVendor()); + Assertions.assertNotNull(InspectHelper.commitIdShort(gitProperties)); + Assertions.assertNotNull(InspectHelper.commitId(gitProperties)); + Assertions.assertNotNull(InspectHelper.commitDate(gitProperties)); + Assertions.assertNotNull(InspectHelper.commitDateTime(gitProperties)); + Assertions.assertNotNull(InspectHelper.commitMessage(gitProperties)); + Assertions.assertNotNull(InspectHelper.branch(gitProperties)); + Assertions.assertNotNull(InspectHelper.buildDate(gitProperties)); + Assertions.assertNotNull(InspectHelper.buildDateTime(gitProperties)); + Assertions.assertNotNull(InspectHelper.buildVersion(gitProperties)); } } \ No newline at end of file diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/PropHelperTest.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/PropHelperTest.java new file mode 100644 index 000000000..477f9d409 --- /dev/null +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/PropHelperTest.java @@ -0,0 +1,69 @@ +package pro.fessional.wings.silencer.support; + +import io.qameta.allure.TmsLink; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +import java.util.List; + +/** + * @author trydofor + * @since 2024-06-28 + */ +@SpringBootTest +class PropHelperTest { + + @Test + @TmsLink("C11036") + void testCommaList() { + Assertions.assertTrue(PropHelper.commaList("").isEmpty()); + Assertions.assertTrue(PropHelper.commaList(",").isEmpty()); + Assertions.assertTrue(PropHelper.commaList(",,").isEmpty()); + Assertions.assertEquals(List.of("1", "2"), PropHelper.commaList("1,,2")); + Assertions.assertEquals(List.of("1", "2"), PropHelper.commaList("1 , - , , 2 ")); + + Assertions.assertEquals(List.of(""), PropHelper.commaList("", false, false)); + Assertions.assertEquals(List.of("", ""), PropHelper.commaList(",", false, false)); + Assertions.assertEquals(List.of("", "", ""), PropHelper.commaList(",,", false, false)); + Assertions.assertEquals(List.of("1", "", "2"), PropHelper.commaList("1,,2", false, false)); + Assertions.assertEquals(List.of("1 ", " ", " 2 "), PropHelper.commaList("1 , , 2 ", false, false)); + + Assertions.assertNull(PropHelper.commaString((String[]) null)); + Assertions.assertEquals("", PropHelper.commaString(List.of(""))); + Assertions.assertEquals(",", PropHelper.commaString(List.of("",""))); + Assertions.assertEquals(",,1", PropHelper.commaString(List.of("", "", "1"))); + Assertions.assertEquals(",,1 ", PropHelper.commaString(List.of("", "", "1 "))); + Assertions.assertEquals(",,1 ,-", PropHelper.commaString(List.of("", "", "1 ", "-"))); + + Assertions.assertEquals("", PropHelper.commaString(List.of(""), true, true)); + Assertions.assertEquals("", PropHelper.commaString(List.of("", ""), true, true)); + Assertions.assertEquals("1", PropHelper.commaString(List.of("", "", "1"), true, true)); + Assertions.assertEquals("1", PropHelper.commaString(List.of("", "", "1 "), true, true)); + Assertions.assertEquals("1", PropHelper.commaString(List.of("", "", "1 ", "- "), true, true)); + Assertions.assertEquals("1 ,- ", PropHelper.commaString(List.of("", "", "1 ", "- "), false, true)); + } + + @Test + @TmsLink("C11037") + void testResourceString() { + ClassPathResource app = new ClassPathResource("application.properties"); + Assertions.assertTrue(app.exists()); + String res1 = PropHelper.stringResource(app); + Assertions.assertEquals("classpath:application.properties", res1); + + Resource res2 = PropHelper.resourceString("classpath:application.properties"); + Assertions.assertTrue(res2.exists()); + + Resource res3 = PropHelper.resourceString("optional:classpath:application.properties"); + Assertions.assertTrue(res3.exists()); + + Resource res4 = PropHelper.resourceString("optional:classpath:application.properties-404"); + Assertions.assertFalse(res4.exists()); + + Resource res5 = PropHelper.resourceString("optional:file:./application.properties-404"); + Assertions.assertFalse(res5.exists()); + } +} \ No newline at end of file diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/TypeSugarMain.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/TypeSugarMain.java similarity index 96% rename from wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/TypeSugarMain.java rename to wings/silencer/src/test/java/pro/fessional/wings/silencer/support/TypeSugarMain.java index bb12c9acc..e5bc1f527 100644 --- a/wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/TypeSugarMain.java +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/TypeSugarMain.java @@ -1,4 +1,4 @@ -package pro.fessional.wings.silencer.enhance; +package pro.fessional.wings.silencer.support; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -11,6 +11,7 @@ import org.openjdk.jmh.annotations.Warmup; import org.springframework.core.ResolvableType; import org.springframework.core.convert.TypeDescriptor; +import pro.fessional.wings.silencer.support.TypeSugar; import java.io.IOException; import java.util.List; diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/TypeSugarTest.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/TypeSugarTest.java new file mode 100644 index 000000000..0799fe419 --- /dev/null +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/support/TypeSugarTest.java @@ -0,0 +1,136 @@ +package pro.fessional.wings.silencer.support; + +import io.qameta.allure.TmsLink; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.core.ResolvableType; +import org.springframework.core.convert.TypeDescriptor; +import pro.fessional.mirana.data.R; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author trydofor + * @since 2024-06-09 + */ +@Slf4j +public class TypeSugarTest { + + public static class Inner { + + } + + @Test + @TmsLink("C11035") + void test() throws ClassNotFoundException { + + // Map + var a0 = ResolvableType.forClassWithGenerics(Map.class, + ResolvableType.forClass(String.class), + ResolvableType.forClassWithGenerics(List.class, Long[].class) + ); + var a1 = TypeSugar.resolve(Map.class, String.class, List.class, Long[].class); + log.info("type a1={}", a1); + + var a2 = TypeDescriptor.map(Map.class, + TypeDescriptor.valueOf(String.class), + TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)) + ); + var a3 = TypeSugar.describe(Map.class, String.class, List.class, Long[].class); + + log.info("type a3={}", a3); + Assertions.assertEquals(a0, a1); + Assertions.assertEquals(a2, a3); + + // Map,String> + var b0 = ResolvableType.forClassWithGenerics(Map.class, + ResolvableType.forClassWithGenerics(List.class, Long[].class), + ResolvableType.forClass(String.class) + ); + var b1 = TypeSugar.resolve(Map.class, List.class, Long[].class, String.class); + log.info("type b1={}", b1); + + var b2 = TypeDescriptor.map(Map.class, + TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)), + TypeDescriptor.valueOf(String.class) + ); + var b3 = TypeSugar.describe(Map.class, List.class, Long[].class, String.class); + + log.info("type b3={}", b3); + Assertions.assertEquals(b0, b1); + Assertions.assertEquals(b2, b3); + + // Map>,String> + var c0 = ResolvableType.forClassWithGenerics(Map.class, + ResolvableType.forClassWithGenerics(List.class, + ResolvableType.forClassWithGenerics(List.class, Long[].class) + ), + ResolvableType.forClass(String.class) + ); + var c1 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); + log.info("type c1={}", c1); + + var c2 = TypeDescriptor.map(Map.class, + TypeDescriptor.collection(List.class, + TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)) + ), + TypeDescriptor.valueOf(String.class) + ); + var c3 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); + + log.info("type c3={}", c3); + Assertions.assertEquals(c0, c1); + Assertions.assertEquals(c2, c3); + + var d0 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); + var d1 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); + var d2 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); + var d3 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); + + log.info("type d1={}", d1); + log.info("type d3={}", d3); + Assertions.assertSame(d0, d1); + Assertions.assertSame(d2, d3); + + // + testStructs(TypeSugar.resolve(boolean.class)); + testStructs(TypeSugar.resolve(boolean[].class)); + testStructs(TypeSugar.resolve(byte.class)); + testStructs(TypeSugar.resolve(byte[].class)); + testStructs(TypeSugar.resolve(char.class)); + testStructs(TypeSugar.resolve(char[].class)); + testStructs(TypeSugar.resolve(short.class)); + testStructs(TypeSugar.resolve(short[].class)); + testStructs(TypeSugar.resolve(int.class)); + testStructs(TypeSugar.resolve(int[].class)); + testStructs(TypeSugar.resolve(long.class)); + testStructs(TypeSugar.resolve(long[].class)); + testStructs(TypeSugar.resolve(float.class)); + testStructs(TypeSugar.resolve(float[].class)); + testStructs(TypeSugar.resolve(double.class)); + testStructs(TypeSugar.resolve(double[].class)); + testStructs(TypeSugar.resolve(String.class)); + testStructs(TypeSugar.resolve(String[].class)); + + testStructs(a0); + testStructs(b0); + testStructs(c0); + testStructs(d0); + testStructs(TypeSugar.resolve(R.class, Inner.class)); + testStructs(TypeSugar.resolve(R.class)); + testStructs(TypeSugar.resolve(List.class)); + testStructs(TypeSugar.resolve(Map.class)); + testStructs(TypeSugar.resolve(Set.class, LocalDate.class)); + } + + private void testStructs(ResolvableType rt) { + String str = TypeSugar.outline(rt); + log.info("structs={}", str); + ResolvableType rt1 = TypeSugar.resolve(str); + Assertions.assertEquals(rt, rt1); + } +} \ No newline at end of file diff --git a/wings/slardar-hazel-caching/src/main/java/pro/fessional/wings/slardar/cache/hazelcast/WingsHazelcastCacheCustomizer.java b/wings/slardar-hazel-caching/src/main/java/pro/fessional/wings/slardar/cache/hazelcast/WingsHazelcastCacheCustomizer.java index d4aa6b3c9..bc0733cbf 100644 --- a/wings/slardar-hazel-caching/src/main/java/pro/fessional/wings/slardar/cache/hazelcast/WingsHazelcastCacheCustomizer.java +++ b/wings/slardar-hazel-caching/src/main/java/pro/fessional/wings/slardar/cache/hazelcast/WingsHazelcastCacheCustomizer.java @@ -75,6 +75,7 @@ private void checkMapConf(Config config, Map mapCnf, String n } } catch (InvalidConfigurationException e) { + // noinspection StringConcatenationArgumentToLogCall log.error("failed to change MapConfig, name=" + name, e); } } diff --git a/wings/slardar-hazel-caching/src/main/java/pro/fessional/wings/slardar/spring/bean/HazelcastServiceConfiguration.java b/wings/slardar-hazel-caching/src/main/java/pro/fessional/wings/slardar/spring/bean/HazelcastServiceConfiguration.java index 82b46bec7..170c766be 100644 --- a/wings/slardar-hazel-caching/src/main/java/pro/fessional/wings/slardar/spring/bean/HazelcastServiceConfiguration.java +++ b/wings/slardar-hazel-caching/src/main/java/pro/fessional/wings/slardar/spring/bean/HazelcastServiceConfiguration.java @@ -32,7 +32,7 @@ public static class GlobalPublisherWired { @Autowired public void auto(HazelcastInstance instance, ApplicationEventPublisher publisher) { HazelcastSyncPublisher global = new HazelcastSyncPublisher(instance, publisher); - EventPublishHelper.setGlobalPublisher(global); + EventPublishHelper.prepareGlobalPublisher(global); log.info("SlardarHazelCaching spring-auto initHazelcastSyncPublisher, uuid=" + global.getMessageListenerUuid()); } } diff --git a/wings/slardar-hazel-caching/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/slardar-hazel-caching/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 0a5adea4f..5f8e3ed5a 100644 --- a/wings/slardar-hazel-caching/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/slardar-hazel-caching/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,21 +1,24 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarCacheAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastConfigConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastConfigConfiguration.wingsHazelcastAloneCustomizer", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.hazelcast-standalone for short."}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastConfigConfiguration.wingsHazelcastAloneCustomizer", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.slardar.hazelcast-standalone for short."}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastConfigConfiguration.wingsHazelcastCacheCustomizer", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastConfigConfiguration.wingsHazelcastGlobalSerializer", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastFacelessConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastFacelessConfiguration.flakeIdService", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastFacelessConfiguration.hzLightIdProvider", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastServiceConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastServiceConfiguration$FlakeIdServiceBean", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastServiceConfiguration$GlobalPublisherWired", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastServiceConfiguration.hazelcastCacheManager", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastServiceConfiguration.hazelcastGlobalLock", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.HazelcastServiceConfiguration.hzLightIdProvider", "type": "java.lang.Boolean"} + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarHazelcastAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/slardar-hazel-session/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarHazelSessionConfiguration.java b/wings/slardar-hazel-session/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarHazelSessionConfiguration.java index aa977640b..2e5ac9bed 100644 --- a/wings/slardar-hazel-session/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarHazelSessionConfiguration.java +++ b/wings/slardar-hazel-session/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarHazelSessionConfiguration.java @@ -39,10 +39,10 @@ public SpringSessionBackedSessionRegistry sessionRegistry(FindByIndexNameSess public HazelcastSessionHelper wingsSessionHelper( FindByIndexNameSessionRepository sessionRepository, HazelcastInstance hazelcastInstance, - @Value("${spring.session.hazelcast.map-name:spring:session:sessions}") String mapName) { + @Value("${spring.session.hazelcast.map-name:spring:session:sessions}") String sessionMapName) { log.info("SlardarHazelSession spring-bean wingsSessionHelper"); - return new HazelcastSessionHelper(sessionRepository, hazelcastInstance, mapName); + return new HazelcastSessionHelper(sessionRepository, hazelcastInstance, sessionMapName); } /** diff --git a/wings/slardar-hazel-session/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/slardar-hazel-session/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 175254ad1..7359a9b73 100644 --- a/wings/slardar-hazel-session/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/slardar-hazel-session/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,14 +1,15 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarHazelSessionAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarHazelSessionConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarHazelSessionConfiguration.sessionRegistry", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarHazelSessionConfiguration.wingsSessionHelper", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarHazelSessionConfiguration.wingsHazelcastSessionSerializer", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarHazelSessionConfiguration.wingsSessionHelper", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarHazelSessionAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/actuator/cache/SlardarCacheEndpoint.java b/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/actuator/cache/SlardarCacheEndpoint.java index 1a706650c..8176ed1a8 100644 --- a/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/actuator/cache/SlardarCacheEndpoint.java +++ b/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/actuator/cache/SlardarCacheEndpoint.java @@ -45,10 +45,10 @@ public SlardarCacheEndpoint(Map cm) { final String k = en.getKey(); if (v instanceof State) { cacheManagers.put(k, v); - log.info("init CacheManager with State, bean=" + k); + log.info("init CacheManager with State, bean={}", k); } else { - log.info("skip CacheManager without State, bean=" + k); + log.info("skip CacheManager without State, bean={}", k); } } } diff --git a/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/spring/help/SecurityConfigHelper.java b/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/spring/help/SecurityConfigHelper.java index 0097dfe29..d81f80d40 100644 --- a/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/spring/help/SecurityConfigHelper.java +++ b/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/spring/help/SecurityConfigHelper.java @@ -8,7 +8,8 @@ import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.util.pattern.PathPattern; import pro.fessional.wings.slardar.security.WingsUserDetailsService; -import pro.fessional.wings.slardar.servlet.request.FakeHttpServletRequest; +import pro.fessional.wings.slardar.servlet.dummy.DummyHttpServletRequest; +import pro.fessional.wings.slardar.servlet.dummy.DummyServletContext; import pro.fessional.wings.slardar.spring.conf.WingsBindAuthnConfigurer; import java.net.URLEncoder; @@ -73,9 +74,9 @@ public static String encodePathPattern(@NotNull String path) { * fake the HttpServletRequest to test matcher */ @NotNull - public static FakeHttpServletRequest fakeMatcherRequest(@NotNull String path) { + public static DummyHttpServletRequest dummyMatcherRequest(@NotNull String path) { path = encodePathPattern(path); - FakeHttpServletRequest request = new FakeHttpServletRequest(); + DummyHttpServletRequest request = new DummyHttpServletRequest(); request.setPathInfo(path); request.setRequestURI(path); return request; @@ -86,11 +87,12 @@ public static FakeHttpServletRequest fakeMatcherRequest(@NotNull String path) { * see Failed to find servlet xx in the servlet context */ @NotNull - public static FakeHttpServletRequest fakeMatcherRequest(@NotNull String path, String servletName) { + public static DummyHttpServletRequest dummyMatcherRequest(@NotNull String path, String servletName) { path = encodePathPattern(path); - FakeHttpServletRequest request = new FakeHttpServletRequest(); + DummyHttpServletRequest request = new DummyHttpServletRequest(); request.setPathInfo(path); request.setRequestURI(path); + request.setServletContext(new DummyServletContext()); if (servletName != null) { request.getHttpServletMapping().setServletName(servletName); } diff --git a/wings/slardar-sprint/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/slardar-sprint/src/main/resources/META-INF/additional-spring-configuration-metadata.json index c78e60017..64bad0b0f 100644 --- a/wings/slardar-sprint/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/slardar-sprint/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,31 +1,30 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarBootAdminAutoConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarActuatorConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarActuatorConfiguration.slardarCacheManageEndpoint", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarBootAdminClientConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarBootAdminClientConfiguration.registrationClient", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarBootAdminServerConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarBootAdminServerConfiguration.basicAuthHttpHeadersProvider", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarBootAdminServerConfiguration.bootAdminMappingOrderPostProcessor", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarBootAdminServerConfiguration.dingTalkNotifier", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarSprintAutoConfiguration", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarActuatorConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarActuatorConfiguration.slardarCacheManageEndpoint", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarSecurityConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarSecurityConfiguration.httpSessionEventPublisher", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarSecurityConfiguration.localeContextHolderTerminalContextListener", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarSecurityConfiguration.passsaltEncoder", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarSecurityConfiguration.passwordEncoder", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarSecurityConfiguration.terminalContextListenerRunner", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarSecurityConfiguration.wingsSecBeanInitConfigurer", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarSecurityConfiguration.wingsSecBeanInitConfigurer", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarBootAdminAutoConfiguration", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarSprintAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/concur/impl/RighterInterceptor.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/concur/impl/RighterInterceptor.java index 20b0ade4b..3d17a89b0 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/concur/impl/RighterInterceptor.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/concur/impl/RighterInterceptor.java @@ -110,6 +110,7 @@ public boolean preHandle(@NotNull HttpServletRequest request, return true; } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.info("failed to deserialize. session=" + session + ", audit=" + audit, e); responseError(response); return false; diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/domainx/DefaultDomainRequestMatcher.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/domainx/DefaultDomainRequestMatcher.java index 89dc3f101..4ec2a8c25 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/domainx/DefaultDomainRequestMatcher.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/domainx/DefaultDomainRequestMatcher.java @@ -86,6 +86,7 @@ public HttpServletRequest match(HttpServletRequest request, String domain) { } } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.warn("failed to getHandler in HandlerMapping=" + hm.getClass(), e); } } diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/cookie/CookieRequestWrapper.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/cookie/CookieRequestWrapper.java index 0b1c3d66b..e5e07992d 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/cookie/CookieRequestWrapper.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/cookie/CookieRequestWrapper.java @@ -38,6 +38,7 @@ public Cookie[] getCookies() { cookies[i] = reader.apply(ck); } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.info("failed to wrap cookie=" + ck, e); } diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/cookie/impl/WingsCookieInterceptorImpl.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/cookie/impl/WingsCookieInterceptorImpl.java index e79353b5a..e05ff9a81 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/cookie/impl/WingsCookieInterceptorImpl.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/cookie/impl/WingsCookieInterceptorImpl.java @@ -17,7 +17,7 @@ import java.util.Map; import java.util.Set; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.notValue; +import static pro.fessional.wings.silencer.support.PropHelper.invalid; /** * Designed for non-runtime tuning, so no write protection is provided. @@ -205,7 +205,7 @@ public void addAlias(Map alias) { for (Map.Entry en : alias.entrySet()) { final String k = en.getKey(); final String v = en.getValue(); - if (k.equals(v) || notValue(v)) { + if (k.equals(v) || invalid(v)) { continue; } aliasEnc.put(k, v); diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/request/FakeHttpServletRequest.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/dummy/DummyHttpServletRequest.java similarity index 98% rename from wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/request/FakeHttpServletRequest.java rename to wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/dummy/DummyHttpServletRequest.java index 8aad9698d..0f843139e 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/request/FakeHttpServletRequest.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/dummy/DummyHttpServletRequest.java @@ -1,4 +1,4 @@ -package pro.fessional.wings.slardar.servlet.request; +package pro.fessional.wings.slardar.servlet.dummy; import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; @@ -39,7 +39,7 @@ */ @Getter @Setter -public class FakeHttpServletRequest implements HttpServletRequest { +public class DummyHttpServletRequest implements HttpServletRequest { private String authType; private Cookie[] cookies; diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/dummy/DummyServletContext.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/dummy/DummyServletContext.java new file mode 100644 index 000000000..2360e4b55 --- /dev/null +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/dummy/DummyServletContext.java @@ -0,0 +1,283 @@ +package pro.fessional.wings.slardar.servlet.dummy; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterRegistration; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.Servlet; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRegistration; +import jakarta.servlet.SessionCookieConfig; +import jakarta.servlet.SessionTrackingMode; +import jakarta.servlet.descriptor.JspConfigDescriptor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.util.ClassUtils; + +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.EventListener; +import java.util.Map; +import java.util.Set; + +/** + * @author trydofor + * @since 2024-08-06 + */ +@Setter +@Getter +public class DummyServletContext implements ServletContext { + + private String contextPath = ""; + private int majorVersion = 6; + private int minorVersion = 0; + private int effectiveMajorVersion = 6; + private int effectiveMinorVersion = 0; + private String mimeType = "text"; + private String servletContextName = ""; + + @Override + public ServletContext getContext(String uripath) { + return this; + } + + @Override + public String getMimeType(String file) { + return mimeType; + } + + @Override + public Set getResourcePaths(String path) { + return Collections.emptySet(); + } + + @Override + public URL getResource(String path) { + return null; + } + + @Override + public InputStream getResourceAsStream(String path) { + return null; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public RequestDispatcher getNamedDispatcher(String name) { + return null; + } + + @Override + public void log(String msg) { + } + + @Override + public void log(String message, Throwable throwable) { + } + + @Override + public String getRealPath(String path) { + return path; + } + + @Override + public String getServerInfo() { + return this.getClass().getSimpleName(); + } + + @Override + public String getInitParameter(String name) { + return ""; + } + + @Override + public Enumeration getInitParameterNames() { + return Collections.emptyEnumeration(); + } + + @Override + public boolean setInitParameter(String name, String value) { + return false; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return Collections.emptyEnumeration(); + } + + @Override + public void setAttribute(String name, Object object) { + + } + + @Override + public void removeAttribute(String name) { + + } + + @Override + public ServletRegistration.Dynamic addServlet(String servletName, String className) { + throw new UnsupportedOperationException(); + } + + @Override + public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { + throw new UnsupportedOperationException(); + } + + @Override + public ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { + throw new UnsupportedOperationException(); + } + + @Override + public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) { + throw new UnsupportedOperationException(); + } + + @Override + public T createServlet(Class clazz) throws ServletException { + throw new UnsupportedOperationException(); + } + + @Override + public ServletRegistration getServletRegistration(String servletName) { + return null; + } + + @Override + public Map getServletRegistrations() { + return Collections.emptyMap(); + } + + @Override + public FilterRegistration.Dynamic addFilter(String filterName, String className) { + throw new UnsupportedOperationException(); + } + + @Override + public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { + throw new UnsupportedOperationException(); + } + + @Override + public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { + throw new UnsupportedOperationException(); + } + + @Override + public T createFilter(Class clazz) throws ServletException { + throw new UnsupportedOperationException(); + } + + @Override + public FilterRegistration getFilterRegistration(String filterName) { + return null; + } + + @Override + public Map getFilterRegistrations() { + return Collections.emptyMap(); + } + + @Override + public SessionCookieConfig getSessionCookieConfig() { + return null; + } + + @Override + public void setSessionTrackingModes(Set sessionTrackingModes) { + + } + + @Override + public Set getDefaultSessionTrackingModes() { + return Collections.emptySet(); + } + + @Override + public Set getEffectiveSessionTrackingModes() { + return Collections.emptySet(); + } + + @Override + public void addListener(String className) { + + } + + @Override + public void addListener(T t) { + + } + + @Override + public void addListener(Class listenerClass) { + + } + + @Override + public T createListener(Class clazz) throws ServletException { + throw new UnsupportedOperationException(); + } + + @Override + public JspConfigDescriptor getJspConfigDescriptor() { + throw new UnsupportedOperationException(); + } + + @Override + public ClassLoader getClassLoader() { + return ClassUtils.getDefaultClassLoader(); + } + + @Override + public void declareRoles(String... roleNames) { + + } + + @Override + public String getVirtualServerName() { + return "Dummy"; + } + + @Override + public int getSessionTimeout() { + return 0; + } + + @Override + public void setSessionTimeout(int sessionTimeout) { + + } + + @Override + public String getRequestCharacterEncoding() { + return "UTF8"; + } + + @Override + public void setRequestCharacterEncoding(String encoding) { + + } + + @Override + public String getResponseCharacterEncoding() { + return "UTF8"; + } + + @Override + public void setResponseCharacterEncoding(String encoding) { + + } +} diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/filter/WingsOverloadFilter.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/filter/WingsOverloadFilter.java index c65be4a59..54becc244 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/filter/WingsOverloadFilter.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/filter/WingsOverloadFilter.java @@ -273,13 +273,8 @@ private void checkAndStats(HttpServletRequest request, ServletResponse response, } } - log.info("wings-snap-response " - + ", total-resp=" + total - + ", p99=" + p99 - + ", p95=" + p95 - + ", p90=" + p90 - + ", process=" + requestProcess.get() - + ", capacity=" + requestCapacity.get()); + log.info("wings-snap-response, total-resp={}, p99={}, p95={}, p90={}, process={}, capacity={}", + total, p99, p95, p90, requestProcess.get(), requestCapacity.get()); lastInfoStat.set(end); } } diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/request/ResourceHttpRequestUtil.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/request/ResourceHttpRequestUtil.java index 28aa09a20..568f61f39 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/request/ResourceHttpRequestUtil.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/request/ResourceHttpRequestUtil.java @@ -37,6 +37,7 @@ public static boolean existResource(ResourceHttpRequestHandler rh, HttpServletRe md.setAccessible(true); } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.warn("failed to check resource=" + rq.getRequestURI(), e); } return md; diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/stream/WingsReuseStreamFilter.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/stream/WingsReuseStreamFilter.java index 517589e7f..bd5025c24 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/stream/WingsReuseStreamFilter.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/servlet/stream/WingsReuseStreamFilter.java @@ -20,8 +20,9 @@ @Setter @Getter public class WingsReuseStreamFilter extends OncePerRequestFilter implements Ordered { + public static final int ORDER = WingsOrdered.Lv4Application; - private int order = WingsOrdered.Lv4Application; + private int order = ORDER; private RequestResponseLogging requestResponseLogging; @Override diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarJacksonWebConfiguration.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarJacksonWebConfiguration.java index 85d80c03f..ce7ef33af 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarJacksonWebConfiguration.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarJacksonWebConfiguration.java @@ -275,14 +275,18 @@ public ApplicationStartedEventRunner jacksonHelperRunner(ApplicationContext cont var builder = context.getBean(Jackson2ObjectMapperBuilder.class); // wings - bindXmlWings(builder.createXmlMapper(true).build()); - bindJsonWings(builder.createXmlMapper(false).build()); + prepareWings( + builder.createXmlMapper(false).build(), + builder.createXmlMapper(true).build() + ); // bean var jsonBean = context.getBeanProvider(ObjectMapper.class); - bindJsonBean(jsonBean.getIfAvailable(() -> context.getBean(ObjectMapper.class))); var xmlBean = context.getBeanProvider(XmlMapper.class); - bindXmlBean(xmlBean.getIfAvailable(() -> builder.createXmlMapper(true).build())); + prepareBean( + jsonBean.getIfAvailable(() -> builder.createXmlMapper(false).build()), + xmlBean.getIfAvailable(() -> builder.createXmlMapper(true).build()) + ); // at last, restore createXmlMapper to false builder.createXmlMapper(false); diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarOverloadConfiguration.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarOverloadConfiguration.java index e46a50307..f6c9aea0f 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarOverloadConfiguration.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarOverloadConfiguration.java @@ -2,7 +2,6 @@ import jakarta.servlet.Filter; import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jetbrains.annotations.NotNull; @@ -14,7 +13,6 @@ import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import pro.fessional.mirana.best.DummyBlock; -import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.slardar.servlet.filter.WingsOverloadFilter; import pro.fessional.wings.slardar.servlet.resolver.WingsRemoteResolver; @@ -39,18 +37,16 @@ public class SlardarOverloadConfiguration { private final Log log = LogFactory.getLog(SlardarOverloadConfiguration.class); @Component - @Order(WingsOrdered.Lv4Application) + @Order @ConditionalWingsEnabled - @RequiredArgsConstructor public class SafelyShutdown implements ApplicationListener { - private final WingsOverloadFilter overloadFilter; - @Override @SuppressWarnings("BusyWait") public void onApplicationEvent(@NotNull ContextClosedEvent event) { + final WingsOverloadFilter overloadFilter = event.getApplicationContext().getBean(WingsOverloadFilter.class); overloadFilter.setRequestCapacity(Integer.MIN_VALUE); log.warn("SlardarWebmvc shutting down, deny new request, current=" + overloadFilter.getRequestProcess()); - for (long breaks = 60 * 1000, step = 30; overloadFilter.getRequestProcess() > 0 && breaks > 0; ) { + for (long breaks = 20 * 1000, step = 100; overloadFilter.getRequestProcess() > 0 && breaks > 0; ) { try { Thread.sleep(step); // busy wait breaks -= step; diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarRemoteConfiguration.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarRemoteConfiguration.java index 34a85889b..fd1d7f090 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarRemoteConfiguration.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarRemoteConfiguration.java @@ -5,7 +5,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; -import pro.fessional.wings.silencer.spring.help.CommonPropHelper; +import pro.fessional.wings.silencer.support.PropHelper; import pro.fessional.wings.slardar.servlet.resolver.WingsRemoteResolver; import pro.fessional.wings.slardar.spring.prop.SlardarRemoteProp; @@ -24,9 +24,9 @@ public class SlardarRemoteConfiguration { public WingsRemoteResolver wingsRemoteResolver(SlardarRemoteProp conf) { log.info("SlardarWebmvc spring-bean wingsRemoteResolver"); final WingsRemoteResolver resolver = new WingsRemoteResolver(); - resolver.addInnerIp(CommonPropHelper.onlyValue(conf.getInnerIp().values())); - resolver.addAgentHeader(CommonPropHelper.onlyValue(conf.getAgentHeader().values())); - resolver.addIpHeader(CommonPropHelper.onlyValue(conf.getIpHeader().values())); + resolver.addInnerIp(PropHelper.onlyValid(conf.getInnerIp().values())); + resolver.addAgentHeader(PropHelper.onlyValid(conf.getAgentHeader().values())); + resolver.addIpHeader(PropHelper.onlyValid(conf.getIpHeader().values())); return resolver; } } diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarSwaggerConfiguration.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarSwaggerConfiguration.java index d98be1e5c..48ae760d0 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarSwaggerConfiguration.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarSwaggerConfiguration.java @@ -20,7 +20,7 @@ import org.springframework.context.annotation.Configuration; import pro.fessional.mirana.page.PageQuery; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; -import pro.fessional.wings.silencer.spring.help.CommonPropHelper; +import pro.fessional.wings.silencer.support.PropHelper; import pro.fessional.wings.slardar.spring.prop.SlardarEnabledProp; import pro.fessional.wings.slardar.spring.prop.SlardarPagequeryProp; import pro.fessional.wings.slardar.spring.prop.SlardarSwaggerProp; @@ -62,7 +62,7 @@ public OpenApiCustomizer slardarOpenApiCustomizer(SlardarSwaggerProp slardarSwag } final Map params = slardarSwaggerProp.toComPara(); - final Map accepts = CommonPropHelper.onlyValue(slardarSwaggerProp.getAccept()); + final Map accepts = PropHelper.onlyValid(slardarSwaggerProp.getAccept()); if (params.isEmpty() && accepts.isEmpty()) return; openApi.getPaths().values() diff --git a/wings/slardar-webmvc/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/slardar-webmvc/src/main/resources/META-INF/additional-spring-configuration-metadata.json index e5a60c151..ac4621390 100644 --- a/wings/slardar-webmvc/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/slardar-webmvc/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,34 +1,23 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarWebCnfAutoConfiguration", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarLocaleConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarLocaleConfiguration.localeResolver", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOkhttpWebConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOkhttpWebConfiguration.okhttpRestTemplate", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOkhttpWebConfiguration.restTemplateBuilder", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarWebFunAutoConfiguration", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarCookieConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.cookie for short."}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarCookieConfiguration", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.slardar.cookie for short."}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarCookieConfiguration.wingsCookieFilter", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarCookieConfiguration.wingsCookieInterceptor", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDebounceConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.debounce for short."}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDebounceConfiguration.debounceInterceptor", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDomainExtendConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.domainx for short."}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDomainExtendConfiguration", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.slardar.domainx for short."}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDomainExtendConfiguration.wingsDomainExtendFilter", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarDoubleKillWebConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.double-kill for short."}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarDoubleKillWebConfiguration.doubleKillExceptionResolver", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDoubleKillWebConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.double-kill for short."}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDoubleKillWebConfiguration.doubleKillExceptionResolver", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarFirstBloodConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.first-blood for short."}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarFirstBloodConfiguration", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.slardar.first-blood for short."}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarFirstBloodConfiguration.firstBloodImageHandler", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.first-blood-image for short."}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarFirstBloodConfiguration.firstBloodInterceptor", "type": "java.lang.Boolean"}, @@ -42,6 +31,17 @@ {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonWebConfiguration.jacksonFilterProvider", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonWebConfiguration.jacksonHelperRunner", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarLocaleConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarLocaleConfiguration.localeResolver", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOkhttpWebConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOkhttpWebConfiguration.okhttpRestTemplate", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOkhttpWebConfiguration.restTemplateBuilder", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOverloadConfiguration", "defaultValue": false, "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOverloadConfiguration.overloadFallback", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOverloadConfiguration.wingsOverloadFilter", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarPageQueryConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.pagequery for short."}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarPageQueryConfiguration.pageQueryArgumentResolver", "type": "java.lang.Boolean"}, @@ -71,9 +71,10 @@ {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarUndertowConfiguration.ut026010Customizer", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarWebMvcConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarWebMvcConfiguration.mvcRequestMatcherBuilder", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.monitor.viewer.LogViewer", "type": "java.lang.Boolean", "description": "wings.slardar.monitor.view.enable for short."} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarWebCnfAutoConfiguration", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarWebFunAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/slardar-webmvc/src/main/resources/wings-conf/wings-remote-resolver-79.properties b/wings/slardar-webmvc/src/main/resources/wings-conf/wings-remote-resolver-79.properties index 9f1eca7b5..d2a062af3 100644 --- a/wings/slardar-webmvc/src/main/resources/wings-conf/wings-remote-resolver-79.properties +++ b/wings/slardar-webmvc/src/main/resources/wings-conf/wings-remote-resolver-79.properties @@ -2,6 +2,7 @@ wings.slardar.remote.inner-ip[local-127]=127. wings.slardar.remote.inner-ip[local-192]=192. wings.slardar.remote.inner-ip[local-0v6]=0:0:0:0:0:0 +wings.slardar.remote.inner-ip[local-ev6]=:: wings.slardar.remote.inner-ip[local-0v4]=0.0.0. ## which header to get the real ip when behind proxy. diff --git a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/controller/TestAsyncController.java b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/controller/TestAsyncController.java index 293b054ee..b3bad027f 100644 --- a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/controller/TestAsyncController.java +++ b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/controller/TestAsyncController.java @@ -23,7 +23,7 @@ @RestController public class TestAsyncController { - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) protected TestAsyncService testAsyncService; @RequestMapping(value = "/test/asyn-type.json") diff --git a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/json/WingsJacksonMapperTest.java b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/json/WingsJacksonMapperTest.java index e77e854d5..19a67bdd4 100644 --- a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/json/WingsJacksonMapperTest.java +++ b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/json/WingsJacksonMapperTest.java @@ -58,7 +58,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.MaskingValue; +import static pro.fessional.wings.silencer.support.PropHelper.MaskingValue; import static pro.fessional.wings.slardar.context.TerminalAttribute.TerminalAddr; import static pro.fessional.wings.slardar.context.TerminalAttribute.TerminalAgent; diff --git a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/webmvc/AsyncControllerTest.java b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/webmvc/AsyncControllerTest.java index 879d6fd03..3dbfcdc70 100644 --- a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/webmvc/AsyncControllerTest.java +++ b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/webmvc/AsyncControllerTest.java @@ -33,13 +33,13 @@ @AutoConfigureMockMvc public class AsyncControllerTest { - @Setter(onMethod_ = {@Value("http://localhost:${local.server.port}")}) + @Setter(onMethod_ = { @Value("http://localhost:${local.server.port}") }) private String host; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private RestTemplate restTemplate; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private MockMvc mockMvc; @Test @@ -85,10 +85,10 @@ private void testMock(String url, String type, boolean err) throws Exception { log.info("mock rul={}, type={}, err={}", url, type, err); MvcResult mvcResult = mockMvc - .perform(get(url).param("err", type)) - .andExpect(request().asyncStarted()) - .andDo(MockMvcResultHandlers.log()) - .andReturn(); + .perform(get(url).param("err", type)) + .andExpect(request().asyncStarted()) + .andDo(MockMvcResultHandlers.log()) + .andReturn(); try { mockMvc.perform(asyncDispatch(mvcResult)) diff --git a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/webmvc/DateTimeConverterTest.java b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/webmvc/DateTimeConverterTest.java index 4cda4e6cd..1e744b339 100644 --- a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/webmvc/DateTimeConverterTest.java +++ b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/webmvc/DateTimeConverterTest.java @@ -20,14 +20,14 @@ * @since 2020-06-03 */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = {"wings.silencer.i18n.zoneid=" + DateTimeConverterTest.SYS_TZ, "wings.slardar.datetime.zoned.auto=true"}) + properties = { "wings.silencer.i18n.zoneid=" + DateTimeConverterTest.SYS_TZ, "wings.slardar.datetime.zoned.auto=true" }) @AutoConfigureMockMvc public class DateTimeConverterTest { public static final String SYS_TZ = "Asia/Shanghai"; public static final String SYS_OZ = "+08:00"; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private MockMvc mockMvc; /** @@ -137,14 +137,14 @@ public void testLdtZdt() throws Exception { */ private void testLdtZdt(String udt, String cdt, String zdt, String utz) throws Exception { final MockHttpServletRequestBuilder builder = post("/test/ldt-zdt.json?d=" + udt) - .header("Zone-Id", utz); + .header("Zone-Id", utz); mockMvc.perform(builder) .andDo(print()) .andExpect(content().json( - "{\"zdt\":\"" + zdt + " " + utz - + "\",\"ldt\":\"" + cdt - + "\",\"sdt\":\"" + cdt + " " + SYS_TZ + "\"}", - true)); + "{\"zdt\":\"" + zdt + " " + utz + + "\",\"ldt\":\"" + cdt + + "\",\"sdt\":\"" + cdt + " " + SYS_TZ + "\"}", + true)); } /** @@ -170,16 +170,16 @@ public void testLdtZdtBody() throws Exception { */ private void testLdtZdtBody(String udt, String cdt, String zdt, String utz) throws Exception { final MockHttpServletRequestBuilder builder = post("/test/ldt-zdt-body.json") - .header("Zone-Id", utz) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"ldt\":\"" + udt + "\"}"); + .header("Zone-Id", utz) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"ldt\":\"" + udt + "\"}"); mockMvc.perform(builder) .andDo(print()) .andExpect(content().json( - "{\"zdt\":\"" + zdt + " " + utz - + "\",\"ldt\":\"" + cdt - + "\",\"sdt\":\"" + cdt + " " + SYS_TZ + "\"}", - true)); + "{\"zdt\":\"" + zdt + " " + utz + + "\",\"ldt\":\"" + cdt + + "\",\"sdt\":\"" + cdt + " " + SYS_TZ + "\"}", + true)); } /** @@ -204,14 +204,14 @@ public void testZdtLdt() throws Exception { */ private void testZdtLdt(String udt, String zdt, String cdt, String utz) throws Exception { final MockHttpServletRequestBuilder builder = get("/test/zdt-ldt.json?d=" + udt) - .header("Zone-Id", utz); + .header("Zone-Id", utz); mockMvc.perform(builder) .andDo(print()) .andExpect(content().json( - "{\"zdt\":\"" + zdt + " " + utz - + "\",\"ldt\":\"" + cdt - + "\",\"sdt\":\"" + cdt + " " + SYS_TZ + "\"}", - true)); + "{\"zdt\":\"" + zdt + " " + utz + + "\",\"ldt\":\"" + cdt + + "\",\"sdt\":\"" + cdt + " " + SYS_TZ + "\"}", + true)); } /** @@ -236,16 +236,16 @@ public void testZdtLdtBody() throws Exception { */ private void testZdtLdtBody(String udt, String zdt, String cdt, String utz) throws Exception { final MockHttpServletRequestBuilder builder = get("/test/zdt-ldt-body.json") - .header("Zone-Id", utz) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"zdt\":\"" + udt + "\"}"); + .header("Zone-Id", utz) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"zdt\":\"" + udt + "\"}"); mockMvc.perform(builder) .andDo(print()) .andExpect(content().json( - "{\"zdt\":\"" + zdt + " " + utz - + "\",\"ldt\":\"" + cdt - + "\",\"sdt\":\"" + cdt + " " + SYS_TZ + "\"}" - , true)); + "{\"zdt\":\"" + zdt + " " + utz + + "\",\"ldt\":\"" + cdt + + "\",\"sdt\":\"" + cdt + " " + SYS_TZ + "\"}" + , true)); } /** @@ -272,14 +272,14 @@ public void testLdtOdt() throws Exception { */ private void testLdtOdt(String udt, String ldt, String odt, String utz, String otz) throws Exception { final MockHttpServletRequestBuilder builder = post("/test/ldt-odt.json?d=" + udt) - .header("Zone-Id", utz); + .header("Zone-Id", utz); mockMvc.perform(builder) .andDo(print()) .andExpect(content().json( - "{\"odt\":\"" + odt + " " + otz - + "\",\"ldt\":\"" + ldt - + "\",\"sdt\":\"" + ldt + " " + SYS_OZ + "\"}", - true)); + "{\"odt\":\"" + odt + " " + otz + + "\",\"ldt\":\"" + ldt + + "\",\"sdt\":\"" + ldt + " " + SYS_OZ + "\"}", + true)); } /** @@ -307,16 +307,16 @@ public void testLdtOdtBody() throws Exception { */ private void testLdtOdtBody(String udt, String ldt, String odt, String utz, String otz) throws Exception { final MockHttpServletRequestBuilder builder = post("/test/ldt-odt-body.json") - .header("Zone-Id", utz) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"ldt\":\"" + udt + "\"}"); + .header("Zone-Id", utz) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"ldt\":\"" + udt + "\"}"); mockMvc.perform(builder) .andDo(print()) .andExpect(content().json( - "{\"odt\":\"" + odt + " " + otz - + "\",\"ldt\":\"" + ldt - + "\",\"sdt\":\"" + ldt + " " + SYS_OZ + "\"}" - , true)); + "{\"odt\":\"" + odt + " " + otz + + "\",\"ldt\":\"" + ldt + + "\",\"sdt\":\"" + ldt + " " + SYS_OZ + "\"}" + , true)); } /** @@ -342,14 +342,14 @@ public void testOdtLdt() throws Exception { */ private void testOdtLdt(String udt, String odt, String cdt, String utz, String otz) throws Exception { final MockHttpServletRequestBuilder builder = get("/test/odt-ldt.json?d=" + udt) - .header("Zone-Id", utz); + .header("Zone-Id", utz); mockMvc.perform(builder) .andDo(print()) .andExpect(content().json( - "{\"odt\":\"" + odt + " " + otz - + "\",\"ldt\":\"" + cdt - + "\",\"sdt\":\"" + cdt + " " + SYS_OZ + "\"}", - true)); + "{\"odt\":\"" + odt + " " + otz + + "\",\"ldt\":\"" + cdt + + "\",\"sdt\":\"" + cdt + " " + SYS_OZ + "\"}", + true)); } /** @@ -375,16 +375,16 @@ public void testOdtLdtBody() throws Exception { */ private void testOdtLdtBody(String udt, String odt, String cdt, String utz, String otz) throws Exception { final MockHttpServletRequestBuilder builder = get("/test/odt-ldt-body.json") - .header("Zone-Id", utz) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"odt\":\"" + udt + "\"}"); + .header("Zone-Id", utz) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"odt\":\"" + udt + "\"}"); mockMvc.perform(builder) .andDo(print()) .andExpect(content().json( - "{\"odt\":\"" + odt + " " + otz - + "\",\"ldt\":\"" + cdt - + "\",\"sdt\":\"" + cdt + " " + SYS_OZ + "\"}" - , true)); + "{\"odt\":\"" + odt + " " + otz + + "\",\"ldt\":\"" + cdt + + "\",\"sdt\":\"" + cdt + " " + SYS_OZ + "\"}" + , true)); } @Test @@ -398,8 +398,8 @@ public void testLdLdBody() throws Exception { private void testLdLdBody(String d, String v) throws Exception { final MockHttpServletRequestBuilder builder = post("/test/ld-ld-body.json") - .contentType(MediaType.APPLICATION_JSON) - .content("{\"ld\":\"" + d + "\"}"); + .contentType(MediaType.APPLICATION_JSON) + .content("{\"ld\":\"" + d + "\"}"); mockMvc.perform(builder) .andDo(print()) @@ -417,8 +417,8 @@ public void testLtLtBody() throws Exception { private void testLtLtBody(String d, String v) throws Exception { final MockHttpServletRequestBuilder builder = post("/test/lt-lt-body.json") - .contentType(MediaType.APPLICATION_JSON) - .content("{\"lt\":\"" + d + "\"}"); + .contentType(MediaType.APPLICATION_JSON) + .content("{\"lt\":\"" + d + "\"}"); mockMvc.perform(builder) .andDo(print()) @@ -431,16 +431,16 @@ public void testLdxAuto1() throws Exception { final String utz = "Asia/Tokyo"; // User +9 zone, 12 o'clock, convert to system +8 zone, 11 o'clock final MockHttpServletRequestBuilder b1q = post("/test/ldx-body-req.json") - .header("Zone-Id", utz) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"ldt\":\"2022-10-03T12:34:56\"}"); + .header("Zone-Id", utz) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"ldt\":\"2022-10-03T12:34:56\"}"); mockMvc.perform(b1q) .andDo(print()) .andExpect(content().string("2022-10-03T11:34:56")); // system +8 zone, 12 o'clock, convert to user +9 zone, 13 o'clock final MockHttpServletRequestBuilder b1s = post("/test/ldx-body-res.json?d=2022-10-03T12:34:56") - .header("Zone-Id", utz); + .header("Zone-Id", utz); mockMvc.perform(b1s) .andDo(print()) .andExpect(content().json("{\"ldt\":\"2022-10-03 13:34:56\"}", true)); @@ -453,7 +453,7 @@ public void testLdxAuto2() throws Exception { // Use +9 zone, 12 o'clock, convert to system +8 zone, 11 o'clock final MockHttpServletRequestBuilder b2q = post("/test/ldt-para-req.json?d=2022-10-03T12:34:56") - .header("Zone-Id", utz); + .header("Zone-Id", utz); mockMvc.perform(b2q) .andDo(print()) .andExpect(content().string("2022-10-03T11:34:56")); @@ -462,7 +462,7 @@ public void testLdxAuto2() throws Exception { // need to use BodyAdvice, which is heavy. // This usage is not common, so this scenario is not supported. final MockHttpServletRequestBuilder b2s = post("/test/ldt-para-res.json?d=2022-10-03T12:34:56") - .header("Zone-Id", utz); + .header("Zone-Id", utz); mockMvc.perform(b2s) .andDo(print()) .andExpect(content().string("\"2022-10-03 12:34:56\"")); diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/AsyncHelper.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/AsyncHelper.java index c78499f30..497aadbcd 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/AsyncHelper.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/AsyncHelper.java @@ -2,9 +2,11 @@ import com.alibaba.ttl.threadpool.TtlExecutors; import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.DisposableBean; import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder; import org.springframework.core.task.AsyncTaskExecutor; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.Supplier; @@ -16,14 +18,33 @@ * @see TtlExecutors * @since 2024-05-13 */ -public class AsyncHelper { +public class AsyncHelper implements DisposableBean { - protected static Executor AsyncExecutor; - protected static AsyncTaskExecutor AppTaskExecutor; + private static Executor AsyncExecutor = null; + private static AsyncTaskExecutor AppTaskExecutor = null; + private static ThreadPoolTaskExecutorBuilder ExecutorBuilder; + private static AsyncTaskExecutor LiteExecutor; + private static boolean helperPrepared = false; - protected AsyncHelper(Executor asy, AsyncTaskExecutor app) { - AsyncExecutor = asy; - AppTaskExecutor = app; + protected AsyncHelper(@NotNull Executor async, @NotNull AsyncTaskExecutor appTask, + @NotNull ThreadPoolTaskExecutorBuilder builder, @NotNull AsyncTaskExecutor lite) { + AsyncExecutor = Objects.requireNonNull(async); + AppTaskExecutor = Objects.requireNonNull(appTask); + ExecutorBuilder = Objects.requireNonNull(builder); + LiteExecutor = Objects.requireNonNull(lite); + helperPrepared = true; + } + + @Override + public void destroy() { + helperPrepared = false; + } + + /** + * whether this helper is prepared + */ + public static boolean isPrepared() { + return helperPrepared; } /** @@ -63,11 +84,6 @@ public static AsyncTaskExecutor AppTask() { return AppTaskExecutor; } - - protected static ThreadPoolTaskExecutorBuilder ExecutorBuilder; - protected static AsyncTaskExecutor LiteExecutor; - - /** * @see org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor#DEFAULT_TASK_EXECUTOR_BEAN_NAME */ diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TaskSchedulerHelper.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TaskSchedulerHelper.java index 697059b8d..ede4d5a90 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TaskSchedulerHelper.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TaskSchedulerHelper.java @@ -1,26 +1,55 @@ package pro.fessional.wings.slardar.async; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.DisposableBean; import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import pro.fessional.mirana.time.ThreadNow; import java.time.Instant; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicLong; /** * @author trydofor * @since 2022-12-05 */ -public class TaskSchedulerHelper { +public class TaskSchedulerHelper implements DisposableBean { - protected static ThreadPoolTaskScheduler FastScheduler; - protected static ThreadPoolTaskScheduler ScheduledScheduler; + private static ThreadPoolTaskScheduler FastScheduler; + private static ThreadPoolTaskScheduler ScheduledScheduler; + private static ThreadPoolTaskSchedulerBuilder FastBuilder; + private static ThreadPoolTaskSchedulerBuilder ScheduledBuilder; + private static boolean helperPrepared = false; - protected TaskSchedulerHelper(ThreadPoolTaskScheduler fast, ThreadPoolTaskScheduler scheduled) { - FastScheduler = fast; - ScheduledScheduler = scheduled; + + protected TaskSchedulerHelper(@NotNull ThreadPoolTaskScheduler fast, @NotNull ThreadPoolTaskScheduler scheduled, + @NotNull ThreadPoolTaskSchedulerBuilder fastBuilder, @NotNull ThreadPoolTaskSchedulerBuilder scheduledBuilder) { + FastScheduler = Objects.requireNonNull(fast); + ScheduledScheduler = Objects.requireNonNull(scheduled); + FastBuilder = Objects.requireNonNull(fastBuilder); + ScheduledBuilder = Objects.requireNonNull(scheduledBuilder); + helperPrepared = true; + } + + @Override + public void destroy() { + helperPrepared = false; + for (var task : TaskRun.values()) { + task.cancel(false); + } + TaskRun.clear(); + } + + /** + * whether this helper is prepared + */ + public static boolean isPrepared() { + return helperPrepared; } /** @@ -30,9 +59,11 @@ public static TtlThreadPoolTaskScheduler Ttl(ThreadPoolTaskSchedulerBuilder buil return builder.configure(new TtlThreadPoolTaskScheduler()); } - /** - * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME - */ + @NotNull + public static ThreadPoolTaskScheduler Scheduler(boolean fast) { + return fast ? Fast() : Scheduled(); + } + @NotNull public static ThreadPoolTaskScheduler Fast() { if (FastScheduler == null) { @@ -42,7 +73,7 @@ public static ThreadPoolTaskScheduler Fast() { } /** - * see NamingSlardarConst#slardarHeavyScheduler + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME */ @NotNull public static ThreadPoolTaskScheduler Scheduled() { @@ -56,7 +87,7 @@ public static ThreadPoolTaskScheduler Scheduled() { /** * just like default @Scheduled * - * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME */ public static void Scheduled(@NotNull Runnable task) { Scheduled().execute(task); @@ -65,32 +96,153 @@ public static void Scheduled(@NotNull Runnable task) { /** * just like default @Scheduled * - * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Instant) */ + @NotNull public static ScheduledFuture Scheduled(long delayMs, @NotNull Runnable task) { - return Scheduled().schedule(task, Instant.ofEpochMilli(ThreadNow.millis() + delayMs)); + return Scheduled(Scheduled(), delayMs, task); + } + + /** + * just like default @Scheduled + * + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Instant) + */ + @NotNull + public static ScheduledFuture Scheduled(@NotNull Instant start, @NotNull Runnable task) { + return Scheduled(Scheduled(), start, task); + } + + /** + * just like default @Scheduled + * + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Trigger) + */ + @Nullable + public static ScheduledFuture Scheduled(@NotNull Trigger trigger, @NotNull Runnable task) { + return Scheduled(Scheduled(), trigger, task); } + /** * just like default @Scheduled * - * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME */ - public static ScheduledFuture Scheduled(Instant start, @NotNull Runnable task) { - return Scheduled().schedule(task, start); + public static void Scheduled(boolean fast, @NotNull Runnable task) { + Scheduler(fast).execute(task); } /** * just like default @Scheduled * - * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Instant) */ - public static ScheduledFuture Scheduled(Trigger trigger, @NotNull Runnable task) { - return Scheduled().schedule(task, trigger); + @NotNull + public static ScheduledFuture Scheduled(boolean fast, long delayMs, @NotNull Runnable task) { + return Scheduled(Scheduler(fast), delayMs, task); } - protected static ThreadPoolTaskSchedulerBuilder FastBuilder; - protected static ThreadPoolTaskSchedulerBuilder ScheduledBuilder; + /** + * just like default @Scheduled + * + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Instant) + */ + @NotNull + public static ScheduledFuture Scheduled(boolean fast, @NotNull Instant start, @NotNull Runnable task) { + return Scheduled(Scheduler(fast), start, task); + } + + /** + * just like default @Scheduled + * + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Trigger) + */ + @Nullable + public static ScheduledFuture Scheduled(boolean fast, @NotNull Trigger trigger, @NotNull Runnable task) { + return Scheduled(Scheduler(fast), trigger, task); + } + + /** + * just like default @Scheduled + * + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Instant) + */ + @NotNull + public static ScheduledFuture Scheduled(@NotNull ThreadPoolTaskScheduler scheduler, long delayMs, @NotNull Runnable task) { + return Scheduled(scheduler, Instant.ofEpochMilli(ThreadNow.millis() + delayMs), task); + } + + /** + * just like default @Scheduled + * + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Instant) + */ + @NotNull + public static ScheduledFuture Scheduled(@NotNull ThreadPoolTaskScheduler scheduler, @NotNull Instant start, @NotNull Runnable task) { + final Task tsk = new Task(task); + final var future = scheduler.schedule(tsk, start); + if (tsk.run) { + TaskRun.put(tsk.seq, future); + } + return future; + } + + /** + * just like default @Scheduled + * + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME + * @see ThreadPoolTaskScheduler#schedule(Runnable, Trigger) + */ + @Nullable + public static ScheduledFuture Scheduled(@NotNull ThreadPoolTaskScheduler scheduler, @NotNull Trigger trigger, @NotNull Runnable task) { + final Task tsk = new Task(task); + final var future = scheduler.schedule(tsk, trigger); + if (future != null && tsk.run) { + TaskRun.put(tsk.seq, future); + } + return future; + } + + /** + * clean done task and get the running size + */ + public static int runningSize() { + TaskRun.entrySet().removeIf(en -> en.getValue().isDone()); + return TaskRun.size(); + } + + private static final ConcurrentHashMap> TaskRun = new ConcurrentHashMap<>(); + private static final AtomicLong TaskSeq = new AtomicLong(0); + + private static class Task implements Runnable { + private final Long seq = TaskSeq.incrementAndGet(); + private volatile boolean run = true; + private final Runnable runnable; + + public Task(Runnable run) { + runnable = run; + } + + @Override + public void run() { + try { + runnable.run(); + } + finally { + run = false; + TaskRun.remove(seq); + } + } + } /** * Get Light ThreadPoolTaskSchedulerBuilder, IllegalStateException if nonull but null. diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/SimpleCacheTemplate.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/SimpleCacheTemplate.java new file mode 100644 index 000000000..68c2c0a7c --- /dev/null +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/SimpleCacheTemplate.java @@ -0,0 +1,279 @@ +package pro.fessional.wings.slardar.cache; + +import lombok.Getter; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.interceptor.SimpleKeyGenerator; +import org.springframework.core.convert.converter.Converter; +import pro.fessional.mirana.best.AssertArgs; +import pro.fessional.wings.silencer.spring.help.ApplicationContextHelper; + +import java.util.function.Supplier; + +/** + * only support raw and method argument key. + * NOT support SpEL or KeyGenerator + *

+ * should declare it as a final field to enhance performance, + * then use it after application context has started + * + * @author trydofor + * @since 2024-06-25 + */ +public class SimpleCacheTemplate { + + private final String manager; + private final String[] names; + private final Cache[] caches; + private volatile int status; // -1:uninit, 0:lazy-inited, 1: fixed + + @Getter + private volatile Converter encoder = null; + @Getter + private volatile Converter decoder = null; + @Getter + private volatile BeanFactory beanFactory = null; + + public SimpleCacheTemplate(@NotNull String manager, @NotNull String... caches) { + this.manager = manager; + this.names = caches; + this.caches = new Cache[caches.length]; + this.status = -1; + } + + public SimpleCacheTemplate(@NotNull Cache... caches) { + this.manager = null; + this.names = null; + this.caches = caches; + this.status = 1; + } + + /** + * encode Value to Cache, default null + */ + @Contract("_->this") + public SimpleCacheTemplate setEncoder(@Nullable Converter encoder) { + this.encoder = encoder; + return this; + } + + /** + * decode Value from Cache, default null + */ + @Contract("_->this") + public SimpleCacheTemplate setDecoder(@Nullable Converter decoder) { + this.decoder = decoder; + return this; + } + + /** + * set the application context to re-init Cache by CacheManager bean. + * mostly for unit test to avoid multiple application context. + */ + @Contract("_->this") + public SimpleCacheTemplate setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + this.status = -1; + return this; + } + // //////// + + /** + * get and decode Value from cache by argument key + */ + public T getArgKey(@NotNull Object... args) { + return getRawKey(rawKey(args)); + } + + /** + * get and decode Value from cache by raw key + */ + public T getRawKey(@NotNull Object key) { + for (Cache cache : getCaches()) { + Cache.ValueWrapper vw = cache.get(key); + if (vw != null && vw.get() != null) { + return decode(vw.get()); + } + } + return null; + } + + /** + * get and decode Value from cache by argument key, put the non-null value if no cache found. + */ + public T getArgKey(@NotNull Supplier value, @NotNull Object... args) { + return getRawKey(value, rawKey(args)); + } + + /** + * get and decode Value from cache by raw key, put the non-null value if no cache found. + */ + public T getRawKey(@NotNull Supplier value, @NotNull Object key) { + for (Cache cache : getCaches()) { + Cache.ValueWrapper vw = cache.get(key); + if (vw != null && vw.get() != null) { + return decode(vw.get()); + } + } + + T v = value.get(); + if (v != null) { + putRawKey(v, key); + } + return v; + } + + /** + * get, decode and convert Value from cache by argument key, put the non-null value if no cache found. + */ + public U getArgKey(@NotNull Converter converter, @NotNull Object... args) { + return getRawKey(converter, rawKey(args)); + } + + /** + * get, decode and convert Value from cache by raw key, put the non-null value if no cache found. + */ + public U getRawKey(@NotNull Converter converter, @NotNull Object key) { + T t = getRawKey(key); + return converter.convert(t); + } + + /** + * get, decode and convert Value from cache by argument key, put the non-null value if no cache found. + */ + public U getArgKey(@NotNull Converter converter, @NotNull Supplier value, @NotNull Object... args) { + return getRawKey(converter, value, rawKey(args)); + } + + /** + * get, decode and convert Value from cache by raw key, put the non-null value if no cache found. + */ + public U getRawKey(@NotNull Converter converter, @NotNull Supplier value, @NotNull Object key) { + T t = getRawKey(value, key); + return converter.convert(t); + } + + // //////// + + public void putArgKey(@NotNull T value, @NotNull Object... args) { + putRawKey(value, rawKey(args)); + } + + public void putRawKey(@NotNull T value, @NotNull Object key) { + final Object obj = encode(value); + for (Cache cache : getCaches()) { + cache.put(key, obj); + } + } + + /** + * encode and put value to cache, return the decoded existing Value + * + * @see Cache#putIfAbsent(Object, Object) + */ + public T putArgKeyIfPresent(@NotNull T value, @NotNull Object... args) { + return putRawKeyIfPresent(value, rawKey(args)); + } + + /** + * encode and put value to cache, return the decoded existing Value + * + * @see Cache#putIfAbsent(Object, Object) + */ + public T putRawKeyIfPresent(@NotNull T value, @NotNull Object key) { + final Object obj = encode(value); + Cache.ValueWrapper vw = null; + for (Cache cache : getCaches()) { + vw = cache.putIfAbsent(key, obj); + } + return vw == null ? null : decode(vw.get()); + } + + /** + * encode and put value to cache, return the decoded and converted existing Value + * + * @see Cache#putIfAbsent(Object, Object) + */ + public U putArgKeyIfPresent(@NotNull Converter converter, @NotNull T value, @NotNull Object... args) { + return putRawKeyIfPresent(converter, value, rawKey(args)); + } + + /** + * encode and put value to cache, return the decoded and converted existing Value + * + * @see Cache#putIfAbsent(Object, Object) + */ + public U putRawKeyIfPresent(@NotNull Converter converter, @NotNull T value, @NotNull Object key) { + T t = putRawKeyIfPresent(value, key); + return converter.convert(t); + } + + // //////// + + public void evictArgKey(@NotNull Object... args) { + evictRawKey(rawKey(args)); + } + + public void evictRawKey(@NotNull Object key) { + for (Cache cache : getCaches()) { + cache.evict(key); + } + } + + public void evictAll() { + for (Cache cache : getCaches()) { + cache.clear(); + } + } + + public Cache[] getCaches() { + if (status == -1) { + synchronized (caches) { + if (status == -1) { + AssertArgs.notNull(manager, "empty manager={}", manager); + CacheManager cacheManager = beanFactory == null + ? ApplicationContextHelper.getBean(manager) + : beanFactory.getBean(manager, CacheManager.class); + + AssertArgs.notNull(cacheManager, "cacheManager not found, manager={}", manager); + AssertArgs.notNull(names, "cache is null"); + for (int i = 0; i < names.length; i++) { + caches[i] = cacheManager.getCache(names[i]); + AssertArgs.notNull(caches[i], "cache not found,name={}, manager={}", names[i], manager); + } + } + status = 0; + } + } + + return caches; + } + + /** + * decode value from cache, force to cast type if the decoder is null + */ + @SuppressWarnings("unchecked") + public T decode(Object obj) { + return decoder == null ? (T) obj : decoder.convert(obj); + } + + /** + * encode value to cache if the encoder not null + */ + public Object encode(T obj) { + return encoder == null ? obj : encoder.convert(obj); + } + + /** + * generate raw key by args/parmas + * + * @see SimpleKeyGenerator#generateKey(Object...) + */ + public Object rawKey(Object... args) { + return SimpleKeyGenerator.generateKey(args); + } +} diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/WingsCacheHelper.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/WingsCacheHelper.java index 20bcfb769..5b8da98bb 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/WingsCacheHelper.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/WingsCacheHelper.java @@ -27,25 +27,51 @@ @Slf4j public class WingsCacheHelper { - private static final Map managers = new ConcurrentHashMap<>(); - private static final Map> namings = new ConcurrentHashMap<>(); - private static CacheManager memory; - private static CacheManager server; + private static final Map NameManager = new ConcurrentHashMap<>(); + private static final Map> ManagerName = new ConcurrentHashMap<>(); + private static CacheManager MemoryManager = null; + private static CacheManager ServerManager = null; + private static boolean helperPrepared = false; + + + /** + * Set CacheManager name and its Resolver + */ + protected WingsCacheHelper(Map mngs) { + NameManager.putAll(mngs); + + MemoryManager = NameManager.get(WingsCache.Manager.Memory); + ServerManager = NameManager.get(WingsCache.Manager.Server); + + ManagerName.clear(); + for (Map.Entry en : NameManager.entrySet()) { + ManagerName.computeIfAbsent(en.getValue(), k -> new HashSet<>()) + .add(en.getKey()); + } + helperPrepared = true; + } + + /** + * whether this helper is prepared + */ + public static boolean isPrepared() { + return helperPrepared; + } @Nullable public static CacheManager getCacheManager(String name) { if (WingsCache.Manager.Memory.equalsIgnoreCase(name)) { - return memory; + return MemoryManager; } if (WingsCache.Manager.Server.equalsIgnoreCase(name)) { - return server; + return ServerManager; } - return managers.get(name); + return NameManager.get(name); } public static Set getManagerNames(CacheManager manage) { if (manage == null) return emptySet(); - return namings.getOrDefault(manage, emptySet()); + return ManagerName.getOrDefault(manage, emptySet()); } @Nullable @@ -56,78 +82,72 @@ public static Cache getCache(String manager, String cache) { @NotNull public static Cache getMemoryCache(String name) { - final Cache cache = memory.getCache(name); + final Cache cache = MemoryManager.getCache(name); AssertState.notNull(cache, "memory cache is null, name={}", name); return cache; } @NotNull public static Cache getServerCache(String name) { - final Cache cache = server.getCache(name); + final Cache cache = ServerManager.getCache(name); AssertState.notNull(cache, "server cache is null, name={}", name); return cache; } - /** - * Set CacheManager name and its Resolver - */ - public static void putManagers(Map mngs) { - managers.putAll(mngs); - - memory = managers.get(WingsCache.Manager.Memory); - server = managers.get(WingsCache.Manager.Server); - - namings.clear(); - for (Map.Entry en : managers.entrySet()) { - namings.computeIfAbsent(en.getValue(), k -> new HashSet<>()).add(en.getKey()); - } - } - @NotNull public static CacheManager getMemory() { - AssertState.notNull(memory, "Memory CacheManager is null"); - return memory; + AssertState.notNull(MemoryManager, "Memory CacheManager is null"); + return MemoryManager; } @NotNull public static CacheManager getServer() { - AssertState.notNull(server, "Server CacheManager is null"); - return server; + AssertState.notNull(ServerManager, "Server CacheManager is null"); + return ServerManager; } /// - private static final Map, Map> classes = new ConcurrentHashMap<>(); + private static final Map, Map> ClassCacheMeta = new ConcurrentHashMap<>(); public static class Meta { - private final Map> metaMap = new HashMap<>(); - private final Map> objManager = new HashMap<>(); - private final Map> objCache = new HashMap<>(); + private final Map> MngRlvCache = new HashMap<>(); + + private final Map> managerName = new HashMap<>(); // lazy + private final Map> managerCache = new HashMap<>(); // lazy public void initOperation(String cr, String cm, Set cs) { if (!cr.isEmpty()) { - metaMap.computeIfAbsent(cr, k -> new HashSet<>()).addAll(cs); + MngRlvCache.computeIfAbsent(cr, k -> new HashSet<>()).addAll(cs); } if (!cm.isEmpty()) { - metaMap.computeIfAbsent(cm, k -> new HashSet<>()).addAll(cs); + MngRlvCache.computeIfAbsent(cm, k -> new HashSet<>()).addAll(cs); } } + /** + * CacheManager and its names + */ public Map> getManagers() { - if (metaMap.isEmpty()) return objManager; - if (objManager.isEmpty()) { - for (String nm : metaMap.keySet()) { + if (MngRlvCache.isEmpty()) return managerName; + + if (managerName.isEmpty()) { + for (String nm : MngRlvCache.keySet()) { final CacheManager m = getCacheManager(nm); AssertState.notNull(m, "no CacheManager for {}", nm); - objManager.put(m, getManagerNames(m)); + managerName.put(m, getManagerNames(m)); } } - return objManager; + return managerName; } + /** + * manager/resolver and its Caches + */ public Map> getCaches() { - if (metaMap.isEmpty()) return objCache; - if (objCache.isEmpty()) { - for (Map.Entry> en : metaMap.entrySet()) { + if (MngRlvCache.isEmpty()) return managerCache; + + if (managerCache.isEmpty()) { + for (Map.Entry> en : MngRlvCache.entrySet()) { String k = en.getKey(); final CacheManager m = getCacheManager(k); AssertState.notNull(m, "no CacheManager for {}", k); @@ -135,58 +155,77 @@ public Map> getCaches() { for (String c : en.getValue()) { st.add(m.getCache(c)); } - objCache.put(k, st); + managerCache.put(k, st); } } - return objCache; + return managerCache; } } + /** + * manager/resolver name and its caches name + */ @NotNull public static Map> getCacheMeta(Class clz) { return getCacheMeta(clz, Null.Str); } + /** + * manager/resolver name and its caches name + */ @NotNull public static Map> getCacheMeta(Class claz, String method) { - final Map map = classes.get(claz); + final Map map = ClassCacheMeta.get(claz); if (map == null) return emptyMap(); if (method == null) method = Null.Str; final Meta mt = map.get(method); - return mt == null ? emptyMap() : mt.metaMap; + return mt == null ? emptyMap() : mt.MngRlvCache; } + /** + * CacheManager and its names + */ @NotNull public static Map> getManager(Class clz) { return getManager(clz, Null.Str); } + /** + * CacheManager and its names + */ @NotNull public static Map> getManager(Class claz, String method) { - final Map map = classes.get(claz); + final Map map = ClassCacheMeta.get(claz); if (map == null) return emptyMap(); if (method == null) method = Null.Str; final Meta mt = map.get(method); return mt == null ? emptyMap() : mt.getManagers(); } + /** + * manager/resolver and its Caches + */ @NotNull public static Map> getCaches(Class clz) { return getCaches(clz, Null.Str); } + /** + * manager/resolver and its Caches + */ @NotNull public static Map> getCaches(Class claz, String method) { - final Map map = classes.get(claz); + final Map map = ClassCacheMeta.get(claz); if (map == null) return emptyMap(); if (method == null) method = Null.Str; final Meta mt = map.get(method); return mt == null ? emptyMap() : mt.getCaches(); } - public static void setOperation(Method method, Collection opr) { + public static void prepareOperation(Method method, Collection opr) { if (opr == null || opr.isEmpty()) return; - final Map entry = classes.computeIfAbsent(method.getDeclaringClass(), k -> new ConcurrentHashMap<>()); + + final Map entry = ClassCacheMeta.computeIfAbsent(method.getDeclaringClass(), k -> new ConcurrentHashMap<>()); final Meta top = entry.computeIfAbsent(Null.Str, k -> new Meta()); final Meta mod = entry.computeIfAbsent(method.getName(), k -> new Meta()); diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/cache2k/NullsCache2k.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/cache2k/NullsCache2k.java index bce2be1bd..80b4a93f6 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/cache2k/NullsCache2k.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/cache2k/NullsCache2k.java @@ -49,9 +49,10 @@ public T get(@NotNull Object key, @NotNull Callable valueLoader) { return value; } - @SuppressWarnings("NullableProblems") + @SuppressWarnings({ "NullableProblems", "ConstantConditions" }) @Override public void put(@NotNull Object key, Object value) { + // SuppressWarnings Condition `value == null` is always `false` if (value == null) { if (nulls != null) { nulls.put(key, Boolean.TRUE); diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/spring/CacheEvictKey.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/spring/CacheEvictKey.java index b8638907a..a623dd7ed 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/spring/CacheEvictKey.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/spring/CacheEvictKey.java @@ -40,7 +40,7 @@ public Object getKey() { * * @see #KeyGenerator */ - @Contract("_->this") + @Contract("_,_,_->this") public final CacheEvictKey add(Object target, Method method, Object... arg) { if (keys.isEmpty()) { keys = new LinkedList<>(); diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/spring/WingsCacheAnnoOprSource.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/spring/WingsCacheAnnoOprSource.java index 87c282e9f..6ddcef48e 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/spring/WingsCacheAnnoOprSource.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/cache/spring/WingsCacheAnnoOprSource.java @@ -17,7 +17,7 @@ public class WingsCacheAnnoOprSource extends AnnotationCacheOperationSource { @Override protected Collection findCacheOperations(@NotNull Method method) { final Collection ops = super.findCacheOperations(method); - WingsCacheHelper.setOperation(method, ops); + WingsCacheHelper.prepareOperation(method, ops); return ops; } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/context/LocaleZoneIdUtil.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/context/LocaleZoneIdUtil.java index 89ba287e8..6651f77f3 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/context/LocaleZoneIdUtil.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/context/LocaleZoneIdUtil.java @@ -2,7 +2,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.springframework.context.i18n.LocaleContextHolder; import java.time.ZoneId; import java.util.Locale; @@ -19,10 +18,7 @@ public class LocaleZoneIdUtil { @Nullable public static ZoneId ZoneIdNullable() { final TerminalContext.Context ctx = TerminalContext.get(false); - if (!ctx.isNull()) { - return ctx.getZoneId(); - } - return LocaleContextHolder.getTimeZone().toZoneId(); + return ctx.isNull() ? null: ctx.getZoneId(); } @@ -32,10 +28,7 @@ public static ZoneId ZoneIdNullable() { @Nullable public static Locale LocaleNullable() { final TerminalContext.Context ctx = TerminalContext.get(false); - if (!ctx.isNull()) { - return ctx.getLocale(); - } - return LocaleContextHolder.getLocale(); + return ctx.isNull() ? null: ctx.getLocale(); } /** diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/event/EventPublishHelper.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/event/EventPublishHelper.java index 6ac0728be..455c688ef 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/event/EventPublishHelper.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/event/EventPublishHelper.java @@ -49,7 +49,7 @@ public class EventPublishHelper { *

* throws IllegalStateException if no globalPublisher * - * @see #hasAsyncGlobal + * @see #isPreparedGlobal */ public static final ApplicationEventPublisher AsyncGlobal = new GlobalPub(true); @@ -61,24 +61,48 @@ public class EventPublishHelper { */ public static final ApplicationEventPublisher AsyncWidely = new GlobalPub(false); - private static Executor executor; + private static Executor asyncExecutor; private static ApplicationEventPublisher springPublisher; private static ApplicationEventPublisher globalPublisher; - public static void setGlobalPublisher(ApplicationEventPublisher globalPublisher) { - EventPublishHelper.globalPublisher = globalPublisher; + public static void prepareAsyncExecutor(@NotNull Executor async) { + asyncExecutor = async; } - public static void setExecutor(Executor executor) { - EventPublishHelper.executor = executor; + public static void prepareSpringPublisher(@NotNull ApplicationEventPublisher spring) { + springPublisher = spring; } - public static void setSpringPublisher(ApplicationEventPublisher springPublisher) { - EventPublishHelper.springPublisher = springPublisher; + public static void prepareGlobalPublisher(@NotNull ApplicationEventPublisher global) { + globalPublisher = global; } - public static boolean hasAsyncGlobal() { - return globalPublisher != null; + /** + * whether global is prepared + */ + public static boolean isPreparedGlobal() { + return globalPublisher != null && asyncExecutor != null; + } + + /** + * whether app async is prepared + */ + public static boolean isPreparedAsync() { + return springPublisher != null && asyncExecutor != null; + } + + /** + * whether app sync is prepared + */ + public static boolean isPreparedSync() { + return springPublisher != null; + } + + /** + * whether this helper is fully prepared + */ + public static boolean isPrepared() { + return springPublisher != null && globalPublisher != null && asyncExecutor != null; } private static class SyncPub implements ApplicationEventPublisher { @@ -93,7 +117,7 @@ private static class AsyncPub implements ApplicationEventPublisher { @Override public void publishEvent(@NotNull Object event) { - executor.execute(() -> springPublisher.publishEvent(event)); + asyncExecutor.execute(() -> springPublisher.publishEvent(event)); } } @@ -105,7 +129,7 @@ private static class GlobalPub implements ApplicationEventPublisher { @Override public void publishEvent(@NotNull Object event) { if (globalPublisher != null) { - executor.execute(() -> globalPublisher.publishEvent(event)); + asyncExecutor.execute(() -> globalPublisher.publishEvent(event)); } else { if (strict) { @@ -114,7 +138,7 @@ public void publishEvent(@NotNull Object event) { else { log.warn("no globalPublisher, publish by spring async in no strict"); } - executor.execute(() -> springPublisher.publishEvent(event)); + asyncExecutor.execute(() -> springPublisher.publishEvent(event)); } } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/FastJsonHelper.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/FastJsonHelper.java index d390553ea..6d477bef8 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/FastJsonHelper.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/FastJsonHelper.java @@ -5,13 +5,14 @@ import com.alibaba.fastjson2.JSONPath; import com.alibaba.fastjson2.JSONReader; import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.core.ResolvableType; import org.springframework.core.convert.TypeDescriptor; import pro.fessional.mirana.lock.ArrayKey; -import pro.fessional.wings.silencer.enhance.TypeSugar; +import pro.fessional.wings.silencer.support.TypeSugar; import java.io.InputStream; import java.lang.reflect.Type; @@ -222,6 +223,23 @@ public static byte[] bytes(Object obj) { return JSON.toJSONBytes(obj, WingsWriter); } + /** + * Serialization using the wings convention, + * output as string wherever possible to ensure data precision, + * but not affecting Java type inverse parsing + */ + @Contract("!null,_->!null") + public static String string(Object obj, Filter... filters) { + if (obj == null) return null; + return JSON.toJSONString(obj, filters, WingsWriter); + } + + @Contract("!null,_->!null") + public static byte[] bytes(Object obj, Filter... filters) { + if (obj == null) return null; + return JSON.toJSONBytes(obj, filters, WingsWriter); + } + //// path private static final ConcurrentHashMap JsonPathCache = new ConcurrentHashMap<>(); diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ComposePropertyFilter.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ComposePropertyFilter.java new file mode 100644 index 000000000..a83deeea0 --- /dev/null +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ComposePropertyFilter.java @@ -0,0 +1,59 @@ +package pro.fessional.wings.slardar.fastjson.filter; + +import com.alibaba.fastjson2.filter.PropertyFilter; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + *

+ * delta - the default return of this
+ * andOr - compose by `and` or `or`
+ * 
+ * + * @author trydofor + * @since 2024-07-27 + */ +public class ComposePropertyFilter implements PropertyFilter { + + private final boolean delta; + private final boolean andOr; + private final List filters; + + public ComposePropertyFilter(boolean andOr, PropertyFilter... filters) { + this(true, andOr, Arrays.asList(filters)); + } + + public ComposePropertyFilter(boolean delta, boolean andOr, PropertyFilter... filters) { + this(delta, andOr, Arrays.asList(filters)); + } + + public ComposePropertyFilter(boolean andOr, @NotNull Collection filters) { + this(true, andOr, filters); + } + + @SuppressWarnings("unchecked") + public ComposePropertyFilter(boolean delta, boolean andOr, @NotNull Collection filters) { + this.delta = delta; + this.andOr = andOr; + this.filters = filters instanceof List ? (List) filters : new ArrayList<>(filters); + } + + @Override + public boolean apply(Object object, String name, Object value) { + for (PropertyFilter ft : filters) { + boolean b = ft.apply(object, name, value); + if (andOr) { + if (!b) return false; // and false + } + else { + if (b) return true; // or true + } + } + + return delta; + } +} diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ComposePropertyPreFilter.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ComposePropertyPreFilter.java new file mode 100644 index 000000000..d0d1f98a9 --- /dev/null +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ComposePropertyPreFilter.java @@ -0,0 +1,60 @@ +package pro.fessional.wings.slardar.fastjson.filter; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.PropertyPreFilter; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + *
+ * delta - the default return of this
+ * andOr - compose by `and` or `or`
+ * 
+ * + * @author trydofor + * @since 2024-07-27 + */ +public class ComposePropertyPreFilter implements PropertyPreFilter { + + private final boolean delta; + private final boolean andOr; + private final List filters; + + public ComposePropertyPreFilter(boolean andOr, PropertyPreFilter... filters) { + this(true, andOr, Arrays.asList(filters)); + } + + public ComposePropertyPreFilter(boolean delta, boolean andOr, PropertyPreFilter... filters) { + this(delta, andOr, Arrays.asList(filters)); + } + + public ComposePropertyPreFilter(boolean andOr, @NotNull Collection filters) { + this(true, andOr, filters); + } + + @SuppressWarnings("unchecked") + public ComposePropertyPreFilter(boolean delta, boolean andOr, @NotNull Collection filters) { + this.delta = delta; + this.andOr = andOr; + this.filters = filters instanceof List ? (List) filters : new ArrayList<>(filters); + } + + @Override + public boolean process(JSONWriter writer, Object source, String name) { + for (PropertyPreFilter ft : filters) { + boolean b = ft.process(writer, source, name); + if (andOr) { + if (!b) return false; // and false + } + else { + if (b) return true; // or true + } + } + + return delta; + } +} diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ExcludePropertyPreFilter.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ExcludePropertyPreFilter.java new file mode 100644 index 000000000..7a8787c56 --- /dev/null +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/fastjson/filter/ExcludePropertyPreFilter.java @@ -0,0 +1,75 @@ +package pro.fessional.wings.slardar.fastjson.filter; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.PropertyPreFilter; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +/** + *
+ * exclude the prop if
+ * * instance of class
+ * * name match regexp
+ * * name equal
+ * 
+ * + * @author trydofor + * @since 2024-07-27 + */ +public class ExcludePropertyPreFilter implements PropertyPreFilter { + + private final Set> clazz = new HashSet<>(); + private final Set equal = new HashSet<>(); + private final Set regex = new HashSet<>(); + + /** + *
+     * support exclude type,
+     * * Class - object is instance of
+     * * String - name equals
+     * * Pattern - name matches regexp
+     * * Collection - any of above type
+     * * Object[] - any of above type
+     * 
+ */ + public ExcludePropertyPreFilter(Object exclude) { + addExclude(exclude); + } + + protected void addExclude(Object exclude) { + if (exclude == null) { + return; + } + else if (exclude instanceof Class clz) { + clazz.add(clz); + } + else if (exclude instanceof String str) { + equal.add(str); + } + else if (exclude instanceof Pattern reg) { + regex.add(reg); + } + else if (exclude instanceof Collection col) { + for (Object o : col) addExclude(o); + } + else if (exclude instanceof Object[] arr) { + for (Object o : arr) addExclude(o); + } + } + + @Override + public boolean process(JSONWriter writer, Object source, String name) { + for (Class clz : clazz) { + if (clz.isInstance(source)) return false; + } + + for (Pattern ptn : regex) { + if (ptn.matcher(name).find()) return false; + } + + return !equal.contains(name); + } +} diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/httprest/okhttp/OkHttpTweakLogInterceptor.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/httprest/okhttp/OkHttpTweakLogInterceptor.java index 5625f4362..9a26de0c8 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/httprest/okhttp/OkHttpTweakLogInterceptor.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/httprest/okhttp/OkHttpTweakLogInterceptor.java @@ -6,6 +6,7 @@ import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; import okhttp3.logging.HttpLoggingInterceptor.Level; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.springframework.boot.logging.LogLevel; import pro.fessional.mirana.time.StopWatch; @@ -13,7 +14,10 @@ import pro.fessional.wings.silencer.watch.Watches; import java.io.IOException; +import java.util.Collection; import java.util.EnumMap; +import java.util.HashSet; +import java.util.Set; import static org.springframework.boot.logging.LogLevel.DEBUG; import static org.springframework.boot.logging.LogLevel.ERROR; @@ -43,10 +47,12 @@ public class OkHttpTweakLogInterceptor implements OkHttpInterceptor { public static final HttpLoggingInterceptor.Logger LoggerWarn = log::warn; private final EnumMap mapping = new EnumMap<>(LogLevel.class); + private final Set nopUrlToken = new HashSet<>(); - public OkHttpTweakLogInterceptor() { + public OkHttpTweakLogInterceptor(Collection nop) { resetMapping(); TweakLogger.asCoreLevel(log.getName()); + nopUrlToken.addAll(nop); } /** @@ -93,7 +99,19 @@ public void resetMapping() { @NotNull @Override public Response intercept(@NotNull Interceptor.Chain chain) throws IOException { - final LogLevel lvl = TweakLogger.currentLevel(log.getName()); + boolean off = false; + if (!nopUrlToken.isEmpty()) { + String url = chain.request().url().toString(); + for (String tkn : nopUrlToken) { + if (StringUtils.containsIgnoreCase(url, tkn)) { + log.debug("exclude intercept, token={}, url={}", tkn, url); + off = true; + break; + } + } + } + + final LogLevel lvl = off ? LogLevel.OFF : TweakLogger.currentLevel(log.getName()); final HttpLoggingInterceptor itc = mapping.get(lvl); final StopWatch current = Watches.current(); @@ -101,14 +119,14 @@ public Response intercept(@NotNull Interceptor.Chain chain) throws IOException { return itc.intercept(chain); } - // + // watch if the caller thread has watching final Request request = chain.request(); final String name = "OkHttp " + request.method() + " " + request.url(); - try (StopWatch.Watch watch = current.start(name)) { + try (StopWatch.Watch ignore = current.start(name)) { return itc.intercept(chain); } finally { - Watches.release(true, null); + Watches.release(true); } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/AesStringDeserializer.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/AesStringDeserializer.java index 31f59460a..ea7fc78b9 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/AesStringDeserializer.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/AesStringDeserializer.java @@ -15,7 +15,7 @@ import java.io.IOException; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.MaskingValue; +import static pro.fessional.wings.silencer.support.PropHelper.MaskingValue; /** * @author trydofor diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/AesStringSerializer.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/AesStringSerializer.java index 4c117f744..817efcf0d 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/AesStringSerializer.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/AesStringSerializer.java @@ -14,7 +14,7 @@ import java.io.IOException; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.MaskingValue; +import static pro.fessional.wings.silencer.support.PropHelper.MaskingValue; /** diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/EmptyValuePropertyFilter.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/EmptyValuePropertyFilter.java index ab472ef25..1892839bf 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/EmptyValuePropertyFilter.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/EmptyValuePropertyFilter.java @@ -83,6 +83,7 @@ public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider } catch (Exception ex) { if (log.isDebugEnabled()) { + // noinspection StringConcatenationArgumentToLogCall log.debug("Skipping '" + writer.getFullName() + "' on '" + pojo.getClass().getName() + "' as an exception was thrown when retrieving its value", ex); } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/JacksonHelper.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/JacksonHelper.java index b226579a2..79a31d93a 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/JacksonHelper.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/JacksonHelper.java @@ -16,7 +16,7 @@ import pro.fessional.mirana.text.WhiteUtil; import pro.fessional.mirana.time.DateFormatter; import pro.fessional.wings.silencer.datetime.DateTimePattern; -import pro.fessional.wings.silencer.enhance.TypeSugar; +import pro.fessional.wings.silencer.support.TypeSugar; import pro.fessional.wings.slardar.autozone.AutoZoneType; import pro.fessional.wings.slardar.autozone.json.JacksonLocalDateTimeDeserializer; import pro.fessional.wings.slardar.autozone.json.JacksonLocalDateTimeSerializer; @@ -204,26 +204,32 @@ public static T buildWings(@NotNull T mapper) { private static ObjectMapper JsonWings = JsonPlain; private static XmlMapper XmlWings = XmlPlain; + private static boolean wingsPrepared = false; - protected static void bindJsonWings(@NotNull ObjectMapper mapper) { - JsonWings = buildWings(mapper); + protected static void prepareWings(@NotNull ObjectMapper json, @NotNull XmlMapper xml) { + JsonWings = buildWings(json); + XmlWings = buildWings(xml); + wingsPrepared = true; } - protected static void bindXmlWings(@NotNull XmlMapper mapper) { - XmlWings = buildWings(mapper); - } private static ObjectMapper JsonBean = null; private static XmlMapper XmlBean = null; + private static boolean beanPrepared = false; - protected static void bindJsonBean(@NotNull ObjectMapper mapper) { - JsonBean = mapper; + protected static void prepareBean(@NotNull ObjectMapper json, @NotNull XmlMapper xml) { + JsonBean = json; + XmlBean = xml; + beanPrepared = true; } - protected static void bindXmlBean(@NotNull XmlMapper mapper) { - XmlBean = mapper; + public static boolean isPrepared(@NotNull Style style) { + return switch (style) { + case Plain -> true; + case Wings -> wingsPrepared; + case Bean -> beanPrepared; + }; } - //// /** diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/ResourceSerializer.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/ResourceSerializer.java index 8bbf9ff4a..606020035 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/ResourceSerializer.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/ResourceSerializer.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import org.springframework.core.io.Resource; -import pro.fessional.wings.silencer.spring.help.CommonPropHelper; +import pro.fessional.wings.silencer.support.PropHelper; import java.io.IOException; @@ -15,7 +15,7 @@ public class ResourceSerializer extends JsonSerializer { @Override public void serialize(Resource value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - final String res = CommonPropHelper.toString(value); + final String res = PropHelper.stringResource(value); gen.writeString(res); } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/MonitorTask.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/MonitorTask.java index 6927d4657..28acd78f3 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/MonitorTask.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/MonitorTask.java @@ -3,13 +3,14 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; +import pro.fessional.mirana.stat.JvmStat; import pro.fessional.wings.silencer.spring.help.ApplicationContextHelper; import pro.fessional.wings.slardar.context.Now; -import java.lang.management.ManagementFactory; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -21,7 +22,7 @@ */ @Slf4j @Setter @Getter -public class MonitorTask implements InitializingBean { +public class MonitorTask implements InitializingBean, DisposableBean { @Setter(onMethod_ = { @Autowired }) private List warnMetrics = Collections.emptyList(); @@ -56,6 +57,7 @@ public void metric(Map> warns) { log.debug("check {} warns by {}", sz, nm); } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.warn("failed to metric, name=" + nm, e); } } @@ -75,13 +77,13 @@ public void report(Map> warns) { log.warn("the app name of report should NOT blank"); } - String jvm = ManagementFactory.getRuntimeMXBean().getName(); + String jvm = JvmStat.jvmName(); if (jvm != null) jvm = jvm.replace("@", "_"); for (WarnReport report : warnReports) { final String rpt = report.getClass().getName(); try { - log.debug("check {} warns by {}", warns.size(), rpt); + log.debug("report {} warns by {}", warns.size(), rpt); final WarnReport.Sts sts = report.report(app, jvm, warns); if (sts == WarnReport.Sts.Fail) { log.warn("failed to report={}", rpt); @@ -91,20 +93,25 @@ public void report(Map> warns) { } } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.warn("failed to report, name=" + rpt, e); } } } + @Override + public void destroy() { + reportHook("shutting"); + } + @Override public void afterPropertiesSet() { - if (hookSelf) { - reportHook("started"); - Runtime.getRuntime().addShutdownHook(new Thread(() -> reportHook("shutting"))); - } + reportHook("started"); } private void reportHook(String key) { + if (!hookSelf) return; + try { WarnMetric.Warn wn = new WarnMetric.Warn(); wn.setType(WarnMetric.Type.Text); diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/metric/JvmMetric.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/metric/JvmMetric.java index 85b0b3f75..c064e679c 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/metric/JvmMetric.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/metric/JvmMetric.java @@ -101,9 +101,9 @@ public static class Rule { public static final String Key$processCent = Key + ".process-cent"; /** - * process Cpu Load without percentage, range `[0, 100*cores]` + * process Cpu Load without percentage, range `[0, 100*cores]`, default=80x4 */ - private int processLoad = 150; + private int processLoad = 320; public static final String Key$processLoad = Key + ".process-load"; /** diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/metric/LogMetric.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/metric/LogMetric.java index 4fca8da99..69805c9e7 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/metric/LogMetric.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/metric/LogMetric.java @@ -62,7 +62,7 @@ public String getKey() { } final long from = readLastForm(); - final LogStat.Stat stat = LogStat.stat(rule.file, from, rule.getPreview(), rule.getRuntimeKeys()); + final LogStat.Stat stat = LogStat.stat(rule.file, from, rule.getPreview(), rule.getSection(), rule.genStatKey()); log.debug("LogStat-{}, stat={}", key, stat); writeLastFrom(stat); @@ -224,6 +224,14 @@ public static class Rule { private int preview = 10; public static final String Key$preview = Key + ".preview"; + /** + * section size of intended lines + * + * @see #Key$section + */ + private int section = 50; + public static final String Key$section = Key + ".section"; + /** * log charset */ @@ -273,26 +281,38 @@ public String maskKey(String kw) { * Auto remove a pair of quotes, construct bytes by charset */ @SneakyThrows - public List getRuntimeKeys() { + public List genStatKey() { List rst = new ArrayList<>(); - if (level != null) { - for (String s : level) { - String kw = trimKey(s, false); - if (kw.isEmpty()) continue; - LogStat.Word wd = new LogStat.Word(); - wd.range2 = bound; - wd.bytes = kw.getBytes(charset); - rst.add(wd); + for (String kw : genRuleKey()) { + LogStat.Word wd = new LogStat.Word(); + wd.range2 = bound; + wd.bytes = kw.getBytes(charset); + rst.add(wd); + } + return rst; + } + + /** + * Auto remove a pair of quotes, merge level and its keyword + */ + public List genRuleKey() { + List rst = new ArrayList<>(); + if (level == null) return rst; + + for (String s : level) { + String kw = trimKey(s, false); + if (!kw.isEmpty()) { + rst.add(kw); } - for (String s : keyword) { - String kw = trimKey(s, false); - if (kw.isEmpty()) continue; - LogStat.Word wd = new LogStat.Word(); - wd.range1 = bound; - wd.bytes = kw.getBytes(charset); - rst.add(wd); + } + + for (String s : keyword) { + String kw = trimKey(s, false); + if (kw.isEmpty()) { + rst.add(kw); } } + return rst; } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/report/DingTalkReport.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/report/DingTalkReport.java index 616d6bb9c..a4a42f1aa 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/report/DingTalkReport.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/monitor/report/DingTalkReport.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import static pro.fessional.wings.silencer.datetime.DateTimePattern.FMT_FULL_19Z; @@ -28,6 +29,7 @@ public class DingTalkReport implements WarnReport { private final String dingConfig; private final DingTalkNotice dingTalkNotice; + private final AtomicLong counter = new AtomicLong(1); protected String gitInfo = null; @@ -81,7 +83,7 @@ public Sts report(String appName, String jvmName, Map keys = new HashSet<>(); private final Cache cache; - @Autowired - public LogViewer(SlardarMonitorProp prop) { - this(prop.getView()); - } - - public LogViewer(LogConf conf) { + public LogViewer(@NotNull LogConf conf, @NotNull Collection keys) { this.conf = conf; this.cache = WingsCache2k.builder(LogViewer.class, "cache", 2_000, conf.getAlive(), null, String.class, String.class).build(); + this.keys.addAll(keys); } - @Operation(summary = "Alarm logs can be viewed in conjunction with alarm notifications when self-monitoring is enabled.", description = """ - # Usage - Pass the log id to view the log. - ## Params - * @param id - log id, max 2k caches in 36H - ## Returns - * @return {200 | string} log context or empty""") - @GetMapping(value = "${" + LogConf.Key$mapping + "}") - public void view(@RequestParam("id") String id, HttpServletResponse res) throws IOException { + public void view(String id, OutputStream output) throws IOException { if (id == null) return; final String log = cache.get(id); if (log == null) return; @@ -69,11 +52,10 @@ public void view(@RequestParam("id") String id, HttpServletResponse res) throws try (FileInputStream fis = new FileInputStream(file)) { final long len = conf.getLength().toBytes(); - final ServletOutputStream outputStream = res.getOutputStream(); - IOUtils.copyLarge(fis, res.getOutputStream(), 0L, len); + IOUtils.copyLarge(fis, output, 0L, len); if (file.length() - len > 0) { final String more = String.format("\n\n...... %,d / %,d bytes", len, file.length()); - outputStream.write(more.getBytes()); + output.write(more.getBytes()); } } } @@ -116,37 +98,55 @@ public void filter(Map> warns) { } } - private boolean canIgnoreHead(String out) { - if (conf.getIgnore().isEmpty()) return false; + protected boolean canIgnoreHead(String out) { + final Collection ignores = conf.getIgnore().values(); + if (ignores.isEmpty()) return false; long max = conf.getLength().toBytes(); final File file = new File(out); if (file.length() > max || !file.canRead()) return false; try (BufferedReader reader = new BufferedReader(new FileReader(file))) { - final Collection ign = conf.getIgnore().values(); String line; int tol = 0; int cnt = 0; - out: + final Pattern head = conf.getHeader(); while ((line = reader.readLine()) != null && max > 0) { - if (line.isEmpty()) { + max -= line.length() + 1; // loose calculation + + if (ignoreLine(line, head)) { continue; } - // - max -= line.length(); // loose calculation + + // only match header line tol++; - for (String s : ign) { + for (String s : ignores) { if (line.contains(s)) { cnt++; - continue out; + break; } } } return tol == cnt; } catch (Exception e) { + return false; + } + } + + private boolean ignoreLine(String line, Pattern head) { + if (line.isEmpty()) return true; + + if (head != null && !head.matcher(line).find()) { return true; } + + for (String key : keys) { + if (line.contains(key)) { + return false; + } + } + + return true; } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/notice/DingTalkConf.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/notice/DingTalkConf.java index c8ad2d86e..a5327ae30 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/notice/DingTalkConf.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/notice/DingTalkConf.java @@ -3,15 +3,14 @@ import lombok.Data; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import pro.fessional.mirana.cond.IfSetter; import pro.fessional.wings.silencer.encrypt.SecretProvider; +import pro.fessional.wings.silencer.support.PropHelper; import pro.fessional.wings.slardar.jackson.AesString; import java.util.HashMap; import java.util.Map; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.mergeNotValue; -import static pro.fessional.wings.silencer.spring.help.CommonPropHelper.notValue; - /** * @author trydofor * @since 2023-02-21 @@ -72,36 +71,30 @@ public String getValidWebhook() { } } - /** - * use all properties from that - */ - public void adopt(DingTalkConf that) { - if (that == null) return; - - dryrun = that.dryrun; - webhookUrl = that.webhookUrl; - digestSecret = that.digestSecret; - accessToken = that.accessToken; - noticeKeyword = that.noticeKeyword; - msgType = that.msgType; - noticeMobiles.putAll(that.noticeMobiles); - } + public static final IfSetter ConfSetter = (thiz, that, absent, present) -> { + if (that == null) return thiz; + + if (absent == IfSetter.Absent.Invalid) { + if (thiz.dryrun == null) thiz.dryrun = that.dryrun; + if (PropHelper.invalid(thiz.webhookUrl)) thiz.webhookUrl = that.webhookUrl; + if (PropHelper.invalid(thiz.digestSecret)) thiz.digestSecret = that.digestSecret; + if (PropHelper.invalid(thiz.accessToken)) thiz.accessToken = that.accessToken; + if (PropHelper.invalid(thiz.noticeKeyword)) thiz.noticeKeyword = that.noticeKeyword; + if (PropHelper.invalid(thiz.msgType)) thiz.msgType = that.msgType; + PropHelper.mergeToInvalid(thiz.noticeMobiles, that.noticeMobiles); + } + else { + thiz.dryrun = that.dryrun; + thiz.webhookUrl = that.webhookUrl; + thiz.digestSecret = that.digestSecret; + thiz.accessToken = that.accessToken; + thiz.noticeKeyword = that.noticeKeyword; + thiz.msgType = that.msgType; + thiz.noticeMobiles.putAll(that.noticeMobiles); + } - /** - * if this.property is invalid, then use that.property. - * except for 'noticeMobiles' which merge value only if key matches. - */ - public void merge(DingTalkConf that) { - if (that == null) return; - - if (dryrun == null) dryrun = that.dryrun; - if (notValue(webhookUrl)) webhookUrl = that.webhookUrl; - if (notValue(digestSecret)) digestSecret = that.digestSecret; - if (notValue(accessToken)) accessToken = that.accessToken; - if (notValue(noticeKeyword)) noticeKeyword = that.noticeKeyword; - if (notValue(msgType)) msgType = that.msgType; - mergeNotValue(noticeMobiles, that.noticeMobiles); - } + return thiz; + }; public interface Loader { /** diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/notice/DingTalkNotice.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/notice/DingTalkNotice.java index 42bfd5cad..339bf86dd 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/notice/DingTalkNotice.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/notice/DingTalkNotice.java @@ -1,5 +1,6 @@ package pro.fessional.wings.slardar.notice; +import com.alibaba.ttl.threadpool.TtlExecutors; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -10,6 +11,7 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -26,6 +28,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static java.nio.charset.StandardCharsets.UTF_8; @@ -41,7 +44,7 @@ */ @RequiredArgsConstructor @Slf4j -public class DingTalkNotice implements SmallNotice, InitializingBean { +public class DingTalkNotice implements SmallNotice, InitializingBean, DisposableBean { @NotNull private final Call.Factory callFactory; @@ -50,6 +53,7 @@ public class DingTalkNotice implements SmallNotice, InitializingBe @Setter(onMethod_ = { @Autowired(required = false), @Qualifier(DEFAULT_TASK_SCHEDULER_BEAN_NAME) }) private Executor executor; + private boolean innerExecutor = false; @Setter(onMethod_ = { @Autowired(required = false) }) @Getter @@ -64,8 +68,8 @@ public DingTalkConf defaultConfig() { @Override public DingTalkConf combineConfig(@Nullable DingTalkConf that) { final DingTalkConf newConf = new DingTalkConf(); - newConf.adopt(that); - newConf.merge(configProp.getDefault()); + DingTalkConf.ConfSetter.toAny(newConf, that); + DingTalkConf.ConfSetter.toInvalid(newConf, configProp.getDefault()); return newConf; } @@ -173,7 +177,15 @@ public void emit(DingTalkConf config, String subject, String content) { public void afterPropertiesSet() { if (executor == null) { log.warn("should reuse autowired thread pool"); - executor = Executors.newSingleThreadExecutor(); + executor = TtlExecutors.getTtlExecutorService(Executors.newWorkStealingPool(2)); + innerExecutor = true; + } + } + + @Override + public void destroy() { + if (innerExecutor && executor instanceof ExecutorService es) { + es.shutdown(); } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarAsyncConfiguration.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarAsyncConfiguration.java index 23b9d4ac6..d266b4065 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarAsyncConfiguration.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarAsyncConfiguration.java @@ -123,10 +123,7 @@ public TaskSchedulerHelper taskSchedulerHelper( @Qualifier(DEFAULT_TASK_SCHEDULER_BEAN_NAME) ThreadPoolTaskScheduler scheduled, ThreadPoolTaskSchedulerBuilder scheduledBuilder) { log.info("Slardar spring-bean taskSchedulerHelper"); - return new TaskSchedulerHelper(scheduled, fast) {{ - FastBuilder = fastSchedulerBuilder; - ScheduledBuilder = scheduledBuilder; - }}; + return new TaskSchedulerHelper(scheduled, fast, fastSchedulerBuilder, scheduledBuilder) {}; } @Bean @@ -143,9 +140,6 @@ public AsyncHelper asyncHelper( final Executor exec = TtlExecutors.getTtlExecutor(executor); AsyncTaskExecutor liteExecutor = new ConcurrentTaskExecutor(exec); - return new AsyncHelper(asyncExec, appExec) {{ - ExecutorBuilder = executorBuilder; - LiteExecutor = liteExecutor; - }}; + return new AsyncHelper(asyncExec, appExec, executorBuilder, liteExecutor) {}; } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarCacheConfiguration.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarCacheConfiguration.java index bbbcecd39..e31d24d9e 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarCacheConfiguration.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarCacheConfiguration.java @@ -125,7 +125,7 @@ public CacheManager cacheManager() { } // name or resolver name - WingsCacheHelper.putManagers(managerMap); + new WingsCacheHelper(managerMap){}; CacheManager pre = null; String cnm = null; diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarEventConfiguration.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarEventConfiguration.java index f38106ad9..b03c59212 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarEventConfiguration.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarEventConfiguration.java @@ -64,9 +64,9 @@ public ApplicationStartedEventRunner eventPublishHelperRunner( @Qualifier(slardarEventExecutor) Executor executor) { log.info("Slardar spring-runs eventPublishHelperRunner"); return new ApplicationStartedEventRunner(WingsOrdered.Lv4Application, ignored -> { - EventPublishHelper.setExecutor(executor); + EventPublishHelper.prepareAsyncExecutor(executor); log.info("Slardar conf eventPublishHelper ApplicationEventPublisher=" + publisher.getClass()); - EventPublishHelper.setSpringPublisher(publisher); + EventPublishHelper.prepareSpringPublisher(publisher); log.info("Slardar conf eventPublishHelper ApplicationEventMulticaster=" + multicaster.getClass()); if (multicaster instanceof SimpleApplicationEventMulticaster mc) { try { diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarMonitorConfiguration.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarMonitorConfiguration.java index 21a8024e5..22522ebb4 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarMonitorConfiguration.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarMonitorConfiguration.java @@ -10,10 +10,10 @@ import org.springframework.boot.info.GitProperties; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; import org.springframework.util.unit.DataSize; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.slardar.monitor.MonitorTask; @@ -27,9 +27,9 @@ import java.io.File; import java.util.Map; -import static pro.fessional.wings.silencer.spring.help.VersionInfoHelper.branch; -import static pro.fessional.wings.silencer.spring.help.VersionInfoHelper.buildDate; -import static pro.fessional.wings.silencer.spring.help.VersionInfoHelper.commitIdShort; +import static pro.fessional.wings.silencer.support.InspectHelper.branch; +import static pro.fessional.wings.silencer.support.InspectHelper.buildDate; +import static pro.fessional.wings.silencer.support.InspectHelper.commitIdShort; /** * @author trydofor @@ -44,9 +44,8 @@ public class SlardarMonitorConfiguration { // Dynamic register Bean LogMetric - @Configuration(proxyBeanMethods = false) + @Component @ConditionalWingsEnabled(abs = SlardarEnabledProp.Key$monitorLog) - @ComponentScan(basePackageClasses = MonitorTask.class) public static class LogMonitor implements BeanFactoryPostProcessor, EnvironmentAware { private SlardarMonitorProp slardarMonitorProp; diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarTweakConfiguration.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarTweakConfiguration.java index 655a6c51b..4b738313d 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarTweakConfiguration.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarTweakConfiguration.java @@ -5,8 +5,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.silencer.support.PropHelper; import pro.fessional.wings.slardar.event.tweak.TweakEventListener; import pro.fessional.wings.slardar.httprest.okhttp.OkHttpTweakLogInterceptor; +import pro.fessional.wings.slardar.spring.prop.SlardarOkhttpProp; + +import java.util.Collection; /** * @author trydofor @@ -20,9 +24,10 @@ public class SlardarTweakConfiguration { @Bean @ConditionalWingsEnabled - public OkHttpTweakLogInterceptor okhttpTweakLogInterceptor() { + public OkHttpTweakLogInterceptor okhttpTweakLogInterceptor(SlardarOkhttpProp prop) { log.info("Slardar spring-bean okhttpTweakLogInterceptor"); - return new OkHttpTweakLogInterceptor(); + Collection nop = PropHelper.onlyValid(prop.getInterceptNop()).values(); + return new OkHttpTweakLogInterceptor(nop); } @Bean diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarAsyncProp.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarAsyncProp.java index 23f8aedd3..5aa5406c7 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarAsyncProp.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarAsyncProp.java @@ -27,10 +27,10 @@ public class SlardarAsyncProp { /** * fast thread pool * - * @see #Key$heavy + * @see #Key$fast */ private TaskSchedulingProperties fast; - public static final String Key$heavy = Key + ".fast"; + public static final String Key$fast = Key + ".fast"; /** * executor prefix @@ -45,7 +45,7 @@ public static class ExecPrefix { /** * AsyncHelper lite Pool */ - private String lite = "lite-"; + private String lite = "lit-exec-"; /** * Callable MVC mapping */ diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarMonitorProp.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarMonitorProp.java index 0a4dfbaf8..cc3bfe3e2 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarMonitorProp.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarMonitorProp.java @@ -7,6 +7,7 @@ import pro.fessional.wings.slardar.monitor.viewer.LogConf; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.Map; /** @@ -23,12 +24,12 @@ public class SlardarMonitorProp { public static final String Key = "wings.slardar.monitor"; /** - * Monitor its own cron, `-` means stop this cron, default 10 minutes. + * Monitor its own cron, `-` means stop this cron, default 17 minutes. * * @see #Key$cron */ public static final String Key$cron = Key + ".cron"; - private String cron = "0 */10 * * * ?"; + private String cron = "17 */17 * * * ?"; /** * whether to send notice for the start and stop of its own jvm hook @@ -70,4 +71,15 @@ public class SlardarMonitorProp { */ private String dingNotice = "monitor"; public static final String Key$dingNotice = Key + ".ding-notice"; + + /** + * generate all log's rule keys + */ + public LinkedHashSet genRuleKey() { + LinkedHashSet rst = new LinkedHashSet<>(); + for (LogMetric.Rule rl : log.values()) { + rst.addAll(rl.genRuleKey()); + } + return rst; + } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarOkhttpProp.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarOkhttpProp.java index 528fe5bc8..3e8fb843e 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarOkhttpProp.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarOkhttpProp.java @@ -4,6 +4,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import java.io.File; +import java.util.Collections; +import java.util.Map; /** * okhttp as one word in string/config @@ -130,4 +132,13 @@ public class SlardarOkhttpProp { */ private boolean redirectNop = false; public static final String Key$redirectNop = Key + ".redirect-nop"; + + /** + * do NOT intercept if the token contained in URL + * + * @see #Key$interceptNop + */ + private Map interceptNop = Collections.emptyMap(); + public static final String Key$interceptNop = Key + ".intercept-nop"; + } diff --git a/wings/slardar/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/slardar/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 87d69907d..cb361d1a6 100644 --- a/wings/slardar/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/slardar/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,27 +1,23 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarAsyncAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration.applicationTaskExecutor", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration.slardarHeavyScheduler", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration.asyncHelper", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration.slardarFastScheduler", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration.taskExecutor", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration.taskScheduler", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration.taskSchedulerHelper", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarCacheAutoConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarAsyncConfiguration.ttlTaskDecorator", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarCacheConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarCacheConfiguration$CacheAop", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarCacheConfiguration$CacheMgr", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarCacheConfiguration.cache2kCacheManager", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDateTimeConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDateTimeConfiguration.localDateStringConverter", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarDateTimeConfiguration.localDateTimeStringConverter", "type": "java.lang.Boolean"}, @@ -53,11 +49,11 @@ {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonConfiguration.emptyValuePropertyFilter", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonConfiguration.i18nResultPropertyFilter", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonConfiguration$LogMonitor", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.monitor-log for short."}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonConfiguration.dingTalkReport", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonConfiguration.jvmMonitor", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.monitor-jvm for short."}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarJacksonConfiguration.monitorTask", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarMonitorConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarMonitorConfiguration$LogMonitor", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.monitor-log for short."}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarMonitorConfiguration.dingTalkReport", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarMonitorConfiguration.jvmMonitor", "type": "java.lang.Boolean", "description": "wings.enabled.slardar.monitor-jvm for short."}, + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarMonitorConfiguration.monitorTask", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOkhttpConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarOkhttpConfiguration.okhttpClient", "type": "java.lang.Boolean"}, @@ -68,30 +64,13 @@ {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarTweakConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarTweakConfiguration.okhttpTweakLogInterceptor", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarTweakConfiguration.tweakEventListener", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.bean.SlardarTweakConfiguration.tweakEventListener", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarAsyncAutoConfiguration", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarAutoConfiguration", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.slardar.spring.conf.SlardarCacheAutoConfiguration", "type": "java.lang.Boolean"} ], - "hints": [ - { - "name": "wings.slardar.cache.primary", - "values": [ - {"value": "Memory", "description": "jvm cache, cache2k by default."}, - {"value": "Server", "description": "server/distributed cache, hazelcast by default."} - ] - }, - { - "name": "wings.slardar.cache.level.keys", - "values": [ - {"value": "program", "description": "program level caching, ttl available in app running."}, - {"value": "general", "description": "general level 24 hours."}, - {"value": "service", "description": "service level 1 hour."}, - {"value": "session", "description": "session, 10 minutes."} - ] - }, - { - "name": "wings.slardar.cache.level.values", - "providers": [ - {"name": "handle-as", "parameters": {"target": "pro.fessional.wings.slardar.spring.prop.SlardarCacheProp$Conf"}} - ] - } - ] -} + "hints": [] +} \ No newline at end of file diff --git a/wings/slardar/src/main/resources/wings-conf/wings-monitor-79.properties b/wings/slardar/src/main/resources/wings-conf/wings-monitor-79.properties index 79ee26c42..c15954352 100644 --- a/wings/slardar/src/main/resources/wings-conf/wings-monitor-79.properties +++ b/wings/slardar/src/main/resources/wings-conf/wings-monitor-79.properties @@ -1,7 +1,7 @@ ## Setting of app builtin simple monitoring, `-1` in the threshold value means ignore. -## Monitor its own cron, `-` means stop this cron, default 10 minutes. -wings.slardar.monitor.cron=0 */10 * * * ? +## Monitor its own cron, `-` means stop this cron, default 17 minutes. +wings.slardar.monitor.cron=17 */17 * * * ? ## whether to send notice for the start and stop of its own jvm hook wings.slardar.monitor.hook=true @@ -15,8 +15,8 @@ wings.slardar.monitor.jvm.system-load=-1 ## process system Cpu Load with percentage to ## the entire system with all cores, range `[0, 100]` wings.slardar.monitor.jvm.process-cent=-1 -## process Cpu Load without percentage, range `[0, 100*cores]` -wings.slardar.monitor.jvm.process-load=150 +## process Cpu Load without percentage, range `[0, 100*cores]`, default=80x4 +wings.slardar.monitor.jvm.process-load=320 ## process Mem Load, range `[0,100]` wings.slardar.monitor.jvm.memory-load=85 ## total threads in jvm. @@ -49,6 +49,8 @@ wings.slardar.monitor.log.default.level=' WARN ',' ERROR ' wings.slardar.monitor.log.default.keyword= ## preview lines after found keyword wings.slardar.monitor.log.default.preview=10 +## section size of intended lines +wings.slardar.monitor.log.default.section=50 ## log charset wings.slardar.monitor.log.default.charset=UTF8 ## delete scanned files older than N days, `-1` means no cleaning @@ -69,6 +71,8 @@ wings.slardar.monitor.view.alive=36H wings.slardar.monitor.view.length=1MB ## host or ip for external access. wings.slardar.monitor.view.domain=http://${server.address:localhost}:${server.port:80} +## regexp of section header, e.g. `2023-02-04T11:09:32.692+08:00`, `2024-07-23 01:31:59.063` +wings.slardar.monitor.view.header=^\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}[-+.:0-9]*\\s+ ## ignored alert string in logs. ## kotlin is support, but not really used @@ -81,8 +85,8 @@ wings.slardar.monitor.view.ignore[AutoLog-Switch]=Auto Switch the following Appe wings.slardar.monitor.view.ignore[No-MessageSource]=not found for MessageSource ## PersistenceProviderResolverHolder, Using jooq can logging.level.javax.persistence.spi=ERROR wings.slardar.monitor.view.ignore[Jpa-Persistence]=.persistence.spi::No valid providers found -## UT005071: Undertow request failed HttpServerExchange{ CONNECT ; CONNECT -wings.slardar.monitor.view.ignore[UT005071-CONNECT]=UT005071: Undertow request failed HttpServerExchange{ CONNECT +## UT005071: Undertow request failed HttpServerExchange{ CONNECT +wings.slardar.monitor.view.ignore[UT005071-CONNECT]=UT005071: Undertow request failed HttpServerExchange wings.slardar.monitor.view.ignore[Spring-WebIgnore]=You are asking Spring Security to ignore ## use DingTalk bot by default with the key `monitor`. diff --git a/wings/slardar/src/main/resources/wings-conf/wings-okhttp-79.properties b/wings/slardar/src/main/resources/wings-conf/wings-okhttp-79.properties index d7fd146b0..03a91016e 100644 --- a/wings/slardar/src/main/resources/wings-conf/wings-okhttp-79.properties +++ b/wings/slardar/src/main/resources/wings-conf/wings-okhttp-79.properties @@ -37,3 +37,6 @@ wings.slardar.okhttp.host-cookie = true ## whether to temporarily do nothing when follow-redirect. wings.slardar.okhttp.redirect-nop = false + +## do NOT intercept if the token contained in URL +wings.slardar.okhttp.intercept-nop[ding-talk] = oapi.dingtalk.com \ No newline at end of file diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestCachingService.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestCachingService.java index 7d6865b46..bf45b555d 100644 --- a/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestCachingService.java +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestCachingService.java @@ -14,9 +14,11 @@ */ @Service @Slf4j -@CacheConfig(cacheNames = WingsCache.Level.Service + "TestCachingService", cacheManager = WingsCache.Manager.Memory) +@CacheConfig(cacheNames = TestCachingService.CacheName, cacheManager = WingsCache.Manager.Memory) public class TestCachingService { + public static final String CacheName = WingsCache.Level.Service + "TestCachingService"; + @Cacheable public Key cache(Key key) { return key; diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/cache/spring/WingsCacheInterceptorTest.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/cache/spring/WingsCacheInterceptorTest.java index 00337fe1b..12e780463 100644 --- a/wings/slardar/src/test/java/pro/fessional/wings/slardar/cache/spring/WingsCacheInterceptorTest.java +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/cache/spring/WingsCacheInterceptorTest.java @@ -7,7 +7,11 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; import pro.fessional.wings.slardar.app.service.TestCachingService; +import pro.fessional.wings.slardar.app.service.TestCachingService.Key; +import pro.fessional.wings.slardar.cache.SimpleCacheTemplate; +import pro.fessional.wings.slardar.cache.WingsCache; /** * @author trydofor @@ -17,40 +21,67 @@ @Slf4j class WingsCacheInterceptorTest { - @Setter(onMethod_ = {@Autowired}) + private final SimpleCacheTemplate cacheTemplate = new SimpleCacheTemplate<>( + WingsCache.Manager.Memory, TestCachingService.CacheName); + + @Setter(onMethod_ = { @Autowired }) protected TestCachingService testCachingService; + @Autowired + public void setApplicationContext(ApplicationContext applicationContext) { + this.cacheTemplate.setBeanFactory(applicationContext); + } + @Test @TmsLink("C13119") void doEvict() { - TestCachingService.Key k1a = new TestCachingService.Key("key1"); - TestCachingService.Key k1b = new TestCachingService.Key("key" + 1); + Key k1a = new Key("key1"); + Key k1b = new Key("key" + 1); { - TestCachingService.Key v1a = testCachingService.cache(k1a); + Key v1a = testCachingService.cache(k1a); Assertions.assertSame(k1a, v1a); - TestCachingService.Key v1b = testCachingService.cache(k1b); + Key v1b = testCachingService.cache(k1b); Assertions.assertSame(k1a, v1b); testCachingService.evict(new CacheEvictKey()); - TestCachingService.Key v1c = testCachingService.cache(k1b); + Key v1c = testCachingService.cache(k1b); Assertions.assertSame(k1b, v1c); } - TestCachingService.Key k2a = new TestCachingService.Key("key2"); - TestCachingService.Key k2b = new TestCachingService.Key("key" + 2); + Key k2a = new Key("key2"); + Key k2b = new Key("key" + 2); { - TestCachingService.Key v2a = testCachingService.cache(k2a); + Key v2a = testCachingService.cache(k2a); Assertions.assertSame(k2a, v2a); testCachingService.evict(new CacheEvictKey().add(k1a).add(k2a)); - TestCachingService.Key v1a = testCachingService.cache(k1a); + Key v1a = testCachingService.cache(k1a); Assertions.assertSame(k1a, v1a); - TestCachingService.Key v2b = testCachingService.cache(k2b); + Key v2b = testCachingService.cache(k2b); + Assertions.assertSame(k2b, v2b); + } + + Key k3a = new Key("key3"); + Key k3b = new Key("key" + 3); + Key k3c = new Key("0key3".substring(1)); + + { + // assert same cache + Key v2b = cacheTemplate.getArgKey(k2a); Assertions.assertSame(k2b, v2b); + + // assert action + cacheTemplate.putArgKey(k3a, k3a); + Key v3a = cacheTemplate.getArgKey(k3b); + Assertions.assertSame(k3a, v3a); + + cacheTemplate.evictArgKey(k3a); + Key v3c = cacheTemplate.getArgKey(() -> k3c, k3b); + Assertions.assertSame(k3c, v3c); } } } \ No newline at end of file diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/fastjson/FastJsonTest.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/fastjson/FastJsonTest.java index 4d7519401..8a9e53365 100644 --- a/wings/slardar/src/test/java/pro/fessional/wings/slardar/fastjson/FastJsonTest.java +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/fastjson/FastJsonTest.java @@ -119,6 +119,9 @@ public void testJsonPath() { String json = FastJsonHelper.string(r); JSONObject obj = FastJsonHelper.object(json); + String json2 = FastJsonHelper.string(r,null); + Assertions.assertEquals(json, json2); + JSONPath p1 = FastJsonHelper.path("$.success"); JSONPath p2 = FastJsonHelper.path("$.success"); Assertions.assertSame(p1, p2); diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/json/TypeReferenceTest.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/json/TypeReferenceTest.java index 188430efd..b6237da34 100644 --- a/wings/slardar/src/test/java/pro/fessional/wings/slardar/json/TypeReferenceTest.java +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/json/TypeReferenceTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; import org.springframework.core.ResolvableType; import org.springframework.core.convert.TypeDescriptor; -import pro.fessional.wings.silencer.enhance.TypeSugar; +import pro.fessional.wings.silencer.support.TypeSugar; import java.lang.reflect.Type; import java.util.List; diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/monitor/viewer/LogViewerTest.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/monitor/viewer/LogViewerTest.java new file mode 100644 index 000000000..7ae9e7131 --- /dev/null +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/monitor/viewer/LogViewerTest.java @@ -0,0 +1,82 @@ +package pro.fessional.wings.slardar.monitor.viewer; + +import io.qameta.allure.TmsLink; +import lombok.Setter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import pro.fessional.wings.slardar.spring.prop.SlardarMonitorProp; + +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * @author trydofor + * @since 2024-07-23 + */ +@SpringBootTest +class LogViewerTest { + + @Setter(onMethod_ = { @Autowired }) + protected SlardarMonitorProp slardarMonitorProp; + + @TmsLink("13128") + @Test + void canIgnoreHead() throws Exception { + final LogViewer lv = new LogViewer(slardarMonitorProp.getView(), slardarMonitorProp.genRuleKey()); + + final Path tmp0 = Files.createTempFile("test-", null); + tmp0.toFile().deleteOnExit(); + Files.writeString(tmp0, """ + ######### #1 KEYWORD: ERROR ######### + 2024-08-28 07:01:54.441 ERROR 3306238 --- [admin-test] [XNIO-1 I/O-2] io.undertow.request : UT005071: Undertow request failed HttpServerExchange{ CONNECT eth0.me:443} + + java.lang.IllegalArgumentException: UT000068: Servlet path match failed + at io.undertow.servlet.handlers.ServletPathMatchesData.getServletHandlerByPath(ServletPathMatchesData.java:83) ~[undertow-servlet-2.3.13.Final.jar!/:2.3.13.Final] + at io.undertow.servlet.handlers.ServletPathMatches.getServletHandlerByPath(ServletPathMatches.java:133) ~[undertow-servlet-2.3.13.Final.jar!/:2.3.13.Final] + at io.undertow.servlet.handlers.ServletInitialHandler.handleRequest(ServletInitialHandler.java:148) ~[undertow-servlet-2.3.13.Final.jar!/:2.3.13.Final] + at io.undertow.server.handlers.HttpContinueReadHandler.handleRequest(HttpContinueReadHandler.java:69) ~[undertow-core-2.3.13.Final.jar!/:2.3.13.Final] + at org.springframework.boot.web.embedded.undertow.DeploymentManagerHttpHandlerFactory$DeploymentManagerHandler.handleRequest(DeploymentManagerHttpHandlerFactory.java:74) ~[spring-boot-3.2.8.jar!/:3.2.8] + at io.undertow.server.handlers.GracefulShutdownHandler.handleRequest(GracefulShutdownHandler.java:94) ~[undertow-core-2.3.13.Final.jar!/:2.3.13.Final] + at io.undertow.server.Connectors.executeRootHandler(Connectors.java:393) ~[undertow-core-2.3.13.Final.jar!/:2.3.13.Final] + at io.undertow.server.protocol.http.HttpReadListener.handleEventWithNoRunningRequest(HttpReadListener.java:265) ~[undertow-core-2.3.13.Final.jar!/:2.3.13.Final] + at io.undertow.server.protocol.http.HttpReadListener.handleEvent(HttpReadListener.java:136) ~[undertow-core-2.3.13.Final.jar!/:2.3.13.Final] + at io.undertow.server.protocol.http.HttpReadListener.handleEvent(HttpReadListener.java:59) ~[undertow-core-2.3.13.Final.jar!/:2.3.13.Final] + at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92) ~[xnio-api-3.8.8.Final.jar!/:3.8.8.Final] + at org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66) ~[xnio-api-3.8.8.Final.jar!/:3.8.8.Final] + at org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:89) ~[xnio-nio-3.8.8.Final.jar!/:3.8.8.Final] + at org.xnio.nio.WorkerThread.run(WorkerThread.java:591) ~[xnio-nio-3.8.8.Final.jar!/:3.8.8.Final] + + 2024-08-28 07:02:13.000 DEBUG 3306238 --- [admin-test] [task-7] org.jooq.tools.LoggerListener : Executing query : update `win_task_define` set `next_lock` = (`next_lock` + ?), `last_exec` = ? where (`id` = ? and `next_lock` = ?) + 2024-08-28 07:02:13.000 DEBUG 3306238 --- [admin-test] [task-7] org.jooq.tools.LoggerListener : -> with bind values : update `win_task_define` set `next_lock` = (`next_lock` + 1), `last_exec` = {ts '2024-08-28 07:02:13.0'} where (`id` = 1107 and `next_lock` = 89552) + 2024-08-28 07:02:13.005 DEBUG 3306238 --- [admin-test] [task-7] org.jooq.tools.LoggerListener : Affected row(s) : 1 + 2024-08-28 07:02:13.005 INFO 3306238 --- [admin-test] [task-7] p.f.w.t.t.s.i.TinyTaskExecServiceImpl : tiny-task exec, id=1107, prop=bill-expire + 2024-08-28 07:02:13.006 INFO 3306238 --- [admin-test] [task-7] p.f.w.t.t.s.i.TinyTaskExecServiceImpl : tiny-task done, id=1107, prop=bill-expire + 2024-08-28 07:02:13.007 DEBUG 3306238 --- [admin-test] [light-id-buffered-provider-2] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query + 2024-08-28 07:02:13.007 DEBUG 3306238 --- [admin-test] [light-id-buffered-provider-2] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT next_val, step_val FROM sys_light_sequence WHERE block_id=? AND seq_name=? FOR UPDATE] + """.stripIndent()); + + + boolean b0 = lv.canIgnoreHead(tmp0.toAbsolutePath().toString()); + Assertions.assertTrue(b0); + + final Path tmp1 = Files.createTempFile("test-", null); + tmp1.toFile().deleteOnExit(); + Files.writeString(tmp1, """ + 2024-07-21 22:05:22.957 ERROR 10884 --- [kite-front] [XNIO-1 I/O-4] io.undertow.request : UT005071: Undertow request failed HttpServerExchange{ CONNECT api.ipify.org:443} + + java.lang.IllegalArgumentException: UT000068: Servlet path match failed + at io.undertow.servlet.handlers.ServletPathMatchesData.getServletHandlerByPath(ServletPathMatchesData.java:83) ~[undertow-servlet-2.3.10.Final.jar!/:2.3.10.Final] + at io.undertow.servlet.handlers.ServletPathMatches.getServletHandlerByPath(ServletPathMatches.java:133) ~[undertow-servlet-2.3.10.Final.jar!/:2.3.10.Final] + + 2024-07-23 01:33:05.446 ERROR 10884 --- [kite-front] [XNIO-1 task-4] p.f.w.w.e.DefaultExceptionResolver : unhandled exception, response default + + java.lang.NumberFormatException: For input string + """.stripIndent()); + + + boolean b1 = lv.canIgnoreHead(tmp1.toAbsolutePath().toString()); + Assertions.assertFalse(b1); + } +} \ No newline at end of file diff --git a/wings/testing-faceless/src/main/java/pro/fessional/wings/testing/faceless/database/TestingDatabaseHelper.java b/wings/testing-faceless/src/main/java/pro/fessional/wings/testing/faceless/database/TestingDatabaseHelper.java index d9dfe8d0d..d22db4cef 100644 --- a/wings/testing-faceless/src/main/java/pro/fessional/wings/testing/faceless/database/TestingDatabaseHelper.java +++ b/wings/testing-faceless/src/main/java/pro/fessional/wings/testing/faceless/database/TestingDatabaseHelper.java @@ -143,12 +143,12 @@ private Map> fetchAllColumn1(String sql) { public static void testcaseNotice(String... mes) { for (String s : mes) { - log.info(">>=>🦁🦁🦁 " + s + " 🦁🦁🦁<=<<"); + log.info(">>=>🦁🦁🦁 {} 🦁🦁🦁<=<<", s); } } public static void breakpointDebug(String... mes) { - Arrays.stream(mes).forEach(s -> log.debug(">>=>🐶🐶🐶 " + s + " 🐶🐶🐶<=<<")); + Arrays.stream(mes).forEach(s -> log.debug(">>=>🐶🐶🐶 {} 🐶🐶🐶<=<<", s)); } public static void execWingsSql(JdbcTemplate jdbcTemplate, String path) { diff --git a/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601u01-test.sql b/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601u01-test.sql index 9b1e9f4d6..eb3b102ff 100644 --- a/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601u01-test.sql +++ b/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601u01-test.sql @@ -1,3 +1,5 @@ DROP TABLE IF EXISTS `tst_sharding`; -- 201/sharding; DROP TABLE IF EXISTS `tst_sharding_postfix`; -- 201/sharding; DROP TABLE IF EXISTS `tst_normal_table`; -- 202/normal; + +-- CALL FLYWAVE('2022-0601u01-test.sql'); \ No newline at end of file diff --git a/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601u02-test.sql b/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601u02-test.sql index 99a72b635..2f7bb7d43 100644 --- a/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601u02-test.sql +++ b/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601u02-test.sql @@ -1,2 +1,4 @@ -- @plain TRUNCATE `tst_sharding`; + +-- CALL FLYWAVE('2022-0601u02-test.sql'); \ No newline at end of file diff --git a/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601v01-test.sql b/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601v01-test.sql index e2e7f359f..f8a877e2e 100644 --- a/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601v01-test.sql +++ b/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601v01-test.sql @@ -1,47 +1,49 @@ -- apply@tst_.* error@stop CREATE TABLE `tst_sharding` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', + `id` BIGINT NOT NULL COMMENT 'primary key', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', - `login_info` TEXT COMMENT 'login info: agent, terminal', - `other_info` TEXT COMMENT 'other info: biz index data', - `language` INT(11) NOT NULL DEFAULT 1020111 COMMENT 'StandardLanguage', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', + `login_info` TEXT NULL COMMENT 'login info: agent, terminal', + `other_info` TEXT NULL COMMENT 'other info: biz index data', + `language` INT NOT NULL DEFAULT 1020111 COMMENT 'StandardLanguage', PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='201/Sharding Test'; CREATE TABLE `tst_sharding_postfix` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', + `id` BIGINT NOT NULL COMMENT 'primary key', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', - `login_info` TEXT COMMENT 'login info: agent, terminal', - `other_info` TEXT COMMENT 'other info: biz index data', - `language` INT(11) NOT NULL DEFAULT 1020111 COMMENT 'StandardLanguage', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', + `login_info` TEXT NULL COMMENT 'login info: agent, terminal', + `other_info` TEXT NULL COMMENT 'other info: biz index data', + `language` INT NOT NULL DEFAULT 1020111 COMMENT 'StandardLanguage', PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='201/Sharding Test'; CREATE TABLE `tst_normal_table` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', + `id` BIGINT NOT NULL COMMENT 'primary key', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', `value_varchar` VARCHAR(256) NOT NULL DEFAULT '0' COMMENT 'String', `value_decimal` DECIMAL(10, 2) NOT NULL DEFAULT '0' COMMENT 'BigDecimal', - `value_boolean` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'Boolean', - `value_int` INT(11) NOT NULL DEFAULT '0' COMMENT 'Integer', - `value_long` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Long', + `value_boolean` BOOLEAN NOT NULL DEFAULT '0' COMMENT 'Boolean', + `value_int` INT NOT NULL DEFAULT '0' COMMENT 'Integer', + `value_long` BIGINT NOT NULL DEFAULT '0' COMMENT 'Long', `value_date` DATE NOT NULL DEFAULT '1000-01-01' COMMENT 'LocalDate', `value_time` TIME NOT NULL DEFAULT '00:00:00' COMMENT 'LocalTime', - `value_lang` INT(11) NOT NULL DEFAULT '1020111' COMMENT 'StandardLanguage', + `value_lang` INT NOT NULL DEFAULT '1020111' COMMENT 'StandardLanguage', PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='202/Normal Test'; REPLACE INTO `sys_light_sequence` (`seq_name`, `block_id`, `next_val`, `step_val`, `comments`) VALUES ('tst_normal_table', 0, 1000, 1, 'for test step 1'); + +-- CALL FLYWAVE('2022-0601v01-test.sql'); \ No newline at end of file diff --git a/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601v02-test.sql b/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601v02-test.sql index 5af828869..2d9483cd8 100644 --- a/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601v02-test.sql +++ b/wings/testing-faceless/src/main/resources/wings-flywave/master/2022-0601v02-test.sql @@ -20,3 +20,5 @@ VALUES (100, -1, 'LOGIN_INFO-00', 'OTHER_INFO-00'), (117, -1, 'LOGIN_INFO-17', 'OTHER_INFO-17'), (118, -1, 'LOGIN_INFO-18', 'OTHER_INFO-18'), (119, -1, 'LOGIN_INFO-19', 'OTHER_INFO-19'); + +-- CALL FLYWAVE('2022-0601v02-test.sql'); \ No newline at end of file diff --git a/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/perm/impl/WarlockPermServiceImpl.java b/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/perm/impl/WarlockPermServiceImpl.java index a215eefba..353514113 100644 --- a/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/perm/impl/WarlockPermServiceImpl.java +++ b/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/perm/impl/WarlockPermServiceImpl.java @@ -25,7 +25,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -130,11 +129,9 @@ public void create(@NotNull String scopes, @NotNull Collection acts) { winPermEntryDao.insert(pos); }); - wingsTableCudHandler.handle(this.getClass(), Cud.Create, t, () -> { - Map> field = new HashMap<>(); - field.put(t.Id.getName(), pos.stream().map(WinPermEntry::getId).toList()); - return field; - }); + wingsTableCudHandler.handle(this.getClass(), Cud.Create, t, field -> + field.put(t.Id.getName(), pos.stream().map(WinPermEntry::getId).toList()) + ); } public void modify(long permId, @NotNull String remark) { @@ -153,7 +150,9 @@ public void modify(long permId, @NotNull String remark) { }); if (rct > 0) { - wingsTableCudHandler.handle(this.getClass(), Cud.Update, t, field -> field.put(t.Id.getName(), List.of(permId))); + wingsTableCudHandler.handle(this.getClass(), Cud.Update, t, field -> + field.put(t.Id.getName(), List.of(permId)) + ); } } } diff --git a/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/perm/impl/WarlockRoleServiceImpl.java b/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/perm/impl/WarlockRoleServiceImpl.java index 87dc7c7e6..3f31d5491 100644 --- a/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/perm/impl/WarlockRoleServiceImpl.java +++ b/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/perm/impl/WarlockRoleServiceImpl.java @@ -27,7 +27,6 @@ import pro.fessional.wings.warlock.service.perm.WarlockRoleService; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -133,6 +132,7 @@ public long create(@NotNull String name, String remark) { winRoleEntryDao.insert(po); } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.error("failed to insert role entry. name=" + name + ", remark=" + remark, e); throw new CodeException(e, CommonErrorEnum.AssertState2, "role.name", name); } @@ -140,11 +140,9 @@ public long create(@NotNull String name, String remark) { return id; }); - wingsTableCudHandler.handle(this.getClass(), Cud.Create, t, () -> { - Map> field = new HashMap<>(); - field.put(t.Id.getName(), List.of(rid)); - return field; - }); + wingsTableCudHandler.handle(this.getClass(), Cud.Create, t, field -> + field.put(t.Id.getName(), List.of(rid)) + ); return rid; } @@ -165,7 +163,9 @@ public void modify(long roleId, String remark) { }); if (rct > 0) { - wingsTableCudHandler.handle(this.getClass(), Cud.Update, t, field -> field.put(t.Id.getName(), List.of(roleId))); + wingsTableCudHandler.handle(this.getClass(), Cud.Update, t, field -> + field.put(t.Id.getName(), List.of(roleId)) + ); } } } diff --git a/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/user/impl/WarlockUserAuthnServiceImpl.java b/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/user/impl/WarlockUserAuthnServiceImpl.java index 7e3a4b6d4..9be5fb0e4 100644 --- a/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/user/impl/WarlockUserAuthnServiceImpl.java +++ b/wings/warlock-bond/src/main/java/pro/fessional/wings/warlock/service/user/impl/WarlockUserAuthnServiceImpl.java @@ -109,6 +109,7 @@ public long create(long userId, @NotNull Authn authn) { winUserAuthnDao.insert(auth); } catch (Exception e) { + // noinspection StringConcatenationArgumentToLogCall log.error("failed to insert authn " + authn, e); // Possibly unique key or value is oversize throw new CodeException(e, CommonErrorEnum.DataExisted); diff --git a/wings/warlock-bond/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/warlock-bond/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 66ec30098..829ff509d 100644 --- a/wings/warlock-bond/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/warlock-bond/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,13 +1,13 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.admin"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf.WarlockBondAutoConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.admin.AdminAuthnController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-auth for short."}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockBondBeanConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockBondBeanConfiguration.autoRegisterCacheConst", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockBondBeanConfiguration.defaultDaoAuthnCombo", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockBondBeanConfiguration.warlockDangerService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockBondBeanConfiguration.warlockGrantService", "type": "java.lang.Boolean"}, @@ -19,7 +19,7 @@ {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockBondBeanConfiguration.warlockUserBasisService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockBondBeanConfiguration.warlockUserLoginService", "type": "java.lang.Boolean"}, - {"name": "pro.fessional.wings.warlock.controller.admin.AdminAuthnController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-auth for short."} + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf.WarlockBondAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/controller/api/AbstractApiAuthController.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/controller/api/AbstractApiAuthController.java index 4e83112c1..b0227cb33 100644 --- a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/controller/api/AbstractApiAuthController.java +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/controller/api/AbstractApiAuthController.java @@ -4,7 +4,6 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.Part; import lombok.Data; -import lombok.Getter; import lombok.Setter; import lombok.SneakyThrows; import org.jetbrains.annotations.NotNull; @@ -19,7 +18,6 @@ import pro.fessional.mirana.io.InputStreams; import pro.fessional.mirana.text.FormatUtil; import pro.fessional.wings.slardar.constants.SlardarServletConst; -import pro.fessional.wings.slardar.context.Now; import pro.fessional.wings.slardar.context.TerminalContext; import pro.fessional.wings.slardar.context.TerminalContext.Context; import pro.fessional.wings.slardar.context.TerminalInterceptor; @@ -59,12 +57,6 @@ public abstract class AbstractApiAuthController { public static final int SHA1_LEN = MdHelp.LEN_SHA1_HEX; public static final int HMAC_LEN = 64; - /** - * Whether it is compatible mode (send clientId directly), or only the ticket mode. - */ - @Setter @Getter - private boolean compatible = true; - @Setter(onMethod_ = { @Autowired }) protected WarlockApiAuthProp apiAuthProp; @@ -75,22 +67,44 @@ public abstract class AbstractApiAuthController { protected TerminalInterceptor terminalInterceptor; /** - * To annotate `@RequestMapping`, Need subclass Override + * annotate `@RequestMapping` as api entry to invoke super.requestMapping with ticket, signed + * + * @see #requestMapping(HttpServletRequest, HttpServletResponse, boolean, boolean) */ public void requestMapping(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) { + requestMapping(request, response, true, true); + } + + /** + * After passing validate, this method performs business logic. + * `true` means it has been processed and can response, + * `false` means it has not been processed. + */ + public abstract boolean handle(@NotNull HttpServletRequest request, @NotNull ApiEntity entity) throws IOException; + + /** + * called by subclass as API entry + * + * @param request request from client + * @param response response to client + * @param ticket clientId is ticket format, otherwise is simple key + * @param signed have signature and digest check + */ + protected void requestMapping(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, boolean ticket, boolean signed) { // Pass pass = null; final String cid = request.getHeader(apiAuthProp.getClientHeader()); if (cid != null) { - final Term term = ticketService.decode(cid); - if (term != null) { - pass = ticketService.findPass(term.getClientId()); - } - else { // invalid token or compatible mode - if (compatible) { - pass = ticketService.findPass(cid); + if (ticket) { + final Term term = ticketService.decode(cid); + if (term != null) { + pass = ticketService.findPass(term.getClientId()); } } + // ticket compatible simple key + if(pass == null) { + pass = ticketService.findPass(cid); + } } if (pass == null) { @@ -99,7 +113,7 @@ public void requestMapping(@NotNull HttpServletRequest request, @NotNull HttpSer } // - final ApiEntity entity = validate(request, pass.getSecret()); + final ApiEntity entity = validate(request, pass, signed); if (entity.error != null) { responseText(response, apiAuthProp.getErrorSignature(), entity.error); return; @@ -112,7 +126,7 @@ public void requestMapping(@NotNull HttpServletRequest request, @NotNull HttpSer try { entity.terminal = ctx; if (handle(request, entity)) { - responseBody(response, entity, pass); + responseBody(response, entity, pass, signed); handled = true; } } @@ -129,67 +143,78 @@ public void requestMapping(@NotNull HttpServletRequest request, @NotNull HttpSer } @SneakyThrows - protected void responseBody(@NotNull HttpServletResponse response, @NotNull ApiEntity entity, @NotNull Pass pass) { + protected void responseBody(@NotNull HttpServletResponse response, @NotNull ApiEntity entity, @NotNull Pass pass, boolean signed) { // Auth-Client response.setHeader(apiAuthProp.getClientHeader(), pass.getClient()); // Auth-Timestamp - final String timestamp = entity.timestamp.isEmpty() ? String.valueOf(Now.millis()) : entity.timestamp; - response.setHeader(apiAuthProp.getTimestampHeader(), timestamp); + final String timestamp = entity.timestamp; + if(!timestamp.isEmpty()) { + response.setHeader(apiAuthProp.getTimestampHeader(), timestamp); + } // Other Headers for (Map.Entry en : entity.resHead.entrySet()) { response.setHeader(en.getKey(), en.getValue()); } - final int sgnLen = entity.signature.length(); - final String secret = pass.getSecret(); // response json if (entity.resFile == null) { final String body = entity.resText; - final String data = body + secret + timestamp; - // Auth-Signature - String signature = signature(data, sgnLen, secret); - if (!signature.isEmpty()) { - response.setHeader(apiAuthProp.getSignatureHeader(), signature); + + if (signed) { + final String secret = pass.getSecret(); + final String data = body + secret + timestamp; + // Auth-Signature + String signature = signature(data, entity.signature.length(), secret); + if (!signature.isEmpty()) { + response.setHeader(apiAuthProp.getSignatureHeader(), signature); + } } + // Content-Type response.setContentType(APPLICATION_JSON_VALUE); responseText(response, HttpStatus.OK.value(), body); } else { // response file - final int size = entity.resFile.available(); - int sumLen = 0; // Digest Algorithm - if (size < apiAuthProp.getDigestMax().toBytes()) { - for (Map.Entry en : entity.reqPara.entrySet()) { - if (en.getKey().endsWith(".sum")) { - sumLen = en.getValue().length(); - break; - } - } - if (sumLen == 0) { - sumLen = entity.digest.length(); - } - } - - final String data; final InputStream body; - if (sumLen == MD5_LEN || sumLen == SHA1_LEN) { - body = new CircleInputStream(entity.resFile); - final String digest = digest(body, sumLen); - data = digest + secret + timestamp; - response.setHeader(apiAuthProp.getDigestHeader(), digest); - } - else { + if (!signed) { body = entity.resFile; - data = secret + timestamp; } + else { + final int size = entity.resFile.available(); + int sumLen = 0; // Digest Algorithm + if (size < apiAuthProp.getDigestMax().toBytes()) { + for (Map.Entry en : entity.reqPara.entrySet()) { + if (en.getKey().endsWith(".sum")) { + sumLen = en.getValue().length(); + break; + } + } + if (sumLen == 0) { + sumLen = entity.digest.length(); + } + } - // Auth-Signature - String signature = signature(data, sgnLen, secret); - if (!signature.isEmpty()) { - response.setHeader(apiAuthProp.getSignatureHeader(), signature); + final String data; + final String secret = pass.getSecret(); + if (sumLen == MD5_LEN || sumLen == SHA1_LEN) { + body = new CircleInputStream(entity.resFile); + final String digest = digest(body, sumLen); + data = digest + secret + timestamp; + response.setHeader(apiAuthProp.getDigestHeader(), digest); + } + else { + body = entity.resFile; + data = secret + timestamp; + } + + // Auth-Signature + String signature = signature(data, entity.signature.length(), secret); + if (!signature.isEmpty()) { + response.setHeader(apiAuthProp.getSignatureHeader(), signature); + } } // Content-Type @@ -222,24 +247,26 @@ protected void responseText(@NotNull HttpServletResponse response, int status, @ @NotNull @SneakyThrows - public ApiEntity parse(@NotNull HttpServletRequest request, boolean mustSign) { + protected ApiEntity parse(@NotNull HttpServletRequest request, boolean signed) { final ApiEntity entity = new ApiEntity(); - final String sgn = request.getHeader(apiAuthProp.getSignatureHeader()); - if (sgn == null || sgn.isEmpty()) { - if (mustSign) { + + if (signed) { + final String sgn = request.getHeader(apiAuthProp.getSignatureHeader()); + if (sgn == null || sgn.isEmpty()) { entity.error = ApiError.SignatureMissing; return entity; } - } - else { - entity.signature = sgn; - } + else { + entity.signature = sgn; + } - final String sum = request.getHeader(apiAuthProp.getDigestHeader()); - if (sum != null) { - entity.digest = sum; + final String sum = request.getHeader(apiAuthProp.getDigestHeader()); + if (sum != null) { + entity.digest = sum; + } } + // identify request and sign final String tms = request.getHeader(apiAuthProp.getTimestampHeader()); if (tms != null) { entity.timestamp = tms; @@ -293,14 +320,16 @@ else if (vls.length == 1) { /** * Validate an Api request and returns Entity if it does not fail (successful or unvalidated), * otherwise it returns null. + * check digest if secret is not empty */ @SneakyThrows @NotNull - public ApiEntity validate(@NotNull HttpServletRequest request, @NotNull String secret) { - final boolean mustSign = apiAuthProp.isMustSignature(); - final ApiEntity entity = parse(request, mustSign); - if (entity.error != null) return entity; + protected ApiEntity validate(@NotNull HttpServletRequest request, @NotNull Pass pass, boolean signed) { + final ApiEntity entity = parse(request, signed); + if (!signed || entity.error != null) return entity; // not signed or error + + final String secret = Null.notNull(pass.getSecret()); final String para = FormatUtil.sortParam(entity.reqPara); // final String data; @@ -334,13 +363,12 @@ else if (cpt != pt) { // validate signature final String sign = signature(data, entity.signature.length(), secret); - if (mustSign && !sign.equalsIgnoreCase(entity.signature)) { + if (!sign.equalsIgnoreCase(entity.signature)) { entity.error = ApiError.SignatureInvalid; } return entity; } - @SneakyThrows private Part checkDigest(String sum, Part pt) { if (sum == null || sum.isEmpty()) return pt; @@ -401,13 +429,6 @@ else if (len == HMAC_LEN) { } } - /** - * After passing validate, this method performs business logic. - * `true` means it has been processed and can response, - * `false` means it has not been processed. - */ - public abstract boolean handle(@NotNull HttpServletRequest request, @NotNull ApiEntity entity) throws IOException; - public enum ApiError { SignatureMissing, SignatureInvalid, diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/controller/api/WebLogViewer.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/controller/api/WebLogViewer.java new file mode 100644 index 000000000..33cabd839 --- /dev/null +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/controller/api/WebLogViewer.java @@ -0,0 +1,42 @@ +package pro.fessional.wings.warlock.controller.api; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.slardar.monitor.viewer.LogConf; +import pro.fessional.wings.slardar.monitor.viewer.LogViewer; +import pro.fessional.wings.slardar.spring.prop.SlardarMonitorProp; + +import java.io.IOException; + +/** + * @author trydofor + * @since 2021-07-20 + */ +@Slf4j +@RestController +@ConditionalWingsEnabled(abs = LogConf.Key$enable) +public class WebLogViewer extends LogViewer { + + @Autowired + public WebLogViewer(SlardarMonitorProp prop) { + super(prop.getView(), prop.genRuleKey()); + } + + @Operation(summary = "Alarm logs can be viewed in conjunction with alarm notifications when self-monitoring is enabled.", description = """ + # Usage + Pass the log id to view the log. + ## Params + * @param id - log id, max 2k caches in 36H + ## Returns + * @return {200 | string} log context or empty""") + @GetMapping(value = "${" + LogConf.Key$mapping + "}") + public void view(@RequestParam("id") String id, HttpServletResponse res) throws IOException { + super.view(id, res.getOutputStream()); + } +} diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/security/justauth/AuthStateBuilder.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/security/justauth/AuthStateBuilder.java index 0eb40daea..a1bc2f868 100644 --- a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/security/justauth/AuthStateBuilder.java +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/security/justauth/AuthStateBuilder.java @@ -11,7 +11,7 @@ import pro.fessional.mirana.code.RandCode; import pro.fessional.mirana.data.Null; import pro.fessional.mirana.text.FormatUtil; -import pro.fessional.wings.silencer.enhance.TypeSugar; +import pro.fessional.wings.silencer.support.TypeSugar; import pro.fessional.wings.slardar.fastjson.FastJsonHelper; import pro.fessional.wings.slardar.security.WingsAuthHelper; import pro.fessional.wings.slardar.servlet.request.RequestHelper; diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockOauthTicketConfiguration.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockOauthTicketConfiguration.java index b4c7378f2..a3cd282df 100644 --- a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockOauthTicketConfiguration.java +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockOauthTicketConfiguration.java @@ -54,13 +54,17 @@ public SimpleTicketServiceImpl warlockTicketService(WarlockTicketProp warlockTic for (Map.Entry en : warlockTicketProp.getClient().entrySet()) { final WarlockTicketService.Pass pass = en.getValue(); - final String client = en.getKey(); + String client = pass.getClient(); + if (client == null || client.isBlank()) { + client = en.getKey(); + pass.setClient(client); + } + final String secret = pass.getSecret(); if (secret == null || secret.isEmpty()) { log.warn("WarlockShadow spring-conf skip warlockTicketService.client=" + client + " for empty secret"); continue; } - pass.setClient(client); log.info("WarlockShadow spring-conf warlockTicketService.client=" + client); bean.addClient(pass); } diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockSecurityBeanConfiguration.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockSecurityBeanConfiguration.java index 8364792b9..049ec342a 100644 --- a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockSecurityBeanConfiguration.java +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockSecurityBeanConfiguration.java @@ -18,7 +18,7 @@ import pro.fessional.mirana.bits.Aes; import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; -import pro.fessional.wings.silencer.spring.help.CommonPropHelper; +import pro.fessional.wings.silencer.support.PropHelper; import pro.fessional.wings.slardar.cache.WingsCache; import pro.fessional.wings.slardar.security.WingsAuthDetailsSource; import pro.fessional.wings.slardar.security.WingsAuthPageHandler; @@ -393,7 +393,7 @@ public WingsAuthDetailsSource wingsAuthDetailsSource(ObjectProvider aesProvider) { log.info("WarlockShadow spring-bean authStateBuilder"); - final AuthStateBuilder bean = new AuthStateBuilder(CommonPropHelper.onlyValue(prop.getSafeState())); + final AuthStateBuilder bean = new AuthStateBuilder(PropHelper.onlyValid(prop.getSafeState())); final Aes aes = aesProvider.getIfAvailable(); if (aes != null) { bean.setAes(aes); diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockSecurityConfConfiguration.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockSecurityConfConfiguration.java index 566061ca1..46f175c00 100644 --- a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockSecurityConfConfiguration.java +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockSecurityConfConfiguration.java @@ -29,9 +29,9 @@ import pro.fessional.wings.silencer.runner.ApplicationRunnerOrdered; import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; -import pro.fessional.wings.silencer.spring.help.CommonPropHelper; +import pro.fessional.wings.silencer.support.PropHelper; import pro.fessional.wings.slardar.security.WingsAuthDetailsSource; -import pro.fessional.wings.slardar.servlet.request.FakeHttpServletRequest; +import pro.fessional.wings.slardar.servlet.dummy.DummyHttpServletRequest; import pro.fessional.wings.slardar.servlet.response.ResponseHelper; import pro.fessional.wings.slardar.spring.conf.WingsBindLoginConfigurer; import pro.fessional.wings.slardar.spring.help.SecurityConfigHelper; @@ -72,7 +72,7 @@ public WebSecurityCustomizer warlockWebCustomizer(WarlockSecurityProp securityPr // https://github.com/spring-projects/spring-security/issues/10938 final Map webIgnore = securityProp.getWebIgnore(); if (!webIgnore.isEmpty()) { - final Set ignores = CommonPropHelper.onlyValue(webIgnore.values()); + final Set ignores = PropHelper.onlyValid(webIgnore.values()); log.info("WarlockShadow conf WebSecurity, ignoring=" + String.join("\n,", ignores)); web.ignoring().requestMatchers(ignores.toArray(String[]::new)); } @@ -168,14 +168,14 @@ public HttpSecurityCustomizer warlockSecurityAuthHttpConfigure(WarlockSecurityPr return http -> http.authorizeHttpRequests(conf -> { // 1 PermitAll - final Set permed = CommonPropHelper.onlyValue(securityProp.getPermitAll().values()); + final Set permed = PropHelper.onlyValid(securityProp.getPermitAll().values()); if (!permed.isEmpty()) { log.info("WarlockShadow conf HttpSecurity, bind PermitAll=" + String.join("\n,", permed)); conf.requestMatchers(permed.toArray(String[]::new)).permitAll(); } // 2 Authenticated - final Set authed = CommonPropHelper.onlyValue(securityProp.getAuthenticated().values()); + final Set authed = PropHelper.onlyValid(securityProp.getAuthenticated().values()); if (!authed.isEmpty()) { log.info("WarlockShadow conf HttpSecurity, bind Authenticated=" + String.join("\n,", authed)); conf.requestMatchers(authed.toArray(String[]::new)).authenticated(); @@ -188,7 +188,7 @@ public HttpSecurityCustomizer warlockSecurityAuthHttpConfigure(WarlockSecurityPr for (Map.Entry> en : securityProp.getAuthority().entrySet()) { final String perm = en.getKey(); for (String url : en.getValue()) { - if (CommonPropHelper.hasValue(url)) { + if (PropHelper.valid(url)) { final Set st = urlPerm.computeIfAbsent(url, k -> new HashSet<>()); st.add(perm); } @@ -197,7 +197,7 @@ public HttpSecurityCustomizer warlockSecurityAuthHttpConfigure(WarlockSecurityPr // desc for (Map.Entry> en : urlPerm.descendingMap().entrySet()) { final String url = en.getKey(); - final Set pms = CommonPropHelper.onlyValue(en.getValue()); + final Set pms = PropHelper.onlyValid(en.getValue()); log.info("WarlockShadow conf HttpSecurity, bind url=" + url + ", any-permit=[" + String.join(",", pms) + "]"); conf.requestMatchers(url).hasAnyAuthority(pms.toArray(Null.StrArr)); } @@ -304,98 +304,135 @@ else if ("fullyAuthenticated".equalsIgnoreCase(str)) { public ApplicationRunnerOrdered securityCheckUrlRunner(WarlockSecurityProp securityProp, ApplicationContext ctx) { log.info("WarlockShadow spring-runs securityCheckUrlRunner"); return new ApplicationRunnerOrdered(WingsOrdered.Lv1Config, ignored -> { - log.info("WarlockShadow check security url config"); - - String servletName = "dispatcherServlet"; - for (ServletRegistrationBean srb : ctx.getBeanProvider(ServletRegistrationBean.class)) { - if (srb.getServlet() instanceof DispatcherServlet) { - servletName = srb.getServletName(); - break; - } + try { + secCheckUrl(securityProp, ctx); } + catch (RuntimeException e) { + log.error("set wings.enabled.warlock.sec-check-url=false to skip check", e); + throw e; + } + }); + } - Map matchers = new LinkedHashMap<>(); - Map requests = new LinkedHashMap<>(); + private static void secCheckUrl(WarlockSecurityProp securityProp, ApplicationContext ctx) { + log.info("WarlockShadow check security url config"); - for (var en : securityProp.getWebIgnore().entrySet()) { - String ptn = en.getValue(); - if (!StringUtils.hasText(ptn)) continue; - matchers.put("WebIgnore:" + en.getKey(), ptn); - requests.put(ptn, SecurityConfigHelper.fakeMatcherRequest(ptn, servletName)); + String servletName = "dispatcherServlet"; + for (ServletRegistrationBean srb : ctx.getBeanProvider(ServletRegistrationBean.class)) { + if (srb.getServlet() instanceof DispatcherServlet) { + servletName = srb.getServletName(); + break; } - for (var en : securityProp.getPermitAll().entrySet()) { - String ptn = en.getValue(); - if (!StringUtils.hasText(ptn)) continue; - matchers.put("PermitAll:" + en.getKey(), ptn); - requests.put(ptn, SecurityConfigHelper.fakeMatcherRequest(ptn, servletName)); - } - for (var en : securityProp.getAuthenticated().entrySet()) { - String ptn = en.getValue(); + } + + Map matchers = new LinkedHashMap<>(); + Map requests = new LinkedHashMap<>(); + + for (var en : securityProp.getWebIgnore().entrySet()) { + String ptn = en.getValue(); + if (!StringUtils.hasText(ptn)) continue; + matchers.put("WebIgnore:" + en.getKey(), ptn); + requests.put(ptn, SecurityConfigHelper.dummyMatcherRequest(ptn, servletName)); + } + for (var en : securityProp.getPermitAll().entrySet()) { + String ptn = en.getValue(); + if (!StringUtils.hasText(ptn)) continue; + matchers.put("PermitAll:" + en.getKey(), ptn); + requests.put(ptn, SecurityConfigHelper.dummyMatcherRequest(ptn, servletName)); + } + for (var en : securityProp.getAuthenticated().entrySet()) { + String ptn = en.getValue(); + if (!StringUtils.hasText(ptn)) continue; + matchers.put("Authenticated:" + en.getKey(), ptn); + requests.put(ptn, SecurityConfigHelper.dummyMatcherRequest(ptn, servletName)); + } + for (var en : securityProp.getAuthority().entrySet()) { + int c = 0; + String k = en.getKey(); + for (String ptn : en.getValue()) { if (!StringUtils.hasText(ptn)) continue; - matchers.put("Authenticated:" + en.getKey(), ptn); - requests.put(ptn, SecurityConfigHelper.fakeMatcherRequest(ptn, servletName)); + matchers.put("Authority:" + k + "[" + (c++) + "]", ptn); + requests.put(ptn, SecurityConfigHelper.dummyMatcherRequest(ptn, servletName)); } - for (var en : securityProp.getAuthority().entrySet()) { - int c = 0; - String k = en.getKey(); - for (String ptn : en.getValue()) { - if (!StringUtils.hasText(ptn)) continue; - matchers.put("Authority:" + k + "[" + (c++) + "]", ptn); - requests.put(ptn, SecurityConfigHelper.fakeMatcherRequest(ptn, servletName)); + } + final AtomicReference opt = new AtomicReference<>(); + MatcherHelper matcherHelper = MatcherHelper.of(ctx, opt); + + // check including + for (var en : matchers.entrySet()) { + String ptn = en.getValue(); + requests.remove(ptn); + if (requests.isEmpty()) break; + + matcherHelper.requestMatchers(ptn); + RequestMatcher mt = opt.get(); + for (var er : requests.entrySet()) { + try { + if (mt.matches(er.getValue())) { + log.warn(en.getKey() + "=" + ptn + " should not contain " + er.getKey()); + } + } + catch (RuntimeException e) { + log.error("failed to check " + en.getKey() + "=" + ptn + " should not contain " + er.getKey()); + throw e; } } - final AtomicReference opt = new AtomicReference<>(); - MatcherHelper matcherHelper = MatcherHelper.of(ctx, opt); + } + matchers.clear(); + requests.clear(); - // check including - for (var en : matchers.entrySet()) { - String ptn = en.getValue(); - requests.remove(ptn); - if (requests.isEmpty()) break; + // check auth url + String loginPage = securityProp.getLoginPage(); + if (StringUtils.hasText(loginPage)) { + requests.put(loginPage, SecurityConfigHelper.dummyMatcherRequest(loginPage, servletName)); + } + String logoutUrl = securityProp.getLogoutUrl(); + if (StringUtils.hasText(logoutUrl)) { + requests.put(logoutUrl, SecurityConfigHelper.dummyMatcherRequest(logoutUrl, servletName)); + } + String loginProcUrl = securityProp.getLoginProcUrl(); + if (StringUtils.hasText(loginProcUrl)) { + requests.put(loginProcUrl, SecurityConfigHelper.dummyMatcherRequest(loginProcUrl, servletName)); + } - matcherHelper.requestMatchers(ptn); - RequestMatcher mt = opt.get(); - for (var er : requests.entrySet()) { - try { - if (mt.matches(er.getValue())) { - log.warn(en.getKey() + "=" + ptn + " should not contain " + er.getKey()); - } - } - catch (RuntimeException e) { - log.error("failed to check " + en.getKey() + "=" + ptn + " should not contain " + er.getKey()); - throw e; + StringBuilder err = new StringBuilder(); + for (var en : securityProp.getWebIgnore().entrySet()) { + String ptn = en.getValue(); + if (!StringUtils.hasText(ptn)) continue; + if (requests.isEmpty()) break; + + matcherHelper.requestMatchers(ptn); + RequestMatcher mt = opt.get(); + for (var er : requests.entrySet()) { + try { + if (mt.matches(er.getValue())) { + err.append("\nWebIgnore:").append(en.getKey()).append(" should exclude ").append(er.getKey()); } } + catch (RuntimeException e) { + log.error("failed to check " + en.getKey() + "=" + ptn + " should not contain " + er.getKey()); + throw e; + } } - matchers.clear(); - requests.clear(); - - // check auth url - String loginPage = securityProp.getLoginPage(); - if (StringUtils.hasText(loginPage)) { - requests.put(loginPage, SecurityConfigHelper.fakeMatcherRequest(loginPage, servletName)); - } - String logoutUrl = securityProp.getLogoutUrl(); - if (StringUtils.hasText(logoutUrl)) { - requests.put(logoutUrl, SecurityConfigHelper.fakeMatcherRequest(logoutUrl, servletName)); - } - String loginProcUrl = securityProp.getLoginProcUrl(); - if (StringUtils.hasText(loginProcUrl)) { - requests.put(loginProcUrl, SecurityConfigHelper.fakeMatcherRequest(loginProcUrl, servletName)); - } + } - StringBuilder err = new StringBuilder(); - for (var en : securityProp.getWebIgnore().entrySet()) { + String anyRequest = securityProp.getAnyRequest(); + if (!StringUtils.hasText(anyRequest) + || "permitAll".equalsIgnoreCase(anyRequest) + || "anonymous".equalsIgnoreCase(anyRequest)) { + for (var en : securityProp.getPermitAll().entrySet()) { String ptn = en.getValue(); if (!StringUtils.hasText(ptn)) continue; if (requests.isEmpty()) break; matcherHelper.requestMatchers(ptn); RequestMatcher mt = opt.get(); - for (var er : requests.entrySet()) { + for (var it = requests.entrySet().iterator(); it.hasNext(); ) { + var er = it.next(); try { if (mt.matches(er.getValue())) { - err.append("\nWebIgnore:").append(en.getKey()).append(" should exclude ").append(er.getKey()); + log.debug("WarlockShadow security url permit all include " + er.getKey()); + it.remove(); } } catch (RuntimeException e) { @@ -404,45 +441,18 @@ public ApplicationRunnerOrdered securityCheckUrlRunner(WarlockSecurityProp secur } } } - - String anyRequest = securityProp.getAnyRequest(); - if (!StringUtils.hasText(anyRequest) - || "permitAll".equalsIgnoreCase(anyRequest) - || "anonymous".equalsIgnoreCase(anyRequest)) { - for (var en : securityProp.getPermitAll().entrySet()) { - String ptn = en.getValue(); - if (!StringUtils.hasText(ptn)) continue; - if (requests.isEmpty()) break; - - matcherHelper.requestMatchers(ptn); - RequestMatcher mt = opt.get(); - for (var it = requests.entrySet().iterator(); it.hasNext(); ) { - var er = it.next(); - try { - if (mt.matches(er.getValue())) { - log.debug("WarlockShadow security url permit all include " + er.getKey()); - it.remove(); - } - } - catch (RuntimeException e) { - log.error("failed to check " + en.getKey() + "=" + ptn + " should not contain " + er.getKey()); - throw e; - } - } - } - if (!requests.isEmpty()) { - err.append("\nPermitAll should include urls: ").append(String.join(", ", requests.keySet())); - } + if (!requests.isEmpty()) { + err.append("\nPermitAll should include urls: ").append(String.join(", ", requests.keySet())); } + } - if (!err.isEmpty()) { - String msg = err.toString(); - log.error(msg); - throw new IllegalStateException( - "\nWarlockSecurityConfConfiguration has security url conflict to fix." + - "\nor disable checking by `wings.enabled.warlock.sec-check-url=false`" + - msg); - } - }); + if (!err.isEmpty()) { + String msg = err.toString(); + log.error(msg); + throw new IllegalStateException( + "\nWarlockSecurityConfConfiguration has security url conflict to fix." + + "\nor disable checking by `wings.enabled.warlock.sec-check-url=false`" + + msg); + } } } diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/prop/WarlockApiAuthProp.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/prop/WarlockApiAuthProp.java index 9cbb9d2c6..4021ef445 100644 --- a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/prop/WarlockApiAuthProp.java +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/prop/WarlockApiAuthProp.java @@ -61,15 +61,6 @@ public class WarlockApiAuthProp { private DataSize digestMax = DataSize.ofMegabytes(10); public static final String Key$digestMax = Key + ".digest-max"; - /** - * whether it must be signed, compatible with the old api. - * - * @see #Key$mustSignature - */ - private boolean mustSignature = true; - public static final String Key$mustSignature = Key + ".must-signature"; - - /** * if there is both a file and a json, * use this name for the json body and submit it as a File. diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/prop/WarlockSecurityProp.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/prop/WarlockSecurityProp.java index b77437f16..ec4289a2e 100644 --- a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/prop/WarlockSecurityProp.java +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/spring/prop/WarlockSecurityProp.java @@ -2,7 +2,7 @@ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; -import pro.fessional.wings.silencer.spring.help.CommonPropHelper; +import pro.fessional.wings.silencer.support.PropHelper; import pro.fessional.wings.slardar.context.TerminalContext; import pro.fessional.wings.warlock.enums.autogen.UserStatus; @@ -386,7 +386,7 @@ public Enum mapAuthTypeDefault() { public Map> mapAuthTypeEnum() { return authType.entrySet() .stream() - .filter(it -> CommonPropHelper.hasValue(it.getValue())) + .filter(it -> PropHelper.valid(it.getValue())) .collect(toMap(Map.Entry::getKey, en -> str2Enum(en.getValue()))); } diff --git a/wings/warlock-shadow/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/warlock-shadow/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 3c0d4bf18..6424dc126 100644 --- a/wings/warlock-shadow/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/warlock-shadow/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,17 +1,32 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.admin"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.api"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.auth"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.mock"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.test"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.user"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf.WarlockShadowAutoConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.admin.AdminTweakController", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-tweak for short."}, + + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.api.WebLogViewer", "type": "java.lang.Boolean", "description": "wings.slardar.monitor.view.enable for short."}, + + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.auth.LoginPageController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-login for short."}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.auth.LoginProcController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-proc for short."}, + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.auth.SimpleOauthController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-oauth for short."}, + + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.mock.MockSampleController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-mock for short."}, + + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.test.TestEnvsController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-test for short."}, + + {"name": "wings.enabled.pro.fessional.wings.warlock.controller.user.AuthedUserController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-user for short."}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockExceptionConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockExceptionConfiguration.defaultExceptionResolver", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockJournalConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockJournalConfiguration.terminalJournalService", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockJustAuthConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockJustAuthConfiguration.authStateCache", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockJustAuthConfiguration.justAuthRequestBuilder", "type": "java.lang.Boolean"}, @@ -26,17 +41,12 @@ {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockOtherBeanConfiguration$MvcRestScan", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockOtherBeanConfiguration.righterSecretProvider", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatching2Configuration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatching2Configuration.slowResponseInterceptor", "type": "java.lang.Boolean"}, - - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf.WarlockSecurityAutoConfiguration", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.accessDeniedHandler", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.authAppPermChecker", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.authenticationEventPublisher", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.authStateBuilder", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.authZonePermChecker", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.authenticationEventPublisher", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.comboWarlockAuthnService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.comboWarlockAuthzService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.comboWingsAuthCheckService", "type": "java.lang.Boolean"}, @@ -64,6 +74,7 @@ {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityBeanConfiguration.wingsUserDetailsService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityConfConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityConfConfiguration.securityCheckUrlRunner", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.sec-check-url for short."}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityConfConfiguration.securityFilterChain", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.sec-http-chain for short."}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityConfConfiguration.warlockSecurityAuthHttpConfigure", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.sec-http-auth for short."}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityConfConfiguration.warlockSecurityAutoHttpConfigure", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.sec-http-auto for short."}, @@ -71,7 +82,7 @@ {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityConfConfiguration.warlockSecurityHttpBaseConfigure", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.sec-http-base for short."}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityConfConfiguration.warlockWebCustomizer", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.sec-web-auto for short."}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityDummyConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.dummy-service for short."}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityDummyConfiguration", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.warlock.dummy-service for short."}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityDummyConfiguration.warlockGrantService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityDummyConfiguration.warlockPermService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityDummyConfiguration.warlockRoleService", "type": "java.lang.Boolean"}, @@ -79,13 +90,12 @@ {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityDummyConfiguration.warlockUserBasisService", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockSecurityDummyConfiguration.warlockUserLoginService", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.controller.admin.AdminTweakController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-tweak for short."}, - {"name": "wings.enabled.pro.fessional.wings.warlock.controller.auth.LoginPageController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-login for short."}, - {"name": "wings.enabled.pro.fessional.wings.warlock.controller.auth.LoginProcController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-proc for short."}, - {"name": "wings.enabled.pro.fessional.wings.warlock.controller.auth.SimpleOauthController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-oauth for short."}, - {"name": "wings.enabled.pro.fessional.wings.warlock.controller.mock.MockSampleController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-mock for short."}, - {"name": "wings.enabled.pro.fessional.wings.warlock.controller.test.TestEnvsController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-test for short."}, - {"name": "wings.enabled.pro.fessional.wings.warlock.controller.user.AuthedUserController", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.mvc-user for short."} + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatching2Configuration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatching2Configuration.slowResponseInterceptor", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf.WarlockSecurityAutoConfiguration", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf.WarlockShadowAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/warlock-shadow/src/main/resources/wings-conf/wings-warlock-apiauth-77.properties b/wings/warlock-shadow/src/main/resources/wings-conf/wings-warlock-apiauth-77.properties index c6d5a2d40..412967915 100644 --- a/wings/warlock-shadow/src/main/resources/wings-conf/wings-warlock-apiauth-77.properties +++ b/wings/warlock-shadow/src/main/resources/wings-conf/wings-warlock-apiauth-77.properties @@ -13,9 +13,6 @@ wings.warlock.apiauth.digest-header=Auth-Digest ## no digest over this size, default 5M. wings.warlock.apiauth.digest-max=5MB -## whether it must be signed, compatible with the old api. -wings.warlock.apiauth.must-signature=true - ## if there is both a file and a json, ## use this name for the json body and submit it as a File. wings.warlock.apiauth.file-json-body=FILE_JSON_BODY diff --git a/wings/warlock-shadow/src/main/resources/wings-conf/wings-warlock-ticket-77.properties b/wings/warlock-shadow/src/main/resources/wings-conf/wings-warlock-ticket-77.properties index 847330e3b..9f3fed538 100644 --- a/wings/warlock-shadow/src/main/resources/wings-conf/wings-warlock-ticket-77.properties +++ b/wings/warlock-shadow/src/main/resources/wings-conf/wings-warlock-ticket-77.properties @@ -11,6 +11,8 @@ wings.warlock.ticket.token-max=5 ## static config of client login. #wings.warlock.ticket.client[wings-trydofor].user-id=79 +## recommend to use key as client +##wings.warlock.ticket.client[wings-trydofor].client=wings-trydofor #wings.warlock.ticket.client[wings-trydofor].secret=wings-trydofor-secret #wings.warlock.ticket.client[wings-trydofor].hosts=localhost #wings.warlock.ticket.client[wings-trydofor].scopes=api1, api2 diff --git a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/TestPlainAsyncConfigurer.java b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/TestPlainAsyncConfigurer.java new file mode 100644 index 000000000..af8fc87db --- /dev/null +++ b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/TestPlainAsyncConfigurer.java @@ -0,0 +1,35 @@ +package pro.fessional.wings.warlock.app; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * @author trydofor + * @since 2024-07-02 + */ +@Configuration +@EnableAsync +@ConditionalWingsEnabled(abs = "test.plain-async", value = false) +public class TestPlainAsyncConfigurer implements AsyncConfigurer { + + @Bean("plainPoolTaskExecutor") + public ThreadPoolTaskExecutor plainPoolTaskExecutor() { + ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); + taskExecutor.setCorePoolSize(5); + taskExecutor.setMaxPoolSize(5); + taskExecutor.setQueueCapacity(10); + taskExecutor.setKeepAliveSeconds(60); + taskExecutor.setThreadNamePrefix("raw-"); + taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + taskExecutor.setWaitForTasksToCompleteOnShutdown(true); + taskExecutor.setAwaitTerminationSeconds(60); + taskExecutor.initialize(); + return taskExecutor; + } +} diff --git a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/controller/TestJournalController.java b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/controller/TestJournalController.java new file mode 100644 index 000000000..1bc45258c --- /dev/null +++ b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/controller/TestJournalController.java @@ -0,0 +1,58 @@ +package pro.fessional.wings.warlock.app.controller; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import pro.fessional.mirana.pain.MessageException; +import pro.fessional.mirana.time.Sleep; +import pro.fessional.wings.faceless.service.journal.impl.DefaultJournalService; +import pro.fessional.wings.slardar.async.AsyncHelper; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author trydofor + * @since 2024-05-07 + */ +@Slf4j +@RestController +public class TestJournalController { + + @Setter(onMethod_ = { @Autowired }) + protected DefaultJournalService defaultJournalService; + + @Setter(onMethod_ = { @Autowired(required = false), @Qualifier("plainPoolTaskExecutor") }) + protected ThreadPoolTaskExecutor plainPoolTaskExecutor; + + private final AtomicLong dummyLightId = new AtomicLong(1); + private int aliveSecond = -1; + + @RequestMapping(value = "/test/ttl-journal.json") + public CompletableFuture testTtlContext(@RequestParam("i") int i, @RequestParam("t") boolean t) { + return t ? AsyncHelper.Async(() -> recurExecute(i)) + : plainPoolTaskExecutor.submitCompletable(() -> recurExecute(i)); + } + + private Long recurExecute(int t) { + if (t % 5 != 0) { + return defaultJournalService.submit(aliveSecond, dummyLightId, "", null, null, null, d -> { + recurExecute(t - 1); + return d.getId(); + }); + } + else { + return defaultJournalService.submit(aliveSecond, dummyLightId, "", null, null, null, d -> { + Sleep.ignoreInterrupt(500); + if (t % 7 == 0) throw new MessageException("error7=" + t); + return d.getId(); + }); + } + } + +} diff --git a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/controller/TestToyApiController.java b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/controller/TestToyApiController.java index ca400da22..d84833671 100644 --- a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/controller/TestToyApiController.java +++ b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/app/controller/TestToyApiController.java @@ -22,6 +22,7 @@ import pro.fessional.wings.slardar.spring.prop.SlardarSessionProp; import pro.fessional.wings.warlock.app.service.TestWatchingService; import pro.fessional.wings.warlock.controller.api.AbstractApiAuthController; +import pro.fessional.wings.warlock.spring.prop.WarlockApiAuthProp; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -40,29 +41,31 @@ @Slf4j public class TestToyApiController extends AbstractApiAuthController { - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) protected TestWatchingService testWatchingService; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private SlardarSessionProp slardarSessionProp; + @Setter(onMethod_ = { @Autowired }) + protected WarlockApiAuthProp warlockApiAuthProp; @PostMapping(value = "/api/test.json", consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity testJsonApi( - @RequestHeader("Auth-Client") String client, - @RequestHeader("Auth-Signature") String signature, - @RequestHeader(value = "Auth-Timestamp", required = false) Long timestamp, - @RequestParam Map para, - @RequestBody String body + @RequestHeader("Auth-Client") String client, + @RequestHeader("Auth-Signature") String signature, + @RequestHeader(value = "Auth-Timestamp", required = false) Long timestamp, + @RequestParam Map para, + @RequestBody String body ) { return ResponseEntity.ok("ok"); } @PostMapping(value = "/api/test.json", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity testFileApi( - @RequestHeader("Auth-Client") String client, - @RequestHeader("Auth-Signature") String signature, - @RequestHeader(value = "Auth-Timestamp", required = false) Long timestamp, - @RequestParam Map para, - @RequestParam Map files + @RequestHeader("Auth-Client") String client, + @RequestHeader("Auth-Signature") String signature, + @RequestHeader(value = "Auth-Timestamp", required = false) Long timestamp, + @RequestParam Map para, + @RequestParam Map files ) { return ResponseEntity.ok("ok"); } @@ -95,13 +98,6 @@ public ResponseEntity testFormLogin(@NotNull HttpServletRequest request) public static final String ApiSimple = "/api/simple.json"; - @Override - @PostMapping(ApiSimple) - public void requestMapping(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) { - log.info("ApiRequestMapping..."); - super.requestMapping(request, response); - } - public static final String ReqJsonBody = "ReqJsonBody"; public static final String ReqFileKey = "ReqFileKey"; public static final String ReqFileName = "ReqFileName"; @@ -118,6 +114,15 @@ public void requestMapping(@NotNull HttpServletRequest request, @NotNull HttpSer public static final String ModJsonJson = "ModJsonJson"; public static final String ModFileJson = "ModFileJson"; + @Override + @PostMapping(ApiSimple) + public void requestMapping(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) { + log.info("ApiRequestMapping..."); + boolean ticket = "true".equalsIgnoreCase(request.getHeader("ticket")); + boolean signed = "true".equalsIgnoreCase(request.getHeader("signed")); + super.requestMapping(request, response, ticket, signed); + } + @Override public boolean handle(@NotNull HttpServletRequest request, @NotNull ApiEntity entity) throws IOException { final HashMap head = new HashMap<>(); diff --git a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/controller/api/ApiAuthControllerTest.java b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/controller/api/ApiAuthControllerTest.java index 28bfc1387..900851bf5 100644 --- a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/controller/api/ApiAuthControllerTest.java +++ b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/controller/api/ApiAuthControllerTest.java @@ -19,6 +19,7 @@ import pro.fessional.mirana.bits.MdHelp; import pro.fessional.mirana.data.Null; import pro.fessional.mirana.text.FormatUtil; +import pro.fessional.wings.faceless.convention.EmptySugar; import pro.fessional.wings.slardar.context.Now; import pro.fessional.wings.slardar.fastjson.FastJsonHelper; import pro.fessional.wings.slardar.httprest.okhttp.OkHttpClientHelper; @@ -56,24 +57,23 @@ * @since 2022-11-16 */ @SpringBootTest( - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "wings.warlock.apiauth.must-signature=false", - "wings.enabled.slardar.restream=false", - }) + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "wings.enabled.slardar.restream=false", + }) @Slf4j class ApiAuthControllerTest { - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private WarlockApiAuthProp apiAuthProp; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private WarlockUrlmapProp urlmapProp; - @Setter(onMethod_ = {@Value("${local.server.port}")}) + @Setter(onMethod_ = { @Value("${local.server.port}") }) private int apiPort; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private OkHttpClient okHttpClient; private final String client = "wings-trydofor"; @@ -93,10 +93,10 @@ class ApiAuthControllerTest { private HttpUrl.Builder urlBuilder() { return new HttpUrl.Builder() - .scheme("http") - .host("localhost") - .port(apiPort) - .encodedPath(ApiSimple); + .scheme("http") + .host("localhost") + .port(apiPort) + .encodedPath(ApiSimple); } private String accessToken = null; @@ -104,33 +104,33 @@ private HttpUrl.Builder urlBuilder() { private String getToken() { if (accessToken != null) return accessToken; final HttpUrl.Builder u1 = new HttpUrl.Builder() - .scheme("http") - .host("localhost") - .port(apiPort) - .encodedPath(urlmapProp.getOauthAuthorize()) - .addQueryParameter(WarlockOauthService.ClientId, client); + .scheme("http") + .host("localhost") + .port(apiPort) + .encodedPath(urlmapProp.getOauthAuthorize()) + .addQueryParameter(WarlockOauthService.ClientId, client); final String t1 = OkHttpClientHelper.getText(okHttpClient, u1.toString()); final String code = FastJsonHelper.object(t1).getString(WarlockOauthService.Code); final HttpUrl.Builder u2 = new HttpUrl.Builder() - .scheme("http") - .host("localhost") - .port(apiPort) - .encodedPath(urlmapProp.getOauthAccessToken()) - .addQueryParameter(WarlockOauthService.ClientId, client) - .addQueryParameter(WarlockOauthService.ClientSecret, secret) - .addQueryParameter(WarlockOauthService.Code, code); + .scheme("http") + .host("localhost") + .port(apiPort) + .encodedPath(urlmapProp.getOauthAccessToken()) + .addQueryParameter(WarlockOauthService.ClientId, client) + .addQueryParameter(WarlockOauthService.ClientSecret, secret) + .addQueryParameter(WarlockOauthService.Code, code); final String t2 = OkHttpClientHelper.postJson(okHttpClient, u2.toString(), ""); - accessToken = FastJsonHelper.object(t2).getString(WarlockOauthService.AccessToken); + accessToken = FastJsonHelper.object(t2).getString(WarlockOauthService.AccessToken); return accessToken; } @Test @TmsLink("C14024") public void testJsonJson() throws IOException { - String[] clients = {client, getToken()}; - String[] timestamps = {String.valueOf(Now.millis()), Null.Str}; + String[] clients = { client, getToken() }; + String[] timestamps = { String.valueOf(Now.millis()), Null.Str }; for (String c : clients) { for (String t : timestamps) { jsonJson(c, t, md5); @@ -158,17 +158,19 @@ private void jsonJson(String client, String timestamp, Function final String para = FormatUtil.sortParam(param); final String signature = sumFun == null - ? Null.Str - : sumFun.apply(para + jsonBody + secret + timestamp); - + ? Null.Str + : sumFun.apply(para + jsonBody + secret + timestamp); + final boolean signed = !signature.isEmpty(); RequestBody body = RequestBody.create(jsonBody, APPLICATION_JSON_VALUE); okhttp3.Request request = new okhttp3.Request.Builder() - .url(ubd.build()) - .header(apiAuthProp.getClientHeader(), client) - .header(apiAuthProp.getTimestampHeader(), timestamp) - .header(apiAuthProp.getSignatureHeader(), signature) - .post(body) - .build(); + .url(ubd.build()) + .header(apiAuthProp.getClientHeader(), client) + .header(apiAuthProp.getTimestampHeader(), timestamp) + .header(apiAuthProp.getSignatureHeader(), signature) + .header("ticket", String.valueOf(client.startsWith("win-"))) + .header("signed", String.valueOf(signed)) + .post(body) + .build(); final Response r2 = OkHttpClientHelper.execute(okHttpClient, request, false); Assertions.assertNotNull(r2); @@ -179,12 +181,12 @@ private void jsonJson(String client, String timestamp, Function final String text = resBody.string(); Assertions.assertEquals(jsonBody, text); - final String stmp = r2.header(apiAuthProp.getTimestampHeader()); + final String stmp = EmptySugar.nullToEmpty(r2.header(apiAuthProp.getTimestampHeader())); if (!timestamp.isEmpty()) { Assertions.assertEquals(timestamp, stmp); } - if (sumFun != null) { + if (signed) { final String sign = r2.header(apiAuthProp.getSignatureHeader()); Assertions.assertNotNull(sign); final String data1 = text + secret + stmp; @@ -201,8 +203,8 @@ private void jsonJson(String client, String timestamp, Function @Test @TmsLink("C14025") public void testFileJson() throws IOException { - String[] clients = {client, getToken()}; - String[] timestamps = {String.valueOf(Now.millis()), Null.Str}; + String[] clients = { client, getToken() }; + String[] timestamps = { String.valueOf(Now.millis()), Null.Str }; for (String c : clients) { for (String t : timestamps) { fileJson(c, t, md5, true); @@ -231,20 +233,23 @@ private void fileJson(String client, String timestamp, Function final String para = FormatUtil.sortParam(param); final String signature = sumFun == null - ? Null.Str - : sumFun.apply(para + secret + timestamp); + ? Null.Str + : sumFun.apply(para + secret + timestamp); + final boolean signed = !signature.isEmpty(); MultipartBody.Builder body = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart(fileKey, fileName, RequestBody.create(fileBody.getBytes(UTF_8), MULTIPART_FORM_DATA_VALUE)); + .setType(MultipartBody.FORM) + .addFormDataPart(fileKey, fileName, RequestBody.create(fileBody.getBytes(UTF_8), MULTIPART_FORM_DATA_VALUE)); okhttp3.Request request = new okhttp3.Request.Builder() - .url(ubd.build()) - .header(apiAuthProp.getClientHeader(), client) - .header(apiAuthProp.getTimestampHeader(), timestamp) - .header(apiAuthProp.getSignatureHeader(), signature) - .post(body.build()) - .build(); + .url(ubd.build()) + .header(apiAuthProp.getClientHeader(), client) + .header(apiAuthProp.getTimestampHeader(), timestamp) + .header(apiAuthProp.getSignatureHeader(), signature) + .header("ticket", String.valueOf(client.startsWith("win-"))) + .header("signed", String.valueOf(signed)) + .post(body.build()) + .build(); final Response r2 = OkHttpClientHelper.execute(okHttpClient, request, false); Assertions.assertNotNull(r2); @@ -260,12 +265,12 @@ private void fileJson(String client, String timestamp, Function final String text = resBody.string(); Assertions.assertEquals(jsonBody, text); - final String stmp = r2.header(apiAuthProp.getTimestampHeader()); + final String stmp = EmptySugar.nullToEmpty(r2.header(apiAuthProp.getTimestampHeader())); if (!timestamp.isEmpty()) { Assertions.assertEquals(timestamp, stmp); } - if (sumFun != null) { + if (signed) { final String sign = r2.header(apiAuthProp.getSignatureHeader()); Assertions.assertNotNull(sign); final String data1 = text + secret + stmp; @@ -283,8 +288,8 @@ private void fileJson(String client, String timestamp, Function @Test @TmsLink("C14026") public void testJsonFile() throws IOException { - String[] clients = {client, getToken()}; - String[] timestamps = {String.valueOf(Now.millis()), Null.Str}; + String[] clients = { client, getToken() }; + String[] timestamps = { String.valueOf(Now.millis()), Null.Str }; for (String c : clients) { for (String t : timestamps) { jsonFile(c, t, md5, true); @@ -311,18 +316,21 @@ private void jsonFile(String client, String timestamp, Function final String para = FormatUtil.sortParam(param); final String signature = sumFun == null - ? Null.Str - : sumFun.apply(para + jsonBody + secret + timestamp); - final String dgst = digest && sumFun != null ? sumFun.apply(jsonBody) : Null.Str; + ? Null.Str + : sumFun.apply(para + jsonBody + secret + timestamp); + final boolean signed = !signature.isEmpty(); + final String dgst = signed && digest ? sumFun.apply(jsonBody) : Null.Str; RequestBody body = RequestBody.create(jsonBody, APPLICATION_JSON_VALUE); okhttp3.Request request = new okhttp3.Request.Builder() - .url(ubd.build()) - .header(apiAuthProp.getClientHeader(), client) - .header(apiAuthProp.getTimestampHeader(), timestamp) - .header(apiAuthProp.getSignatureHeader(), signature) - .header(apiAuthProp.getDigestHeader(), dgst) - .post(body) - .build(); + .url(ubd.build()) + .header(apiAuthProp.getClientHeader(), client) + .header(apiAuthProp.getTimestampHeader(), timestamp) + .header(apiAuthProp.getSignatureHeader(), signature) + .header(apiAuthProp.getDigestHeader(), dgst) + .header("ticket", String.valueOf(client.startsWith("win-"))) + .header("signed", String.valueOf(signed)) + .post(body) + .build(); final Response r2 = OkHttpClientHelper.execute(okHttpClient, request, false); Assertions.assertNotNull(r2); @@ -333,7 +341,7 @@ private void jsonFile(String client, String timestamp, Function final String text = resBody.string(); Assertions.assertEquals(fileBody, text); - final String stmp = r2.header(apiAuthProp.getTimestampHeader()); + final String stmp = EmptySugar.nullToEmpty(r2.header(apiAuthProp.getTimestampHeader())); if (!timestamp.isEmpty()) { Assertions.assertEquals(timestamp, stmp); } @@ -344,7 +352,7 @@ private void jsonFile(String client, String timestamp, Function Assertions.assertEquals(fileName, disposition.getFilename()); final String data1; - if (digest && sumFun != null) { + if (signed && digest) { final String sum = sumFun.apply(fileBody); Assertions.assertEquals(sum, r2.header(apiAuthProp.getDigestHeader())); data1 = sum + secret + stmp; @@ -353,7 +361,7 @@ private void jsonFile(String client, String timestamp, Function data1 = secret + stmp; } - if (sumFun != null) { + if (signed) { final String sign = r2.header(apiAuthProp.getSignatureHeader()); Assertions.assertNotNull(sign); @@ -369,8 +377,8 @@ private void jsonFile(String client, String timestamp, Function @Test @TmsLink("C14027") public void testFileFile() throws IOException { - String[] clients = {client, getToken()}; - String[] timestamps = {String.valueOf(Now.millis()), Null.Str}; + String[] clients = { client, getToken() }; + String[] timestamps = { String.valueOf(Now.millis()), Null.Str }; for (String c : clients) { for (String t : timestamps) { fileFile(c, t, md5, true); @@ -386,8 +394,10 @@ private void fileFile(String client, String timestamp, Function param.put(ReqMethod, ModFileFile); param.put(ResFileName, fileName); param.put(ResFileBody, fileBody); + + final boolean signed = sumFun != null; final String dgst; - if (digest && sumFun != null) { + if (signed && digest) { dgst = sumFun.apply(fileBody); param.put(fileSum, dgst); } @@ -396,7 +406,7 @@ private void fileFile(String client, String timestamp, Function } final String para = FormatUtil.sortParam(param); - param.remove(fileSum); + param.remove(fileSum); // remove from query string, post by form HttpUrl.Builder ubd = urlBuilder(); for (Map.Entry en : param.entrySet()) { @@ -406,22 +416,27 @@ private void fileFile(String client, String timestamp, Function } } - final String signature = sumFun == null - ? Null.Str - : sumFun.apply(para + jsonBody + secret + timestamp); + final String signature = !signed + ? Null.Str + : sumFun.apply(para + secret + timestamp); MultipartBody.Builder body = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart(fileKey, fileName, RequestBody.create(fileBody.getBytes(UTF_8), MULTIPART_FORM_DATA_VALUE)) - .addFormDataPart(fileSum, dgst); + .setType(MultipartBody.FORM) + .addFormDataPart(fileKey, fileName, RequestBody.create(fileBody.getBytes(UTF_8), MULTIPART_FORM_DATA_VALUE)); + + if (!dgst.isEmpty()) { + body.addFormDataPart(fileSum, dgst); // pass fileSum by form + } okhttp3.Request request = new okhttp3.Request.Builder() - .url(ubd.build()) - .header(apiAuthProp.getClientHeader(), client) - .header(apiAuthProp.getTimestampHeader(), timestamp) - .header(apiAuthProp.getSignatureHeader(), signature) - .post(body.build()) - .build(); + .url(ubd.build()) + .header(apiAuthProp.getClientHeader(), client) + .header(apiAuthProp.getTimestampHeader(), timestamp) + .header(apiAuthProp.getSignatureHeader(), signature) + .header("ticket", String.valueOf(client.startsWith("win-"))) + .header("signed", String.valueOf(signed)) + .post(body.build()) + .build(); final Response r2 = OkHttpClientHelper.execute(okHttpClient, request, false); Assertions.assertNotNull(r2); @@ -432,7 +447,7 @@ private void fileFile(String client, String timestamp, Function final String text = resBody.string(); Assertions.assertEquals(fileBody, text); - final String stmp = r2.header(apiAuthProp.getTimestampHeader()); + final String stmp = EmptySugar.nullToEmpty(r2.header(apiAuthProp.getTimestampHeader())); if (!timestamp.isEmpty()) { Assertions.assertEquals(timestamp, stmp); } @@ -443,7 +458,7 @@ private void fileFile(String client, String timestamp, Function Assertions.assertEquals(fileName, disposition.getFilename()); final String data1; - if (digest && sumFun != null) { + if (signed && digest) { final String sum = sumFun.apply(fileBody); Assertions.assertEquals(sum, r2.header(apiAuthProp.getDigestHeader())); data1 = sum + secret + stmp; @@ -452,7 +467,7 @@ private void fileFile(String client, String timestamp, Function data1 = secret + stmp; } - if (sumFun != null) { + if (signed) { final String sign = r2.header(apiAuthProp.getSignatureHeader()); Assertions.assertNotNull(sign); diff --git a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/controller/api/OkHttpTokenizeTest.java b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/controller/api/OkHttpTokenizeTest.java index 53f48119c..f9fc42b5c 100644 --- a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/controller/api/OkHttpTokenizeTest.java +++ b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/controller/api/OkHttpTokenizeTest.java @@ -22,11 +22,7 @@ * @author trydofor * @since 2022-11-16 */ -@SpringBootTest( - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "wings.warlock.apiauth.must-signature=false", - }) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Slf4j class OkHttpTokenizeTest { diff --git a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/other/WarlockWatchingTest.java b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/other/WarlockWatchingTest.java index 939ba0376..257d92f0e 100644 --- a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/other/WarlockWatchingTest.java +++ b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/other/WarlockWatchingTest.java @@ -17,33 +17,40 @@ import pro.fessional.wings.slardar.httprest.okhttp.OkHttpClientHelper; import pro.fessional.wings.warlock.app.service.TestWatchingService; +import java.util.concurrent.atomic.AtomicReference; + /** * @author trydofor * @since 2022-11-22 */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "spring.wings.warlock.enabled.watching=true", - "wings.warlock.watching.jooq-threshold=0", - "wings.warlock.watching.service-threshold=0", - "wings.warlock.watching.controller-threshold=0", - }) + properties = { + "spring.wings.warlock.enabled.watching=true", + "wings.warlock.watching.jooq-threshold=0", + "wings.warlock.watching.service-threshold=0", + "wings.warlock.watching.controller-threshold=0", + }) @Slf4j @DependsOnDatabaseInitialization public class WarlockWatchingTest { - @Setter(onMethod_ = {@Value("http://localhost:${local.server.port}")}) + @Setter(onMethod_ = { @Value("http://localhost:${local.server.port}") }) private String host; - @Setter(onMethod_ = {@Autowired}) + @Setter(onMethod_ = { @Autowired }) private OkHttpClient okHttpClient; - /** - * Check the log - */ @Test @TmsLink("C14037") public void testWatching() { + final AtomicReference tkn = new AtomicReference<>(); + final AtomicReference wtc = new AtomicReference<>(); + + Watches.setWatchHandler((t, w) -> { + tkn.set(t); + wtc.set(w.toString()); + }); + final StopWatch.Watch watch = Watches.acquire("testWatching"); final Request.Builder body = new Request.Builder().url(host + "/test/watching.json"); final Response r1 = OkHttpClientHelper.execute(okHttpClient, body, false); @@ -55,5 +62,8 @@ public void testWatching() { // async in async task pool Assertions.assertTrue(2 <= TestWatchingService.AsyncWatch.size()); Assertions.assertTrue(TestWatchingService.WatchOwner.getWatches().isEmpty()); + + Assertions.assertEquals("testWatching", tkn.get()); + Assertions.assertTrue(wtc.get().contains("testWatching"), wtc.get()); } } diff --git a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/security/GuestSessionTest.java b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/security/GuestSessionTest.java index cfc97fbb1..f5522ff1f 100644 --- a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/security/GuestSessionTest.java +++ b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/security/GuestSessionTest.java @@ -15,7 +15,7 @@ import pro.fessional.wings.slardar.httprest.okhttp.OkHttpClientHelper; /** - * Principal required + * Principal required * * @author trydofor * @since 2021-03-09 diff --git a/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/webmvc/JournalControllerTest.java b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/webmvc/JournalControllerTest.java new file mode 100644 index 000000000..3884f5950 --- /dev/null +++ b/wings/warlock-shadow/src/test/java/pro/fessional/wings/warlock/webmvc/JournalControllerTest.java @@ -0,0 +1,79 @@ +package pro.fessional.wings.warlock.webmvc; + +import io.qameta.allure.TmsLink; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.web.client.RestTemplate; +import pro.fessional.mirana.best.DummyBlock; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; + + +/** + * @author trydofor + * @since 2022-12-03 + */ +@Slf4j +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = "test.plain-async=true") +@AutoConfigureMockMvc +public class JournalControllerTest { + + @Setter(onMethod_ = { @Value("http://localhost:${local.server.port}") }) + private String host; + + @Setter(onMethod_ = { @Autowired }) + private RestTemplate restTemplate; + + @Test + @TmsLink("C14086") + public void testTtlContext() throws InterruptedException { + + final ExecutorService executorService = Executors.newFixedThreadPool(50); + + final Pattern number = Pattern.compile("\\d+"); + final int total = 1000; + final AtomicInteger error = new AtomicInteger(0); + + final ConcurrentHashMap normal = new ConcurrentHashMap<>(); + + for (int i = 0; i < total; i++) { + final String ix = String.valueOf(i); + final boolean ttl = i % 2 == 0; + executorService.submit(() -> { + final String id = restTemplate.getForObject( + host + + "/test/ttl-journal.json?t=" + + ttl + "&i=" + ix, String.class); + if (number.matcher(id).matches()) { + normal.compute(id, (k, v) -> v == null ? 1 : v + 1); + } + else { + error.incrementAndGet(); + } + }); + } + executorService.shutdown(); + while (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { + DummyBlock.empty(); + } + + for (Map.Entry en : normal.entrySet()) { + Assertions.assertEquals(1, en.getValue(), en.getKey()); + } + log.info("normal={}, error={}, total={}", normal.size(), error.get(), total); + Assertions.assertEquals(total, error.get() + normal.size()); + } +} diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/SysConstantEnumTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/SysConstantEnumTable.java index 6cad9f45b..4af3c04e3 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/SysConstantEnumTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/SysConstantEnumTable.java @@ -34,7 +34,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -184,7 +184,6 @@ public SelectField mapping(Function5 SelectField mapping(Class toType, Function5 from) { return convertFrom(toType, Records.mapping(from)); } - /** * alias asK5 diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/SysStandardI18nTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/SysStandardI18nTable.java index 74ff36310..b363bf12e 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/SysStandardI18nTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/SysStandardI18nTable.java @@ -34,7 +34,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -184,7 +184,6 @@ public SelectField mapping(Function5 SelectField mapping(Class toType, Function5 from) { return convertFrom(toType, Records.mapping(from)); } - /** * alias asM5 diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinConfRuntimeTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinConfRuntimeTable.java index ac90edf85..5a23cbc4c 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinConfRuntimeTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinConfRuntimeTable.java @@ -6,10 +6,10 @@ import org.jetbrains.annotations.NotNull; import org.jooq.Field; -import org.jooq.Function6; +import org.jooq.Function8; import org.jooq.Name; import org.jooq.Records; -import org.jooq.Row6; +import org.jooq.Row8; import org.jooq.Schema; import org.jooq.SelectField; import org.jooq.Table; @@ -34,7 +34,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -63,25 +63,35 @@ public Class getRecordType() { */ public final TableField Key = createField(DSL.name("key"), SQLDataType.VARCHAR(200).nullable(false), this, ""); + /** + * The column win_conf_runtime.enabled. + */ + public final TableField Enabled = createField(DSL.name("enabled"), SQLDataType.BOOLEAN.nullable(false).defaultValue(DSL.inline("1", SQLDataType.BOOLEAN)), this, ""); + /** * The column win_conf_runtime.current. */ - public final TableField Current = createField(DSL.name("current"), SQLDataType.VARCHAR(5000).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField Current = createField(DSL.name("current"), SQLDataType.CLOB.nullable(false), this, ""); /** * The column win_conf_runtime.previous. */ - public final TableField Previous = createField(DSL.name("previous"), SQLDataType.VARCHAR(5000).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField Previous = createField(DSL.name("previous"), SQLDataType.CLOB.nullable(false), this, ""); /** * The column win_conf_runtime.initial. */ - public final TableField Initial = createField(DSL.name("initial"), SQLDataType.VARCHAR(5000).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField Initial = createField(DSL.name("initial"), SQLDataType.CLOB.nullable(false), this, ""); + + /** + * The column win_conf_runtime.outline. + */ + public final TableField Outline = createField(DSL.name("outline"), SQLDataType.VARCHAR(5000).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); /** * The column win_conf_runtime.comment. */ - public final TableField Comment = createField(DSL.name("comment"), SQLDataType.VARCHAR(500).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + public final TableField Comment = createField(DSL.name("comment"), SQLDataType.VARCHAR(5000).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); /** * The column win_conf_runtime.handler. @@ -167,18 +177,18 @@ public WinConfRuntimeTable rename(Table name) { } // ------------------------------------------------------------------------- - // Row6 type methods + // Row8 type methods // ------------------------------------------------------------------------- @Override - public Row6 fieldsRow() { - return (Row6) super.fieldsRow(); + public Row8 fieldsRow() { + return (Row8) super.fieldsRow(); } /** * Convenience mapping calling {@link SelectField#convertFrom(Function)}. */ - public SelectField mapping(Function6 from) { + public SelectField mapping(Function8 from) { return convertFrom(Records.mapping(from)); } @@ -186,10 +196,9 @@ public SelectField mapping(Function6 SelectField mapping(Class toType, Function6 from) { + public SelectField mapping(Class toType, Function8 from) { return convertFrom(toType, Records.mapping(from)); } - /** * alias asS4 diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinPermEntryTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinPermEntryTable.java index 03cd5d144..c0ef876f7 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinPermEntryTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinPermEntryTable.java @@ -41,7 +41,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -215,7 +215,6 @@ public SelectField mapping(Class toType, Function8delete_dt condition diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinRoleEntryTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinRoleEntryTable.java index ce9b3a3d6..d539a7680 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinRoleEntryTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinRoleEntryTable.java @@ -40,7 +40,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -209,7 +209,6 @@ public SelectField mapping(Class toType, Function7delete_dt condition diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinRoleGrantTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinRoleGrantTable.java index 360b4935a..07466e7fe 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinRoleGrantTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinRoleGrantTable.java @@ -36,7 +36,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -186,7 +186,6 @@ public SelectField mapping(Function5 SelectField mapping(Class toType, Function5 from) { return convertFrom(toType, Records.mapping(from)); } - /** * alias asE2 diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserAuthnTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserAuthnTable.java index a7e5dbc29..dada855af 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserAuthnTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserAuthnTable.java @@ -41,7 +41,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -245,7 +245,6 @@ public SelectField mapping(Class toType, Function14delete_dt condition diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserBasisTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserBasisTable.java index b819af618..6a8b7cf49 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserBasisTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserBasisTable.java @@ -47,7 +47,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -246,7 +246,6 @@ public SelectField mapping(Class toType, Function13delete_dt condition diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserGrantTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserGrantTable.java index 4572d988e..aae4d52e4 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserGrantTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserGrantTable.java @@ -36,7 +36,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -186,7 +186,6 @@ public SelectField mapping(Function5 SelectField mapping(Class toType, Function5 from) { return convertFrom(toType, Records.mapping(from)); } - /** * alias asR2 diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserLoginTable.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserLoginTable.java index 689e547e6..af289fb4e 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserLoginTable.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/WinUserLoginTable.java @@ -36,7 +36,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -210,7 +210,6 @@ public SelectField mapping(Class toType, Function8 fetchOptionalByKey(String value) { return fetchOptional(WinConfRuntimeTable.WinConfRuntime.Key, value); } + /** + * Fetch records that have enabled BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfEnabled(Boolean lowerInclusive, Boolean upperInclusive) { + return fetchRange(WinConfRuntimeTable.WinConfRuntime.Enabled, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have enabled IN (values) + */ + public List fetchByEnabled(Boolean... values) { + return fetch(WinConfRuntimeTable.WinConfRuntime.Enabled, values); + } + + public List fetchByEnabled(Collection values) { + return fetch(WinConfRuntimeTable.WinConfRuntime.Enabled, values); + } + /** * Fetch records that have current BETWEEN lowerInclusive AND * upperInclusive @@ -145,6 +164,25 @@ public List fetchByInitial(Collection values) return fetch(WinConfRuntimeTable.WinConfRuntime.Initial, values); } + /** + * Fetch records that have outline BETWEEN lowerInclusive AND + * upperInclusive + */ + public List fetchRangeOfOutline(String lowerInclusive, String upperInclusive) { + return fetchRange(WinConfRuntimeTable.WinConfRuntime.Outline, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have outline IN (values) + */ + public List fetchByOutline(String... values) { + return fetch(WinConfRuntimeTable.WinConfRuntime.Outline, values); + } + + public List fetchByOutline(Collection values) { + return fetch(WinConfRuntimeTable.WinConfRuntime.Outline, values); + } + /** * Fetch records that have comment BETWEEN lowerInclusive AND * upperInclusive diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/interfaces/IWinConfRuntime.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/interfaces/IWinConfRuntime.java index 64be01a2e..d4572a3b6 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/interfaces/IWinConfRuntime.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/interfaces/IWinConfRuntime.java @@ -14,7 +14,7 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" @@ -32,6 +32,16 @@ public interface IWinConfRuntime extends Serializable { */ public String getKey(); + /** + * Setter for win_conf_runtime.enabled. + */ + public void setEnabled(Boolean value); + + /** + * Getter for win_conf_runtime.enabled. + */ + public Boolean getEnabled(); + /** * Setter for win_conf_runtime.current. */ @@ -62,6 +72,16 @@ public interface IWinConfRuntime extends Serializable { */ public String getInitial(); + /** + * Setter for win_conf_runtime.outline. + */ + public void setOutline(String value); + + /** + * Getter for win_conf_runtime.outline. + */ + public String getOutline(); + /** * Setter for win_conf_runtime.comment. */ diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/pojos/WinConfRuntime.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/pojos/WinConfRuntime.java index b5b410b84..8600ca6ce 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/pojos/WinConfRuntime.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/pojos/WinConfRuntime.java @@ -30,9 +30,11 @@ public class WinConfRuntime implements IWinConfRuntime { private static final long serialVersionUID = 1L; private String key; + private Boolean enabled; private String current; private String previous; private String initial; + private String outline; private String comment; private String handler; @@ -40,25 +42,31 @@ public WinConfRuntime() {} public WinConfRuntime(IWinConfRuntime value) { this.key = value.getKey(); + this.enabled = value.getEnabled(); this.current = value.getCurrent(); this.previous = value.getPrevious(); this.initial = value.getInitial(); + this.outline = value.getOutline(); this.comment = value.getComment(); this.handler = value.getHandler(); } public WinConfRuntime( String key, + Boolean enabled, String current, String previous, String initial, + String outline, String comment, String handler ) { this.key = key; + this.enabled = enabled; this.current = current; this.previous = previous; this.initial = initial; + this.outline = outline; this.comment = comment; this.handler = handler; } @@ -143,6 +151,86 @@ public void setKeyIf(UnaryOperator key) { } + /** + * Getter for win_conf_runtime.enabled. + */ + @Override + public Boolean getEnabled() { + return this.enabled; + } + + /** + * Setter for win_conf_runtime.enabled. + */ + @Override + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + @Transient + public void setEnabledIf(Boolean enabled, boolean bool) { + if (bool) { + this.enabled = enabled; + } + } + + @Transient + public void setEnabledIf(Supplier enabled, boolean bool) { + if (bool) { + this.enabled = enabled.get(); + } + } + + @Transient + public void setEnabledIf(Boolean enabled, Predicate bool) { + if (bool.test(enabled)) { + this.enabled = enabled; + } + } + + @Transient + public void setEnabledIf(Boolean enabled, Predicate bool, Supplier... enableds) { + if (bool.test(enabled)) { + this.enabled = enabled; + return; + } + for (Supplier supplier : enableds) { + enabled = supplier.get(); + if (bool.test(enabled)) { + this.enabled = enabled; + return; + } + } + } + + @Transient + public void setEnabledIfNot(Boolean enabled, Predicate bool) { + if (!bool.test(enabled)) { + this.enabled = enabled; + } + } + + @Transient + public void setEnabledIfNot(Boolean enabled, Predicate bool, Supplier... enableds) { + if (!bool.test(enabled)) { + this.enabled = enabled; + return; + } + for (Supplier supplier : enableds) { + enabled = supplier.get(); + if (!bool.test(enabled)) { + this.enabled = enabled; + return; + } + } + } + + @Transient + public void setEnabledIf(UnaryOperator enabled) { + this.enabled = enabled.apply(this.enabled); + } + + /** * Getter for win_conf_runtime.current. */ @@ -383,6 +471,86 @@ public void setInitialIf(UnaryOperator initial) { } + /** + * Getter for win_conf_runtime.outline. + */ + @Override + public String getOutline() { + return this.outline; + } + + /** + * Setter for win_conf_runtime.outline. + */ + @Override + public void setOutline(String outline) { + this.outline = outline; + } + + @Transient + public void setOutlineIf(String outline, boolean bool) { + if (bool) { + this.outline = outline; + } + } + + @Transient + public void setOutlineIf(Supplier outline, boolean bool) { + if (bool) { + this.outline = outline.get(); + } + } + + @Transient + public void setOutlineIf(String outline, Predicate bool) { + if (bool.test(outline)) { + this.outline = outline; + } + } + + @Transient + public void setOutlineIf(String outline, Predicate bool, Supplier... outlines) { + if (bool.test(outline)) { + this.outline = outline; + return; + } + for (Supplier supplier : outlines) { + outline = supplier.get(); + if (bool.test(outline)) { + this.outline = outline; + return; + } + } + } + + @Transient + public void setOutlineIfNot(String outline, Predicate bool) { + if (!bool.test(outline)) { + this.outline = outline; + } + } + + @Transient + public void setOutlineIfNot(String outline, Predicate bool, Supplier... outlines) { + if (!bool.test(outline)) { + this.outline = outline; + return; + } + for (Supplier supplier : outlines) { + outline = supplier.get(); + if (!bool.test(outline)) { + this.outline = outline; + return; + } + } + } + + @Transient + public void setOutlineIf(UnaryOperator outline) { + this.outline = outline.apply(this.outline); + } + + /** * Getter for win_conf_runtime.comment. */ @@ -558,6 +726,12 @@ public boolean equals(Object obj) { } else if (!this.key.equals(other.key)) return false; + if (this.enabled == null) { + if (other.enabled != null) + return false; + } + else if (!this.enabled.equals(other.enabled)) + return false; if (this.current == null) { if (other.current != null) return false; @@ -576,6 +750,12 @@ else if (!this.previous.equals(other.previous)) } else if (!this.initial.equals(other.initial)) return false; + if (this.outline == null) { + if (other.outline != null) + return false; + } + else if (!this.outline.equals(other.outline)) + return false; if (this.comment == null) { if (other.comment != null) return false; @@ -596,9 +776,11 @@ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((this.key == null) ? 0 : this.key.hashCode()); + result = prime * result + ((this.enabled == null) ? 0 : this.enabled.hashCode()); result = prime * result + ((this.current == null) ? 0 : this.current.hashCode()); result = prime * result + ((this.previous == null) ? 0 : this.previous.hashCode()); result = prime * result + ((this.initial == null) ? 0 : this.initial.hashCode()); + result = prime * result + ((this.outline == null) ? 0 : this.outline.hashCode()); result = prime * result + ((this.comment == null) ? 0 : this.comment.hashCode()); result = prime * result + ((this.handler == null) ? 0 : this.handler.hashCode()); return result; @@ -609,9 +791,11 @@ public String toString() { StringBuilder sb = new StringBuilder("WinConfRuntime ("); sb.append(key); + sb.append(", ").append(enabled); sb.append(", ").append(current); sb.append(", ").append(previous); sb.append(", ").append(initial); + sb.append(", ").append(outline); sb.append(", ").append(comment); sb.append(", ").append(handler); @@ -626,9 +810,11 @@ public String toString() { @Override public void from(IWinConfRuntime from) { setKey(from.getKey()); + setEnabled(from.getEnabled()); setCurrent(from.getCurrent()); setPrevious(from.getPrevious()); setInitial(from.getInitial()); + setOutline(from.getOutline()); setComment(from.getComment()); setHandler(from.getHandler()); } diff --git a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/records/WinConfRuntimeRecord.java b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/records/WinConfRuntimeRecord.java index 9ac81fcd8..32d280c84 100644 --- a/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/records/WinConfRuntimeRecord.java +++ b/wings/warlock/src/main/java-gen/pro/fessional/wings/warlock/database/autogen/tables/records/WinConfRuntimeRecord.java @@ -6,8 +6,8 @@ import org.jooq.Field; import org.jooq.Record1; -import org.jooq.Record6; -import org.jooq.Row6; +import org.jooq.Record8; +import org.jooq.Row8; import org.jooq.impl.UpdatableRecordImpl; import pro.fessional.wings.warlock.database.autogen.tables.WinConfRuntimeTable; import pro.fessional.wings.warlock.database.autogen.tables.interfaces.IWinConfRuntime; @@ -22,13 +22,13 @@ @Generated( value = { "https://www.jooq.org", - "jOOQ version:3.18.7", + "jOOQ version:3.18.9", "schema version:2020102701" }, comments = "This class is generated by jOOQ" ) @SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) -public class WinConfRuntimeRecord extends UpdatableRecordImpl implements Record6, IWinConfRuntime { +public class WinConfRuntimeRecord extends UpdatableRecordImpl implements Record8, IWinConfRuntime { private static final long serialVersionUID = 1L; @@ -48,12 +48,28 @@ public String getKey() { return (String) get(0); } + /** + * Setter for win_conf_runtime.enabled. + */ + @Override + public void setEnabled(Boolean value) { + set(1, value); + } + + /** + * Getter for win_conf_runtime.enabled. + */ + @Override + public Boolean getEnabled() { + return (Boolean) get(1); + } + /** * Setter for win_conf_runtime.current. */ @Override public void setCurrent(String value) { - set(1, value); + set(2, value); } /** @@ -61,7 +77,7 @@ public void setCurrent(String value) { */ @Override public String getCurrent() { - return (String) get(1); + return (String) get(2); } /** @@ -69,7 +85,7 @@ public String getCurrent() { */ @Override public void setPrevious(String value) { - set(2, value); + set(3, value); } /** @@ -77,7 +93,7 @@ public void setPrevious(String value) { */ @Override public String getPrevious() { - return (String) get(2); + return (String) get(3); } /** @@ -85,7 +101,7 @@ public String getPrevious() { */ @Override public void setInitial(String value) { - set(3, value); + set(4, value); } /** @@ -93,7 +109,23 @@ public void setInitial(String value) { */ @Override public String getInitial() { - return (String) get(3); + return (String) get(4); + } + + /** + * Setter for win_conf_runtime.outline. + */ + @Override + public void setOutline(String value) { + set(5, value); + } + + /** + * Getter for win_conf_runtime.outline. + */ + @Override + public String getOutline() { + return (String) get(5); } /** @@ -101,7 +133,7 @@ public String getInitial() { */ @Override public void setComment(String value) { - set(4, value); + set(6, value); } /** @@ -109,7 +141,7 @@ public void setComment(String value) { */ @Override public String getComment() { - return (String) get(4); + return (String) get(6); } /** @@ -117,7 +149,7 @@ public String getComment() { */ @Override public void setHandler(String value) { - set(5, value); + set(7, value); } /** @@ -125,7 +157,7 @@ public void setHandler(String value) { */ @Override public String getHandler() { - return (String) get(5); + return (String) get(7); } // ------------------------------------------------------------------------- @@ -138,17 +170,17 @@ public Record1 key() { } // ------------------------------------------------------------------------- - // Record6 type implementation + // Record8 type implementation // ------------------------------------------------------------------------- @Override - public Row6 fieldsRow() { - return (Row6) super.fieldsRow(); + public Row8 fieldsRow() { + return (Row8) super.fieldsRow(); } @Override - public Row6 valuesRow() { - return (Row6) super.valuesRow(); + public Row8 valuesRow() { + return (Row8) super.valuesRow(); } @Override @@ -157,27 +189,37 @@ public Field field1() { } @Override - public Field field2() { - return WinConfRuntimeTable.WinConfRuntime.Current; + public Field field2() { + return WinConfRuntimeTable.WinConfRuntime.Enabled; } @Override public Field field3() { - return WinConfRuntimeTable.WinConfRuntime.Previous; + return WinConfRuntimeTable.WinConfRuntime.Current; } @Override public Field field4() { - return WinConfRuntimeTable.WinConfRuntime.Initial; + return WinConfRuntimeTable.WinConfRuntime.Previous; } @Override public Field field5() { - return WinConfRuntimeTable.WinConfRuntime.Comment; + return WinConfRuntimeTable.WinConfRuntime.Initial; } @Override public Field field6() { + return WinConfRuntimeTable.WinConfRuntime.Outline; + } + + @Override + public Field field7() { + return WinConfRuntimeTable.WinConfRuntime.Comment; + } + + @Override + public Field field8() { return WinConfRuntimeTable.WinConfRuntime.Handler; } @@ -187,27 +229,37 @@ public String component1() { } @Override - public String component2() { - return getCurrent(); + public Boolean component2() { + return getEnabled(); } @Override public String component3() { - return getPrevious(); + return getCurrent(); } @Override public String component4() { - return getInitial(); + return getPrevious(); } @Override public String component5() { - return getComment(); + return getInitial(); } @Override public String component6() { + return getOutline(); + } + + @Override + public String component7() { + return getComment(); + } + + @Override + public String component8() { return getHandler(); } @@ -217,27 +269,37 @@ public String value1() { } @Override - public String value2() { - return getCurrent(); + public Boolean value2() { + return getEnabled(); } @Override public String value3() { - return getPrevious(); + return getCurrent(); } @Override public String value4() { - return getInitial(); + return getPrevious(); } @Override public String value5() { - return getComment(); + return getInitial(); } @Override public String value6() { + return getOutline(); + } + + @Override + public String value7() { + return getComment(); + } + + @Override + public String value8() { return getHandler(); } @@ -248,43 +310,57 @@ public WinConfRuntimeRecord value1(String value) { } @Override - public WinConfRuntimeRecord value2(String value) { - setCurrent(value); + public WinConfRuntimeRecord value2(Boolean value) { + setEnabled(value); return this; } @Override public WinConfRuntimeRecord value3(String value) { - setPrevious(value); + setCurrent(value); return this; } @Override public WinConfRuntimeRecord value4(String value) { - setInitial(value); + setPrevious(value); return this; } @Override public WinConfRuntimeRecord value5(String value) { - setComment(value); + setInitial(value); return this; } @Override public WinConfRuntimeRecord value6(String value) { + setOutline(value); + return this; + } + + @Override + public WinConfRuntimeRecord value7(String value) { + setComment(value); + return this; + } + + @Override + public WinConfRuntimeRecord value8(String value) { setHandler(value); return this; } @Override - public WinConfRuntimeRecord values(String value1, String value2, String value3, String value4, String value5, String value6) { + public WinConfRuntimeRecord values(String value1, Boolean value2, String value3, String value4, String value5, String value6, String value7, String value8) { value1(value1); value2(value2); value3(value3); value4(value4); value5(value5); value6(value6); + value7(value7); + value8(value8); return this; } @@ -295,9 +371,11 @@ public WinConfRuntimeRecord values(String value1, String value2, String value3, @Override public void from(IWinConfRuntime from) { setKey(from.getKey()); + setEnabled(from.getEnabled()); setCurrent(from.getCurrent()); setPrevious(from.getPrevious()); setInitial(from.getInitial()); + setOutline(from.getOutline()); setComment(from.getComment()); setHandler(from.getHandler()); resetChangedOnNotNull(); @@ -323,13 +401,15 @@ public WinConfRuntimeRecord() { /** * Create a detached, initialised WinConfRuntimeRecord */ - public WinConfRuntimeRecord(String key, String current, String previous, String initial, String comment, String handler) { + public WinConfRuntimeRecord(String key, Boolean enabled, String current, String previous, String initial, String outline, String comment, String handler) { super(WinConfRuntimeTable.WinConfRuntime); setKey(key); + setEnabled(enabled); setCurrent(current); setPrevious(previous); setInitial(initial); + setOutline(outline); setComment(comment); setHandler(handler); resetChangedOnNotNull(); @@ -343,9 +423,11 @@ public WinConfRuntimeRecord(WinConfRuntime value) { if (value != null) { setKey(value.getKey()); + setEnabled(value.getEnabled()); setCurrent(value.getCurrent()); setPrevious(value.getPrevious()); setInitial(value.getInitial()); + setOutline(value.getOutline()); setComment(value.getComment()); setHandler(value.getHandler()); resetChangedOnNotNull(); diff --git a/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/conf/RuntimeConfService.java b/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/conf/RuntimeConfService.java index 072a848b2..4b779111f 100644 --- a/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/conf/RuntimeConfService.java +++ b/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/conf/RuntimeConfService.java @@ -1,11 +1,14 @@ package pro.fessional.wings.warlock.service.conf; +import org.jetbrains.annotations.NotNull; +import org.springframework.core.ResolvableType; import org.springframework.core.convert.TypeDescriptor; import pro.fessional.mirana.cast.EnumConvertor; -import pro.fessional.wings.silencer.enhance.TypeSugar; +import pro.fessional.wings.silencer.support.TypeSugar; import java.util.List; import java.util.Map; +import java.util.Set; /** * Support for ConversionService and Json parsing configuration @@ -15,41 +18,41 @@ */ public interface RuntimeConfService { - default String getString(String key) { + default String getString(@NotNull String key) { return getObject(key, TypeSugar.StringDescriptor); } - default String getString(Class key) { + default String getString(@NotNull Class key) { return getString(key.getName()); } - default String getString(Enum key) { + default String getString(@NotNull Enum key) { return getString(EnumConvertor.enum2Str(key)); } - default int getInt(String key, int els) { + default int getInt(@NotNull String key, int els) { final Integer obj = getSimple(key, Integer.class); return obj == null ? els : obj; } - default int getInt(Class key, int els) { + default int getInt(@NotNull Class key, int els) { return getInt(key.getName(), els); } - default int getInt(Enum key, int els) { + default int getInt(@NotNull Enum key, int els) { return getInt(EnumConvertor.enum2Str(key), els); } - default boolean getBoolean(String key, boolean els) { + default boolean getBoolean(@NotNull String key, boolean els) { final Boolean obj = getSimple(key, Boolean.class); return obj == null ? els : obj; } - default boolean getBoolean(Class key, boolean els) { + default boolean getBoolean(@NotNull Class key, boolean els) { return getBoolean(key.getName(), els); } - default boolean getBoolean(Enum key, boolean els) { + default boolean getBoolean(@NotNull Enum key, boolean els) { return getBoolean(EnumConvertor.enum2Str(key), els); } @@ -58,63 +61,85 @@ default long getLong(String key, long els) { return obj == null ? els : obj; } - default long getLong(Class key, long els) { + default long getLong(@NotNull Class key, long els) { return getLong(key.getName(), els); } - default long getLong(Enum key, long els) { + default long getLong(@NotNull Enum key, long els) { return getLong(EnumConvertor.enum2Str(key), els); } - default T getSimple(String key, Class vt) { - return getObject(key, TypeSugar.describe(vt)); + default T getSimple(@NotNull String key, @NotNull Class type) { + return getObject(key, TypeSugar.describe(type)); } - default T getSimple(Class key, Class vt) { - return getSimple(key.getName(), vt); + default T getSimple(@NotNull Class key, @NotNull Class type) { + return getSimple(key.getName(), type); } - default T getSimple(Enum key, Class vt) { - return getSimple(EnumConvertor.enum2Str(key), vt); + default T getSimple(@NotNull Enum key, @NotNull Class type) { + return getSimple(EnumConvertor.enum2Str(key), type); } - default > T getEnum(Class key) { + default > T getEnum(@NotNull Class key) { return getSimple(key.getName(), key); } - default > List getEnums(Class key) { + default > List getEnums(@NotNull Class key) { return getList(key.getName(), key); } - default List getList(String key, Class vt) { - return getObject(key, TypeSugar.describe(List.class, vt)); + @NotNull + default List getList(@NotNull String key, @NotNull Class type) { + return getObject(key, TypeSugar.describe(List.class, type)); } - default List getList(Class key, Class vt) { - return getList(key.getName(), vt); + @NotNull + default List getList(@NotNull Class key, @NotNull Class type) { + return getList(key.getName(), type); } - default List getList(Enum key, Class vt) { - return getList(EnumConvertor.enum2Str(key), vt); + @NotNull + default List getList(@NotNull Enum key, @NotNull Class type) { + return getList(EnumConvertor.enum2Str(key), type); } - default Map getMap(String key, Class kt, Class vt) { - return getObject(key, TypeSugar.describe(Map.class, kt, vt)); + + @NotNull + default Set getSet(@NotNull String key, @NotNull Class type) { + return getObject(key, TypeSugar.describe(Set.class, type)); + } + + @NotNull + default Set getSet(@NotNull Class key, @NotNull Class type) { + return getSet(key.getName(), type); + } + + @NotNull + default Set getSet(@NotNull Enum key, @NotNull Class type) { + return getSet(EnumConvertor.enum2Str(key), type); } - default Map getMap(Class key, Class kt, Class vt) { - return getMap(key.getName(), kt, vt); + @NotNull + default Map getMap(@NotNull String key, @NotNull Class keyType, @NotNull Class valueType) { + return getObject(key, TypeSugar.describe(Map.class, keyType, valueType)); } - default Map getMap(Enum key, Class kt, Class vt) { - return getMap(EnumConvertor.enum2Str(key), kt, vt); + @NotNull + default Map getMap(@NotNull Class key, @NotNull Class keyType, @NotNull Class valueType) { + return getMap(key.getName(), keyType, valueType); } - default T getObject(Class key, TypeDescriptor type) { + @NotNull + default Map getMap(@NotNull Enum key, @NotNull Class keyType, @NotNull Class valueType) { + return getMap(EnumConvertor.enum2Str(key), keyType, valueType); + } + + default T getObject(@NotNull Class key, @NotNull TypeDescriptor type) { return getObject(key.getName(), type); } - default T getObject(Enum key, TypeDescriptor type) { + default T getObject(@NotNull Enum key, @NotNull TypeDescriptor type) { return getObject(EnumConvertor.enum2Str(key), type); } @@ -126,59 +151,103 @@ default T getObject(Enum key, TypeDescriptor type) { * @param Type of value * @return value */ - T getObject(String key, TypeDescriptor type); + T getObject(@NotNull String key, @NotNull TypeDescriptor type); /** - * set value of config + * set value of config, return false if not found * * @param key key * @param value config */ - void setObject(String key, Object value); + boolean setObject(@NotNull String key, @NotNull Object value); - default void setObject(Class key, Object value) { - setObject(key.getName(), value); + default boolean setObject(@NotNull Class key, @NotNull Object value) { + return setObject(key.getName(), value); } - default void setObject(Enum key, Object value) { - setObject(EnumConvertor.enum2Str(key), value); + default boolean setObject(@NotNull Enum key, @NotNull Object value) { + return setObject(EnumConvertor.enum2Str(key), value); } /** - * create new config + * create new config, return true if handled * * @param key config key * @param value config value - * @param comment config comment - * @param handler type handler name + * @param comment config comment, empty if null + * @param handler type handler name, auto select if null + * @param outline type outline, resolved from value if null * @return whether handled */ - boolean newObject(String key, Object value, String comment, String handler); + boolean newObject(@NotNull String key, @NotNull Object value, String comment, String handler, ResolvableType outline); + + default boolean newObject(@NotNull Class key, @NotNull Object value, String comment, String handler, ResolvableType outline) { + return newObject(key.getName(), value, comment, handler, outline); + } - default boolean newObject(Class key, Object value, String comment, String handler) { - return newObject(key.getName(), value, comment, handler); + default boolean newObject(@NotNull Enum key, @NotNull Object value, String comment, String handler, ResolvableType outline) { + return newObject(EnumConvertor.enum2Str(key), value, comment, handler, outline); } - default boolean newObject(Enum key, Object value, String comment, String handler) { - return newObject(EnumConvertor.enum2Str(key), value, comment, handler); + default boolean newObject(@NotNull String key, @NotNull Object value, String comment, String handler, TypeDescriptor outline) { + return newObject(key, value, comment, handler, outline == null ? (ResolvableType) null : outline.getResolvableType()); + } + + default boolean newObject(@NotNull Class key, @NotNull Object value, String comment, String handler, TypeDescriptor outline) { + return newObject(key.getName(), value, comment, handler, outline == null ? (ResolvableType) null : outline.getResolvableType()); + } + + default boolean newObject(@NotNull Enum key, @NotNull Object value, String comment, String handler, TypeDescriptor outline) { + return newObject(EnumConvertor.enum2Str(key), value, comment, handler, outline == null ? (ResolvableType) null : outline.getResolvableType()); + } + + default boolean newObject(@NotNull String key, @NotNull Object value, String comment, String handler, Class outline, Class... gernics) { + return newObject(key, value, comment, handler, outline == null ? (ResolvableType) null : TypeSugar.resolve(outline, gernics)); + } + + default boolean newObject(@NotNull Class key, @NotNull Object value, String comment, String handler, Class outline, Class... gernics) { + return newObject(key.getName(), value, comment, handler, outline == null ? (ResolvableType) null : TypeSugar.resolve(outline, gernics)); + } + + default boolean newObject(@NotNull Enum key, @NotNull Object value, String comment, String handler, Class outline, Class... gernics) { + return newObject(EnumConvertor.enum2Str(key), value, comment, handler, outline == null ? (ResolvableType) null : TypeSugar.resolve(outline, gernics)); + } + + default boolean newObject(@NotNull String key, @NotNull Object value, String comment, String handler) { + return newObject(key, value, comment, handler, (ResolvableType) null); + } + + default boolean newObject(@NotNull Class key, @NotNull Object value, String comment, String handler) { + return newObject(key.getName(), value, comment, handler, (ResolvableType) null); + } + + default boolean newObject(@NotNull Enum key, @NotNull Object value, String comment, String handler) { + return newObject(EnumConvertor.enum2Str(key), value, comment, handler, (ResolvableType) null); + } + + default boolean newObject(@NotNull String key, @NotNull Object value, String comment) { + return newObject(key, value, comment, null, (ResolvableType) null); + } + + default boolean newObject(@NotNull Class key, @NotNull Object value, String comment) { + return newObject(key.getName(), value, comment, null, (ResolvableType) null); + } + + default boolean newObject(@NotNull Enum key, @NotNull Object value, String comment) { + return newObject(EnumConvertor.enum2Str(key), value, comment, null, (ResolvableType) null); } /** - * create new config with auto selected handler, success or throw an error. - * - * @param key config key - * @param value config value - * @param comment config comment + * enable/disable the config, success or throw an error. */ - boolean newObject(String key, Object value, String comment); + boolean enable(@NotNull String key, boolean enable); - default boolean newObject(Class key, Object value, String comment) { - return newObject(key.getName(), value, comment); + default boolean enable(@NotNull Class key, boolean enable) { + return enable(key.getName(), enable); } - default boolean newObject(Enum key, Object value, String comment) { - return newObject(EnumConvertor.enum2Str(key), value, comment); + default boolean enable(@NotNull Enum key, boolean enable) { + return enable(EnumConvertor.enum2Str(key), enable); } - } diff --git a/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/conf/impl/RuntimeConfServiceImpl.java b/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/conf/impl/RuntimeConfServiceImpl.java index 4227f7803..a41e231a4 100644 --- a/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/conf/impl/RuntimeConfServiceImpl.java +++ b/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/conf/impl/RuntimeConfServiceImpl.java @@ -3,12 +3,14 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import org.jooq.Record2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.event.EventListener; +import org.springframework.core.ResolvableType; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import pro.fessional.mirana.best.AssertArgs; @@ -16,7 +18,7 @@ import pro.fessional.wings.faceless.database.WingsTableCudHandler; import pro.fessional.wings.faceless.database.WingsTableCudHandler.Cud; import pro.fessional.wings.silencer.enhance.ThisLazy; -import pro.fessional.wings.silencer.enhance.TypeSugar; +import pro.fessional.wings.silencer.support.TypeSugar; import pro.fessional.wings.warlock.caching.CacheEventHelper; import pro.fessional.wings.warlock.database.autogen.tables.WinConfRuntimeTable; import pro.fessional.wings.warlock.database.autogen.tables.daos.WinConfRuntimeDao; @@ -24,8 +26,6 @@ import pro.fessional.wings.warlock.event.cache.TableChangeEvent; import pro.fessional.wings.warlock.service.conf.RuntimeConfService; -import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -56,12 +56,12 @@ public class RuntimeConfServiceImpl extends ThisLazy imp private final Map handlerMap = new LinkedHashMap<>(); - public void putHandler(String type, ConversionService handler) { + public void putHandler(@NotNull String type, @NotNull ConversionService handler) { handlerMap.put(type, handler); } @Override - public T getObject(String key, TypeDescriptor type) { + public T getObject(@NotNull String key, @NotNull TypeDescriptor type) { // dot not @Cacheable it but thisLazy, because, // (1) calling method inside make cache invalid // (2) recursive calling @@ -70,13 +70,14 @@ public T getObject(String key, TypeDescriptor type) { } @Override - public void setObject(String key, Object value) { + public boolean setObject(@NotNull String key, @NotNull Object value) { final WinConfRuntimeTable t = winConfRuntimeDao.getTable(); final String handler = winConfRuntimeDao.fetchOne(String.class, t, t.Key.eq(key), t.Handler); ConversionService service = handlerMap.get(handler); final String str = service.convert(value, String.class); AssertArgs.notNull(str, "can not covert value to string, key={}", key); + final int rc = winConfRuntimeDao .ctx() .update(t) @@ -86,42 +87,63 @@ public void setObject(String key, Object value) { .execute(); if (rc > 0) { - wingsTableCudHandler.handle(this.getClass(), Cud.Update, t, () -> { - Map> field = new HashMap<>(); + wingsTableCudHandler.handle(this.getClass(), Cud.Update, t, field -> { field.put(t.Key.getName(), List.of(key)); field.put(t.Current.getName(), List.of(str)); - return field; }); } + + return rc >= 1; } @Override - public boolean newObject(String key, Object value, String comment, String handler) { - if (key == null || key.isEmpty() || value == null || handler == null) return false; - ConversionService service = handlerMap.get(handler); - if (service == null || !service.canConvert(value.getClass(), String.class)) return false; + public boolean newObject(@NotNull String key, @NotNull Object value, String comment, String handler, ResolvableType structs) { + AssertArgs.notEmpty(key, "empty key"); + final Class valClaz = value.getClass(); + + ConversionService service = null; + if (handler == null) { // auto select + for (var en : handlerMap.entrySet()) { + if (en.getValue().canConvert(valClaz, String.class)) { + service = en.getValue(); + handler = en.getKey(); + break; + } + } + } + else { // specified + ConversionService cs = handlerMap.get(handler); + if (cs.canConvert(valClaz, String.class)) { + service = cs; + } + } + + if (service == null) return false; final String str = service.convert(value, String.class); AssertArgs.notNull(str, "can not covert value to string, key={}", key); + + if (structs == null) structs = ResolvableType.forClass(valClaz); + final WinConfRuntime pojo = new WinConfRuntime(); pojo.setKey(key); + pojo.setEnabled(true); pojo.setCurrent(str); pojo.setPrevious(Null.Str); pojo.setInitial(str); + pojo.setOutline(TypeSugar.outline(structs)); pojo.setComment(StringUtils.trimToEmpty(comment)); pojo.setHandler(handler); final int rc = winConfRuntimeDao.insertInto(pojo, false); - log.debug("rc={}, key={}, han={}, val={}", rc, key, handler, str); + log.info("newObject rc={}, key={}, han={}, val={}", rc, key, handler, str); if (rc > 0) { Cud type = rc == 1 ? Cud.Create : Cud.Update; final WinConfRuntimeTable t = winConfRuntimeDao.getTable(); - wingsTableCudHandler.handle(this.getClass(), type, t, () -> { - Map> field = new HashMap<>(); + wingsTableCudHandler.handle(this.getClass(), type, t, field -> { field.put(t.Key.getName(), List.of(key)); field.put(t.Current.getName(), List.of(str)); - return field; }); } @@ -129,18 +151,30 @@ public boolean newObject(String key, Object value, String comment, String handle } @Override - public boolean newObject(String key, Object value, String comment) { - for (String handler : new ArrayList<>(handlerMap.keySet())) { - if (newObject(key, value, comment, handler)) { - return true; - } + public boolean enable(@NotNull String key, boolean enable) { + WinConfRuntimeTable t = winConfRuntimeDao.getTable(); + final int rc = winConfRuntimeDao + .ctx() + .update(t) + .set(t.Enabled, enable) + .where(t.Key.eq(key)) + .execute(); + + log.info("enable rc={}, key={}, enable={}", rc, key, enable); + + if (rc > 0) { + wingsTableCudHandler.handle(this.getClass(), Cud.Update, t, field -> { + field.put(t.Key.getName(), List.of(key)); + field.put(t.Enabled.getName(), List.of(enable)); + }); } - return false; + + return rc >= 1; } @Cacheable @SuppressWarnings("unchecked") - public T getObjectCache(String key, TypeDescriptor type) { + public T getObjectCache(@NotNull String key, @NotNull TypeDescriptor type) { if (winConfRuntimeDao.notTableExist()) { log.warn("winConfRuntimeDao.notTableExist, key={}", key); return null; @@ -152,6 +186,7 @@ public T getObjectCache(String key, TypeDescriptor type) { .select(t.Current, t.Handler) .from(t) .where(t.Key.eq(key)) + .and(t.Enabled.eq(true)) .fetchOne(); if (r2 != null) { diff --git a/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/other/TerminalJournalService.java b/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/other/TerminalJournalService.java index cafd4f0ed..2d4c72528 100644 --- a/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/other/TerminalJournalService.java +++ b/wings/warlock/src/main/java/pro/fessional/wings/warlock/service/other/TerminalJournalService.java @@ -22,8 +22,19 @@ public TerminalJournalService(LightIdService ids, BlockIdProvider bid, CommitJou } @Override - public @NotNull R submit(@NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo, @NotNull Function commitSet) { - if (loginInfo == null || loginInfo.isEmpty()) { + @NotNull + public Journal create(long parentId, @NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo) { + return super.create(parentId, eventName, terminal(loginInfo), targetKey, otherInfo); + } + + @Override + @NotNull + public R submit(@NotNull String eventName, @Nullable String loginInfo, @Nullable String targetKey, @Nullable String otherInfo, @NotNull Function commitSet) { + return super.submit(eventName, terminal(loginInfo), targetKey, otherInfo, commitSet); + } + + private String terminal(String loginInfo) { + if (loginInfo == null || loginInfo.isBlank()) { final TerminalContext.Context ctx = TerminalContext.get(false); if (!ctx.isNull()) { loginInfo = JsonTemplate.obj(obj -> { @@ -35,6 +46,7 @@ public TerminalJournalService(LightIdService ids, BlockIdProvider bid, CommitJou }); } } - return super.submit(eventName, loginInfo, targetKey, otherInfo, commitSet); + + return loginInfo; } } diff --git a/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockAutoRunConfiguration.java b/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockAutoRunConfiguration.java index e1f8733fa..cb7ed45cc 100644 --- a/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockAutoRunConfiguration.java +++ b/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockAutoRunConfiguration.java @@ -10,8 +10,8 @@ import pro.fessional.wings.faceless.enums.StandardLanguageEnum; import pro.fessional.wings.faceless.enums.StandardTimezoneEnum; import pro.fessional.wings.faceless.enums.TimezoneEnumUtil; +import pro.fessional.wings.silencer.runner.ApplicationInspectRunner; import pro.fessional.wings.silencer.runner.ApplicationStartedEventRunner; -import pro.fessional.wings.silencer.runner.CommandLineRunnerOrdered; import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.warlock.spring.prop.WarlockCheckProp; @@ -34,9 +34,9 @@ public class WarlockAutoRunConfiguration { */ @Bean @ConditionalWingsEnabled - public CommandLineRunnerOrdered databaseCheckerRunner(DataSource dataSource, WarlockCheckProp prop) { + public ApplicationInspectRunner databaseCheckerRunner(DataSource dataSource, WarlockCheckProp prop) { log.info("Warlock spring-runs databaseCheckerRunner"); - return new CommandLineRunnerOrdered(WingsOrdered.Lv2Resource, ignored -> { + return new ApplicationInspectRunner(WingsOrdered.Lv2Resource, ignored -> { DatabaseChecker.version(dataSource); DatabaseChecker.timezone(dataSource, prop.getTzOffset(), prop.isTzFail()); }); diff --git a/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockJournalConfiguration.java b/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockJournalConfiguration.java index 0b79247c7..53ff5c04f 100644 --- a/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockJournalConfiguration.java +++ b/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockJournalConfiguration.java @@ -7,6 +7,7 @@ import pro.fessional.wings.faceless.database.manual.single.modify.commitjournal.CommitJournalModify; import pro.fessional.wings.faceless.service.lightid.BlockIdProvider; import pro.fessional.wings.faceless.service.lightid.LightIdService; +import pro.fessional.wings.faceless.spring.prop.JournalProp; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.warlock.service.other.TerminalJournalService; @@ -26,11 +27,15 @@ public class WarlockJournalConfiguration { @Bean @ConditionalWingsEnabled public TerminalJournalService terminalJournalService( + JournalProp journalProp, @SuppressWarnings("all") LightIdService lightIdService, @SuppressWarnings("all") BlockIdProvider blockIdProvider, @SuppressWarnings("all") CommitJournalModify journalModify ) { - log.info("WarlockShadow spring-bean terminalJournalService Overriding"); - return new TerminalJournalService(lightIdService, blockIdProvider, journalModify); + TerminalJournalService bean = new TerminalJournalService(lightIdService, blockIdProvider, journalModify); + bean.setPropagation(journalProp.getPropagation()); + bean.setAliveSecond(journalProp.getAlive()); + log.info("WarlockShadow spring-bean terminalJournalService Overriding, propagation=" + journalProp.getPropagation() + ", alive=" + journalProp.getAlive()); + return bean; } } diff --git a/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockTableChangeConfiguration.java b/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockTableChangeConfiguration.java index 747d9390a..30f535b23 100644 --- a/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockTableChangeConfiguration.java +++ b/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/bean/WarlockTableChangeConfiguration.java @@ -2,7 +2,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import pro.fessional.wings.faceless.spring.bean.FacelessJooqCudConfiguration; @@ -25,17 +24,8 @@ public class WarlockTableChangeConfiguration { @Bean @ConditionalWingsEnabled public TableChangePublisherImpl tableChangePublisher() { - log.info("Warlock spring-bean tableChangePublisher"); - final ApplicationEventPublisher publisher; - if (EventPublishHelper.hasAsyncGlobal()) { - publisher = EventPublishHelper.AsyncGlobal; - log.info("Warlock conf tableChangePublisher with async global"); - } - else { - publisher = EventPublishHelper.AsyncSpring; - log.info("Warlock conf tableChangePublisher with async spring"); - } - return new TableChangePublisherImpl(publisher); + log.info("Warlock spring-bean tableChangePublisher with AsyncWidely Publisher"); + return new TableChangePublisherImpl(EventPublishHelper.AsyncWidely); } @Bean diff --git a/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/conf/WarlockAwesomeAutoConfiguration.java b/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/conf/WarlockAwesomeAutoConfiguration.java deleted file mode 100644 index c6bde33d5..000000000 --- a/wings/warlock/src/main/java/pro/fessional/wings/warlock/spring/conf/WarlockAwesomeAutoConfiguration.java +++ /dev/null @@ -1,17 +0,0 @@ -package pro.fessional.wings.warlock.spring.conf; - -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Import; -import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; -import pro.fessional.wings.warlock.spring.bean.WarlockAwesomeConfiguration; - - -/** - * @author trydofor - * @since 2019-12-01 - */ -@AutoConfiguration -@ConditionalWingsEnabled -@Import(WarlockAwesomeConfiguration.class) -public class WarlockAwesomeAutoConfiguration { -} diff --git a/wings/warlock/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/wings/warlock/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 868c98f68..2915ac9b5 100644 --- a/wings/warlock/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/wings/warlock/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,10 +1,20 @@ { "groups": [ - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean"} + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf"} ], "properties": [ - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf.WarlockAutoConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.SysConstantEnumDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.SysStandardI18nDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.WinConfRuntimeDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.WinPermEntryDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.WinRoleEntryDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.WinRoleGrantDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.WinUserAuthnDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.WinUserBasisDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.WinUserGrantDao", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.database.autogen.tables.daos.WinUserLoginDao", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockAutoRunConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockAutoRunConfiguration.databaseCheckerRunner", "type": "java.lang.Boolean"}, @@ -15,6 +25,9 @@ {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockAwesomeConfiguration.registerRuntimeModeRunner", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockAwesomeConfiguration.runtimeConfService", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockJournalConfiguration", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockJournalConfiguration.terminalJournalService", "type": "java.lang.Boolean"}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockLockBeanConfiguration", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockLockBeanConfiguration.databaseGlobalLock", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockLockBeanConfiguration.jvmStaticGlobalLock", "type": "java.lang.Boolean"}, @@ -23,9 +36,11 @@ {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockTableChangeConfiguration.tableChangePublisher", "type": "java.lang.Boolean"}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockTableChangeConfiguration.wingsTableCudHandler", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatchingConfiguration", "type": "java.lang.Boolean", "description": "wings.enabled.warlock.watching for short."}, + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatchingConfiguration", "defaultValue": false, "type": "java.lang.Boolean", "description": "wings.enabled.warlock.watching for short."}, {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatchingConfiguration.slowSqlJooqListener", "type": "java.lang.Boolean"}, - {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatchingConfiguration.watchingAround", "type": "java.lang.Boolean"} + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.bean.WarlockWatchingConfiguration.watchingAround", "type": "java.lang.Boolean"}, + + {"name": "wings.enabled.pro.fessional.wings.warlock.spring.conf.WarlockAutoConfiguration", "type": "java.lang.Boolean"} ], "hints": [] } \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/branch/somefix/01-authn-fix/2021-09-18u01-rename-authn.sql b/wings/warlock/src/main/resources/wings-flywave/branch/somefix/01-authn-fix/2021-09-18u01-rename-authn.sql index 3a380db68..d480d91db 100644 --- a/wings/warlock/src/main/resources/wings-flywave/branch/somefix/01-authn-fix/2021-09-18u01-rename-authn.sql +++ b/wings/warlock/src/main/resources/wings-flywave/branch/somefix/01-authn-fix/2021-09-18u01-rename-authn.sql @@ -2,3 +2,5 @@ ALTER TABLE `win_user_authn` RENAME TO `win_user_anthn`; update `sys_light_sequence` set `seq_name`='win_user_anthn' where `seq_name`='win_user_authn'; + +-- CALL FLYWAVE('2021-09-18u01-rename-authn.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/branch/somefix/01-authn-fix/2021-09-18v01-rename-authn.sql b/wings/warlock/src/main/resources/wings-flywave/branch/somefix/01-authn-fix/2021-09-18v01-rename-authn.sql index c64826c59..8351b4ba7 100644 --- a/wings/warlock/src/main/resources/wings-flywave/branch/somefix/01-authn-fix/2021-09-18v01-rename-authn.sql +++ b/wings/warlock/src/main/resources/wings-flywave/branch/somefix/01-authn-fix/2021-09-18v01-rename-authn.sql @@ -2,3 +2,5 @@ ALTER TABLE `win_user_anthn` RENAME TO `win_user_authn`; update `sys_light_sequence` set `seq_name`='win_user_authn' where `seq_name`='win_user_anthn'; + +-- CALL FLYWAVE('2021-09-18v01-rename-authn.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/branch/somefix/04-conf-size/2021-10-26u03-conf-size.sql b/wings/warlock/src/main/resources/wings-flywave/branch/somefix/04-conf-size/2021-10-26u03-conf-size.sql new file mode 100644 index 000000000..1ea4435c4 --- /dev/null +++ b/wings/warlock/src/main/resources/wings-flywave/branch/somefix/04-conf-size/2021-10-26u03-conf-size.sql @@ -0,0 +1,10 @@ +ALTER TABLE `win_conf_runtime` + DROP COLUMN `enabled`, + DROP COLUMN `outline`, + CHANGE COLUMN `current` `current` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'current value', + CHANGE COLUMN `previous` `previous` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'previous value', + CHANGE COLUMN `initial` `initial` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'initial value', + CHANGE COLUMN `comment` `comment` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'comment', + CHANGE COLUMN `handler` `handler` VARCHAR(200) NOT NULL DEFAULT 'prop' COMMENT 'data handling:prop|json'; + +-- CALL FLYWAVE('2021-10-26u03-conf-size.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/branch/somefix/04-conf-size/2021-10-26v03-conf-size.sql b/wings/warlock/src/main/resources/wings-flywave/branch/somefix/04-conf-size/2021-10-26v03-conf-size.sql new file mode 100644 index 000000000..19d1403c1 --- /dev/null +++ b/wings/warlock/src/main/resources/wings-flywave/branch/somefix/04-conf-size/2021-10-26v03-conf-size.sql @@ -0,0 +1,19 @@ +ALTER TABLE `win_conf_runtime` + ADD COLUMN `enabled` BOOLEAN NOT NULL DEFAULT '1' COMMENT 'enabled' AFTER `key`, + ADD COLUMN `outline` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'value ResolvableType' AFTER `initial`, + CHANGE COLUMN `current` `current` TEXT NOT NULL COMMENT 'current value', + CHANGE COLUMN `previous` `previous` TEXT NOT NULL COMMENT 'previous value', + CHANGE COLUMN `initial` `initial` TEXT NOT NULL COMMENT 'initial value', + CHANGE COLUMN `comment` `comment` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'usage or purpose', + CHANGE COLUMN `handler` `handler` VARCHAR(200) NOT NULL DEFAULT 'prop' COMMENT 'data handling:prop|json|kryo'; + +UPDATE `win_conf_runtime` +SET outline = `key` +WHERE `key` = 'pro.fessional.wings.warlock.service.conf.mode.RunMode'; + +UPDATE `win_conf_runtime` +SET outline = `key`, + `initial` = 'Nothing' +WHERE `key` = 'pro.fessional.wings.warlock.service.conf.mode.ApiMode'; + +-- CALL FLYWAVE('2021-10-26v03-conf-size.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/master/03-enum/2020-10-23v01-auth_enum.sql b/wings/warlock/src/main/resources/wings-flywave/master/03-enum/2020-10-23v01-auth_enum.sql index 09e7e3a88..83b38ef8a 100644 --- a/wings/warlock/src/main/resources/wings-flywave/master/03-enum/2020-10-23v01-auth_enum.sql +++ b/wings/warlock/src/main/resources/wings-flywave/master/03-enum/2020-10-23v01-auth_enum.sql @@ -13,3 +13,5 @@ VALUES (1200200, 'user_status', 'user_status', 'user status', 'classpath:/wings- (1330100, 'grant_type', 'grant_type', 'grant type', 'classpath:/wings-tmpl/ConstantEnumTemplate.java'), (1330101, 'grant_type', 'perm', 'permit', 'permit'), (1330102, 'grant_type', 'role', 'role', 'role'); + +-- CALL FLYWAVE('2020-10-23v01-auth_enum.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24u01-user_login.sql b/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24u01-user_login.sql index a94b62324..939fec33b 100644 --- a/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24u01-user_login.sql +++ b/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24u01-user_login.sql @@ -1,3 +1,5 @@ DROP TABLE IF EXISTS `win_user_basis`; -- 120/User Basis; DROP TABLE IF EXISTS `win_user_authn`; -- 121/User Authn; DROP TABLE IF EXISTS `win_user_login`; -- 122/User Login; + +-- CALL FLYWAVE('2020-10-24u01-user_login.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24u02-role_permit.sql b/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24u02-role_permit.sql index bbe3a3321..6b794cede 100644 --- a/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24u02-role_permit.sql +++ b/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24u02-role_permit.sql @@ -2,3 +2,5 @@ DROP TABLE IF EXISTS `win_perm_entry`; -- 130/Perm Entry; DROP TABLE IF EXISTS `win_role_entry`; -- 131/Role Entry; DROP TABLE IF EXISTS `win_role_grant`; -- 134/Role Grant; DROP TABLE IF EXISTS `win_user_grant`; -- 135/User Grant; + +-- CALL FLYWAVE('2020-10-24u02-role_permit.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24v01-user_login.sql b/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24v01-user_login.sql index eb4c4ac9c..ba6cae86b 100644 --- a/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24v01-user_login.sql +++ b/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24v01-user_login.sql @@ -1,36 +1,36 @@ CREATE TABLE `win_user_basis` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key/user_id/uid', + `id` BIGINT NOT NULL COMMENT 'primary key/user_id/uid', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime(sys)', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', `nickname` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'nickname', `passsalt` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'password salt/random, read-only, no external use', - `gender` INT(11) NOT NULL DEFAULT '0' COMMENT 'gender/12001##:unknown|mail|female', + `gender` INT NOT NULL DEFAULT '0' COMMENT 'gender/12001##:unknown|mail|female', `avatar` VARCHAR(1000) NOT NULL DEFAULT '' COMMENT 'avatar url', `locale` CHAR(5) NOT NULL DEFAULT 'zh_CN' COMMENT 'language/Locale:StandardLanguageEnum', - `zoneid` INT(11) NOT NULL DEFAULT '1010201' COMMENT 'timezone/ZoneId:StandardTimezoneEnum', + `zoneid` INT NOT NULL DEFAULT '1010201' COMMENT 'timezone/ZoneId:StandardTimezoneEnum', `remark` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'comment', - `status` INT(11) NOT NULL DEFAULT '0' COMMENT 'user status/12002##:', + `status` INT NOT NULL DEFAULT '0' COMMENT 'user status/12002##:', PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='120/User Basis'; CREATE TABLE `win_user_authn` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', + `id` BIGINT NOT NULL COMMENT 'primary key', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime(sys)', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', - `user_id` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'basic user/win_user_basis.id', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', + `user_id` BIGINT NOT NULL DEFAULT '0' COMMENT 'basic user/win_user_basis.id', `auth_type` VARCHAR(10) NOT NULL COMMENT 'auth type/wings.warlock.security.auth-type.*', `username` VARCHAR(200) NOT NULL COMMENT 'account/id:email|mobile|union_id|api_key', `password` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'password/spring style|api_secret', `extra_para` VARCHAR(3000) NOT NULL DEFAULT '' COMMENT 'para for 3rd auth', `extra_user` VARCHAR(9000) NOT NULL DEFAULT '' COMMENT 'user info of 3rd', `expired_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'expiration, not for token, empty is disabled', - `failed_cnt` INT(11) NOT NULL DEFAULT '0' COMMENT 'continuous error count: clear on success', - `failed_max` INT(11) NOT NULL DEFAULT '5' COMMENT 'max continuous error', + `failed_cnt` INT NOT NULL DEFAULT '0' COMMENT 'continuous error count: clear on success', + `failed_max` INT NOT NULL DEFAULT '5' COMMENT 'max continuous error', PRIMARY KEY (`id`), UNIQUE INDEX uq_uid_type (`user_id`, `auth_type`), UNIQUE INDEX uq_type_name (`auth_type`, `username`) @@ -38,14 +38,14 @@ CREATE TABLE `win_user_authn` ( DEFAULT CHARSET = utf8mb4 COMMENT ='121/User Authn'; CREATE TABLE `win_user_login` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', - `user_id` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'basic user/win_user_basis.id', + `id` BIGINT NOT NULL COMMENT 'primary key', + `user_id` BIGINT NOT NULL DEFAULT '0' COMMENT 'basic user/win_user_basis.id', `auth_type` VARCHAR(20) NOT NULL COMMENT 'auth type/wings.warlock.security.auth-type.*', `login_ip` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'login IP', `login_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', `terminal` VARCHAR(1000) NOT NULL DEFAULT '' COMMENT 'login terminal', `details` VARCHAR(9000) NOT NULL DEFAULT '' COMMENT 'auth details', - `failed` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'fail or not', + `failed` BOOLEAN NOT NULL DEFAULT '0' COMMENT 'fail or not', PRIMARY KEY (`id`), INDEX ix_user_id (`user_id`) ) ENGINE = InnoDB @@ -72,3 +72,5 @@ VALUES (0, NOW(3), 0, 'nobody', UUID(), 1200103, '', 'zh_CN', 1010201, 'system u INSERT IGNORE INTO `win_user_authn`(`id`, `create_dt`, `commit_id`, `user_id`, `auth_type`, `username`, `password`, `expired_dt`) VALUES (1, NOW(3), 0, 1, 'username', 'root', CONCAT('{never}', UUID()), '2999-09-09'); + +-- CALL FLYWAVE('2020-10-24v01-user_login.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24v02-role_permit.sql b/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24v02-role_permit.sql index 336d7918f..c297ab810 100644 --- a/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24v02-role_permit.sql +++ b/wings/warlock/src/main/resources/wings-flywave/master/04-auth/2020-10-24v02-role_permit.sql @@ -1,9 +1,9 @@ CREATE TABLE `win_perm_entry` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', + `id` BIGINT NOT NULL COMMENT 'primary key', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime(sys)', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', `scopes` VARCHAR(200) NOT NULL COMMENT 'all lowercase, period-separated', `action` VARCHAR(50) NOT NULL COMMENT 'all lowercase', `remark` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'comment', @@ -13,11 +13,11 @@ CREATE TABLE `win_perm_entry` ( DEFAULT CHARSET = utf8mb4 COMMENT ='130/Perm Entry'; CREATE TABLE `win_role_entry` ( - `id` BIGINT(20) NOT NULL COMMENT 'primary key', + `id` BIGINT NOT NULL COMMENT 'primary key', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', `modify_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' ON UPDATE NOW(3) COMMENT 'modified datetime(sys)', `delete_dt` DATETIME(3) NOT NULL DEFAULT '1000-01-01' COMMENT 'logic deleted datetime', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', `name` VARCHAR(50) NOT NULL COMMENT 'all uppercase, no separated, no ROLE_ prefix', `remark` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'comment', PRIMARY KEY (`id`), @@ -26,21 +26,21 @@ CREATE TABLE `win_role_entry` ( DEFAULT CHARSET = utf8mb4 COMMENT ='131/Role Entry'; CREATE TABLE `win_role_grant` ( - `refer_role` BIGINT(20) NOT NULL COMMENT 'current role/win_role_entry.id', - `grant_type` INT(11) NOT NULL COMMENT 'grant type/13301##:Role,Perm', - `grant_entry` BIGINT(20) NOT NULL COMMENT 'entry to grant: id/win_role_entry.id, win_perm_entry.id', + `refer_role` BIGINT NOT NULL COMMENT 'current role/win_role_entry.id', + `grant_type` INT NOT NULL COMMENT 'grant type/13301##:Role,Perm', + `grant_entry` BIGINT NOT NULL COMMENT 'entry to grant: id/win_role_entry.id, win_perm_entry.id', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', PRIMARY KEY (`refer_role`, `grant_type`, `grant_entry`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='134/Role Grant'; CREATE TABLE `win_user_grant` ( - `refer_user` BIGINT(20) NOT NULL COMMENT 'current user/win_user_basis.id', - `grant_type` INT(11) NOT NULL COMMENT 'grant type/13301##:Role,Perm', - `grant_entry` BIGINT(20) NOT NULL COMMENT 'entry to grant: id/win_role_entry.id, win_perm_entry.id', + `refer_user` BIGINT NOT NULL COMMENT 'current user/win_user_basis.id', + `grant_type` INT NOT NULL COMMENT 'grant type/13301##:Role,Perm', + `grant_entry` BIGINT NOT NULL COMMENT 'entry to grant: id/win_role_entry.id, win_perm_entry.id', `create_dt` DATETIME(3) NOT NULL DEFAULT NOW(3) COMMENT 'created datetime(sys)', - `commit_id` BIGINT(20) NOT NULL COMMENT 'commit id', + `commit_id` BIGINT NOT NULL COMMENT 'commit id', PRIMARY KEY (`refer_user`, `grant_type`, `grant_entry`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='135/User Grant'; @@ -88,3 +88,5 @@ VALUES (1, 1330101, 1, NOW(3), 0), -- Grant super perm to root user REPLACE INTO `win_user_grant`(`refer_user`, `grant_type`, `grant_entry`, `create_dt`, `commit_id`) VALUES (1, 1330102, 1, NOW(3), 0); + +-- CALL FLYWAVE('2020-10-24v02-role_permit.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/master/05-conf/2020-10-25u01-conf_runtime.sql b/wings/warlock/src/main/resources/wings-flywave/master/05-conf/2020-10-25u01-conf_runtime.sql index 6ad5d2773..ce23d198d 100644 --- a/wings/warlock/src/main/resources/wings-flywave/master/05-conf/2020-10-25u01-conf_runtime.sql +++ b/wings/warlock/src/main/resources/wings-flywave/master/05-conf/2020-10-25u01-conf_runtime.sql @@ -1,2 +1,3 @@ DROP TABLE IF EXISTS `win_conf_runtime`; -- 110/Runtime Config; +-- CALL FLYWAVE('2020-10-25u01-conf_runtime.sql'); \ No newline at end of file diff --git a/wings/warlock/src/main/resources/wings-flywave/master/05-conf/2020-10-25v01-conf_runtime.sql b/wings/warlock/src/main/resources/wings-flywave/master/05-conf/2020-10-25v01-conf_runtime.sql index d83835795..2c61c5193 100644 --- a/wings/warlock/src/main/resources/wings-flywave/master/05-conf/2020-10-25v01-conf_runtime.sql +++ b/wings/warlock/src/main/resources/wings-flywave/master/05-conf/2020-10-25v01-conf_runtime.sql @@ -1,15 +1,19 @@ CREATE TABLE `win_conf_runtime` ( `key` VARCHAR(200) NOT NULL COMMENT 'conf key:Enum|Class|String', - `current` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'current value', - `previous` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'previous value', - `initial` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'initial value', - `comment` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'comment', - `handler` VARCHAR(200) NOT NULL DEFAULT 'prop' COMMENT 'data handling:prop|json', + `enabled` BOOLEAN NOT NULL DEFAULT '1' COMMENT 'enabled', + `current` TEXT NOT NULL COMMENT 'current value', + `previous` TEXT NOT NULL COMMENT 'previous value', + `initial` TEXT NOT NULL COMMENT 'initial value', + `outline` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'value ResolvableType', + `comment` VARCHAR(5000) NOT NULL DEFAULT '' COMMENT 'usage or purpose', + `handler` VARCHAR(200) NOT NULL DEFAULT 'prop' COMMENT 'data handling:prop|json|kryo', PRIMARY KEY (`key`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='110/Runtime Config'; -- ----------- -INSERT IGNORE INTO `win_conf_runtime` (`key`, `current`, `previous`, `initial`, `comment`) -VALUES ('pro.fessional.wings.warlock.service.conf.mode.RunMode', 'Local', '', 'Local', 'RunMode') - , ('pro.fessional.wings.warlock.service.conf.mode.ApiMode', 'Nothing', '', 'Local', 'ApiMode'); +INSERT IGNORE INTO `win_conf_runtime` (`key`, `current`, `previous`, `initial`, `outline`, `comment`, `handler`) +VALUES ('pro.fessional.wings.warlock.service.conf.mode.RunMode', 'Local', 'pro.fessional.wings.warlock.service.conf.mode.RunMode', '', 'Local', 'RunMode', 'prop') + , ('pro.fessional.wings.warlock.service.conf.mode.ApiMode', 'Nothing', 'pro.fessional.wings.warlock.service.conf.mode.ApiMode', '', 'Nothing', 'ApiMode', 'prop'); + +-- CALL FLYWAVE('2020-10-25v01-conf_runtime.sql'); \ No newline at end of file diff --git a/wings/warlock/src/test/java/pro/fessional/wings/warlock/service/conf/RuntimeConfServiceTest.java b/wings/warlock/src/test/java/pro/fessional/wings/warlock/service/conf/RuntimeConfServiceTest.java index f7e9139bd..19b3d0016 100644 --- a/wings/warlock/src/test/java/pro/fessional/wings/warlock/service/conf/RuntimeConfServiceTest.java +++ b/wings/warlock/src/test/java/pro/fessional/wings/warlock/service/conf/RuntimeConfServiceTest.java @@ -9,14 +9,18 @@ import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitialization; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cache.CacheManager; +import org.springframework.context.ApplicationContext; import pro.fessional.mirana.time.Sleep; +import pro.fessional.wings.silencer.support.TypeSugar; import pro.fessional.wings.silencer.modulate.RunMode; +import pro.fessional.wings.slardar.cache.SimpleCacheTemplate; import pro.fessional.wings.slardar.cache.WingsCacheHelper; import pro.fessional.wings.testing.silencer.TestingLoggerAssert; import pro.fessional.wings.warlock.caching.CacheConst; import pro.fessional.wings.warlock.service.conf.impl.RuntimeConfServiceImpl; import java.math.BigDecimal; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -25,6 +29,7 @@ import java.util.Map; import java.util.Set; + /** * Need init database via BootDatabaseTest * Need sleep for cache evict @@ -33,14 +38,23 @@ * @since 2022-03-09 */ @SpringBootTest(properties = { - "logging.level.root=DEBUG", // AssertionLogger - "wings.faceless.jooq.cud.table[win_conf_runtime]=key,current,handler", + "logging.level.root=DEBUG", // AssertionLogger + "wings.faceless.jooq.cud.table[win_conf_runtime]=key,current,handler", }) @DependsOnDatabaseInitialization class RuntimeConfServiceTest { - @Setter(onMethod_ = {@Autowired}) - private RuntimeConfService runtimeConfService; + private final SimpleCacheTemplate cacheTemplate = new SimpleCacheTemplate<>( + CacheConst.RuntimeConfService.CacheManager, + CacheConst.RuntimeConfService.CacheName); + + @Setter(onMethod_ = { @Autowired }) + protected RuntimeConfService runtimeConfService; + + @Autowired + public void setApplicationContext(ApplicationContext applicationContext) { + this.cacheTemplate.setBeanFactory(applicationContext); + } @Test @TmsLink("C14008") @@ -53,6 +67,8 @@ void testSimple() { assertSimple(ZonedDateTime.class, ZonedDateTime.of(ldt, ZoneId.of("Asia/Shanghai"))); assertSimple(Long.class, 1023L); assertSimple(Integer.class, 10); + + assertEnable(Integer.class); // final Map> mgr = WingsCacheHelper.getManager(RuntimeConfServiceImpl.class); Assertions.assertEquals(1, mgr.size()); @@ -67,25 +83,45 @@ void testSimple() { } private void assertSimple(Class clz, T obj) { - runtimeConfService.newObject(clz, obj, "test " + clz.getSimpleName()); + runtimeConfService.newObject(clz, obj, "test " + clz.getSimpleName(), null); Sleep.ignoreInterrupt(1000); // wait for event sync final T obj1 = runtimeConfService.getSimple(clz, clz); Assertions.assertEquals(obj, obj1); + + final Object obj2 = cacheTemplate.getArgKey(clz.getName(), TypeSugar.describe(clz)); + Assertions.assertEquals(obj, obj2); + } + + private void assertEnable(Class clz) { + runtimeConfService.enable(clz, false); + Sleep.ignoreInterrupt(1000); // wait for event sync + final T obj1 = runtimeConfService.getSimple(clz, clz); + Assertions.assertNull(obj1); + runtimeConfService.enable(clz, true); + Sleep.ignoreInterrupt(1000); // wait for event sync + final T obj2 = runtimeConfService.getSimple(clz, clz); + Assertions.assertNotNull(obj2); } @Test @TmsLink("C14009") void testCollection() { List ls = List.of("Jan", "Fer"); - runtimeConfService.newObject(List.class, ls, "test list"); + runtimeConfService.newObject(List.class, ls, "test list", null, List.class, String.class); Sleep.ignoreInterrupt(1000); // wait for event sync final List ls1 = runtimeConfService.getList(List.class, String.class); Assertions.assertEquals(ls, ls1); + Set ss = Set.of(LocalDate.now(), LocalDate.now().plusDays(1)); + runtimeConfService.newObject(Set.class, ss, "test set LocalDate", null, Set.class, LocalDate.class); + Sleep.ignoreInterrupt(1000); // wait for event sync + final Set ss1 = runtimeConfService.getSet(Set.class, LocalDate.class); + Assertions.assertEquals(ss, ss1); + Map map = new HashMap<>(); map.put("Jan", true); map.put("Fer", false); - runtimeConfService.newObject(Map.class, map, "test map"); + runtimeConfService.newObject(Map.class, map, "test map", null, Map.class, String.class, Boolean.class); Sleep.ignoreInterrupt(1000); // wait for event sync final Map map1 = runtimeConfService.getMap(Map.class, String.class, Boolean.class); Assertions.assertEquals(map, map1); @@ -102,7 +138,7 @@ public static class Dto { @TmsLink("C14010") void testJson() { Dto dto = new Dto(); - runtimeConfService.newObject(Dto.class, dto, "Need init database via BootDatabaseTest"); + runtimeConfService.newObject(Dto.class, dto, "Need init database via BootDatabaseTest", null); Sleep.ignoreInterrupt(1000); // wait for event sync final Dto dto1 = runtimeConfService.getSimple(Dto.class, Dto.class); Assertions.assertEquals(dto, dto1); @@ -124,7 +160,7 @@ void testKryo() { void testMode() { final List arm = List.of(RunMode.Develop, RunMode.Local); final String key = "RuntimeConfServiceTest.testMode"; - runtimeConfService.newObject(key, arm, "test RunMode"); + runtimeConfService.newObject(key, arm, "test RunMode", null, List.class, RunMode.class); Sleep.ignoreInterrupt(1000); // wait for event sync final List arm1 = runtimeConfService.getList(key, RunMode.class); Assertions.assertEquals(arm, arm1); @@ -147,7 +183,7 @@ void testCacheWithCud() { final List arm = List.of(RunMode.Develop, RunMode.Local); final String key = "RuntimeConfCacheTest.testCache"; // insert on duplicated key - runtimeConfService.newObject(key, arm, "test RunMode"); + runtimeConfService.newObject(key, arm, "test RunMode", null, List.class, RunMode.class); Sleep.ignoreInterrupt(1000); // wait for event sync final List arm1 = runtimeConfService.getList(key, RunMode.class); final List arm2 = runtimeConfService.getList(key, RunMode.class);