在JAVA中使用LUA腳本記,javaj調用lua腳本的函數(轉)


最近在做一些奇怪的東西,需要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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM