Embedding Lua, in Scala, using Java(转)
LuaJ
I was reading over the list of features that CurioDB lacks compared to Redis , that I’d previously documented. It contained the item “no Lua scripting”, which I’d written confidently at the time, certain that I wouldn’t possibly be able to implement something so serious in my toy project. But then I thought to myself, “why not?”, and after a bit of research, I discovered the fantastic LuaJ library , which transformed the task from a significant engineering feat, into yet another case of merely gluing some libraries together.
LuaJ provides a complete API for compiling and running Lua scripts, and exposing JVM code to them as Lua functions. LuaJ also makes a complete range of Lua data types available to your JVM code. I purposely refer to the JVM ( Java Virtual Machine ) here rather than Java the language, as in my case, it’s actually Scala being used to code against LuaJ, which makes the overall accomplishment seem even more death-defying.
Obligatory Blog Snippets
Let’s take a look. First up, we’ll compile and run a snippet of Lua (sans imports and class scaffolding, which you can pick up from the final result in CurioDB if you wish):
// The global namespace we'll run the script in, which we could preload
// variables into if needed.
val globals = JsePlatform.standardGlobals()
// A tiny string of Lua code - it returns the second string from a
// table called "names" (Lua tables use 1-based indexes).
val script = "names = {'foo', 'bar', 'baz'}; return names[2]"
// Running the script returns a LuaValue object, which from our Lua
// code above, we know to be a string, so we cast it to one and print
// the value, which should be "bar".
val result = globals.load(script).call()
println(result.asjstring)
As simple as that. Now let’s look at passing Lua variables from JVM land into a Lua script, and pulling Lua variables back out as JVM objects:
// The same table declared above in Lua, but this time in Scala.
val names = LuaValue.listOf(Array("foo", "bar", "baz").map(LuaValue.valueOf))
// Create the global namespace again, and pass the Lua table into it.
val globals = JsePlatform.standardGlobals()
globals.set("names", names)
// Our Lua code is inline here, and just returns the variable "names".
val result = globals.load("return names").call()
// The result is our original table back in Scala, where we can access
// items by index, just as we previously did in Lua.
println(result.get(2).asjstring)
There you have it - JVM objects representing Lua variables, moving into and back out of Lua code. Now let’s look at the reverse, calling JVM code from inside Lua. To do this, we implement a class representing a Lua function:
// Simple function that takes a string, and prefixes it with "Hello ".
class SayHelloFunction extends OneArgFunction {
override def call(name: LuaValue) =
LuaValue.valueOf("Hello " + name.tojstring)
}
// Again, build the global namespace, this time adding the function
// object to it with a given name, and calling it from within Lua.
val globals = JsePlatform.standardGlobals()
globals.set("say_hello", new SayHelloFunction())
val result = globals.load("return say_hello('grandma')").call()
println(result.asjstring) // prints "Hello grandma".
That’s it, the full round trip - Lua calling JVM code, and vice versa. With that working, the rest is up to your imagination. I’ve only scratched the surface of what LuaJ provides - all of the data types found in Lua, its standard library, and much more.
The final result was a little more involved than the above implies. CurioDB is carefully designed to be non-blocking, using tell rather than ask , where actors only ever send messages forwards, without expectation of a reply. The challenge here was introducing the synchronous redis.call
API into a fundamentally asynchronous system. The solution involved modelling the scripting API as a client actor, with a controlled amount of blocking, much like the way TCP and HTTP connections are managed in the system.
Call-ception
A really fun and whacky side effect of implementing this possibly a little too correctly(for lack of a better term), is that it unintentionally allows the Lua API to recursively call itself:
$ redis-cli EVAL "return redis.call('EVAL', 'return redis.call(\'EVAL\', \'return redis.call(\\\\\'TIME\\\\\')\', 0)', 0)" 0
1) (integer) 227734
2) (integer) 541653
Is this a feature, or a bug? The scripting API in Redis specifically disallows this, most likely for good reason.
http://www.tuicool.com/articles/M36byiy
http://stackoverflow.com/questions/32573748/multiple-return-values-in-luaj
http://levelup.sinaapp.com/
http://www.luaj.org/luaj/3.0/README.html
http://luaj.org/luaj/3.0/api/org/luaj/vm2/LuaValue.html
http://stackoverflow.com/questions/12358047/how-can-i-pass-objects-to-an-exposed-luaj-function
Embedding Lua, in Scala, using Java(转)的更多相关文章
- Scala For Java的一些参考
变量 String yourPast = "Good Java Programmer"; val yourPast : String = "Good Java ...
- 使用Scala实现Java项目的单词计数:串行及Actor版本
其实我想找一门“具有Python的简洁写法和融合Java平台的优势, 同时又足够有挑战性和灵活性”的编程语言. Scala 就是一个不错的选择. Scala 有很多语言特性, 建议先掌握基础常用的: ...
- scala调用java的方法,返回了一个对象链表List<Student>,在scala中遍历该链表获取指定Student的名字name
假设Student类如下: class Student { private int no; private String name; public int getNo() { return no; } ...
- scala vs java 相同点和差异
本贴是我摘抄自国外网站,用作备忘,也作为分享! Similarities between Scala and Java Following are some of the major similari ...
- Spark:用Scala和Java实现WordCount
http://www.cnblogs.com/byrhuangqiang/p/4017725.html 为了在IDEA中编写scala,今天安装配置学习了IDEA集成开发环境.IDEA确实很优秀,学会 ...
- IDEA15使用maven编译scala和java
机器安装maven,在IDEA中配置maven的home 创建项目:new-maven–> scala-archetype-simple project structure–>创建src. ...
- scala 与 java 之间的关系
scala来源于java,但又高于java. scala的设计者Martin Odersky就是一个JAVA控,这位牛人设计了javac和编写了jdk中的通用代码.可以说java语言本身就是Marti ...
- 编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本]
编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本] 1. 开发环境 Jdk 1.7.0_72 Maven 3.2.1 Scala 2.10.6 Spark 1.6 ...
- Scala IDEA for Eclipse里用maven来创建scala和java项目代码环境(图文详解)
这篇博客 是在Scala IDEA for Eclipse里手动创建scala代码编写环境. Scala IDE for Eclipse的下载.安装和WordCount的初步使用(本地模式和集群模式) ...
随机推荐
- 广东省-IT公司红黑榜排名
红榜Top100 Order Company Name Point Change 1 百富计算机技术(深圳)有限公司 94.00 -- 2 中国网通广州分公司 88.00 -- 3 深圳市汇 ...
- fck编辑器的使用
FCK编辑器的使用 注意:编辑器有浏览器缓存,所以修改配置后,一定要删一下缓存 这个编辑器是采用 html+javascript 开发出来的 通常作为插件来使用: 1,下载插件包 2,解压,加压之后看 ...
- LAN路由
一.实验的目的: 实现不同子网之前的信息交流 二.如果 1.虚拟子网 VMnet8:192.168.233.0/24 VMnet1:172.16.1.0/24 2.虚拟机vm1 ip:1 ...
- 什么是Java “实例化”
实例化:对象也是引用数据类型,只能使用new运算符从堆中分配内存: 使用已经定义好的类,创建该类对象的过程称为“实例化”. 只有先实例化类的对象,才可以访问到类中的成员(属性和方法). 使用成员运算符 ...
- GNU libmicrohttpd 0.9.29 发布 - 开源中国社区
GNU libmicrohttpd 0.9.29 发布 - 开源中国社区 GNU libmicrohttpd 0.9.29 发布
- 图像处理特征不变算子系列之Moravec算子(一)
论文转载请注明出处:http://blog.csdn.net/kezunhai 1977年,Moravec提出了兴趣点(Points of Interests)的概念,并应用于解决Stanford C ...
- MATLAB——scatter的简单应用
scatter可用于描绘散点图. 1.scatter(X,Y) X和Y是数据向量,以X中数据为横坐标,以Y中数据位纵坐标描绘散点图,点的形状默认使用圈. 样例: X = [1:10]; Y = X ...
- DecimalFormat
public class TestDemo { public static void main(String[] args) { String format = new DecimalFormat(& ...
- Android.mk编译.apk .so .jar .a第三方.apk .so .jar .a的方法
一.编译一个简单的APK LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Build all java files in the java s ...
- SE 2014年5月27日
R1模拟总部,R2 与R3模拟分部 如图配置 要求使用 GRE over IPSec VPN 主模式,启用动态路由协议rip使得总部与两分部内网可相互通讯,但要求分部用户数据流不允许互通! 步骤: 1 ...