Pessoal,
Criei uma solução que a princípio evita qualquer tipo de injeção de código. O que fiz foi utilizar Reflection (já embutida de forma facilitada no Groovy).
Toda classe no Groovy possui dois métodos que facilitam fazer reflection: getProperty e setProperty. Como os nomes dizem, eles servem para obter e setar valores de propriedades da instância da qual são chamados.
Assim, vamos imaginar uma classe Pessoa com os atributos nome e sexo.
Poderíamos fazer assim:
def pessoa = Pessoa.get(1)
println pessoa.getProperty("nome")
Isso nos imprime o valor do atributo nome para a respectiva instância.
Com isso, não é mais possível alterar o valor de algum campo (conforme podia ser feito com o Eval.me() - post anterior).
Vamos considerar o seguinte modelo: NotaFiscal (descricao), ItemNotaFiscal (descricao e valor) e UsuarioResponsavel (nome). Onde uma Nota pode ter
n itens e apenas
um usuário responsável por sua criação.
Assim, fica fácil em Groovy fazer o seguinte:
def nota = NotaFiscal.get(1)
println nota.usuarioResponsavel.nome
Isso vai imprimir o
nome do usuário responsável pela criação da respectiva
nota.
O meu problema é que não posso fazer a seguinte chamada: nota.getProperty("nota.usuarioResponsavel.nome"), pois, obviamente, o campo
nome não é um atributo da
nota, mas da classe
UsuarioResponsavel.
Isso me gerou o 1o desafio, felizmente não foi tão complicado, pois eu já tinha algo parecido. Um algoritmo que navega recursivamente pela string "nota.usuarioResponsavel.nome" até mostrar, nesse caso, o nome do usuário.
Ele funciona assim:
def nota = Nota.get(1)
def nome = getFieldValue("nota.usuarioResponsavel.nome",nota)
println nome
O código acima, imprime o valor do atributo
nome para a instancia
usuarioResponsavel que está dentro da instancia
nota.
Entretanto, o maior problema que enfrentei foi quando precisei fazer o mesmo, porém para os itens da nota, ou seja: "nota.itensNotaFiscal.descricao". Nesse caso não era só fazer a chamada getFieldValue("nota.itensNotaFiscal.descricao",nota). Pois itensNotaFiscal é uma coleção e ela não possui o campo descricao (o Groovy que faz a mágica de expandir isso). Porém, como estou fazendo tudo na mão, foi preciso tratar esses casos.
Enfim, o algoritmo são duas funções abaixo, uma delas recursiva:
def getFieldValue(f, o) {
def r = []
getFieldValueRec(f,o,r,f.substring(f.lastIndexOf(".")+1, f.length()))
}
private def getFieldValueRec(f, o, r, fResult) {
if (f.contains(".")) {
def current = f.substring(0,f.indexOf("."))
def others = f.substring(f.indexOf(".") + 1, f.length())
o = getFieldValueRec(current, o, r, fResult)
if (o instanceof Collection) {
for (item in o) {
getFieldValueRec(others, item, r, fResult)
}
}
else {
o = getFieldValueRec(others, o, r, fResult)
}
return r
}
else {
def aux = o.getProperty(f)
if (f == fResult)
r << aux
return aux
}
}
Para utilizar, basta chamar dessa maneira:
println getFieldValue("nota.itensNotaFiscal.descricao", Nota.get(1))
Com isso, fica fácil fazer a seguinte chamada http:
http://localhost:8080/ProgramaX/nota/get?notaId=1&data=nota.itensNotaFiscal.descricao
Isso vai nos retornar uma lista com a descrição de cada item da respectiva nota. A diferença para o anterior, é que o código Groovy injetado é limitado aos atributos. Não dá, por exemplo, para chamar um método.
Agora é injetar isso em cada classe de domínio e utilizar para os mais variados fins, como em chamdas AJAX onde é preciso atualizar um determinado campo HTML.