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(转)的更多相关文章

  1. Scala For Java的一些参考

          变量 String yourPast = "Good Java Programmer"; val yourPast : String = "Good Java ...

  2. 使用Scala实现Java项目的单词计数:串行及Actor版本

    其实我想找一门“具有Python的简洁写法和融合Java平台的优势, 同时又足够有挑战性和灵活性”的编程语言. Scala 就是一个不错的选择. Scala 有很多语言特性, 建议先掌握基础常用的: ...

  3. scala调用java的方法,返回了一个对象链表List<Student>,在scala中遍历该链表获取指定Student的名字name

    假设Student类如下: class Student { private int no; private String name; public int getNo() { return no; } ...

  4. scala vs java 相同点和差异

    本贴是我摘抄自国外网站,用作备忘,也作为分享! Similarities between Scala and Java Following are some of the major similari ...

  5. Spark:用Scala和Java实现WordCount

    http://www.cnblogs.com/byrhuangqiang/p/4017725.html 为了在IDEA中编写scala,今天安装配置学习了IDEA集成开发环境.IDEA确实很优秀,学会 ...

  6. IDEA15使用maven编译scala和java

    机器安装maven,在IDEA中配置maven的home 创建项目:new-maven–> scala-archetype-simple project structure–>创建src. ...

  7. scala 与 java 之间的关系

    scala来源于java,但又高于java. scala的设计者Martin Odersky就是一个JAVA控,这位牛人设计了javac和编写了jdk中的通用代码.可以说java语言本身就是Marti ...

  8. 编写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 ...

  9. Scala IDEA for Eclipse里用maven来创建scala和java项目代码环境(图文详解)

    这篇博客 是在Scala IDEA for Eclipse里手动创建scala代码编写环境. Scala IDE for Eclipse的下载.安装和WordCount的初步使用(本地模式和集群模式) ...

随机推荐

  1. Delphi数据类型转换(有几个字符串函数没见过,比如StringToWideChar和WideCharToString)

    DateTimeToFileDate                  函数                     将DELPHI的日期格式转换为DOS的日期格式         DateTimeT ...

  2. [Android学习笔记]EditText的使用

    EditText就是我们最常用的文本输入框 常用属性见官方文档 主要是以下几个问题: 1.取消默认获取焦点 Activity启动时候会把焦点默认停留在第一个EditText控件上 一般的解决方法是在此 ...

  3. [Android学习笔记]设置Activity方向

    1.设置Activity方向 在AndroidMainfest.xml里设置Activity默认方向 <activity android:name=".myActivity" ...

  4. iOS 如何创建单例对象

    一.什么是单例? 说到单例我就想起了我的java啊 ,不禁感叹起我的大学时光,学了4年的java开发,到现在还是放弃了我的java,踏入了iOS的行列. 算了,入正轨,我现在正是铁树银花的青春美少女, ...

  5. 警惕!iPhone 6即将上市 诈骗邮件已现身网络

    随着iPhone 6即将上市,各路小道消息已经開始满天飞.就在近几日,一些记者还收到了假的iPhone 6将要上市的通知邮件.趋势科技也收到了几封,下面是样本之中的一个: (垃圾邮件样本) 不清楚iP ...

  6. jquery ajax协调SpringMVCD实现局部刷新IV

    feedback.jsp: <%@ page language="java" import="java.util.*" pageEncoding=&quo ...

  7. pan

    百度云盘地址第一季http://pan.baidu.com/share/link?shareid=198342&uk=1191518428第二季http://pan.baidu.com/sha ...

  8. POJ 2756 Autumn is a Genius 大数加减法

    Description Jiajia and Wind have a very cute daughter called Autumn. She is so clever that she can d ...

  9. myeclipse10.7皴,出口解决war包时报“SECURITY ALERT: INTEGERITY CHECK ERROR”

    一.操作系统的环境是win7,64bit和32bit的操作系统我试过都OK 依照网上一些Crack破解程序步骤操作就能够完毕破解过程, 也能够到我的CSDN资源里下载文件包 myeclipse10.7 ...

  10. hdu3974(线段树+dfs)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3974 题意:给定点的上下级关系,规定如果给i分配任务a,那么他的所有下属.都停下手上的工作,开始做a. ...