Groovy-gravar uma classe compilada via programa

Bom dia, estou precisando da seguinte ajuda:

Estou criando um sistema (SWING) onde o usuário vai definir diversos Scripts para calcular valores das NotasFiscais, Pedidos, etc.

Esses Scripts vão ser criados/editados em uma tela dentro do próprio sistema, e eu vou grava-los no Banco de Dados para que assim eu possa distribuir os scripts para todos os terminais de maneira transparente. Coloquei abaixo um exemplo de como eu vou executar esses Scripts na hora de calcular um NF por exemplo:

             try {
			//Carregar o Script do BD
			String scriptTeste = "public class teste{" +
							"    public void calcNF(teste.NotaFiscal nf) {" +
							"        nf.valor = 50;" +
							"    }" +
							"}";

			
			ClassLoader parent = getClass().getClassLoader();
			GroovyClassLoader loader = new GroovyClassLoader(parent);
			
			Class groovyClass = loader.parseClass(scriptTeste );
			
			GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();

			NotaFiscal nf  = new NotaFiscal();
			Object[] args = {nf};
			groovyObject.invokeMethod("calcNF", args);
			System.out.println(nf.getValor());
		} catch (Exception e) {
			e.printStackTrace();
		}

Está tudo funcionando normal.

A edição desses Scripts é feita pelos clientes MUITO raramente, então eu criei também uma maneira de controlar a versão do Script no Banco de Dados, sendo assim, eu só precisaria compilar o Script (Que é um processo lento) quando ele for alterado.

O PROBLEMA É:
Existe alguma maneira de eu Serializar aquele objecto “groovyClass” que é um objeto tipo Class? Para que quando eu for executar de novo o Script eu verifico a versão se ela não foi alterada eu não preciso compilar o texto do Script que está no banco de dados é só eu dar um load nesse Class que está no HD.

Muito obrigado pela ajuda.

Compilar um script é tão rápido que acho que não vale a pena o esforço.

Eu tive um requisito parecido… escrever regras de negócio externas ao código (os scripts ficam no banco) e estas regras mudam com uma baixa frequência.

O que eu fiz então foi um pool de objetos groovy que é criado no startUp da app e ja deixa instanciado alguns objetos para o usuário.
deste modo, para que alterações de scripts sejam enxergadas pelo app, basta reiniciar o app (ou web container).

Se o app necessita ter alta disponibilidade (nao pode ser reiniciado) bastaria colocar um método no pool que o auto-atualiza: faz o lock do mesmo, invalida objetos emprestados pelo pool (pode fazer com que as operacoes correntes falhem e tenham que ser refeitas), busca os scripts do banco e os atualiza no pool, caso necessário, e faz então o unlock.

Como nao se pode ter tudo, nao podendo o app ser reiniciado, a atualização dos scripts ocorrerá em runtime o que podera tomar tempo dependendo da quantidade dos scripts e do desempennho do banco.

Bem… essa é a ideia que me veio na cabeça… não sei se te serviria… :slight_smile:
Feito!

Estou passando passando por situação semelhante.

Eu fiz uma taglib jsp “genérica”, e uso o groovy como expression language nela. Assim que executo uma expressão pela primeira vez, verifico se ela já existe em um cache que criei. Se não existir, compilo a expressão e a coloco no cache.

Esse processo de compilação, por mais que seja curto, leva algum tempo. Quando reinicio o servidor, o cache é perdido, pois o mesmo é em memória. E na próxima vez que chamar uma jsp que tem uma determinada expressão, ela vai ser compilada novamente.

A minha idéia era criar um cache persistido em disco, para que quando reiniciar o servidor o único trabalho seja o de carregar a classe já compilada.

Andei procurando no javadoc (ou groovydoc?) das classes groovy.lang.GroovyShell e groovy.lang.Script, que são as classes que uso para executar as expressões, mas não há nada sobre obter o código compilado dos scripts.

Acho que a única solução possível é invocar o compilador groovy manualmente, armazenar as classes compiladas e depois carregá-las.

:slight_smile:

oi neófito,

Não sou muito “sabido” no assunto, mas tem a classe “org.codehaus.groovy.tools.Compiler” que faz compilações padrão (para o file system)…

JavaDoc
http://groovy.codehaus.org/api/org/codehaus/groovy/tools/Compiler.html

Dá uma olhada.
Feito

Eu sou bem radical com a questão de performance. Antes de tratar esse problema, eu prefiro me certificar de que realmente o problema existe. Não adianta adicionar complexidade ao código, se for para resolver um problema que, no fundo, não está lá.

Se você está tendo um problema, iniciar com um cache em memória pode ser uma ótima idéia. Se ainda tiver problema, aí pense em soluções mais radicais, em disco.

O groovy costuma a ser muito eficiente. A classe GroovyEngine já gerencia um cache interno, automaticamente, e verifica os sistema de arquivos em busca de mudanças. O GroovyClassLoader já tem uma compilação mais simples e rápida.

Tudo depende do quão acoplado seus scripts são entre si.

psssss…

tava brincando com a classe Compiler do Groovy e fiz um hello word…
mas me atrapalhei todo na hora de postar… acabei postando na janela errada… :?

o codigo (beeeeem basicão) esta aqui:
http://www.guj.com.br/posts/list/0/107191.java#578751

feito!

Conheço a classe. :smiley:

Bom, não fiz nenhum teste minucioso, mas é perceptível uma pequena demora ao alterar uma expressão groovy em um jsp. E como você mesmo disse, desde o início eu implementei um cache em memória. E concordo com você que só devemos implementar uma solução mais sofisticada se realmente houver necessidade. Mas já andei pensando em como implementar.

Se eu tiver mesmo a necessidade de um cache persistido, pode ser um problema criá-lo porque minhas expressões estão em jsp’s. Elas não são arquivos, são apenas strings (expressões) que estão dentro de um jsp. Não possuem um caminho, um package, e muito menos um nome de arquivo. Vai ser necessário criar algum mecanismo para associar um script compilado à um script em código fonte. A forma mais fácil seria dar um jeito de colocar uma string contendo o código fonte no próprio script compilado, e ter uma forma de remover scripts não mais usados.

É isso ae. :wink:

1 curtida

Acabei de realizar um teste para medir o tempo para compilação e execução de uma expressão simples com groovy aqui. Na primeira vez, executou em 297 ms, na segunda, 0 ms. Uma diferença substancial.

Então, se em um jsp houver umas 10 expressões semelhantes, só o tempo de compilação poderia chegar em quase 3 segundos! Isso aconteceria toda vez que o servidor fosse reiniciado e a página carregada. É um bom tempo.