diff --git a/WingsBoot.t.md b/WingsBoot.t.md index 648b11e65..375279426 100644 --- a/WingsBoot.t.md +++ b/WingsBoot.t.md @@ -34,6 +34,8 @@ Use `t.md` as local [Test Management](https://www.jetbrains.com/help/idea/test-m * 11028 WingsEnabledFalseTest: disable config and bean * 11029 WingsEnabledTopFalseTest: disable top config * 11030 TtlMDCAdapterTest: ttl MDC in multiple thread +* 11031 WingsReorderDefaultTest: default Order/Ordered +* 11032 WingsReorderEnableTest: configed Order/Ordered ## 12 Faceless @@ -309,6 +311,8 @@ Use `t.md` as local [Test Management](https://www.jetbrains.com/help/idea/test-m * 13118 EventPublishHelperTest: async global AttributeRidEvent * 13119 WingsCacheInterceptorTest: evict multiple cache keys * 13120 AsyncControllerTest: Future request mapping with async service +* 13121 BootAdminServerTest: servlet mapping order +* 13122 AsyncHelperTest: async ttl decorator ## 14 Warlock diff --git a/observe/docs b/observe/docs index 616a405fa..c11179d94 160000 --- a/observe/docs +++ b/observe/docs @@ -1 +1 @@ -Subproject commit 616a405fa7f9f3d7e069b39d7ebd7bb447e5e35e +Subproject commit c11179d9405d56bdce1d844167180312da5251a8 diff --git a/pom.xml b/pom.xml index c677adb47..3df4fa200 100644 --- a/pom.xml +++ b/pom.xml @@ -20,8 +20,8 @@ https://wings.fessional.pro - - 3.2.111-SNAPSHOT + + 3.2.120-SNAPSHOT 3.2.2 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 0cbc7ecae..1b5dbb92f 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 @@ -7,7 +7,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.Trigger; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.support.CronTrigger; import org.springframework.scheduling.support.PeriodicTrigger; import org.springframework.scheduling.support.SimpleTriggerContext; @@ -107,7 +106,8 @@ public boolean force(long id) { } final boolean fast = BoxedCastUtil.orTrue(td.getTaskerFast()); - TaskSchedulerHelper.referScheduler(fast).schedule(() -> { + final var scheduler = fast ? TaskSchedulerHelper.Fast() : TaskSchedulerHelper.Scheduled(); + scheduler.schedule(() -> { long execTms = ThreadNow.millis(); long doneTms = -1; long failTms = -1; @@ -213,7 +213,7 @@ private boolean relaunch(long id) { saveNextExec(next, td); final boolean fast = BoxedCastUtil.orTrue(td.getTaskerFast()); - final ThreadPoolTaskScheduler taskScheduler = TaskSchedulerHelper.referScheduler(fast); + final var taskScheduler = fast ? TaskSchedulerHelper.Fast() : TaskSchedulerHelper.Scheduled(); if (taskScheduler.getScheduledExecutor().isShutdown()) { log.error("TaskScheduler={} is shutdown, name={} id={}", fast, td.getTaskerName(), td.getId()); 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 9428511da..45cca1194 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 TaskSchedulerHelper.referScheduler(fast); + return fast ? TaskSchedulerHelper.Fast() : TaskSchedulerHelper.Scheduled(); } @Override 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 84b25cedb..9110285cd 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 @@ -38,7 +38,7 @@ void cancelSchedule() { al.rule(".. cancel", event -> event.getFormattedMessage().contains(".. cancel=true")); al.start(); - final ThreadPoolTaskScheduler scheduler = TaskSchedulerHelper.referScheduler(false); + final ThreadPoolTaskScheduler scheduler = TaskSchedulerHelper.Scheduled(); final ScheduledFuture f1 = scheduler.schedule(() -> log.info("-1 run={}", System.currentTimeMillis()), Instant.ofEpochMilli(System.currentTimeMillis() - 1000)); Sleep.ignoreInterrupt(500); diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazy.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazy.java new file mode 100644 index 000000000..82f725143 --- /dev/null +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazy.java @@ -0,0 +1,21 @@ +package pro.fessional.wings.silencer.enhance; + +/** + *
+ * @Setter(onMethod_ = {@Autowired, @Lazy})
+ * protected RuntimeConfServiceImpl thisLazy = this;
+ * 
+ * + * @author trydofor + * @since 2024-05-10 + */ +@SuppressWarnings("unchecked") +public class ThisLazy implements ThisLazyAware { + + protected T thisLazy = (T) this; + + @Override + public void setThisLazy(T self) { + this.thisLazy = self; + } +} diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyAware.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyAware.java new file mode 100644 index 000000000..9a2c461ff --- /dev/null +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/ThisLazyAware.java @@ -0,0 +1,20 @@ +package pro.fessional.wings.silencer.enhance; + +/** + *
+ * @Setter(onMethod_ = {@Autowired, @Lazy})
+ * protected RuntimeConfServiceImpl thisLazy = this;
+ * 
+ * + * @author trydofor + * @since 2024-05-10 + */ +public interface ThisLazyAware { + + /** + * inject enhanced this before Bean Initialization + * + * @param thisLazy enhanced bean + */ + void setThisLazy(T thisLazy); +} 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 9d59e3d7d..5f1ef98e3 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,15 +2,21 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.BeansException; +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.ThisLazyAware; import pro.fessional.wings.silencer.message.MessageSourceHelper; import pro.fessional.wings.silencer.runner.ApplicationInspectRunner; import pro.fessional.wings.silencer.runner.ApplicationRunnerOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.silencer.spring.boot.WingsReorderProcessor; +import pro.fessional.wings.silencer.spring.prop.SilencerEnabledProp; import java.util.Map; @@ -24,11 +30,20 @@ public class SilencerConfiguration { private static final Log log = LogFactory.getLog(SilencerConfiguration.class); + /** + * reorder beans by config + */ + @Bean + @ConditionalWingsEnabled(abs = SilencerEnabledProp.Key$beanReorder) + public static WingsReorderProcessor wingsReorderProcessor() { + log.info("Silencer spring-auto wingsReorderProcessor"); + return new WingsReorderProcessor(); + } + /** * @link Internationalization * @see org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration */ - @Bean @ConditionalWingsEnabled public MessageSourceHelper messageSourceHelper(MessageSource messageSource) { @@ -42,6 +57,22 @@ public MessageSourceHelper messageSourceHelper(MessageSource messageSource) { return bean; } + @Bean + @ConditionalWingsEnabled + @SuppressWarnings("all") + public static BeanPostProcessor thisLazyAwarePostProcessor() { + log.info("Silencer spring-auto thisLazyAwarePostProcessor"); + return new BeanPostProcessor() { + @Override + public Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException { + if (bean instanceof ThisLazyAware self) { + self.setThisLazy(self); + } + return bean; + } + }; + } + /** * applicationRunner are executed before commandLineRunner in SpringApplication#callRunners * diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/boot/WingsReorderProcessor.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/boot/WingsReorderProcessor.java new file mode 100644 index 000000000..c3e3d3341 --- /dev/null +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/boot/WingsReorderProcessor.java @@ -0,0 +1,86 @@ +package pro.fessional.wings.silencer.spring.boot; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.ResolvableType; +import org.springframework.core.env.Environment; + +import java.util.Collections; +import java.util.Map; + +/** + * (@Order | @Priority) > Ordered.getOrder + * + * @author trydofor + * @see org.springframework.core.annotation.AnnotationAwareOrderComparator + * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#getBeanProvider(ResolvableType, boolean) + * @since 2024-05-11 + */ +public class WingsReorderProcessor implements BeanFactoryPostProcessor, EnvironmentAware { + + private static final Log log = LogFactory.getLog(WingsReorderProcessor.class); + + /** + * reorder bean by beanName(one-one) or beanClass(one-many). + */ + public static final String PrefixReorder = "wings.reorder"; + + /** + * set bean as primary by beanName + */ + public static final String PrefixPrimary = "wings.primary"; + + private Environment environment; + + @Override + public void setEnvironment(@NotNull Environment env) { + this.environment = env; + } + + @Override + public void postProcessBeanFactory(@NotNull ConfigurableListableBeanFactory beanFactory) throws BeansException { + Map reorderProp = Binder.get(environment) + .bind(PrefixReorder, Bindable.mapOf(String.class, Integer.class)) + .orElseGet(Collections::emptyMap); + + Map primaryProp = Binder.get(environment) + .bind(PrefixPrimary, Bindable.mapOf(String.class, Boolean.class)) + .orElseGet(Collections::emptyMap); + + if (reorderProp.isEmpty() && primaryProp.isEmpty()) { + log.info("WingsReorderProcessor skipped, for no properties under " + PrefixReorder + " and " + PrefixPrimary); + return; + } + + for (String bn : beanFactory.getBeanDefinitionNames()) { + BeanDefinition definition = beanFactory.getBeanDefinition(bn); + Integer order = reorderProp.get(bn); + String logExt = ""; + if (order == null) { + String clz = definition.getBeanClassName(); + order = reorderProp.get(clz); + logExt = ", class=" + clz; + } + + if (order != null) { + log.info("WingsReorderProcessor reorder bean=" + bn + ", order=" + order + logExt); + definition.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, order); + } + + Boolean primary = primaryProp.get(bn); + if (primary != null) { + log.info("WingsReorderProcessor reorder bean=" + bn + ", primary=" + primary); + definition.setPrimary(primary); + } + } + } +} diff --git a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/prop/SilencerEnabledProp.java b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/prop/SilencerEnabledProp.java index 21cb18eda..a912cbb5b 100644 --- a/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/prop/SilencerEnabledProp.java +++ b/wings/silencer/src/main/java/pro/fessional/wings/silencer/spring/prop/SilencerEnabledProp.java @@ -82,4 +82,12 @@ public class SilencerEnabledProp { */ private boolean tweakStack = true; public static final String Key$tweakStack = Key + ".tweak-stack"; + + /** + * Whether to enable bean reorder with `wings.reorder.*` + * + * @see #Key$beanReorder + */ + private boolean beanReorder = true; + public static final String Key$beanReorder = Key + ".bean-reorder"; } diff --git a/wings/silencer/src/main/resources/wings-conf/wings-enabled-79.properties b/wings/silencer/src/main/resources/wings-conf/wings-enabled-79.properties index 4aba8860e..6e47098cf 100644 --- a/wings/silencer/src/main/resources/wings-conf/wings-enabled-79.properties +++ b/wings/silencer/src/main/resources/wings-conf/wings-enabled-79.properties @@ -22,4 +22,7 @@ wings.enabled.silencer.scanner=false #wings.enabled.silencer.tweak-logback=true ## Whether to tweak the CodeException stack in global or thread -#wings.enabled.silencer.tweak-stack=true \ No newline at end of file +#wings.enabled.silencer.tweak-stack=true + +## Whether to enable bean reorder with `wings.reorder.*` +#wings.enabled.silencer.bean-reorder=true diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/bean/TestReorderConfiguration.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/bean/TestReorderConfiguration.java new file mode 100644 index 000000000..cf5b4a923 --- /dev/null +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/bean/TestReorderConfiguration.java @@ -0,0 +1,94 @@ +package pro.fessional.wings.silencer.app.bean; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; + +/** + * @author trydofor + * @since 2024-05-11 + */ +@ConditionalWingsEnabled +@Configuration(proxyBeanMethods = false) +public class TestReorderConfiguration { + + @Bean + @ConditionalWingsEnabled + @Order(2) + public PlainClass plainClass1() { + return new PlainClass(1); + } + + @Bean + @ConditionalWingsEnabled + @Order(1) + public PlainClass plainClass2() { + return new PlainClass(2); + } + + @Bean + @ConditionalWingsEnabled + @Order(2) + public GetterClass getterClass1() { + return new GetterClass(1); + } + + @Bean + @ConditionalWingsEnabled + @Order(1) + public GetterClass getterClass2() { + return new GetterClass(2); + } + + + @Bean + @ConditionalWingsEnabled + @Order(2) + public OrderedClass orderedClass1() { + return new OrderedClass(1); + } + + @Bean + @ConditionalWingsEnabled + @Order(1) + public OrderedClass orderedClass2() { + return new OrderedClass(2); + } + + + @RequiredArgsConstructor + public static class PlainClass { + private final int order; + + @Override + public String toString() { + return String.valueOf(order); + } + } + + @RequiredArgsConstructor + @Getter + public static class GetterClass { + private final int order; + + @Override + public String toString() { + return String.valueOf(order); + } + } + + @RequiredArgsConstructor + @Getter + public static class OrderedClass implements Ordered { + private final int order; + + @Override + public String toString() { + return String.valueOf(order); + } + } +} diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/TestReorderService.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/TestReorderService.java new file mode 100644 index 000000000..0d17709d5 --- /dev/null +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/TestReorderService.java @@ -0,0 +1,8 @@ +package pro.fessional.wings.silencer.app.service; + +/** + * @author trydofor + * @since 2024-05-11 + */ +public interface TestReorderService { +} diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestReorderServiceImpl1.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestReorderServiceImpl1.java new file mode 100644 index 000000000..50f6cd202 --- /dev/null +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestReorderServiceImpl1.java @@ -0,0 +1,18 @@ +package pro.fessional.wings.silencer.app.service.impl; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; +import pro.fessional.wings.silencer.app.service.TestReorderService; + +/** + * @author trydofor + * @since 2024-05-11 + */ +@Service +@Order(2) +public class TestReorderServiceImpl1 implements TestReorderService { + @Override + public String toString() { + return "1"; + } +} diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestReorderServiceImpl2.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestReorderServiceImpl2.java new file mode 100644 index 000000000..dfcab3984 --- /dev/null +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/app/service/impl/TestReorderServiceImpl2.java @@ -0,0 +1,18 @@ +package pro.fessional.wings.silencer.app.service.impl; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; +import pro.fessional.wings.silencer.app.service.TestReorderService; + +/** + * @author trydofor + * @since 2024-05-11 + */ +@Service +@Order(1) +public class TestReorderServiceImpl2 implements TestReorderService { + @Override + public String toString() { + return "2"; + } +} diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsReorderDefaultTest.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsReorderDefaultTest.java new file mode 100644 index 000000000..0d41cfcf9 --- /dev/null +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsReorderDefaultTest.java @@ -0,0 +1,91 @@ +package pro.fessional.wings.silencer.spring.boot; + +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.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import pro.fessional.wings.silencer.app.bean.TestReorderConfiguration.GetterClass; +import pro.fessional.wings.silencer.app.bean.TestReorderConfiguration.OrderedClass; +import pro.fessional.wings.silencer.app.bean.TestReorderConfiguration.PlainClass; +import pro.fessional.wings.silencer.app.service.TestReorderService; + +import java.util.List; + +/** + * @author trydofor + * @since 2019-06-25 + */ + +@SpringBootTest(properties = { + "wings.reorder.plainClass2=3", + "wings.reorder.getterClass2=3", + "wings.reorder.orderedClass2=3", + "wings.reorder.testReorderServiceImpl2=3", + "wings.enabled.silencer.bean-reorder=false", +}) +public class WingsReorderDefaultTest { + + @Setter(onMethod_ = {@Autowired}) + private List getterClasses; + + @Setter(onMethod_ = {@Autowired}) + private List orderedClass; + + @Setter(onMethod_ = {@Autowired}) + private List plainClasses; + + @Setter(onMethod_ = {@Autowired}) + private List services; + + @Setter(onMethod_ = {@Autowired}) + private ObjectProvider getterClassesProvider; + + @Setter(onMethod_ = {@Autowired}) + private ObjectProvider orderedClassProvider; + + @Setter(onMethod_ = {@Autowired}) + private ObjectProvider plainClassProvider; + + @Setter(onMethod_ = {@Autowired}) + private ObjectProvider servicesProvider; + + @Test + @TmsLink("C11031") + public void orderDefault() { + // Defined order + Assertions.assertEquals("1,2", toString(getterClassesProvider.stream().toList())); + Assertions.assertEquals("1,2", toString(orderedClassProvider.stream().toList())); + Assertions.assertEquals("1,2", toString(plainClassProvider.stream().toList())); + // scaned order + // Assertions.assertEquals("2,1", toString(servicesProvider.stream().toList())); + + // @Order first + Assertions.assertEquals("2,1", toString(getterClasses)); + Assertions.assertEquals("2,1", toString(orderedClass)); + Assertions.assertEquals("2,1", toString(plainClasses)); + Assertions.assertEquals("2,1", toString(services)); + + // @Order > Ordered.getOrder + Assertions.assertEquals("2,1", toString(getterClassesProvider.orderedStream().toList())); + Assertions.assertEquals("2,1", toString(orderedClassProvider.orderedStream().toList())); + Assertions.assertEquals("2,1", toString(plainClassProvider.orderedStream().toList())); + Assertions.assertEquals("2,1", toString(servicesProvider.orderedStream().toList())); + + + TestReorderService impl2 = servicesProvider.getIfUnique(); + Assertions.assertNull(impl2); + } + + private String toString(List lst) { + Assertions.assertNotNull(lst); + StringBuilder buf = new StringBuilder(); + for (Object o : lst) { + buf.append(','); + buf.append(o.toString()); + } + return buf.substring(1); + } +} diff --git a/wings/silencer/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsReorderEnableTest.java b/wings/silencer/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsReorderEnableTest.java new file mode 100644 index 000000000..004b0c665 --- /dev/null +++ b/wings/silencer/src/test/java/pro/fessional/wings/silencer/spring/boot/WingsReorderEnableTest.java @@ -0,0 +1,91 @@ +package pro.fessional.wings.silencer.spring.boot; + +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.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import pro.fessional.wings.silencer.app.bean.TestReorderConfiguration.GetterClass; +import pro.fessional.wings.silencer.app.bean.TestReorderConfiguration.OrderedClass; +import pro.fessional.wings.silencer.app.bean.TestReorderConfiguration.PlainClass; +import pro.fessional.wings.silencer.app.service.TestReorderService; + +import java.util.List; + +/** + * @author trydofor + * @since 2019-06-25 + */ + +@SpringBootTest(properties = { + "wings.reorder.plainClass2=3", + "wings.reorder.getterClass2=3", + "wings.reorder.orderedClass2=3", +// "wings.reorder.testReorderServiceImpl2=3", // or + "wings.reorder.pro.fessional.wings.silencer.app.service.impl.TestReorderServiceImpl2=3", + "wings.primary.testReorderServiceImpl2=true", +}) +public class WingsReorderEnableTest { + + @Setter(onMethod_ = {@Autowired}) + private List getterClasses; + + @Setter(onMethod_ = {@Autowired}) + private List orderedClass; + + @Setter(onMethod_ = {@Autowired}) + private List plainClasses; + + @Setter(onMethod_ = {@Autowired}) + private List services; + + @Setter(onMethod_ = {@Autowired}) + private ObjectProvider getterClassesProvider; + + @Setter(onMethod_ = {@Autowired}) + private ObjectProvider orderedClassProvider; + + @Setter(onMethod_ = {@Autowired}) + private ObjectProvider plainClassProvider; + + @Setter(onMethod_ = {@Autowired}) + private ObjectProvider servicesProvider; + + @Test + @TmsLink("C11032") + public void orderEnable() { + // Defined order + Assertions.assertEquals("1,2", toString(getterClassesProvider.stream().toList())); + Assertions.assertEquals("1,2", toString(orderedClassProvider.stream().toList())); + Assertions.assertEquals("1,2", toString(plainClassProvider.stream().toList())); + // + // Assertions.assertEquals("2,1", toString(servicesProvider.stream().toList())); + + // @Order first + Assertions.assertEquals("1,2", toString(getterClasses)); + Assertions.assertEquals("1,2", toString(orderedClass)); + Assertions.assertEquals("1,2", toString(plainClasses)); + Assertions.assertEquals("1,2", toString(services)); + + // @Order > Ordered.getOrder + Assertions.assertEquals("1,2", toString(getterClassesProvider.orderedStream().toList())); + Assertions.assertEquals("1,2", toString(orderedClassProvider.orderedStream().toList())); + Assertions.assertEquals("1,2", toString(plainClassProvider.orderedStream().toList())); + Assertions.assertEquals("1,2", toString(servicesProvider.orderedStream().toList())); + + TestReorderService impl2 = servicesProvider.getIfUnique(); + Assertions.assertEquals("2", impl2.toString()); + } + + private String toString(List lst) { + Assertions.assertNotNull(lst); + StringBuilder buf = new StringBuilder(); + for (Object o : lst) { + buf.append(','); + buf.append(o.toString()); + } + return buf.substring(1); + } +} diff --git a/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarBootAdminServerConfiguration.java b/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarBootAdminServerConfiguration.java index a0486bbfa..57b723971 100644 --- a/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarBootAdminServerConfiguration.java +++ b/wings/slardar-sprint/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarBootAdminServerConfiguration.java @@ -10,13 +10,10 @@ import de.codecentric.boot.admin.server.notify.AbstractStatusChangeNotifier; import de.codecentric.boot.admin.server.web.client.BasicAuthHttpHeaderProvider; import de.codecentric.boot.admin.server.web.client.BasicAuthHttpHeaderProvider.InstanceCredentials; -import de.codecentric.boot.admin.server.web.servlet.AdminControllerHandlerMapping; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jetbrains.annotations.NotNull; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -46,22 +43,6 @@ public class SlardarBootAdminServerConfiguration { private final static Log log = LogFactory.getLog(SlardarBootAdminServerConfiguration.class); - @Bean - @ConditionalWingsEnabled - @Conditional(SpringBootAdminServerEnabledCondition.class) - public static BeanPostProcessor bootAdminMappingOrderPostProcessor() { - log.info("SlardarSprint spring-bean bootAdminMappingOrderPostProcessor of BootAdmin server"); - return new BeanPostProcessor() { - @Override - public Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException { - if (bean instanceof AdminControllerHandlerMapping ob) { - ob.setOrder(-1); - } - return bean; - } - }; - } - @Bean @ConditionalWingsEnabled @Conditional(SpringBootAdminServerEnabledCondition.class) diff --git a/wings/slardar-sprint/src/main/resources/wings-conf/wings-reorder-77.properties b/wings/slardar-sprint/src/main/resources/wings-conf/wings-reorder-77.properties new file mode 100644 index 000000000..12c2e60a1 --- /dev/null +++ b/wings/slardar-sprint/src/main/resources/wings-conf/wings-reorder-77.properties @@ -0,0 +1,4 @@ +## ControllerEndpointHandlerMapping.order = -100 +## RequestMappingHandlerMapping.order = 0 +## AdminControllerHandlerMapping.order = 0 +wings.reorder.adminHandlerMapping=-1 \ No newline at end of file diff --git a/wings/slardar-sprint/src/test/java/pro/fessional/wings/slardar/webmvc/BootAdminServerTest.java b/wings/slardar-sprint/src/test/java/pro/fessional/wings/slardar/webmvc/BootAdminServerTest.java new file mode 100644 index 000000000..77e359b89 --- /dev/null +++ b/wings/slardar-sprint/src/test/java/pro/fessional/wings/slardar/webmvc/BootAdminServerTest.java @@ -0,0 +1,59 @@ +package pro.fessional.wings.slardar.webmvc; + +import de.codecentric.boot.admin.server.config.EnableAdminServer; +import de.codecentric.boot.admin.server.web.servlet.AdminControllerHandlerMapping; +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.context.SpringBootTest; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.List; + +/** + * @author trydofor + * @since 2024-05-13 + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.boot.admin.server.enabled=true", + }) +@EnableAdminServer +@Slf4j +public class BootAdminServerTest { + + @Setter(onMethod_ = {@Autowired}) + protected List requestMappingHandlerMapping; + + /** + *
+     * default order,
+     * 1.ControllerEndpointHandlerMapping for @ControllerEndpoint and @RestControllerEndpoint
+     * 2.RequestMappingHandlerMapping for @RequestMapping
+     * 3.AdminControllerHandlerMapping for BootAdminServer
+     *
+     * after reorder
+     * 1.ControllerEndpointHandlerMapping for @ControllerEndpoint and @RestControllerEndpoint
+     * 2.AdminControllerHandlerMapping for BootAdminServer
+     * 3.RequestMappingHandlerMapping for @RequestMapping
+     * 
+ */ + @Test + @TmsLink("C13121") + public void mappingOrder() { + int good = 0; + for (RequestMappingHandlerMapping mapping : requestMappingHandlerMapping) { + log.info("requestMappingHandlerMapping class=" + mapping.getClass().getName()); + if (mapping instanceof AdminControllerHandlerMapping) { + good = 1; + } + else { + if (good > 0) good++; + } + } + Assertions.assertTrue(good >= 2); + } +} diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarFirstBloodConfiguration.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarFirstBloodConfiguration.java index d62970b95..b111a1b20 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarFirstBloodConfiguration.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarFirstBloodConfiguration.java @@ -6,9 +6,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; import org.springframework.web.servlet.ModelAndView; import pro.fessional.mirana.code.RandCode; +import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.slardar.concur.impl.FirstBloodHandler; import pro.fessional.wings.slardar.concur.impl.FirstBloodImageHandler; @@ -33,6 +35,7 @@ public class SlardarFirstBloodConfiguration { @Bean @ConditionalWingsEnabled(abs = SlardarEnabledProp.Key$firstBloodImage) + @Order(WingsOrdered.Lv4Application) public FirstBloodImageHandler firstBloodImageHandler(@Autowired(required = false) WingsRemoteResolver remoteResolver, SlardarFirstBloodProp prop) { log.info("SlardarWebmvc spring-bean firstBloodImageHandler"); final FirstBloodImageHandler handler = new FirstBloodImageHandler(); diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarSessionConfiguration.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarSessionConfiguration.java index a1c473d7b..f189a683d 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarSessionConfiguration.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarSessionConfiguration.java @@ -9,6 +9,7 @@ import org.springframework.boot.web.servlet.server.Session.Cookie; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.session.web.http.CookieHttpSessionIdResolver; import org.springframework.session.web.http.CookieSerializer; import org.springframework.session.web.http.DefaultCookieSerializer; @@ -16,6 +17,7 @@ import org.springframework.session.web.http.HttpSessionIdResolver; import org.springframework.util.StringUtils; import pro.fessional.mirana.best.AssertArgs; +import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.slardar.session.WingsSessionIdResolver; import pro.fessional.wings.slardar.spring.prop.SlardarEnabledProp; @@ -93,6 +95,7 @@ public WingsSessionIdResolver httpSessionIdResolver( @Bean @ConditionalWingsEnabled + @Order(WingsOrdered.Lv4Application + 10) public DefaultCookieSerializerCustomizer slardarCookieSerializerCustomizer(SlardarSessionProp prop) { log.info("SlardarWebmvc spring-bean slardarCookieSerializerCustomizer"); return it -> { diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarTerminalConfiguration.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarTerminalConfiguration.java index 793c21ed2..551528572 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarTerminalConfiguration.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarTerminalConfiguration.java @@ -5,12 +5,16 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; +import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; import pro.fessional.wings.slardar.constants.SlardarServletConst; import pro.fessional.wings.slardar.context.SecurityContextUtil; import pro.fessional.wings.slardar.context.TerminalInterceptor; +import pro.fessional.wings.slardar.context.TerminalInterceptor.TerminalBuilder; +import pro.fessional.wings.slardar.context.TerminalInterceptor.TerminalLogger; import pro.fessional.wings.slardar.context.TerminalSecurityAttribute; import pro.fessional.wings.slardar.security.WingsUserDetails; import pro.fessional.wings.slardar.servlet.resolver.WingsLocaleResolver; @@ -18,7 +22,9 @@ import pro.fessional.wings.slardar.spring.prop.SlardarEnabledProp; import pro.fessional.wings.slardar.spring.prop.SlardarTerminalProp; +import java.time.ZoneId; import java.util.ArrayList; +import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; @@ -37,7 +43,8 @@ public class SlardarTerminalConfiguration { @Bean @ConditionalWingsEnabled - public TerminalInterceptor.TerminalBuilder remoteTerminalBuilder(WingsRemoteResolver resolver) { + @Order(WingsOrdered.Lv4Application + 10) + public TerminalBuilder remoteTerminalBuilder(WingsRemoteResolver resolver) { log.info("SlardarWebmvc spring-bean remoteTerminalBuilder"); return (builder, request) -> builder .terminal(TerminalAddr, resolver.resolveRemoteIp(request)) @@ -46,11 +53,13 @@ public TerminalInterceptor.TerminalBuilder remoteTerminalBuilder(WingsRemoteReso @Bean @ConditionalWingsEnabled - public TerminalInterceptor.TerminalBuilder securityTerminalBuilder(WingsLocaleResolver resolver) { + @Order(WingsOrdered.Lv4Application + 20) + public TerminalBuilder securityTerminalBuilder(SlardarTerminalProp prop, WingsLocaleResolver resolver) { log.info("SlardarWebmvc spring-bean securityTerminalBuilder"); return (builder, request) -> { final Authentication authn = SecurityContextUtil.getAuthentication(false); final WingsUserDetails details = SecurityContextUtil.getUserDetails(authn); + if (details == null) { final Long userId = (Long) request.getAttribute(SlardarServletConst.AttrUserId); final var locale = resolver.resolveI18nContext(request, userId); @@ -59,8 +68,16 @@ public TerminalInterceptor.TerminalBuilder securityTerminalBuilder(WingsLocaleRe .userOrGuest(userId); } else { - builder.locale(details.getLocale()) - .timeZone(details.getZoneId()) + Locale lcl = details.getLocale(); + ZoneId zid = details.getZoneId(); + if (prop.isLocaleRequest() || prop.isTimezoneRequest()) { + final var locale = resolver.resolveI18nContext(request, details.getUserId()); + if (prop.isLocaleRequest()) lcl = locale.getLocale(); + if (prop.isTimezoneRequest()) zid = details.getZoneId(); + } + + builder.locale(lcl) + .timeZone(zid) .user(details.getUserId()) .authType(details.getAuthType()) .username(details.getUsername()) @@ -75,7 +92,7 @@ public TerminalInterceptor.TerminalBuilder securityTerminalBuilder(WingsLocaleRe @Bean @ConditionalWingsEnabled - public TerminalInterceptor terminalInterceptor(SlardarTerminalProp prop, ObjectProvider builders, ObjectProvider loggers) { + public TerminalInterceptor terminalInterceptor(SlardarTerminalProp prop, ObjectProvider builders, ObjectProvider loggers) { log.info("SlardarWebmvc spring-bean terminalInterceptor"); final TerminalInterceptor bean = new TerminalInterceptor(); diff --git a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarTerminalProp.java b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarTerminalProp.java index d60c216b0..79cd9c613 100644 --- a/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarTerminalProp.java +++ b/wings/slardar-webmvc/src/main/java/pro/fessional/wings/slardar/spring/prop/SlardarTerminalProp.java @@ -32,4 +32,20 @@ public class SlardarTerminalProp { */ private Map includePatterns = Collections.emptyMap(); public static final String Key$includePatterns = Key + ".include-patterns"; + + /** + * whether to set locale from request first than server + * + * @see #Key$localeRequest + */ + private boolean localeRequest = true; + public static final String Key$localeRequest = Key + ".locale-request"; + + /** + * whether to set timezone from request first than server + * + * @see #Key$timezoneRequest + */ + private boolean timezoneRequest = false; + public static final String Key$timezoneRequest = Key + ".timezone-request"; } diff --git a/wings/slardar-webmvc/src/main/resources/wings-conf/wings-terminal-79.properties b/wings/slardar-webmvc/src/main/resources/wings-conf/wings-terminal-79.properties index 4d583175e..a8797bfa0 100644 --- a/wings/slardar-webmvc/src/main/resources/wings-conf/wings-terminal-79.properties +++ b/wings/slardar-webmvc/src/main/resources/wings-conf/wings-terminal-79.properties @@ -2,5 +2,12 @@ wings.slardar.terminal.exclude-patterns[error]=/error wings.slardar.terminal.exclude-patterns[api]=/api/** wings.slardar.terminal.exclude-patterns[oauth]=/oauth/** + ## exclude takes precedence over include #wings.slardar.terminal.include-patterns[oauth]= + +## whether to set locale from request first than server +wings.slardar.terminal.locale-request=true + +## whether to set timezone from request first than server +wings.slardar.terminal.timezone-request=false 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 070998acb..293b054ee 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 @@ -29,13 +29,13 @@ public class TestAsyncController { @RequestMapping(value = "/test/asyn-type.json") public CompletableFuture testAsyncType(@RequestParam("err") AsyncType err) { log.info("testAsyncType type={}", err); - return testAsyncService.asyncType(err); + return testAsyncService.typeAsync(err); } @RequestMapping(value = "/test/asyn-void.json") public CompletableFuture testAsyncVoid(@RequestParam("err") AsyncType err) { log.info("testAsyncVoid type={}", err); - testAsyncService.asyncVoid(err); + testAsyncService.voidAsync(err); return CompletableFuture.completedFuture(err.name()); } @@ -43,7 +43,7 @@ public CompletableFuture testAsyncVoid(@RequestParam("err") AsyncType er public DeferredResult testAsyncDefer(@RequestParam("err") AsyncType err) { DeferredResult result = new DeferredResult<>(1000L); log.info("testAsyncDefer type={}", err); - testAsyncService.asyncDefer(result, err); + testAsyncService.deferAsync(result, err); return result; } diff --git a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/controller/TestDoubleKillController.java b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/controller/TestDoubleKillController.java index 96e700c81..e9091ff35 100644 --- a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/controller/TestDoubleKillController.java +++ b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/controller/TestDoubleKillController.java @@ -1,6 +1,8 @@ package pro.fessional.wings.slardar.app.controller; +import jakarta.servlet.http.HttpServletRequest; import lombok.Setter; +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.RestController; @@ -14,6 +16,7 @@ * @since 2021-02-01 */ @RestController +@Slf4j public class TestDoubleKillController { @Setter(onMethod_ = {@Autowired}) @@ -21,8 +24,9 @@ public class TestDoubleKillController { @GetMapping("/test/double-kill.json") @DoubleKill(expression = "@httpSessionIdResolver.resolveSessionIds(#p0)") - public R doubleKill() { + public R doubleKill(HttpServletRequest requiredBySpel) { Sleep.ignoreInterrupt(10_000); + log.info("just log uri={}", requiredBySpel.getRequestURI()); return R.ok("login page"); } diff --git a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/service/TestAsyncService.java b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/service/TestAsyncService.java index 145d171f8..8f3b1cbbb 100644 --- a/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/service/TestAsyncService.java +++ b/wings/slardar-webmvc/src/test/java/pro/fessional/wings/slardar/app/service/TestAsyncService.java @@ -1,6 +1,7 @@ package pro.fessional.wings.slardar.app.service; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.context.request.async.DeferredResult; @@ -25,13 +26,13 @@ public enum AsyncType { public static String UserIdFailedFuture = "asyncType FailedFuture"; @Async - public CompletableFuture asyncType(AsyncType type) { + public CompletableFuture typeAsync(AsyncType type) { final String name = Thread.currentThread().getName(); if (name.contains("exec-")) { log.info("asyncType={}", type); } else { - log.error("bad thread name prefix, asyncType should contain 'exec-'"); + Assertions.fail("bad thread prefix, should start with 'exec-'"); } return switch (type) { @@ -45,7 +46,7 @@ public CompletableFuture asyncType(AsyncType type) { public static String VoidFailedFuture = "asyncVoid FailedFuture"; @Async - public void asyncVoid(AsyncType type) { + public void voidAsync(AsyncType type) { syncResult(type); } @@ -66,7 +67,7 @@ public String syncResult(AsyncType type) { log.info("asyncVoid"); } else { - log.error("bad thread name prefix, asyncVoid should contain 'exec-'"); + Assertions.fail("bad thread prefix, should start with 'exec-'"); } if (type == AsyncType.UncaughtException) { @@ -79,13 +80,13 @@ else if (type == AsyncType.FailedFuture) { } @Async - public void asyncDefer(DeferredResult result, AsyncType type) { + public void deferAsync(DeferredResult result, AsyncType type) { final String name = Thread.currentThread().getName(); if (name.contains("exec-")) { log.info("asyncVoid"); } else { - log.error("bad thread name prefix, asyncVoid should contain 'exec-'"); + Assertions.fail("bad thread prefix, should start with 'exec-'"); } if (type == AsyncType.UncaughtException) { 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 8fa0f48b4..879d6fd03 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 @@ -100,6 +100,9 @@ private void testMock(String url, String type, boolean err) throws Exception { // [root] Assertions.assertTrue(e.getMessage().contains("timeToWait")); } + else { + Assertions.fail(e); + } } } } 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 new file mode 100644 index 000000000..c78499f30 --- /dev/null +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/AsyncHelper.java @@ -0,0 +1,92 @@ +package pro.fessional.wings.slardar.async; + +import com.alibaba.ttl.threadpool.TtlExecutors; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder; +import org.springframework.core.task.AsyncTaskExecutor; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +/** + * ... + * + * @author trydofor + * @see TtlExecutors + * @since 2024-05-13 + */ +public class AsyncHelper { + + protected static Executor AsyncExecutor; + protected static AsyncTaskExecutor AppTaskExecutor; + + protected AsyncHelper(Executor asy, AsyncTaskExecutor app) { + AsyncExecutor = asy; + AppTaskExecutor = app; + } + + /** + * @see org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor#DEFAULT_TASK_EXECUTOR_BEAN_NAME + */ + @NotNull + public static Executor Async() { + if (AsyncExecutor == null) { + throw new IllegalStateException("AsyncExecutor must init before using"); + } + return AsyncExecutor; + } + + /** + * just like default @Async, but not AsyncUncaughtExceptionHandler + */ + public static CompletableFuture Async(@NotNull Runnable task) { + return CompletableFuture.runAsync(task, AsyncExecutor); + } + + /** + * just like default @Async, but not AsyncUncaughtExceptionHandler + */ + public static CompletableFuture Async(@NotNull Supplier supplier) { + return CompletableFuture.supplyAsync(supplier, AsyncExecutor); + } + + /** + * @see org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration#APPLICATION_TASK_EXECUTOR_BEAN_NAME + */ + @NotNull + public static AsyncTaskExecutor AppTask() { + if (AppTaskExecutor == null) { + throw new IllegalStateException("AppTaskExecutor must init before using"); + } + + return AppTaskExecutor; + } + + + protected static ThreadPoolTaskExecutorBuilder ExecutorBuilder; + protected static AsyncTaskExecutor LiteExecutor; + + + /** + * @see org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor#DEFAULT_TASK_EXECUTOR_BEAN_NAME + */ + @NotNull + public static AsyncTaskExecutor Lite() { + if (LiteExecutor == null) { + throw new IllegalStateException("LiteExecutor must init before using"); + } + return LiteExecutor; + } + + /** + * Get ThreadPoolTaskExecutorBuilder, IllegalStateException if nonull but null. + */ + @NotNull + public static ThreadPoolTaskExecutorBuilder ExecutorBuilder() { + if (ExecutorBuilder == null) { + throw new IllegalStateException("LightBuilder must init before using"); + } + return ExecutorBuilder; + } +} 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 65775fc93..697059b8d 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,7 +1,7 @@ package pro.fessional.wings.slardar.async; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import pro.fessional.mirana.time.ThreadNow; @@ -15,79 +15,102 @@ */ public class TaskSchedulerHelper { - protected static ThreadPoolTaskScheduler LightTasker; - protected static ThreadPoolTaskScheduler HeavyTasker; + protected static ThreadPoolTaskScheduler FastScheduler; + protected static ThreadPoolTaskScheduler ScheduledScheduler; - protected TaskSchedulerHelper(ThreadPoolTaskScheduler light, ThreadPoolTaskScheduler heavy) { - LightTasker = light; - HeavyTasker = heavy; + protected TaskSchedulerHelper(ThreadPoolTaskScheduler fast, ThreadPoolTaskScheduler scheduled) { + FastScheduler = fast; + ScheduledScheduler = scheduled; + } + + /** + * configure TtlThreadPoolTaskScheduler by builder + */ + public static TtlThreadPoolTaskScheduler Ttl(ThreadPoolTaskSchedulerBuilder builder) { + return builder.configure(new TtlThreadPoolTaskScheduler()); } /** * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#DEFAULT_TASK_SCHEDULER_BEAN_NAME */ - @Contract("true->!null") - public static ThreadPoolTaskScheduler Light(boolean nonnull) { - if (nonnull && LightTasker == null) { - throw new IllegalStateException("LightTasker must init before using"); + @NotNull + public static ThreadPoolTaskScheduler Fast() { + if (FastScheduler == null) { + throw new IllegalStateException("FastScheduler must init before using"); } - return LightTasker; + return FastScheduler; } /** * see NamingSlardarConst#slardarHeavyScheduler */ - @Contract("true->!null") - public static ThreadPoolTaskScheduler Heavy(boolean nonnull) { - if (nonnull && HeavyTasker == null) { - throw new IllegalStateException("HeavyTasker must init before using"); + @NotNull + public static ThreadPoolTaskScheduler Scheduled() { + if (ScheduledScheduler == null) { + throw new IllegalStateException("ScheduledScheduler must init before using"); } - return HeavyTasker; + return ScheduledScheduler; } /** - * Get Light Scheduler if fast, otherwise Heavy. + * just like default @Scheduled + * + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME */ - @NotNull - public static ThreadPoolTaskScheduler referScheduler(boolean fast) { - return fast ? Light(true) : Heavy(true); + public static void Scheduled(@NotNull Runnable task) { + Scheduled().execute(task); } /** - * Execute an async task immediately, `fast` means that the task will be finished soon, e.g. 10s. + * just like default @Scheduled * - * @see ThreadPoolTaskScheduler#execute(Runnable) + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME */ - public static void execute(boolean fast, @NotNull Runnable task) { - referScheduler(fast).execute(task); + public static ScheduledFuture Scheduled(long delayMs, @NotNull Runnable task) { + return Scheduled().schedule(task, Instant.ofEpochMilli(ThreadNow.millis() + delayMs)); } /** - * Execute an async task after delayMs millis (ThreadNow), `fast` means that the task will be finished soon, e.g. 10s. + * just like default @Scheduled * - * @see ThreadPoolTaskScheduler#schedule(Runnable, Instant) + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME */ - public static ScheduledFuture execute(boolean fast, long delayMs, @NotNull Runnable task) { - return referScheduler(fast).schedule(task, Instant.ofEpochMilli(ThreadNow.millis() + delayMs)); + public static ScheduledFuture Scheduled(Instant start, @NotNull Runnable task) { + return Scheduled().schedule(task, start); } /** - * Execute an async task at specified instant, `fast` means that the task will be finished soon, e.g. 10s. + * just like default @Scheduled * - * @see ThreadPoolTaskScheduler#schedule(Runnable, Instant) + * @see org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.DEFAULT_TASK_SCHEDULER_BEAN_NAME + */ + public static ScheduledFuture Scheduled(Trigger trigger, @NotNull Runnable task) { + return Scheduled().schedule(task, trigger); + } + + protected static ThreadPoolTaskSchedulerBuilder FastBuilder; + protected static ThreadPoolTaskSchedulerBuilder ScheduledBuilder; + + /** + * Get Light ThreadPoolTaskSchedulerBuilder, IllegalStateException if nonull but null. */ - public static ScheduledFuture execute(boolean fast, Instant start, @NotNull Runnable task) { - return referScheduler(fast).schedule(task, start); + @NotNull + public static ThreadPoolTaskSchedulerBuilder FastBuilder() { + if (FastBuilder == null) { + throw new IllegalStateException("FastBuilder must init before using"); + } + return FastBuilder; } /** - * Execute an async task by given trigger, `fast` means that the task will be finished soon, e.g. 10s. - * Note, errorHandler, unlike other methods, does not handle DelegatingErrorHandlingRunnable. - * - * @see ThreadPoolTaskScheduler#schedule(Runnable, Trigger) + * Get Light ThreadPoolTaskSchedulerBuilder, IllegalStateException if nonull but null. */ - public static ScheduledFuture execute(boolean fast, Trigger trigger, @NotNull Runnable task) { - return referScheduler(fast).schedule(task, trigger); + @NotNull + public static ThreadPoolTaskSchedulerBuilder ScheduledBuilder() { + if (ScheduledBuilder == null) { + throw new IllegalStateException("ScheduledBuilder must init before using"); + } + return ScheduledBuilder; } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TtlTaskDecorator.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TtlTaskDecorator.java new file mode 100644 index 000000000..8d41bba4f --- /dev/null +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TtlTaskDecorator.java @@ -0,0 +1,58 @@ +package pro.fessional.wings.slardar.async; + +import com.alibaba.ttl.TtlRunnable; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.springframework.core.task.TaskDecorator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * ttl decorate first. then others + * + * @author trydofor + * @since 2024-05-14 + */ +@Getter @Setter +public class TtlTaskDecorator implements TaskDecorator { + + protected final List decorators = new ArrayList<>(); + + protected volatile boolean releaseTtlValueReferenceAfterRun = false; + /** + * false for dev, true for product + */ + protected volatile boolean idempotent = true; + + public TtlTaskDecorator(Collection decorators) { + add(decorators); + } + + public void add(Collection decorators) { + if (decorators == null) return; + for (TaskDecorator decorator : decorators) { + add(decorator); + } + } + + public void add(TaskDecorator decorator) { + if (decorator != null && !(decorator instanceof TtlTaskDecorator)) { + decorators.add(decorator); + } + } + + @Override + @NotNull + public Runnable decorate(@NotNull Runnable runnable) { + // ttl decorate first + runnable = TtlRunnable.get(runnable, releaseTtlValueReferenceAfterRun, idempotent); + // other decorate + for (TaskDecorator taskDecorator : decorators) { + runnable = taskDecorator.decorate(runnable); + } + return runnable; + } +} diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TtlThreadPoolTaskScheduler.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TtlThreadPoolTaskScheduler.java index 29844a921..ce67809a9 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TtlThreadPoolTaskScheduler.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/async/TtlThreadPoolTaskScheduler.java @@ -1,21 +1,10 @@ package pro.fessional.wings.slardar.async; -import com.alibaba.ttl.TtlCallable; -import com.alibaba.ttl.TtlRunnable; import com.alibaba.ttl.threadpool.TtlExecutors; -import org.jetbrains.annotations.NotNull; -import org.springframework.scheduling.Trigger; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; -import org.springframework.util.concurrent.ListenableFuture; -import java.time.Duration; -import java.time.Instant; -import java.util.Date; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionHandler; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; /** @@ -26,138 +15,9 @@ */ public class TtlThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { - protected final boolean releaseTtlValueReferenceAfterRun; - protected final boolean idempotent; - - public TtlThreadPoolTaskScheduler() { - this(false, true); - } - - public TtlThreadPoolTaskScheduler(boolean releaseTtlValueReferenceAfterRun, boolean idempotent) { - this.idempotent = idempotent; - this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun; - } - - @Override - @NotNull - protected ExecutorService initializeExecutor( - @NotNull ThreadFactory threadFactory, - @NotNull RejectedExecutionHandler rejectedExecutionHandler - ) { - final ExecutorService es = super.initializeExecutor(threadFactory, rejectedExecutionHandler); - return TtlExecutors.getTtlExecutorService(es); - } - - @Override - public void execute(@NotNull Runnable task) { - super.execute(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent)); - } - - @Override - @NotNull - public Future submit(@NotNull Runnable task) { - return super.submit(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent)); - } - - @Override - @NotNull - public Future submit(@NotNull Callable task) { - return super.submit(TtlCallable.get(task, releaseTtlValueReferenceAfterRun, idempotent)); - } - - @Override - @NotNull - @Deprecated - public ListenableFuture submitListenable(@NotNull Runnable task) { - return super.submitListenable(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent)); - } - - @Override - @NotNull - @Deprecated - public ListenableFuture submitListenable(@NotNull Callable task) { - return super.submitListenable(TtlCallable.get(task, releaseTtlValueReferenceAfterRun, idempotent)); - } - - @Override - public ScheduledFuture schedule(@NotNull Runnable task, @NotNull Trigger trigger) { - return super.schedule(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent), trigger); - } - - @Override - @NotNull - @Deprecated - public ScheduledFuture schedule(@NotNull Runnable task, @NotNull Date startTime) { - return schedule(task, startTime.toInstant()); - } - - @Override - @NotNull - @Deprecated - public ScheduledFuture scheduleAtFixedRate(@NotNull Runnable task, @NotNull Date startTime, long period) { - return scheduleAtFixedRate(task, startTime.toInstant(), Duration.ofMillis(period)); - } - - @Override - @NotNull - @Deprecated - public ScheduledFuture scheduleAtFixedRate(@NotNull Runnable task, long period) { - return scheduleAtFixedRate(task, Duration.ofMillis(period)); - } - - @Override - @NotNull - @Deprecated - public ScheduledFuture scheduleWithFixedDelay(@NotNull Runnable task, @NotNull Date startTime, long delay) { - return scheduleWithFixedDelay(task, startTime.toInstant(), Duration.ofMillis(delay)); - } - - @Override - @NotNull - @Deprecated - public ScheduledFuture scheduleWithFixedDelay(@NotNull Runnable task, long delay) { - return scheduleWithFixedDelay(task, Duration.ofMillis(delay)); - } - - @Override - @NotNull - public ScheduledFuture schedule(@NotNull Runnable task, @NotNull Instant startTime) { - return super.schedule(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent), startTime); - } - - @Override - @NotNull - public ScheduledFuture scheduleAtFixedRate(@NotNull Runnable task, @NotNull Instant startTime, @NotNull Duration period) { - return super.scheduleAtFixedRate(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent), startTime, period); - } - - @Override - @NotNull - public ScheduledFuture scheduleAtFixedRate(@NotNull Runnable task, @NotNull Duration period) { - return super.scheduleAtFixedRate(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent), period); - } - - @Override - @NotNull - public ScheduledFuture scheduleWithFixedDelay(@NotNull Runnable task, @NotNull Instant startTime, @NotNull Duration delay) { - return super.scheduleWithFixedDelay(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent), startTime, delay); - } - - @Override - @NotNull - public ScheduledFuture scheduleWithFixedDelay(@NotNull Runnable task, @NotNull Duration delay) { - return super.scheduleWithFixedDelay(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent), delay); - } - - @Override - @NotNull - public Thread newThread(@NotNull Runnable task) { - return super.newThread(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent)); - } - @Override - @NotNull - public Thread createThread(@NotNull Runnable task) { - return super.createThread(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent)); + protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { + ScheduledExecutorService executor = super.createExecutor(poolSize, threadFactory, rejectedExecutionHandler); + return TtlExecutors.getTtlScheduledExecutorService(executor); } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/concur/impl/DoubleKillAround.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/concur/impl/DoubleKillAround.java index b47a2b5bd..177019c47 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/concur/impl/DoubleKillAround.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/concur/impl/DoubleKillAround.java @@ -1,6 +1,5 @@ package pro.fessional.wings.slardar.concur.impl; -import com.alibaba.ttl.threadpool.TtlExecutors; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -35,7 +34,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; -import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; /** @@ -97,8 +95,6 @@ public Object doubleKill(ProceedingJoinPoint joinPoint) throws Throwable { final ProgressContext.Bar bar = ProgressContext.gen(arrKey, now, ttl); if (doubleKill.async()) { - checkTtlExecutor(); - asyncExecutor.execute(() -> { try { syncProceed(joinPoint, bar); @@ -128,20 +124,6 @@ public Object doubleKill(ProceedingJoinPoint joinPoint) throws Throwable { } } - private void checkTtlExecutor() { - if (TtlExecutors.isTtlWrapper(asyncExecutor)) return; - - synchronized (evaluator) { - if (TtlExecutors.isTtlWrapper(asyncExecutor)) return; - - if (asyncExecutor == null) { - log.warn("config default Executors use newWorkStealingPool"); - asyncExecutor = Executors.newWorkStealingPool(); - } - asyncExecutor = TtlExecutors.getTtlExecutor(asyncExecutor); - } - } - private Object syncProceed(ProceedingJoinPoint joinPoint, ProgressContext.Bar bar) throws Throwable { try { final Object r = joinPoint.proceed(); 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 cde49d3d7..6161a4589 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 @@ -11,16 +11,21 @@ import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; +import pro.fessional.wings.slardar.async.AsyncHelper; import pro.fessional.wings.slardar.async.TaskSchedulerHelper; +import pro.fessional.wings.slardar.async.TtlTaskDecorator; import pro.fessional.wings.slardar.async.TtlThreadPoolTaskScheduler; import pro.fessional.wings.slardar.spring.prop.SlardarAsyncProp; +import java.util.List; import java.util.concurrent.Executor; import static org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME; @@ -42,20 +47,43 @@ @Configuration(proxyBeanMethods = false) @ConditionalWingsEnabled public class SlardarAsyncConfiguration { - public static final String slardarHeavyScheduler = "slardarHeavyScheduler"; + public static final String slardarFastScheduler = "slardarFastScheduler"; private static final Log log = LogFactory.getLog(SlardarAsyncConfiguration.class); + private final SlardarAsyncProp asyncProp; + private final ThreadPoolTaskSchedulerBuilder fastSchedulerBuilder; + + public SlardarAsyncConfiguration(SlardarAsyncProp asyncProp) { + this.asyncProp = asyncProp; + + ThreadPoolTaskSchedulerBuilder builder = new ThreadPoolTaskSchedulerBuilder(); + TaskSchedulingProperties fast = asyncProp.getFast(); + builder = builder.poolSize(fast.getPool().getSize()); + TaskSchedulingProperties.Shutdown shutdown = fast.getShutdown(); + builder = builder.awaitTermination(shutdown.isAwaitTermination()); + builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); + builder = builder.threadNamePrefix(fast.getThreadNamePrefix()); + fastSchedulerBuilder = builder; + } + + @Bean + @Primary + @ConditionalWingsEnabled + public TaskDecorator ttlTaskDecorator(List others) { + log.info("Slardar spring-bean ttlTaskDecorator, others count=" + others.size()); + return new TtlTaskDecorator(others); + } + /** * Executor in the context, regular (@Async) execution (that is @EnableAsync) will use it transparently */ @Bean(name = DEFAULT_TASK_EXECUTOR_BEAN_NAME) @ConditionalWingsEnabled public Executor taskExecutor(ThreadPoolTaskExecutorBuilder builder) { - final ThreadPoolTaskExecutor executor = builder.build(); - executor.initialize(); - log.info("Slardar spring-bean taskExecutor via ttlExecutor, prefix=" + executor.getThreadNamePrefix()); - return TtlExecutors.getTtlExecutor(executor); + final ThreadPoolTaskExecutor bean = builder.build(); + log.info("Slardar spring-bean taskExecutor of @Async, prefix=" + bean.getThreadNamePrefix()); + return bean; } /** @@ -64,46 +92,60 @@ public Executor taskExecutor(ThreadPoolTaskExecutorBuilder builder) { @Bean(name = APPLICATION_TASK_EXECUTOR_BEAN_NAME) @ConditionalWingsEnabled public AsyncTaskExecutor applicationTaskExecutor(ThreadPoolTaskExecutorBuilder builder) { - final ThreadPoolTaskExecutor executor = builder.build(); - executor.setThreadNamePrefix("app-" + executor.getThreadNamePrefix()); - executor.initialize(); - final Executor ttlExecutor = TtlExecutors.getTtlExecutor(executor); - log.info("Slardar spring-bean applicationTaskExecutor via ttlExecutor, prefix=" + executor.getThreadNamePrefix()); - return new ConcurrentTaskExecutor(ttlExecutor); + final ThreadPoolTaskExecutor bean = builder.build(); + bean.setThreadNamePrefix(asyncProp.getExecPrefix().getApplication()); + log.info("Slardar spring-bean applicationTaskExecutor of Callable MVC, prefix=" + bean.getThreadNamePrefix()); + return bean; } // Do NOT use @Primary to avoid overwriting the @Async thread pool. @Bean(name = DEFAULT_TASK_SCHEDULER_BEAN_NAME) @ConditionalWingsEnabled public ThreadPoolTaskScheduler taskScheduler(ThreadPoolTaskSchedulerBuilder builder) { - final TtlThreadPoolTaskScheduler scheduler = new TtlThreadPoolTaskScheduler(); - final TtlThreadPoolTaskScheduler bean = builder.configure(scheduler); - log.info("Slardar spring-bean taskScheduler via TtlThreadPoolTaskScheduler, prefix=" + bean.getThreadNamePrefix()); + final TtlThreadPoolTaskScheduler bean = TaskSchedulerHelper.Ttl(builder); + log.info("Slardar spring-bean taskScheduler of @Scheduled, prefix=" + bean.getThreadNamePrefix()); return bean; } - @Bean(name = slardarHeavyScheduler) + @Bean(name = slardarFastScheduler) @ConditionalWingsEnabled - public ThreadPoolTaskScheduler slardarHeavyScheduler(SlardarAsyncProp prop) { - final TtlThreadPoolTaskScheduler scheduler = new TtlThreadPoolTaskScheduler(); - ThreadPoolTaskSchedulerBuilder builder = new ThreadPoolTaskSchedulerBuilder(); - final TaskSchedulingProperties heavy = prop.getHeavy(); - builder = builder.poolSize(heavy.getPool().getSize()); - TaskSchedulingProperties.Shutdown shutdown = heavy.getShutdown(); - builder = builder.awaitTermination(shutdown.isAwaitTermination()); - builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); - builder = builder.threadNamePrefix(heavy.getThreadNamePrefix()); - log.info("Slardar spring-bean slardarHeavyScheduler via TtlThreadPoolTaskExecutor, prefix=" + heavy.getThreadNamePrefix()); - return builder.configure(scheduler); + public ThreadPoolTaskScheduler slardarFastScheduler() { + TtlThreadPoolTaskScheduler bean = TaskSchedulerHelper.Ttl(fastSchedulerBuilder); + log.info("Slardar spring-bean slardarFastScheduler of fast Scheduled, prefix=" + bean.getThreadNamePrefix()); + return bean; } @Bean @ConditionalWingsEnabled public TaskSchedulerHelper taskSchedulerHelper( - @Qualifier(DEFAULT_TASK_SCHEDULER_BEAN_NAME) ThreadPoolTaskScheduler light, - @Qualifier(slardarHeavyScheduler) ThreadPoolTaskScheduler heavy) { + @Qualifier(slardarFastScheduler) ThreadPoolTaskScheduler fast, + @Qualifier(DEFAULT_TASK_SCHEDULER_BEAN_NAME) ThreadPoolTaskScheduler scheduled, + ThreadPoolTaskSchedulerBuilder scheduledBuilder) { log.info("Slardar spring-bean taskSchedulerHelper"); - return new TaskSchedulerHelper(light, heavy) {}; + return new TaskSchedulerHelper(scheduled, fast) {{ + FastBuilder = fastSchedulerBuilder; + ScheduledBuilder = scheduledBuilder; + }}; + } + + @Bean + @ConditionalWingsEnabled + public AsyncHelper asyncHelper( + @Qualifier(DEFAULT_TASK_EXECUTOR_BEAN_NAME) Executor asyncExec, + @Qualifier(APPLICATION_TASK_EXECUTOR_BEAN_NAME) AsyncTaskExecutor appExec, + ThreadPoolTaskExecutorBuilder executorBuilder + ) { + log.info("Slardar spring-bean asyncHelper"); + final ThreadPoolTaskExecutor executor = executorBuilder.build(); + executor.setThreadNamePrefix(asyncProp.getExecPrefix().getLite()); + executor.initialize(); + final Executor exec = TtlExecutors.getTtlExecutor(executor); + AsyncTaskExecutor liteExecutor = new ConcurrentTaskExecutor(exec); + + return new AsyncHelper(asyncExec, appExec) {{ + ExecutorBuilder = executorBuilder; + LiteExecutor = liteExecutor; + }}; } } diff --git a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarOkhttpConfiguration.java b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarOkhttpConfiguration.java index c633d506e..1a01711dc 100644 --- a/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarOkhttpConfiguration.java +++ b/wings/slardar/src/main/java/pro/fessional/wings/slardar/spring/bean/SlardarOkhttpConfiguration.java @@ -15,6 +15,7 @@ import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import pro.fessional.wings.silencer.runner.CommandLineRunnerOrdered; import pro.fessional.wings.silencer.spring.WingsOrdered; import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled; @@ -61,6 +62,7 @@ public CookieJar okhttpHostCookieJar() { @Bean @ConditionalWingsEnabled @ConditionalOnExpression("${" + SlardarOkhttpProp.Key$followRedirect + ":false} || ${" + SlardarOkhttpProp.Key$followRedirectSsl + ":false}") + @Order(WingsOrdered.Lv4Application) public OkHttpRedirectNopInterceptor okhttpRedirectNopInterceptor() { log.info("Slardar spring-bean okhttpRedirectNopInterceptor"); return new OkHttpRedirectNopInterceptor(); 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 85a65e617..23f8aedd3 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 @@ -25,10 +25,30 @@ public class SlardarAsyncProp { /** - * heavy thread pool + * fast thread pool * * @see #Key$heavy */ - private TaskSchedulingProperties heavy; - public static final String Key$heavy = Key + ".heavy"; + private TaskSchedulingProperties fast; + public static final String Key$heavy = Key + ".fast"; + + /** + * executor prefix + * + * @see #Key$execPrefix + */ + private ExecPrefix execPrefix = new ExecPrefix(); + public static final String Key$execPrefix = Key + ".exec-prefix"; + + @Data + public static class ExecPrefix { + /** + * AsyncHelper lite Pool + */ + private String lite = "lite-"; + /** + * Callable MVC mapping + */ + private String application = "app-exec-"; + } } diff --git a/wings/slardar/src/main/resources/wings-conf/spring-task-79.properties b/wings/slardar/src/main/resources/wings-conf/spring-task-79.properties index caab5122a..5af41ebdc 100644 --- a/wings/slardar/src/main/resources/wings-conf/spring-task-79.properties +++ b/wings/slardar/src/main/resources/wings-conf/spring-task-79.properties @@ -10,5 +10,5 @@ spring.task.execution.thread-name-prefix=exec- ## @EnableScheduling @Scheduled threadPoolTaskScheduler spring.task.scheduling.pool.size=8 spring.task.scheduling.shutdown.await-termination=true -spring.task.scheduling.shutdown.await-termination-period=30s +spring.task.scheduling.shutdown.await-termination-period=180s spring.task.scheduling.thread-name-prefix=task- diff --git a/wings/slardar/src/main/resources/wings-conf/wings-async-79.properties b/wings/slardar/src/main/resources/wings-conf/wings-async-79.properties index 358360d03..f8aa57a2a 100644 --- a/wings/slardar/src/main/resources/wings-conf/wings-async-79.properties +++ b/wings/slardar/src/main/resources/wings-conf/wings-async-79.properties @@ -7,8 +7,12 @@ wings.slardar.async.event.shutdown.await-termination=true wings.slardar.async.event.shutdown.await-termination-period=60s wings.slardar.async.event.thread-name-prefix=event- -## heavy thread pool -wings.slardar.async.heavy.pool.size=8 -wings.slardar.async.heavy.shutdown.await-termination=true -wings.slardar.async.heavy.shutdown.await-termination-period=60s -wings.slardar.async.heavy.thread-name-prefix=heavy- +## fast task thread pool +wings.slardar.async.fast.pool.size=8 +wings.slardar.async.fast.shutdown.await-termination=true +wings.slardar.async.fast.shutdown.await-termination-period=60s +wings.slardar.async.fast.thread-name-prefix=fast- + +## executor prefix +wings.slardar.async.exec-prefix.lite=lit-exec- +wings.slardar.async.exec-prefix.application=app-exec- \ No newline at end of file diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestAsyncService.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestAsyncService.java index 4d707880b..22cdd3e65 100644 --- a/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestAsyncService.java +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestAsyncService.java @@ -1,6 +1,7 @@ package pro.fessional.wings.slardar.app.service; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import pro.fessional.wings.slardar.context.TerminalContext; @@ -26,7 +27,7 @@ public enum AsyncType { public static String UserIdFailedFuture = "asyncUserId FailedFuture"; @Async - public CompletableFuture asyncUserId(AsyncType type) { + public CompletableFuture userIdAsync(AsyncType type) { final String name = Thread.currentThread().getName(); final long uid; if (name.contains("exec-")) { @@ -35,7 +36,7 @@ public CompletableFuture asyncUserId(AsyncType type) { log.info("asyncUserId={}", uid); } else { - log.error("bad thread name prefix, asyncUserId should contain 'exec-'"); + Assertions.fail("bad thread prefix, should start with 'exec-'"); uid = DefaultUserId.Null; } @@ -46,16 +47,36 @@ public CompletableFuture asyncUserId(AsyncType type) { }; } + public Long userId(AsyncType type) { + final String name = Thread.currentThread().getName(); + final long uid; + if (name.contains("exec-")) { + final TerminalContext.Context ctx = TerminalContext.get(); + uid = ctx.getUserId(); + log.info("asyncUserId={}", uid); + } + else { + Assertions.fail("bad thread prefix, should start with 'exec-'"); + uid = DefaultUserId.Null; + } + + return switch (type) { + case UncaughtException -> throw new RuntimeException(UserIdUncaughtException); + case Return -> uid; + case FailedFuture -> throw new RuntimeException(UserIdFailedFuture); + }; + } + public static String VoidUncaughtException = "asyncVoid UncaughtException"; @Async - public void asyncVoid(AsyncType type) { + public void voidAsync(AsyncType type) { final String name = Thread.currentThread().getName(); if (name.contains("exec-")) { log.info("asyncVoid"); } else { - log.error("bad thread name prefix, asyncVoid should contain 'exec-'"); + Assertions.fail("bad thread prefix, should start with 'exec-'"); } if (type == AsyncType.UncaughtException) { diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestTtlDecorator.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestTtlDecorator.java new file mode 100644 index 000000000..a8d4d8c04 --- /dev/null +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/app/service/TestTtlDecorator.java @@ -0,0 +1,22 @@ +package pro.fessional.wings.slardar.app.service; + +import org.springframework.core.task.TaskDecorator; +import org.springframework.stereotype.Service; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author trydofor + * @since 2024-05-14 + */ +@Service +public class TestTtlDecorator implements TaskDecorator { + + public static final AtomicInteger Count = new AtomicInteger(0); + + @Override + public Runnable decorate(Runnable runnable) { + Count.incrementAndGet(); + return runnable; + } +} diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/async/AsyncHelperTest.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/async/AsyncHelperTest.java new file mode 100644 index 000000000..60d97f746 --- /dev/null +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/async/AsyncHelperTest.java @@ -0,0 +1,91 @@ +package pro.fessional.wings.slardar.async; + +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.context.SpringBootTest; +import pro.fessional.mirana.time.Sleep; +import pro.fessional.wings.slardar.app.service.TestAsyncService; +import pro.fessional.wings.slardar.app.service.TestAsyncService.AsyncType; +import pro.fessional.wings.slardar.app.service.TestTtlDecorator; +import pro.fessional.wings.slardar.context.TerminalContext; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author trydofor + * @since 2022-12-03 + */ +@Slf4j +@SpringBootTest +public class AsyncHelperTest { + + @Setter(onMethod_ = {@Autowired}) + protected TestAsyncService testAsyncService; + + @Setter(onMethod_ = {@Autowired}) + protected TestTtlDecorator testTtlDecorator; + + @Test + @TmsLink("C13122") + void testAsync() throws Exception { + final TerminalContext.Builder builder = new TerminalContext.Builder(); + final long userId = 1L; + builder.user(userId); + TerminalContext.login(builder.build()); + + CompletableFuture uid = testAsyncService.userIdAsync(AsyncType.Return); + Assertions.assertEquals(userId, uid.get()); + + final AtomicInteger eqs1 = new AtomicInteger(0); + Sleep.ignoreInterrupt(500); + // If a non-TtlThreadPoolTaskScheduler is set up, but a ttlExecutor is used. + // then only one thread will succeed in TTL, others will fail + final var task1 = AsyncHelper.Async(() -> delayUid("TaskSchedulerTest Default", userId, eqs1)); + task1.join(); + Assertions.assertEquals(1, eqs1.get(), "userid not equals, see log"); + eqs1.set(0); + + // exception + failedFuture(AsyncHelper.Async(()->testAsyncService.userId(AsyncType.FailedFuture)), TestAsyncService.UserIdFailedFuture); + failedFuture(AsyncHelper.Async(()->testAsyncService.userId(AsyncType.UncaughtException)), TestAsyncService.UserIdUncaughtException); + + Assertions.assertNotNull(testTtlDecorator); + Assertions.assertTrue(TestTtlDecorator.Count.get() > 0); + } + + private void failedFuture(CompletableFuture future, String msg) { + try { + future.get(); + Assertions.fail(); + } + catch (Exception e) { + boolean got = false; + if (e instanceof ExecutionException ee) { + if (ee.getCause() instanceof RuntimeException re) { + got = msg.equals(re.getMessage()); + } + } + Assertions.assertTrue(got); + } + } + + private void delayUid(String caller, long userId, AtomicInteger eqs) { + final String name = Thread.currentThread().getName(); + if (name.contains("exec-")) { + final long ud = TerminalContext.get().getUserId(); + log.info("{} , uid={}", caller, ud); + if (ud == userId) { + eqs.incrementAndGet(); + } + } + else { + Assertions.fail("bad thread prefix, should start with 'exec-'"); + } + } +} diff --git a/wings/slardar/src/test/java/pro/fessional/wings/slardar/async/TaskSchedulerTest.java b/wings/slardar/src/test/java/pro/fessional/wings/slardar/async/TaskSchedulerTest.java index 8597b9abd..4dd55e2e1 100644 --- a/wings/slardar/src/test/java/pro/fessional/wings/slardar/async/TaskSchedulerTest.java +++ b/wings/slardar/src/test/java/pro/fessional/wings/slardar/async/TaskSchedulerTest.java @@ -45,7 +45,7 @@ void testTask() throws Exception { builder.user(userId); TerminalContext.login(builder.build()); - CompletableFuture uid = testAsyncService.asyncUserId(AsyncType.Return); + CompletableFuture uid = testAsyncService.userIdAsync(AsyncType.Return); Assertions.assertEquals(userId, uid.get()); final AtomicInteger cnt1 = new AtomicInteger(0); @@ -56,18 +56,22 @@ void testTask() throws Exception { final ScheduledFuture task1 = threadPoolTaskScheduler.scheduleWithFixedDelay(() -> delayUid("TaskSchedulerTest Default", userId, cnt1, eqs1), Duration.ofMillis(1_000)); Sleep.ignoreInterrupt(5_000); task1.cancel(false); + Assertions.assertTrue(eqs1.get() > 0, "userid not equals, see log"); Assertions.assertEquals(cnt1.get(), eqs1.get(), "userid not equals, see log"); cnt1.set(0); eqs1.set(0); Sleep.ignoreInterrupt(500); - final ScheduledFuture task2 = threadPoolTaskScheduler.scheduleWithFixedDelay(TtlRunnable.get(() -> delayUid("TaskSchedulerTest TtlRun", userId, cnt1, eqs1), false, true), Duration.ofMillis(1_000)); + final ScheduledFuture task2 = threadPoolTaskScheduler.scheduleWithFixedDelay( + TtlRunnable.get(() -> delayUid("TaskSchedulerTest TtlRun", userId, cnt1, eqs1), + false, true), Duration.ofMillis(1_000)); Sleep.ignoreInterrupt(5_000); task2.cancel(false); + Assertions.assertTrue(eqs1.get() > 0, "userid not equals, see log"); Assertions.assertEquals(cnt1.get(), eqs1.get(), "userid not equals, see log"); // exception - failedFuture(testAsyncService.asyncUserId(AsyncType.FailedFuture), TestAsyncService.UserIdFailedFuture); - failedFuture(testAsyncService.asyncUserId(AsyncType.UncaughtException), TestAsyncService.UserIdUncaughtException); + failedFuture(testAsyncService.userIdAsync(AsyncType.FailedFuture), TestAsyncService.UserIdFailedFuture); + failedFuture(testAsyncService.userIdAsync(AsyncType.UncaughtException), TestAsyncService.UserIdUncaughtException); /* * == by default == @@ -75,7 +79,7 @@ void testTask() throws Exception { * public void pro.fessional.wings.slardar.app.service.TestAsyncService.asyncVoid(pro.fessional.wings.slardar.app.service.TestAsyncService$AsyncType) * java.lang.RuntimeException: asyncVoid UncaughtException */ - testAsyncService.asyncVoid(AsyncType.UncaughtException); + testAsyncService.voidAsync(AsyncType.UncaughtException); } private void failedFuture(CompletableFuture future, String msg) { @@ -96,7 +100,7 @@ private void failedFuture(CompletableFuture future, String msg) { private void delayUid(String caller, long userId, AtomicInteger cnt, AtomicInteger eqs) { final String name = Thread.currentThread().getName(); - if (name.startsWith("win-task-")) { + if (name.contains("task-")) { final long ud = TerminalContext.get().getUserId(); log.info("{} delay {}, uid={}", caller, cnt.incrementAndGet(), ud); if (ud == userId) { @@ -104,7 +108,7 @@ private void delayUid(String caller, long userId, AtomicInteger cnt, AtomicInteg } } else { - log.error("bad thread prefix, should start with 'win-task-'"); + Assertions.fail("bad thread prefix, should start with 'task-'"); } } } diff --git a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/errorhandle/DefaultExceptionResolver.java b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/errorhandle/DefaultExceptionResolver.java index 5b86cdf32..5c93b4160 100644 --- a/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/errorhandle/DefaultExceptionResolver.java +++ b/wings/warlock-shadow/src/main/java/pro/fessional/wings/warlock/errorhandle/DefaultExceptionResolver.java @@ -18,6 +18,7 @@ import pro.fessional.mirana.data.R; import pro.fessional.mirana.pain.CodeException; import pro.fessional.mirana.pain.HttpStatusException; +import pro.fessional.mirana.pain.MessageException; import pro.fessional.mirana.text.JsonTemplate; import pro.fessional.wings.slardar.context.LocaleZoneIdUtil; import pro.fessional.wings.slardar.context.TerminalContextException; @@ -55,8 +56,8 @@ public DefaultExceptionResolver(SimpleResponse defaultResponse, MessageSource me @Override protected SimpleResponse resolve(@NotNull Exception exception) { SimpleResponse response = null; + Throwable cause = exception; try { - Throwable cause = exception; for (; response == null && cause != null; cause = cause.getCause()) { if (cause instanceof HttpStatusException ex) { response = handle(ex); @@ -77,10 +78,14 @@ else if (cause instanceof AccessDeniedException ex) { response = handleAccessDenied(ex); } } + + if (response == null) { + cause = exception; + } // handler if (handler != null) { // use original exception if response is null, otherwise the cause - response = handler.handle(response == null ? exception : cause, response); + response = handler.handle(cause, response); } } catch (Throwable e) { @@ -92,7 +97,12 @@ else if (cause instanceof AccessDeniedException ex) { response = defaultResponse; } else { - log.debug("handled exception, response simple", exception); + if (cause instanceof MessageException) { + log.debug("handled MessageException, response simple", exception); + } + else { + log.info("handled exception, response simple", exception); + } } return response; @@ -123,7 +133,7 @@ protected SimpleResponse handle(CodeException cex) { return new SimpleResponse(defaultResponse.getHttpStatus(), defaultResponse.getContentType(), body); } - protected SimpleResponse handleUnauthorized(Exception ex) { + protected SimpleResponse handleUnauthorized(Exception ignore) { final String body = JsonTemplate.obj(obj -> { obj.putVal("success", false); String code = AuthnErrorEnum.Unauthorized.getCode(); @@ -133,7 +143,7 @@ protected SimpleResponse handleUnauthorized(Exception ex) { return new SimpleResponse(HttpStatus.UNAUTHORIZED.value(), defaultResponse.getContentType(), body); } - protected SimpleResponse handleAccessDenied(Exception ex) { + protected SimpleResponse handleAccessDenied(Exception ignore) { final String body = JsonTemplate.obj(obj -> { obj.putVal("success", false); String code = AuthzErrorEnum.AccessDenied.getCode(); 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 dd7b18a48..ca63fcd81 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 @@ -10,6 +10,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.util.AntPathMatcher; import org.springframework.web.servlet.LocaleResolver; @@ -274,6 +275,7 @@ public DefaultUserDetailsCombo defaultUserDetailsCombo(WarlockSecurityProp prop) @Bean @ConditionalWingsEnabled + @Order(WingsOrdered.Lv3Service + 10) public AuthZonePermChecker authZonePermChecker(WarlockSecurityProp prop) { log.info("WarlockShadow spring-bean authZonePermChecker"); final AuthZonePermChecker bean = new AuthZonePermChecker(); @@ -283,6 +285,7 @@ public AuthZonePermChecker authZonePermChecker(WarlockSecurityProp prop) { @Bean @ConditionalWingsEnabled + @Order(WingsOrdered.Lv3Service + 20) public AuthAppPermChecker authAppPermChecker(@Value("${spring.application.name:wings-default}") String appName, WarlockSecurityProp prop) { log.info("WarlockShadow spring-bean authAppPermChecker"); final AuthAppPermChecker bean = new AuthAppPermChecker(); @@ -416,6 +419,7 @@ public WingsAuthPageHandler wingsAuthPageHandler(ObjectProvider implements RuntimeConfService { public static final String PropHandler = "prop"; public static final String JsonHandler = "json"; @@ -61,7 +61,7 @@ public void putHandler(String type, ConversionService handler) { @Override public T getObject(String key, TypeDescriptor type) { - return selfLazy.getObjectCache(key, type); + return thisLazy.getObjectCache(key, type); } @Override @@ -133,10 +133,6 @@ public boolean newObject(String key, Object value, String comment) { return false; } - // cache self-invoke - @Setter(onMethod_ = {@Autowired, @Lazy}) - protected RuntimeConfServiceImpl selfLazy; - @Cacheable @SuppressWarnings("unchecked") public T getObjectCache(String key, TypeDescriptor type) {