diff --git a/gradle/version.gradle b/gradle/version.gradle index e8c4f64c..8bf62e2d 100644 --- a/gradle/version.gradle +++ b/gradle/version.gradle @@ -1,6 +1,6 @@ import java.util.regex.Pattern -version = "3.2.0"//detectSemVersion() +version = "3.2.1"//detectSemVersion() logger.lifecycle("Project version: $version") String detectSemVersion() { diff --git a/xm-commons-lep-groovy/src/test/java/com/icthh/xm/commons/lep/spring/LepIntTest.java b/xm-commons-lep-groovy/src/test/java/com/icthh/xm/commons/lep/spring/LepIntTest.java new file mode 100644 index 00000000..93455bf2 --- /dev/null +++ b/xm-commons-lep-groovy/src/test/java/com/icthh/xm/commons/lep/spring/LepIntTest.java @@ -0,0 +1,59 @@ +package com.icthh.xm.commons.lep.spring; + +import com.icthh.xm.commons.lep.XmLepScriptConfigServerResourceLoader; +import com.icthh.xm.commons.lep.api.LepManagementService; +import com.icthh.xm.commons.security.spring.config.XmAuthenticationContextConfiguration; +import com.icthh.xm.commons.tenant.TenantContextHolder; +import com.icthh.xm.commons.tenant.TenantContextUtils; +import com.icthh.xm.commons.tenant.spring.config.TenantContextConfiguration; +import lombok.SneakyThrows; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Map; + +import static com.icthh.xm.commons.lep.spring.DynamicLepClassResolveIntTest.loadFile; +import static org.junit.Assert.assertEquals; + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = { + DynamicLepTestConfig.class, + TenantContextConfiguration.class, + XmAuthenticationContextConfiguration.class +}) +@ActiveProfiles("resolveclasstest") +public class LepIntTest { + + @Autowired + private LepManagementService lepManagerService; + + @Autowired + private TenantContextHolder tenantContextHolder; + + @Autowired + private DynamicTestLepService testLepService; + + @Autowired + private XmLepScriptConfigServerResourceLoader resourceLoader; + + @Before + public void init() { + TenantContextUtils.setTenant(tenantContextHolder, "TEST"); + lepManagerService.beginThreadContext(); + } + + @Test + @SneakyThrows + public void testLepContextCastToMap() { + String code = loadFile("lep/TestLepContextCastToMap.groovy"); + resourceLoader.onRefresh("/config/tenants/TEST/testApp/lep/service/TestLepMethodWithInput$$around.groovy", code); + String result = testLepService.testLepMethod(Map.of("parameter", "testValue")); + assertEquals("testValue", result); + } + +} diff --git a/xm-commons-lep-groovy/src/test/resources/lep/TestLepContextCastToMap.groovy b/xm-commons-lep-groovy/src/test/resources/lep/TestLepContextCastToMap.groovy new file mode 100644 index 00000000..e6ff9c16 --- /dev/null +++ b/xm-commons-lep-groovy/src/test/resources/lep/TestLepContextCastToMap.groovy @@ -0,0 +1,12 @@ +import groovy.util.logging.Slf4j + +@Slf4j +class Service { + Map lepContext + + public Service(Map lepContext) { + log.info("lepContext.inArgs.input.parameter {}", lepContext.inArgs.input.parameter) + this.lepContext = lepContext + } +} +return new Service(lepContext).lepContext.inArgs.input.parameter diff --git a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/api/BaseLepContext.java b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/api/BaseLepContext.java index 3decc78c..233e6b9f 100644 --- a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/api/BaseLepContext.java +++ b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/api/BaseLepContext.java @@ -5,11 +5,17 @@ import com.icthh.xm.commons.lep.spring.lepservice.LepServiceFactory; import com.icthh.xm.commons.security.XmAuthenticationContext; import com.icthh.xm.commons.tenant.TenantContext; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.experimental.Delegate; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; -public abstract class BaseLepContext { +import static java.util.Optional.ofNullable; + +public abstract class BaseLepContext implements Map { public Object commons; public Object inArgs; @@ -22,14 +28,30 @@ public abstract class BaseLepContext { public Object methodResult; public LepServiceFactory lepServices; - private Map additionalContext = new HashMap<>(); + private transient Map additionalContext = new HashMap<>(); + @Setter + private transient LepContextMapSupport mapSupport; + @Delegate(excludes = ExcludedMethods.class) + private transient Map emptyMap = Map.of(); + + public final Object get(Object fieldName) { + if (mapSupport == null) { // fallback to reflection. to simplify groovy tests + return ofNullable(additionalContext.get(fieldName)).orElseGet(() -> getFieldValue(fieldName)); + } + return ofNullable(mapSupport.get(String.valueOf(fieldName), this)).orElse(additionalContext.get(fieldName)); + } - public final Object get(String additionalContextKey) { - return additionalContext.get(additionalContextKey); + @SneakyThrows + private Object getFieldValue(Object fieldName) { + return this.getClass().getField(String.valueOf(fieldName)).get(this); } public final void addAdditionalContext(String additionalContextKey, Object additionalContextValue) { additionalContext.put(additionalContextKey, additionalContextValue); } + private interface ExcludedMethods { + Object get(Object key); + } + } diff --git a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/api/LepContextMapSupport.java b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/api/LepContextMapSupport.java new file mode 100644 index 00000000..474b831f --- /dev/null +++ b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/api/LepContextMapSupport.java @@ -0,0 +1,58 @@ +package com.icthh.xm.commons.lep.api; + +import com.icthh.xm.commons.logging.util.BasePackageDetector; +import lombok.SneakyThrows; +import org.reflections.Reflections; +import org.springframework.stereotype.Component; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.function.Predicate.not; + +public class LepContextMapSupport { + private final Map, Map> classFieldHandles = new HashMap<>(); + + public LepContextMapSupport(BasePackageDetector basePackageDetector) { + this(basePackageDetector.getBasePackage()); + } + + public LepContextMapSupport(String basePackage) { + Reflections reflections = new Reflections(basePackage); + Set> classes = reflections.getSubTypesOf(BaseLepContext.class); + initializeVarHandles(classes); + } + + @SneakyThrows + private void initializeVarHandles(Set> classes) { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + for (Class type : classes) { + Map fieldHandles = new HashMap<>(); + for (Field field : getFields(type)) { + VarHandle handle = lookup.unreflectVarHandle(field); + fieldHandles.put(field.getName(), handle); + } + classFieldHandles.put(type, fieldHandles); + } + } + + private List getFields(Class type) { + return Stream.of(type.getFields()) + .filter(not(Field::isSynthetic)) + .filter(it -> !Modifier.isStatic(it.getModifiers())) + .collect(Collectors.toList()); + } + + public Object get(String fieldName, BaseLepContext instance) { + VarHandle handle = classFieldHandles.get(instance.getClass()).get(fieldName); + return handle == null ? null : handle.get(instance); + } +} diff --git a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/impl/utils/ClassPathLepRepository.java b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/impl/utils/ClassPathLepRepository.java index 08a4d474..a4dc4b36 100644 --- a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/impl/utils/ClassPathLepRepository.java +++ b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/impl/utils/ClassPathLepRepository.java @@ -1,13 +1,9 @@ package com.icthh.xm.commons.lep.impl.utils; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; import com.icthh.xm.commons.lep.api.XmLepConfigFile; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.reflections.Reflections; -import org.reflections.scanners.ResourcesScanner; -import org.reflections.scanners.Scanner; import org.reflections.scanners.Scanners; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -15,7 +11,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; diff --git a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepContextService.java b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepContextService.java index e89c49e1..26a6168f 100644 --- a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepContextService.java +++ b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepContextService.java @@ -4,6 +4,7 @@ import com.icthh.xm.commons.lep.api.BaseLepContext; import com.icthh.xm.commons.lep.api.LepAdditionalContext; import com.icthh.xm.commons.lep.api.LepContextFactory; +import com.icthh.xm.commons.lep.api.LepContextMapSupport; import com.icthh.xm.commons.lep.api.LepEngine; import com.icthh.xm.commons.lep.commons.CommonsExecutor; import com.icthh.xm.commons.lep.commons.CommonsService; @@ -28,6 +29,7 @@ public class LepContextService { private final XmAuthenticationContextHolder xmAuthContextHolder; private final List> additionalContexts; private final CommonsService commonsService; + private final LepContextMapSupport lepContextMapSupport; public final BaseLepContext createLepContext(LepEngine lepEngine, TargetProceedingLep lepMethod) { BaseLepContext baseLepContext = lepContextFactory.buildLepContext(lepMethod); @@ -37,6 +39,7 @@ public final BaseLepContext createLepContext(LepEngine lepEngine, TargetProceedi baseLepContext.tenantContext = tenantContextHolder.getContext(); baseLepContext.authContext = xmAuthContextHolder.getContext(); baseLepContext.commons = new CommonsExecutor(commonsService); + baseLepContext.setMapSupport(lepContextMapSupport); additionalContexts.forEach(context -> baseLepContext.addAdditionalContext(context.additionalContextKey(), context.additionalContextValue())); baseLepContext.lepServices = new LepServiceFactoryImpl(lepEngine.getId(), lepServiceFactory); diff --git a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepSpringConfiguration.java b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepSpringConfiguration.java index bdeff83d..c1d355ea 100644 --- a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepSpringConfiguration.java +++ b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepSpringConfiguration.java @@ -7,6 +7,7 @@ import com.icthh.xm.commons.lep.api.BaseLepContext; import com.icthh.xm.commons.lep.api.LepAdditionalContext; import com.icthh.xm.commons.lep.api.LepContextFactory; +import com.icthh.xm.commons.lep.api.LepContextMapSupport; import com.icthh.xm.commons.lep.api.LepEngine; import com.icthh.xm.commons.lep.api.LepEngineFactory; import com.icthh.xm.commons.lep.api.LepManagementService; @@ -23,6 +24,7 @@ import com.icthh.xm.commons.lep.spring.lepservice.LepServiceFactoryWithLepFactoryMethod; import com.icthh.xm.commons.lep.spring.web.LepInterceptor; import com.icthh.xm.commons.logging.config.LoggingConfigService; +import com.icthh.xm.commons.logging.util.BasePackageDetector; import com.icthh.xm.commons.security.XmAuthenticationContextHolder; import com.icthh.xm.commons.tenant.TenantContextHolder; import com.icthh.xm.lep.api.LepKeyResolver; @@ -130,6 +132,17 @@ public LepServiceFactoryResolver lepServiceFactoryResolver() { return new LepServiceFactoryResolver(); } + @Bean + public LepContextMapSupport lepContextMapSupport(BasePackageDetector basePackageDetector) { + return new LepContextMapSupport(basePackageDetector); + } + + @Bean + @ConditionalOnMissingBean + public BasePackageDetector basePackageDetector(ApplicationContext applicationContext) { + return new BasePackageDetector(applicationContext); + } + @Bean public LepContextService lepContextService(LepContextFactory lepContextFactory, LepServiceFactoryWithLepFactoryMethod lepServiceFactory, @@ -137,7 +150,8 @@ public LepContextService lepContextService(LepContextFactory lepContextFactory, TenantContextHolder tenantContextHolder, XmAuthenticationContextHolder xmAuthContextHolder, List> additionalContexts, - CommonsService commonsService) { + CommonsService commonsService, + LepContextMapSupport lepContextMapSupport) { return new LepContextService( lepContextFactory, lepServiceFactory, @@ -145,7 +159,8 @@ public LepContextService lepContextService(LepContextFactory lepContextFactory, tenantContextHolder, xmAuthContextHolder, additionalContexts, - commonsService + commonsService, + lepContextMapSupport ); } diff --git a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepThreadHelper.java b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepThreadHelper.java index c0a1ac30..8faae727 100644 --- a/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepThreadHelper.java +++ b/xm-commons-lep/src/main/java/com/icthh/xm/commons/lep/spring/LepThreadHelper.java @@ -10,10 +10,8 @@ import org.springframework.security.core.context.SecurityContextHolder; import java.util.concurrent.Callable; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import java.util.function.Supplier; public class LepThreadHelper {