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的初步使用(本地模式和集群模式) ...
随机推荐
- Note:This element neither has attached source nor attached Javadoc
在用Eclipse编写程序时,发现把鼠标放到类名上时出现标题的提示 解决方法: 右击项目,选择 properties –> Java Build Path –> Libraries,如图 ...
- Python基础 - 内建函数
什么是内建函数 在Python的手册中,名叫build-in Functions,中文可以称为内建函数. 内建函数就像dos系统的内部命令,他不依赖于外部模块,也就是说: 内建函数就是:安装好Pyth ...
- Replace - with an en dash character (–, –) ?
这个安卓开发过程中eclipse的提示,新浪网友给出这个解决方法:http://blog.sina.com.cn/s/blog_5ea8670101015dgk.html 太笨了. 看看stacko ...
- 你属于几K(千)?
月薪2k.对出勤率负责:5k,对按时完毕率负责:8k,对质量负责:12k.对小团队的情绪负责:16k,对到款率负责:20k,要想着怎么保住自己的饭碗.40k.保住部门.100k.保住大部门:100k+ ...
- Java使用反射机制优化工厂方法
我先举个例子,有一个接口People,这个接口有一个方法: package com.wjy.reflect; public interface People { public abstract voi ...
- elf 文件格式探秘——程序运行背后的故事
摘要:本文主要讲解elf文件格式,通过readelf命令结合底层的相关数据结构,讲解相关内容,分析程序运行的基本原理. 本文来源:elf 文件格式探秘——程序运行背后的故事 http://blog.c ...
- Redis在win7上的安装与可视化应用
Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...
- 日版 Galaxy Note sc05d 涮机
问题描写叙述:手机已坏.进不了系统 以下提供线涮刷机教程: 1.首先拔掉你的sd卡和sim卡.双清你的手机.可能有些版本号的机油没有双清模式,那就进到设置里边恢复出厂设置,然后关掉你的手机再开机.等到 ...
- Spring中Quartz动态设置cronExpression
字段 允许值 允许的特殊字符 秒 0-59 , - * / 分 0-59 , - * / 小时 0-23 , - * / 日期 1-31 , - * ? / L W C 月份 1-12 或者 JAN- ...
- POJ 1515 Street Directions
题意: 一幅无向图 将尽量多的无向边定向成有向边 使得图强连通 无向图保证是连通的且没有重边 思路: 桥必须是双向的 因此先求边双连通分量 并将桥保存在ans中 每一个双连通分量内的边一定都 ...