Попробовав подключить LuaJava к проекту и получив кучу непонятных проблем из-за того, что я пользуюсь 64 битной ОС, из-за этого я решил использовать библиотеку JNLua. В данной статье будет представлен класс позволяющий использовать Lua в проектах для придания гибкости приложению. Для начала необходимо скачать библиотеку и необходимые dll с сайта проекта последняя версия 1.0.4, для запуска приложения потребуется сделать 2 шага, во первых необходимо наличие jnlua52.dll и javavm.dll в папке с библотеками прописанной в свойствах VM Djava.library.path= и во вторых вам необходимо скопировать lua52.dll в папку system32 в противном случае проект не запустится, к сожалению размещение lua52.dll в папке библиотек не дает необходимого эффекта поэтому и необходимо скопировать его в system32, надеюсь в следующих версиях это исправят. JNLua интересен тем, что он позволяет использовать спецификацию JSR 223: Scripting for the JavaTM Platform, достаточно подробно это описано на странице проекта JSR223Provider на основании информации изложенной в этой статье я написал свой класс позволяющий запускать скрипты Lua из Java(в данном примере он выполнен как singelton), вот его код:
Код
public class LuaScript { ScriptEngineManager manager; ScriptEngine engine; Compilable compilable;
private static LuaScript instance;
public static synchronized LuaScript getInstance() { if (instance == null) { instance = new LuaScript(); } return instance; }
public Object runScriptFunction(String functionName, Object... params) { Invocable invocable = (Invocable) engine; try { //Logger.getLogger(this.getClass().getName()).log(Level.WARNING,"start script function {0}" ,functionName); return invocable.invokeFunction(functionName, params); } catch (ScriptException e) { Logger.getLogger(LuaScript.class.getName()).log(Level.WARNING, "{0} run error\n{1}", new Object[]{functionName, e.getMessage()}); } catch (NoSuchMethodException e) { Logger.getLogger(LuaScript.class.getName()).log(Level.WARNING, "{0} run error\n{1}", new Object[]{functionName, e.getMessage()}); } return null; }
/** * запускает скрипт Lua в котором описан интерфейс runnable в отдельном потоке * @param LuaInterface */ public void runLuaRunnable(String LuaInterface) { Invocable invocable = (Invocable) engine; try { engine.eval(LuaInterface); Object luaRunnable = engine.get("runnable"); Runnable runnable = invocable.getInterface(luaRunnable, Runnable.class); Thread thread = new Thread(runnable); thread.start(); thread.join(); } catch (ScriptException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * запускает скрипт Lua в котором описан интерфейс runnable в отдельном потоке ExecutorService пакета java.util.concurrent * @param service это ExecutorService из пакета java.util.concurrent * @param LuaInterface это наименование интерфейса для запуска. * @return Future если необходимо отследить момент завершения задачи */ public Future runLuaRunnable(ExecutorService service,String LuaInterface) { Invocable invocable = (Invocable) engine; try { engine.eval(LuaInterface); Object luaRunnable = engine.get("runnable"); Runnable runnable = invocable.getInterface(luaRunnable, Runnable.class); return service.submit(runnable); } catch (ScriptException e) { e.printStackTrace(); return null; } }
// -- Private classes /** * Provides an UTF-8 input stream based on a reader. */ private static class ReaderInputStream extends InputStream { private static final Charset UTF8 = Charset.forName("UTF-16"); private Reader reader; private CharsetEncoder encoder; private boolean flushed; private CharBuffer charBuffer = CharBuffer.allocate(1024); private ByteBuffer byteBuffer = ByteBuffer.allocate(1024); private StringBuilder result; /** * Creates a new instance. */ public ReaderInputStream(Reader reader) { this.reader = reader; encoder = UTF8.newEncoder(); charBuffer.limit(0); byteBuffer.limit(0); flushed=true; result = new StringBuilder(); } @Override public int read() throws IOException { if (!byteBuffer.hasRemaining()) { if (!charBuffer.hasRemaining()) { charBuffer.clear(); while(reader.read(charBuffer)!=0) { charBuffer.flip(); result.append(charBuffer.toString()); } } } return charBuffer.length(); } } }
В классе LuaScript определен внутренний класс ReaderInputStream для удобства чтения файлов со скриптами. Загрузка скриптов производится при помощи функции load в качестве параметров она принимает файл или строку содержащую скрипт, основным в данном методе является вызов compiledScript.eval(engine.getBindings(ScriptContext.ENGINE_SCOPE)); в нем происходит проверка скрипта на синтаксический ошибки, и загрузка содержимого в стек Lua, после этого можно вызывать функции Lua используя метод runScriptFunction(String functionName, Object... params) в случае возникновении ошибок они будут выведены в лог. Также есть метод isFuntionLoad позволяющий проверить наличие Функции Lua в стеке и соответственно возможности ее запуска.
Использования объектов Java в скриптах Lua происходит через использования механизма отражения(reflected), классы и объекты java отражаются в Lua. В коде Lua это выглядит следующим образом:
Код
System = java.require("java.lang.System") -- импортируем класс в Луа //import class into Lua print(System:currentTimeMillis()) -- вызываем статический метод //invoke static method
out = System.out -- чтение статического поля read static field out:println("Hello, world!") -- вызов метода println объекта out для вывода строки(равносильно вызову System.out.println("Hello, world!") из java
StringBuilder = java.require("java.lang.StringBuilder") -- импортируем класс в Луа sb = StringBuilder:new() -- вызов конструктора sb:append("a") -- вызов метода //invoke method sb:append("b") out:println(sb:toString())
TimeZone = java.require("java.util.TimeZone") --импортируем класс в Луа timeZone = TimeZone:getDefault() -- вызов метода out:println(timeZone.displayName) -- читаем свойство и выводим его в System.out //read property; invokes getDisplayName()
Calendar = java.require("java.util.Calendar") --импортируем класс в Луа today = Calendar:getInstance() -- получаем экземпляр класса Calendar из java(изменив объект тут он изменится и в java) print(tostring(today)) -- invokes Object.toString() tomorrow = today:clone() tomorrow:add(Calendar.DAY_OF_MONTH, 1) assert(today < tomorrow) -- invokes Comparable.compareTo()
При использовании в скриптах Lua статических переменных объявленных в java убедитесь что они не null для этого пишите код проверки в скриптах Lua чтобы потом долго не разбираться почему у вас при выполнении скрипта вываливается ошибка, пример ниже.
Код
function create(name) print=name; local Factory = java.require("modelview.DataFactory") local tmp = Factory:CreateData(name) if tmp == nil then return "object not load" end return tmp end
Собственно все, на этом данная статья заканчивается.