LuaJava – uma ferramenta de scripting para Java

Autor: Edmar Souza Jr.

Hoje vamos falar sobre como estender a linguagem Java usando a linguagem Lua. Mostraremos o Lua Java e como usá-lo para as principais operações.

Lua

Hoje não estamos aqui para falar do satélite natural da Terra, mas sim da linguagem de scripting Lua. Desenvolvida totalmente em território tupiniquim pelo time da TecGraf (Pontifícia Universidade Católica do Rio de Janeiro – Puc-rio). Lua é leve, veloz, é basicamente procedural, tipada dinamicamente, tem suporte a threads e possúi suporte a Orientação a Objetos usando metamecanismos (LuaTables!!).

Lua já foi empregada com sucesso em vários jogos comerciais, entre eles MDK2, World of Warcraft e Grim Fandango.

Você pode obter mais informações sobre Lua em www.lua.org.
LuaJava

Não, a Nasa não descobriu uma ilha chamada Java na Lua, nem tão pouco os cientistas da ilha de Java estão indo para a Lua. LuaJava é uma biblioteca que permite estender a linguagem Java usando Lua como ferramenta de scripting.

A grande jogada da biblioteca LuaJava é permitir interoperabilidade total entre o ambiente Java e o ambiente Lua. Você pode, por exemplo, criar classes em Lua e usar uma interface para instanceá-las em Java, ou então “importar” uma biblioteca escrita em Java (seja ela criada por você ou do pacote padrão) e usar em seus scripts Lua.
Indo para Lua com Java

A primeira coisa que precisamos para dar início a nossa viagem é do LuaJava, que pode ser adquirido em: http://www.keplerproject.org/luajava/.

Existem dois arquivos necessários para o funcionamento de LuaJava: luajava-x.x.jar e luajava-x.x.dll (libluajava-x.x.so para usuários do Linux)

Para instalar a biblioteca basta copiar o arquivo luajava-x.x.dll para a pasta bin no diretório do seu Java runtime (ou no diretório bin do seu Jdk), o arquivo luajava-x.x.jar deve ficar em algum caminho do CLASSPATH.

Feito isso você estará com o LuaJava instalado e funcionando.

Iniciando o LuaJava

A primeira coisa que você que já programou com Lua em C ou C++ antes deve saber é que as funções da API Lua estão totalmente disponíveis aqui através da classe org.keplerproject.luajava.LuaState. Entretanto, quem não tem intimidade com a API Lua, não fique desesperado. A LuaJava oferece várias funções que facilitam a integração de Lua com Java.

A Classe LuaState

O principal objeto da LuaJava é o LuaState. O LuaState guarda uma referencia para a máquina virtual Lua, e é a partir dele que você vai carregar arquivos de script, registrar classes, chamar funções, etc.

Para iniciar o LuaJava, você deve criar uma nova instância do objeto LuaState. Você pode também opcionalmente abrir as bibliotecas padrão do Lua. O seguinte trecho de código mostra um programa completo que abre uma nova instância da LuaJava e imprime Hello World na tela usando a função print do Lua.

package testeluajava;
import org.keplerproject.luajava.*;
public class Main {
	public static void main(String[] args) {
		LuaState l = LuaStateFactory.newLuaState();
		l.openLibs();
		l.LdoString("print \"Hello World\" ");
		l.close();
	}
}

Claro, não precisamos nos fixar a executar comandos com o LdoString. Podemos usar o LdoFile para executar nossos arquivos de Script:

package testeluajava;
import org.keplerproject.luajava.*;
public class Main {
	public static void main(String[] args) {
		LuaState l = LuaStateFactory.newLuaState();
		l.openLibs();
		l.LdoFile("teste.lua");
		l.close();
	}
}

//teste.lua

print "Entre com seu nome:";
local nome = io.read();
local texto = "Hello " .. nome;
print (texto);

O forte do LuaJava como dissemos anteriormente é permitir a interoperabilidade entre o ambiente Lua e o ambiente Java.

Usando classes Java em Lua.

Para usar classes escritas em Java no ambiente Lua, o LuaJava nos fornece duas funções: luajava.bindClass e luajava.new.

A função luajava.bindClass permite importar uma referencia para uma classe escrita dentro do ambiente Java, isso é feito da seguinte maneira:

//teste.lua

JFrame = luajava.bindClass("javax.swing.JFrame");

O único parâmetro que a função bindClass recebe é o nome completo (incluindo os pacotes) para uma classe Java. No exemplo acima JFrame agora é uma referencia para a nossa classe javax.swing.JFrame. Para podermos usar esta referencia para instancear um novo objeto, basta usar a função luajava.new da seguinte maneira:

//teste.lua

c = luajava.new(JFrame);

Após isso, podemos trabalhar com o objeto normalmente como se fosse uma classe nativa do Lua. O código abaixo cria uma janela com o título “Hello PDJ” com 300 x 300 pixels e que encerra a aplicação quando fechada.

//teste.lua

JFrame = luajava.bindClass("javax.swing.JFrame");
f = luajava.new(JFrame);
f:setTitle("Hello PDJ");
f:setSize(300,300);
f:setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f:setVisible(true);

Também podemos importar classes criadas por nós mesmos. Vamos considerar como exemplo a seguinte classe:

//PdjBlog.java

package testeluajava;
import java.util.ArrayList;
public class PdjBlog {
	private ArrayList<String> posts = null;
	public PdjBlog() {
		this.posts= new ArrayList();
	}
	public void newPost(String post) {
		this.posts.add(post);
	}
	public void printPosts() {
		for (int i = 0; i < this.posts.size(); i++) {
			System.out.println(this.posts.get(i));
		}
	}
}

Em Lua podemos usá-la da seguinte maneira:

//teste.lua

PdjBlog = luajava.bindClass("testeluajava.PdjBlog");
blog = luajava.new(PdjBlog);
blog:newPost("Teste Lua Java!!");
blog:newPost("Usando Lua com Java!!");
blog:newPost("Outro Teste Lua Java!!");
blog:printPosts();

Usando classes Lua em Java

Assim como é possível criar classes em Java e usá-las em Lua, também é possível fazer o contrário, definindo classes em Lua e usando-as em Java.

O processo para usar classes Lua em Java já é um pouco mais complicado. Para usar as classes você deve definir ma interface em Java que descreva a classe Lua que você deseja utilizar.

Podemos pegar como exemplo a seguinte interface:

//INpc.java

package testeluajava;
public interface INpc {
	void setNome(String nome);
	void falar();
}

Em Lua esta interface poderia ser definida como a seguinte classe:

//npc.lua

Npc = {
	Nome = "";
	setNome = function(nome)
		Nome = nome;
	end;

	falar= function()
		local texto = "Olá eu sou um npc, meu nome é " .. Nome;
		print (texto);
	end;
}

Para usar esta classe precisamos pegar sua referencia através do método getLuaObject da classe LuaState, como no código a seguir:

LuaObject obj = l.getLuaObject("Npc");

A função getLuaObject recebe um parâmetro que é o Nome do objeto no ambiente Lua, ele retornar uma referencia para esse objeto com o qual podemos trabalhar. Porém ainda não estamos aptos a usar a nossa classe Lua. O LuaObject pode ser qualquer coisa adquirida do ambiente Lua. Podemos por exemplo criar uma função no ambiente Lua e chamá-la do nosso código Java da seguinte maneira:

//teste.lua

function HelloWorld()
	print "Hello world from Lua function";
end;

//Código java

package testeluajava;
import org.keplerproject.luajava.*;
public class Main {
	public static void main(String[] args) {
		LuaState l = LuaStateFactory.newLuaState();
		l.openLibs();
		l.LdoFile("teste.lua");
		try {
			LuaObject func = l.getLuaObject("HelloWorld");
			func.call(null);
		}catch(Exception e) {
			System.out.println(e.getMessage());
		}
		l.close();
	}
}

Voltando a nossa classe npc, para podermos usá-la precisamos criar uma “proxy” para ela, ou seja, precisamos dizer ao LuaJava que a nossa Interface INpc é compatível com nossa classe npc e que podemos usá-la para manipular nosso objeto. Fazemos isso através da função createProxy, como mostrado a seguir:

INpc npc = (INpc) obj.createProxy("testeluajava.INpc");

A função createProxy recebe como parâmetro o nome da Interface que desejamos fazer a Proxy (note que novamente precisamos passar o nome do pacote junto). A função retorna um objeto o qual fazemos casting para INpc. Uma vez feito isso podemos chamar as funções da classe normalmente. Aqui vai a listagem completa do código Java.

package testeluajava;
import org.keplerproject.luajava.*;
public class Main {
	public static void main(String[] args) {
		LuaState l = LuaStateFactory.newLuaState();
		l.openLibs();
		l.LdoFile("npc.lua");
		try {
			LuaObject obj = l.getLuaObject("Npc");
			INpc npc = (INpc) obj.createProxy("testeluajava.INpc");
			npc.setNome("Supersayajin");
			npc.falar();
		}catch(Exception e) {
			System.out.println(e.getMessage());
		}
		l.close();
	}
}

Note que, como estamos lidando com código externo a nossa aplicação é sempre bom tratar possíveis exceções. A função createProxy ainda nos obriga a tratar possíveis LuaException e ClassNotFoundExceptions.

Passando objetos por parâmetro

Quando trabalhamos com proxys chamando classes Lua no ambiente Java não precisamos chamar a função luajava.bindClass para usarmos em Lua uma classe Java que estejamos passando por parâmetro para algum método, a tipagem dinâmica do Lua cuida disso para nós, altere a INpc adicionando um novo método void postarNoBlog(PdjBlog blog); e implemente essa rotina na sua classe em lua:

postarNoBlog = function(blog)
	blog:newPost("Post do Npc ".. Nome);
end;

Quando chamarmos a função postarNoBlog podemos passar para ela um objeto do tipo PdjBlog, sem precisarmos usar a função luajava.bindClass. A seguir segue todo o código fonte para esse exemplo:

//PdjBlog.Java

package testeluajava;
import java.util.ArrayList;
public class PdjBlog {
	private ArrayList<String> posts = null;
	public PdjBlog() {
		this.posts= new ArrayList();
	}
	public void newPost(String post) {
		this.posts.add(post);
	}
	public void printPosts() {
		for (int i = 0; i < this.posts.size(); i++) {
			System.out.println(this.posts.get(i));
		}
	}
}

//INpc.java

package testeluajava;
public interface INpc {
	void setNome(String nome);
	void falar();
	void postarNoBlog(PdjBlog blog);
}

//Main.java

package testeluajava;
import org.keplerproject.luajava.*;
public class Main {
	public static void main(String[] args) {
		LuaState l = LuaStateFactory.newLuaState();
		l.openLibs();
		l.LdoFile("npc.lua");
		try {
			PdjBlog blog = new PdjBlog();
			LuaObject obj = l.getLuaObject("Npc");
			INpc npc = (INpc) obj.createProxy("testeluajava.INpc");
			npc.setNome("Supersayajin");
			npc.postarNoBlog(blog);
			blog.printPosts();
		}catch(Exception e) {
			System.out.println(e.getMessage());
		}
		l.close();
	}
}

//npc.lua

Npc = {
	Nome = "";
	setNome = function(nome)
		Nome = nome;
	end;

	falar = function()
		local texto = "Olá eu sou um npc, meu nome é " .. Nome;
		print (texto);
	end;

	postarNoBlog = function(blog)
		blog:newPost("Post do Npc ".. Nome);
	end;
}

Bom pessoal, esta foi uma pequena introdução ao que é possível fazer com LuaJava. Mais detalhes e tutoriais sobre o LuaJava podem ser conseguidos no site oficial do projeto www.keplerproject.org/luajava

Espero que tenham gostado.

Abraços e até a próxima. 😉

Share and Enjoy

  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS
 

Leave a Reply

Your email address will not be published. Required fields are marked *

Email
Print