feature #I9H6GN 支持 kotlin 脚本语言

This commit is contained in:
Dale Lee 2024-05-02 15:33:16 +08:00
parent 252c81dd15
commit 33f05eb7cc
16 changed files with 452 additions and 1 deletions

View File

@ -8,7 +8,8 @@ public enum ScriptTypeEnum {
PYTHON("python", "python"),
LUA("luaj", "lua"),
AVIATOR("AviatorScript", "aviator"),
JAVA("java", "java");
JAVA("java", "java"),
KOTLIN("kotlin", "kotlin");
private String engineName;

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>liteflow-script-plugin</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liteflow-script-kotlin</artifactId>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-core</artifactId>
<version>${revision}</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-scripting-jsr223</artifactId>
<version>1.9.23</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,15 @@
package com.yomahub.liteflow.script.kotlin;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.jsr223.JSR223ScriptExecutor;
/**
* Kotlin脚本执行器
* @author DaleLee
*/
public class KotlinScriptExecutor extends JSR223ScriptExecutor {
@Override
public ScriptTypeEnum scriptType() {
return ScriptTypeEnum.KOTLIN;
}
}

View File

@ -0,0 +1,2 @@
# Kotlin的实现
com.yomahub.liteflow.script.kotlin.KotlinScriptExecutor

View File

@ -23,6 +23,7 @@
<module>liteflow-script-lua</module>
<module>liteflow-script-aviator</module>
<module>liteflow-script-java</module>
<module>liteflow-script-kotlin</module>
</modules>
</project>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>liteflow-testcase-el</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liteflow-testcase-el-script-kotlin-springboot</artifactId>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-kotlin</artifactId>
<version>2.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,24 @@
package com.yomahub.liteflow.test.script;
import com.yomahub.liteflow.core.FlowInitHook;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.spi.holder.SpiFactoryCleaner;
import com.yomahub.liteflow.spring.ComponentScanner;
import com.yomahub.liteflow.thread.ExecutorHelper;
import org.junit.jupiter.api.AfterAll;
public class BaseTest {
@AfterAll
public static void cleanScanCache() {
ComponentScanner.cleanCache();
FlowBus.cleanCache();
ExecutorHelper.loadInstance().clearExecutorServiceMap();
SpiFactoryCleaner.clean();
LiteflowConfigGetter.clean();
FlowInitHook.cleanHook();
FlowBus.clearStat();
}
}

View File

@ -0,0 +1,93 @@
package com.yomahub.liteflow.test.script.kotlin;
import com.yomahub.liteflow.slot.DefaultContext;
import org.junit.jupiter.api.Test;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
public class T2 {
@Test
public void test1() throws ScriptException {
// 初始化 ScriptEngineManager Kotlin 脚本引擎
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("kotlin");
if (engine == null) {
throw new IllegalStateException("Kotlin script engine not found");
}
// 假设我们有以下Kotlin脚本它期望一个名为'message'的外部参数
String script = "fun greet(name: String) {\n" +
" println(\"Hello, $name!\")\n" +
" }\n" +
" println(bindings[\"message\"])";
// 编译脚本为CompiledScript对象
Compilable compilable = (Compilable) engine;
CompiledScript compiledScript = compilable.compile(script);
// 准备脚本上下文用于传递外部参数
Bindings bindings = engine.createBindings();
// 设置外部参数
bindings.put("message", "User");
// 使用相同的上下文多次执行已编译的脚本
for (int i = 0; i < 2; i++) {
compiledScript.eval(bindings);
}
// engine.put("message","User");
// engine.eval(script);
//engine.eval(script,bindings);
}
@Test
public void test2() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("kotlin");
String script = "" +
"var defaultContext = bindings[\"defaultContext\"]\n" +
"println(defaultContext.getData(\"key\"))";
// 编译脚本为CompiledScript对象
Compilable compilable = (Compilable) engine;
CompiledScript compiledScript = compilable.compile(script);
// 准备脚本上下文用于传递外部参数
Bindings bindings = new SimpleBindings();
DefaultContext context = new DefaultContext();
context.setData("key", "value");
// 设置外部参数
bindings.put("defaultContext", context);
compiledScript.eval(bindings);
}
@Test
public void test3() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("kotlin");
String script =
"fun getNum() = 15\n" +
"getNum()";
// 编译脚本为CompiledScript对象
Compilable compilable = (Compilable) engine;
CompiledScript compiledScript = compilable.compile(script);
// 准备脚本上下文用于传递外部参数
// Bindings bindings = new SimpleBindings();
// DefaultContext context = new DefaultContext();
// context.setData("key", "value");
// // 设置外部参数
// bindings.put("defaultContext", context);
/* Object res = compiledScript.eval();
System.out.println(res);*/
Object eval = engine.eval(script);
System.out.println(eval);
}
}

View File

@ -0,0 +1,49 @@
package com.yomahub.liteflow.test.script.kotlin;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class TestKotlin {
public static void main(String[] args) throws ScriptException, NoSuchMethodException {
// 获取脚本引擎管理器
ScriptEngineManager manager = new ScriptEngineManager();
// 获取Kotlin脚本引擎
ScriptEngine engine = manager.getEngineByName(ScriptTypeEnum.KOTLIN.getEngineName()); // ".kts" 是Kotlin脚本文件的扩展名
// 检查是否找到了Kotlin脚本引擎
if (engine == null) {
System.out.println("No Kotlin script engine found.");
return;
}
System.out.println(engine instanceof Compilable);
// 定义一个Kotlin脚本
String script = "println(\"Hello, Kotlin JSR 223!\")";
Compilable compilable = (Compilable) engine;
CompiledScript compile = compilable.compile(script);
compile.eval();
// 编译并执行脚本
engine.eval(script);
// 如果ScriptEngine也实现了Invocable接口我们可以调用脚本中的函数
// if (engine instanceof Invocable) {
// Invocable inv = (Invocable) engine;
//
// // 调用脚本中的greet函数
// String greeting = (String) inv.invokeFunction("greet", "World");
// System.out.println(greeting); // 输出: Hello, World!
// } else {
// System.out.println("The script engine does not support Invocable interface.");
// }
}
}

View File

@ -0,0 +1,68 @@
package com.yomahub.liteflow.test.script.kotlin.common;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.script.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/common/application.properties")
@SpringBootTest(classes = LiteFlowKotlinScriptCommonELTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.script.kotlin.common.cmp"})
public class LiteFlowKotlinScriptCommonELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testCommon1() {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals(Integer.valueOf(5), context.getData("s1"));
}
@Test
public void testFor1() {
DefaultContext context = new DefaultContext();
context.setData("k1", 1);
context.setData("k2", 2);
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg", context);
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s2==>a==>a==>a",response.getExecuteStepStr());
}
@Test
public void testIf1() {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s3==>b",response.getExecuteStepStr());
}
@Test
public void testIf2() {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s4",response.getExecuteStepStr());
}
@Test
public void testSwitch1() {
DefaultContext context = new DefaultContext();
context.setData("id", "c");
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg", context);
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s5==>c",response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.kotlin.common.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.kotlin.common.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.kotlin.common.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1 @@
liteflow.rule-source=common/flow.xml

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<nodes>
<node id="s1" type="script" language="kotlin">
<![CDATA[
import com.yomahub.liteflow.slot.DefaultContext
fun sum(a: Int, b: Int) = a + b
var a = 2
var b = 3
val defaultContext = bindings["defaultContext"] as DefaultContext
defaultContext.setData("s1", sum(a, b))
println("Hello Kotlin!")
]]>
</node>
<node id="s2" type="for_script" language="kotlin">
<![CDATA[
import com.yomahub.liteflow.slot.DefaultContext
fun getCount(): Int {
val ctx = bindings["defaultContext"] as DefaultContext
var n1 = ctx.getData("k1") as Int;
var n2 = ctx.getData("k2") as Int;
return n1 + n2;
}
getCount()
]]>
</node>
<node id="s3" type="boolean_script" language="kotlin">
<![CDATA[
fun getBoolean1() = 2 > 1
getBoolean1()
]]>
</node>
<node id="s4" type="boolean_script" language="kotlin">
<![CDATA[
fun getBoolean2() = 2 < 1
getBoolean2()
]]>
</node>
<node id="s5" type="switch_script" language="kotlin">
<![CDATA[
import com.yomahub.liteflow.slot.DefaultContext
fun getId(ctx: DefaultContext) : String {
return ctx.getData("id") as String
}
getId(bindings["defaultContext"] as DefaultContext)
]]>
</node>
</nodes>
<chain name="chain1">
THEN(a, b, c, s1);
</chain>
<chain name="chain2">
FOR(s2).DO(a);
</chain>
<chain name="chain3">
IF(s3, b);
</chain>
<chain name="chain4">
IF(s4, b);
</chain>
<chain name="chain5">
SWITCH(s5).TO(a, b, c);
</chain>
</flow>

View File

@ -39,6 +39,7 @@
<module>liteflow-testcase-el-script-java-springboot</module>
<module>liteflow-testcase-el-builder</module>
<module>liteflow-testcase-el-routechain</module>
<module>liteflow-testcase-el-script-kotlin-springboot</module>
</modules>
<build>