27 de out de 2017

JavaFX com Bean Validation e CDI 2.0

Quando JavaFX 2 foi lançado me lembro de muitas pessoas procurando por frameworks que facilitariam a integração de JavaFX com frameworks de validação, um container de injeção de independência e outros serviços enteprise. Atualmente pode integrar JavaFX com Bean Validation e CDI 2.0 sem o uso de qualquer framework externo. Nesse artigo eu vou mostrar como validar os campos de sua aplicação JavaFX usando Bean Validation  e uma aplicação com CDI.

Antes de falar sobre essas coisas eu recomendo que você cheque os seguintes artigos:


Validando Controls do JavaFX usando Bean Validation



Com Bean Validation 2.0 temos o "unwrap" automático de propriedades JavaFX. Isso significa que se você tiver um campo do tipo ObservableValue o validator vai conseguir tirar o valor real e aplicar a validação. No entanto, os campos de control do JavaFX ainda não são suportados, então se você tentar validar os campos diretamente você vai ter uma exceção:  


Isso acontece por uqe a implementação do validator não sabe como retirar valores de um controller JavaFX. O que precisamos é criar uma classe que implementa javax.validation.valueextraction.ValueExtractor. Precisamos também usar o tipo do controle que essa classe vai saber tirar o valor (usar genéricos para isso), criar o método que faz a atual extração do valor (tipo um getText em um TextField) e, por fim, usar a anotação  javax.validation.valueextraction.ExtractedValue para configurar o tipo que é tirado do control(String, Integer, LocalDate...). Para evitar passos adicionais usamos a anotação  javax.validation.valueextraction.UnwrapByDefault na nossa implementação. As nossas classes para tirar valores do DatePicker e do TextField estão abaixo:

Value Extractor para o DatePicker

Value Extractor para o TextField

Finalmente, precisamos registrar isso com o framework de validação. Há 
Finally we must register it in within the bean validation framework. There are a couple of algumas formas de fazer isso, escolhemos o modo com SPI  que é criar um arquivo com o nome META-INF/services/javax.validation.valueextraction.ValueExtractor com o nome das classes dos nossos ValueExtractors:

Conteúdo do arquivo META-INF/services/javax.validation.valueextraction.ValueExtractor


Poderíamos ter uma extensão de bean validation para os controles do JavaFX, assim não teríamos que criar novamente. Se você sabe de algum projeto assim me fale que eu menciono aqui!

Mostrar os erros de validation para os usuários


Você provavelmente quer mostrar os erros para os usuários quando os valores que ele entrou não são válidos. Uma vez que você tenha acesso a classe Validator no controller você simplesmente pode chamar o método validate no controller  e mostrar aos usuários os erros de validação em um label, por exemplo. Eu pessoalmente não gosto dessa solução pois eu acho que é muito intrusivo já que você terá que modificar a view para incluir mais labels só para a validação. Uma solução mais simples é mostrar um tooltip com o erro de validação no campo que deu problema. Poderíamos também mostrar um dialog ou bordar o campo de vermelho, mas só mostramos o tooltip mesmo:

Método showValidationErrors pega o controle que tem erro de validação adiciona um tooltip e mostra

Para mostrar o tooltip precisamos acessar o controle em sí e para isso precisamos fazer um pouco de reflexão por isso fomos ao monte meditar , por isso o campo usado deve ter o modificador público ou ter métodos de acesso a ele.

CDI, Bean Validation e JavaFX


Não há nada para adicionar sobre CDI aqui do que eu já mencionei no post Using CDI 2.0 in a JavaFX application. Eu gostaria de compartilhar que CDI e Bean Validation funcionam em uma aplicação JavaFX e por isso eu posso simplesmente injetar o validator na minha classe!Uma única dependência vai tornar isso possível: org.hibernate.validator:hibernate-validator-cdi. Veja abaixo todos os arquivos da aplicação:

Our maven project and its files


Essas são as dependências que eu adicionei ao pom.xml:

These are all dependencies required to use bean validation and CDI on a JavaFX application

O código de uma aplicação de exemplo pode ser encontrado no meu github github. Abaixo você pode ver como a validação usando tooltips funciona na aplicação:



Por quey isso é importante?


Estar apto a integrar JavaFX com JavaEE (ou devo dizer EE4J já?) é algo chave para quem quer criar aplicação reais com JavaFX, isso trás persistência, validação, injeção de dependência e mais! it will bring persistence, validation, injection and more. A principal questão é: será que isso funcionaria em uma aplicação Android que usa Gluon/ JavaFX Ports?

Para mais exemplos de validação você pode ver essa pull request do Hendrik Ebbers  para o projeto hibernate validator.