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

com.icthh.xm.commons.lep.groovy.GroovyLepEngine Maven / Gradle / Ivy

There is a newer version: 4.0.20
Show newest version
package com.icthh.xm.commons.lep.groovy;

import com.icthh.xm.commons.lep.LepPathResolver;
import com.icthh.xm.commons.lep.ProceedingLep;
import com.icthh.xm.commons.lep.api.BaseLepContext;
import com.icthh.xm.commons.lep.api.LepEngine;
import com.icthh.xm.commons.lep.api.LepKey;
import com.icthh.xm.commons.lep.groovy.storage.LepStorage;
import com.icthh.xm.commons.lep.impl.LoggingWrapper;
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.runtime.InvokerHelper;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import static com.icthh.xm.commons.lep.groovy.storage.LepStorage.FILE_EXTENSION;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

@Slf4j
public class GroovyLepEngine extends LepEngine {

    public static final String LEP_PREFIX = "lep://";
    public static final String COMMONS_SCRIPT = "/Commons$$";
    private final String tenant;
    private final LepStorage leps;
    private final GroovyScriptEngine gse;
    private final LoggingWrapper loggingWrapper;
    private final LepPathResolver lepPathResolver;
    private final List tenantCommonsFolders;

    private final Map lepMetadata = new ConcurrentHashMap<>();

    public GroovyLepEngine(String tenant,
                           LepStorage leps,
                           LoggingWrapper loggingWrapper,
                           ClassLoader classLoader,
                           Map lepMetadata,
                           LepResourceConnector lepResourceConnector,
                           LepPathResolver lepPathResolver,
                           boolean isWarmupEnabled) {
        this.tenant = tenant;
        this.leps = leps;
        this.loggingWrapper = loggingWrapper;
        this.gse = buildGroovyEngine(classLoader, lepResourceConnector);
        this.lepMetadata.putAll(lepMetadata);
        this.lepPathResolver = lepPathResolver;
        this.tenantCommonsFolders = lepPathResolver.getLepCommonsPaths(tenant);
        if (isWarmupEnabled) {
            warmupScripts();
        } else {
            log.warn("Warmup lep script for tenant {} disabled", tenant);
        }
    }

    protected GroovyScriptEngine buildGroovyEngine(ClassLoader classLoader, LepResourceConnector lepResourceConnector) {
        var gse = new GroovyScriptEngine(lepResourceConnector, classLoader);
        CompilerConfiguration config = gse.getConfig();
        config.setRecompileGroovySource(true);
        config.setMinimumRecompilationInterval(50);
        gse.setConfig(config);
        gse.getGroovyClassLoader().setShouldRecompile(true);
        return gse;
    }

    private void warmupScripts() {
        StopWatch stopWatch = StopWatch.createStarted();
        log.info("Start warmup lep scripts");
        this.leps.forEach(lep -> {
            try {
                // skip classes that can't be are entry point, and will be compiled on import from other script
                if (!isCommonsClass(lep.getPath())) {
                    if (lepMetadata.containsKey(lep.metadataKey()) && lepMetadata.get(lep.metadataKey()).isScript()) {
                        StopWatch warmUpTime = StopWatch.createStarted();
                        log.info("START | Warmup lep {}", lep.getPath());
                        Class scriptClass = gse.loadScriptByName(LEP_PREFIX + lep.getPath());
                        InvokerHelper.getMetaClass(scriptClass); // build metaclass
                        log.info("STOP | Warmup lep {}, time: {} ms", lep.getPath(), warmUpTime.getTime(MILLISECONDS));
                    }
                }
            } catch (Throwable e) {
                log.error("Error create script {}", lep.getPath(), e);
            }
        });
        log.info("Stop warm-up LEP scripts, time = {} ms, ", stopWatch.getTime(MILLISECONDS));
    }

    @Override
    public boolean isExists(LepKey lepKey) {
        List before = getBeforeKeys(lepKey);
        List main = getMainKeys(lepKey);
        return getExistingKey(before).isPresent() || getExistingKey(main).isPresent();
    }

    @Override
    @SneakyThrows
    public Object invoke(LepKey lepKey, ProceedingLep lepMethod, BaseLepContext lepContext) {
        List beforeKeys = getBeforeKeys(lepKey);
        getExistingKey(beforeKeys).ifPresent(key -> executeLep(key, lepKey, lepMethod, lepContext));

        List mainKeys = getMainKeys(lepKey);
        Optional mainKey = getExistingKey(mainKeys);
        if (mainKey.isPresent()) {
            return executeLep(mainKey.get(), lepKey, lepMethod, lepContext);
        } else {
            return lepContext.lep.proceed();
        }
    }

    @SneakyThrows
    private Object executeLep(String key, LepKey lepKey, ProceedingLep lepMethod, BaseLepContext lepContext) {
        String scriptName = LEP_PREFIX + key + FILE_EXTENSION;
        return loggingWrapper.doWithLogs(lepMethod, scriptName, lepKey, () ->
            // map HAVE TO be mutable!
            gse.run(LEP_PREFIX + key, new Binding(new HashMap<>(Map.of("lepContext", lepContext))))
        );
    }

    private List getMainKeys(LepKey lepKey) {
        String lepPath = lepPathResolver.getLepPath(lepKey, tenant);
        String legacyLepPath = lepPathResolver.getLegacyLepPath(lepKey, tenant);
        return List.of(
            legacyLepPath + "$$tenant",
            legacyLepPath + "$$around",
            lepPath + "$$tenant",
            lepPath + "$$around",
            legacyLepPath,
            lepPath
        );
    }

    private List getBeforeKeys(LepKey lepKey) {
        String lepPath = lepPathResolver.getLepPath(lepKey, tenant);
        String legacyLepPath = lepPathResolver.getLegacyLepPath(lepKey, tenant);
        return List.of(
            legacyLepPath + "$$before",
            lepPath + "$$before"
        );
    }

    private Optional getExistingKey(List keys) {
        return keys.stream().filter(leps::isExists).findFirst();
    }

    private boolean isCommonsClass(String path) {
        return !path.contains(COMMONS_SCRIPT) && tenantCommonsFolders.stream().anyMatch(path::startsWith);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy