SelectOneMenu + converter = erro de validação
Friday, February 1st, 2008Isso é um problema bem comum, e para quem está começando com JSF (ou mesmo já é veterano de guerra) acaba perdendo um tempinho considerável para soluciona-lo. Sendo, vou explicar o cenário abaixo, logo vou explanar o problema e finalizo com a solução para o mesmo.
O cenário
Imagine que você queira uma combobox (h:selectOneMenu) para exibir uma lista de municípios (objetos do tipo Municipio) para o usuário, daí quando o usuário selecionar o valor na combo e submeter o formulário este valor selecionado será inserido em um atributo do tipo Municipio no teu managed bean. Até aí tudo, nada de complicado, basta implementar um JSF custom converter, algo como MunicipioConverter, para converter o valor selecionado no cliente (na maioria das vezes é o id do item) para um objeto do tipo Municipio e pronto, resolvido o problema! A questão é que ao submeter o formulário você receberá de cara este erro:
“Validation Error: Value is not valid“
E você não entenderá o porquê dele está ocorrendo já que teu converter, managed bean e página estão corretamente implementados e configurados.
O problema
Bem, já está óbvio que o erro ocorre na Fase de Validação do ciclo de vida do JSF. Na fase de validação o erro ocorre porque a especificação do JSF diz que o componente UISelectOne deve assegurar que o valor selecionado esteja dentro dos valores selecionáveis exibidos ao usuário, ou seja, que o valor selecionado seja igual a um dos valores avaliados, caso isso não ocorra então será ecoado o erro de validação acima.
De acordo com o Javadoc nós temos:
In addition to the standard validation behavior inherited from
UIInput, ensure that any specified value is equal to one of the available options. If it is not, enqueue an error message and set thevalidproperty tofalse.
Sendo mais especifico ainda, o “erro” ocorre quando o componente (UISelectOne) testa a igualdade do teu objeto (aquele retornado pelo converter de Municipio) -que é uma nova instância- com a lista das opções avaliadas, como você não sobreescreveu os métodos equals() e hashCode() da classe Municipio a igualdade entre objetos é feito por referência, um simples "obj1 == obj2", logo por ser uma nova intância (novo endereço de memória) isso retornará false, e então o erro ecoa.
Como podem ver, isso não é erro no teu converter ou configuração da tua aplicação, mas sim uma validação imposta pela especificação. Acredito que isso ocorra para barrar valores inválidos enviados por algum cracker, enfim, é um medida de segurança básica que JSF já te fornece.
A solução
Se você está recebendo este erro provavelmente você chegou a conclusão que o teu objeto convertido e retornado pela tua implementação do custom converter (MunicipioConverter) não pertence a nenhuma das opções avaliadas pelo componente.
Sendo, a solução é simples, sobreescreva os métodos equals() e hashCode() da tua classe Municipio ![]()
Pronto, assim quando entrar na fase de validação o componente chamará estes métodos e encontrará o valor selecionado entre as opções avalidadas.
Concluindo
Bem senhores, este problema ocorre quando utilizamos nossos objetos (tipos) customizados (Municipio, Pessoa, Item etc) e não sobreescrevemos os métodos para comparação entre objetos. Isso não ocorre quando utilizamos tipos primitivos, wrappers, String, BigDecimal, isto é, objetos que possuem os métodos equals() e hashCode() sobreescritos corretamente.
Enfim, isto foi um resumão de como contornar este problema, acredito que muitos já passaram por isso e quem não passou provavelmente irá passar algum dia
Isso é tudo, boa sorte.