在JAVA中使用LUA脚本记,javaj调用lua脚本的函数(转)

简介: 最近在做一些奇怪的东西,需要Java应用能够接受用户提交的脚本并执行,网络部分我选择了NanoHTTPD提供基本的HTTP服务器支持,并在Java能承载的许多脚本语言中选择了很久,比如Rhino,Jython和JRuby之类,但它们都太过庞大,并且很难实现沙盒保护服务器环境。

最近在做一些奇怪的东西,需要Java应用能够接受用户提交的脚本并执行,网络部分我选择了NanoHTTPD提供基本的HTTP服务器支持,并在Java能承载的许多脚本语言中选择了很久,比如Rhino,Jython和JRuby之类,但它们都太过庞大,并且很难实现沙盒保护服务器环境。最后我的目光投向了Lua,那个被称为粘合剂的语言。遇到的第一个难题是选择所使用的库,纯Java实现的Lua解释器有很多,什么LuaJ,LuaJava,kahlua,还有不知名的mochalua,jill等等(好多好多),其中许多解释器是纯Java实现的,LuaJava则使用了JNI,考虑再三以后我选择了LuaJ,毕竟是纯Java实现,拿来就能用的。
LuaJ也有对应JME和JSE平台的,JSE版是JME版的超集,还带有LuaJava里的luajava模块,能够直接在.lua中调用Java方法,创建Java实例,是很方便的。
折腾了几天,觉得对LuaJ也有足够的了解了,于是把一些相关的代码整理如下:

1
2
3
4
5
6
7
8
9
// 创建一个Lua执行的全局环境。
LuaValue global = JsePlatform.debugGlobals();   // 获得loadstring变量,这个变量存储了一个方法,相当于JavaScript里的eval。 LuaValue loadstring = global.get("loadstring"); // 第一个call()方法是调用loadstring这个方法,其参数中使用了LueValue.valueOf()这个静态方法把Java的数据封装成Lua能够使用的数据,第二个call()方法是执行字符串中的表达式,结果是输出了“Hello world!”。 loadstring.call(LuaValue.valueOf("print('Hello world!')")).call(); // 与之类似的还有loadfile,不过它的作用是接受一个文件路径,读入这个文件的内容,执行时调用call。 global.get("loadfile").call("./test.lua").call();

LuaJ直到代码运行结束前都会阻塞线程,这时候开启一个新的线程专门运行即可,但坑爹的是LuaJ运行以后无法中断(即使你中断了它所在的线程),比如你的.lua中有一个while true do end循环,那么你将永远无法中断它,除非退出你的整个Java应用…
怎么样,有没有很坑爹?我谷歌了大半天,发现LuaJ好像是没有官方的解决方案的(同时讨论这类东西的少得可怜!)…我也曾迁移代码到LuaJava上,发现调用了L.close()方法也是不能中断执行,最后终于抓住了一根救命稻草。
这根稻草来自ComputerCraft,一个在MineCraft中模拟计算机的模组,也是使用的LuaJ,但是却能中断一段代码的执行,于是我用jd-gui查看了它的源代码,最终有效实现了LuaJ的执行中中断。

首先容我介绍一下Lua中的一些自带的方法:
debug.sethook()方法能够精确到每一个函数设置钩子回调,这个回调里可以做任何想要做的事情;
coroutine.create()方法能够创建一个协同线程,
coroutine.yield()方法能够暂停这个协同线程(这正是我们想要的),
coroutine.resume()方法用来恢复这个协同线程。
接下来看代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package net.airtheva;
 
import java.io.File;   import org.luaj.vm2.LuaThread; import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.OneArgFunction; import org.luaj.vm2.lib.ZeroArgFunction; import org.luaj.vm2.lib.jse.JsePlatform;   public class LuaWorker {     class _Worker implements Runnable {       @Override     public void run() {             mIsStopping = false;       mIsStopped = false;             // 产生协同线程。       mLuaThread = mCoroutineCreate.call(mLoadString.call(LuaValue.valueOf("while true do print('!') end")));             // 执行协同线程(该线程将被阻塞)。       mCoroutineResume.call(mLuaThread);           }       }     Thread mThread;     LuaValue mGlobal;     LuaValue mLoadString;     LuaValue mDebugSetHook;     LuaValue mNativeCoroutineCreate;   LuaValue mCoroutineCreate;   LuaValue mCoroutineYield;   LuaValue mCoroutineResume;     LuaValue mLuaThread;     boolean mIsStopping = true;   boolean mIsStopped = true;     public LuaWorker() {         mGlobal = JsePlatform.debugGlobals();         mLoadString = mGlobal.get("loadstring");         mDebugSetHook = mGlobal.get("debug").get("sethook");         LuaValue coroutine = mGlobal.get("coroutine");     mNativeCoroutineCreate = coroutine.get("create");     coroutine.set("create", new OneArgFunction() {             @Override       public LuaValue call(LuaValue value) {         Debug.L("Called.");         LuaThread thread = mNativeCoroutineCreate.call(value).checkthread();         mDebugSetHook.invoke(new LuaValue[] {             thread,             new OneArgFunction() {               @Override               public LuaValue call(LuaValue value) {                 if(mIsStopping) {                   //LuaThread.yield(LuaValue.NIL);                   mCoroutineYield.call(); // 暂停本线程,上面那行也能起到一样的效果。                   mIsStopped = true;                 }                 return LuaValue.NIL;               }             },             LuaValue.valueOf("crl"), // 这里ComputerCraft用的是LuaValue.NIL,但我这边好像停不下来…             LuaValue.valueOf(100000) // 这个100000是照着抄的,其实我不知道这是啥意思,等深入使用Lua了应该就会知道了。         });         return thread;       }           });         mCoroutineCreate = coroutine.get("create");     mCoroutineYield = coroutine.get("yield");     mCoroutineResume = coroutine.get("resume");       }     public void Start() {         mThread = new Thread(new _Worker());     mThread.start();       }     public void Stop() {         // 可能回收没做好。         mIsStopping = true;     mThread.interrupt();     mThread = null;       }   }

然后捣鼓LuaJava的时候发现在eclipse中能够正常运行,导出成.jar以后LuaJava工作不正常,折腾了两天后终于发现原来是编码问题,如果你也出现了问题可以试着指定-Dfile.encoding=UTF-8(坑爹的Windows)。但是因为LuaJava也不知道怎么才能停止,而且它的接口并没有这么丰富,所以最后还是回到了LuaJ的怀抱,这里只是记录一下(毕竟坑了我两天!)。

 

http://airtheva.net/wordpress/?p=159

相关文章
|
1月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
61 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
1月前
lua面向对象(类)和lua协同线程与协同函数、Lua文件I/O
Lua的面向对象编程、协同线程与协同函数的概念和使用,以及Lua文件I/O操作的基本方法。
32 4
lua面向对象(类)和lua协同线程与协同函数、Lua文件I/O
|
1月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
27 1
|
1月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
40 1
|
2月前
|
Java
java基础(11)函数重载以及函数递归求和
Java支持函数重载,即在同一个类中可以声明多个同名方法,只要它们的参数类型和个数不同。函数重载与修饰符、返回值无关,但与参数的类型、个数、顺序有关。此外,文中还展示了如何使用递归方法`sum`来计算两个数之间的和,递归的终止条件是当第一个参数大于第二个参数时。
32 1
java基础(11)函数重载以及函数递归求和
|
1月前
|
Java 编译器 C语言
【一步一步了解Java系列】:Java中的方法对标C语言中的函数
【一步一步了解Java系列】:Java中的方法对标C语言中的函数
22 3
|
1月前
|
缓存 分布式计算 NoSQL
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
31 2
|
1月前
|
数据可视化 开发者 索引
详解Wireshark LUA插件函数:function p_myproto.dissector(buffer, pinfo, tree)
在 Wireshark 中,LUA 插件通过 `function p_myproto.dissector(buffer, pinfo, tree)` 扩展协议解析能力,解析自定义应用层协议。参数 `buffer` 是 `PacketBuffer` 类型,表示原始数据包内容;`pinfo` 是 `ProtoInfo` 类型,包含数据包元信息(如 IP 地址、协议类型等);`tree` 是
72 1
|
1月前
|
Java Python
如何通过Java程序调用python脚本
如何通过Java程序调用python脚本
31 0
|
1月前
|
SQL Java 数据库连接
如何在 Java 脚本中有效地使用 JDBC
如何在 Java 脚本中有效地使用 JDBC
17 0
下一篇
无影云桌面