在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也有足夠的瞭解了,因而把一些相關的代碼整理以下:html

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的執行中中斷。java

首先容我介紹一下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=159app

相關文章
相關標籤/搜索