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默认窗体随想

    Delphi中新建一个Form或者Frame时,它的字体都是西文习惯,这样就有可能造成在其他机器上由于字体的原因,窗体十分不美观.怎样才能为Delphi设置一个默认窗体,让它的字体Font符合中国习惯 ...

  2. Delphi 数据类型列表 good

    Delphi 数据类型列表 分类 范围 字节 备注 简单类型 序数 整数 Integer -2147483648 .. 2147483647 4 有符号32位 Cardinal 0 .. 429496 ...

  3. rcp(插件开发)org.eclipse.ui.decorators 使用

    org.eclipse.ui.decorators这个扩展点可以为对应的节点添加不同的图标显示. 使用方式都差不多,以下就转载一下使用方式: 1.添加扩展点 org.eclipse.ui.decora ...

  4. java web从零单排第十六期《struts2》控制标签(2)

    1.s:subset标签概述: s:subset标签功能是从一个集合中取出部分元素合并成一个新的集合,新生成的这个集合是原来集合的子集.属性和意义如下: 属性名 是否必需 默认值 类型 说明介绍 co ...

  5. Java线程面试题 Top 50(转)

    不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎.大多数待遇丰厚的Java开发职位都要求开发者精通多线程 ...

  6. C编译: 使用gdb调试

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! gdb是the GNU Debugger的简称.它是一款UNIX平台的调试器(de ...

  7. hdu 1849 (尼姆博弈)

    http://acm.hdu.edu.cn/showproblem.php? pid=1849 简单的尼姆博弈: 代码例如以下: #include <iostream> #include ...

  8. C#应用Newtonsoft.Json.dll,控制json的时间格式

    原文:C#应用Newtonsoft.Json.dll,控制json的时间格式 var aIsoDateTimeConverter = new IsoDateTimeConverter();aIsoDa ...

  9. JSP网站开发基础总结《九》(转)

    本篇属于附加篇,在之前的总结中给大家提到过一个关于登录状态验证的效果,当时是通过Session对象完成的,今天我查了一下,JSP为我们封装了一个用于过滤用的过滤器类Filter,通过它我们就可以非常轻 ...

  10. 《WordPress插件开发手冊》文件夹

    翻译前言:国内没有关于WordPress插件开发比較具体而且系统的资料 前言 第一章:准备一个本地开发环境 介绍 在你的电脑上安装一个站点server 下载并配置一个本地的WordPress 创建一个 ...