Java EE 6: Começando com Bean Validation
Por Lucas Souza em 03/02/10Não existe tarefa mais comum hoje em dia do que validar dados em uma aplicação. Por exemplo, validamos se na camada de apresentação o usuário preencheu algum campo obrigatório, depois fazemos a mesma lógica de validação em nossa regra de negócio e por último validamos se os dados que serão salvos no banco também estão corretos. O que na maioria das vezes os desenvolvedores fazem é validar estas regras em todos os lugares, e muitas vezes resulta em validações complicadas e possíveis erros na aplicação. Muitos desenvolvedores consideram essa tarefa enfadonha.
Na nova versão do Java EE lançada dezembro de 2009 existem grandes novidades: Servlets 3.0, JAX-RS, CDI, JSF 2.0 e a Bean Validation, definida pela JSR 303. Com a Bean Validation é possível realizarmos validações atráves de metadados (anotações) e as utilizarmos em todas as camadas de nossa aplicação.
Podemos até mesmo criar nossas próprias regras de validações e aplicarmos em outros pontos do nosso sistema. Essas regras podem ser facilmente integradas com JPA e JSF, por exemplo. Vamos considerar uma classe que represente um contato:
public class Contato {
private String nome;
private String email;
// getters e setters
public void enviaEmail() {
// codigo envio de email
}
}
E vamos adicionar nossas regras de validação utilizando a Bean Validation:
public class Contato {
@NotEmpty
@Pattern(regexp = ".+@.+\\.[a-z]+")
private String email;
@NotEmpty
private String nome;
// getters e setters
public void enviaEmail() {
// codigo envio de email
}
}
A anotação @NotEmpty garante que o email não poderá ser vazio assim como o atributo nome. No atributo email também colocamos a anotação @Pattern, onde podemos passar qualquer expressão regular, que o Bean Validation validará o valor passado ao atributo nome com a expressão regular.
Lembrando que o Bean Validation é uma especificação e como toda especificação, para utilizarmos precisamos de uma implementação para a utilizarmos. A implementação usada nos exemplos é a Hibernate Validator, e é a implementação de referência.
Integrando com o JSF
O código abaixo mostra um formulário simples utilizando JSF, que chama um método enviaEmail do ManagedBean quando clicamos no botão “EnviaEmail“:
<h:form id="formulario">
<h:panelGrid columns="3">
<h:inputText id="email" value="#{contato.email}"/>
<h:message for="email" styleClass="error"/>
<h:commandButton action="#{contato.enviaEmail}"
value="Enviar Email"/>
</h:panelGrid>
</h:form>
Precisamos ainda anotar nossa classe Contato com @ManagedBean(name="contato") e @RequestScoped para que a mesma seja um ManageBean e consiga integrar-se com o JSF.
Quando clicarmos no botão “Enviar Email” automaticamente será validado se o atributo email foi preenchido a atende a expressão regular da anotação @Pattern.
Podemos muitas vezes nos deparar com algum caso onde é necessário fazermos uma validação um pouco mais específica, algo que não esteja implementado por padrão no Bean Validation. Validar um CEP seria um bom exemplo. O primeiro passo para fazermos nossa própria validação é criar uma anotação que sugira o nome desta validação.
@Constraint(validatedBy = CepValidator.class)
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cep {
String message() default "Cep inválido";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
Reparem que em nenhum momento fizemos de fato a validação se o CEP é válido ou não. Por isso anotamos a anotação CEP que acabamos de criar com @Constraint e setamos o atributo validatedBy com a classe que está nossa validação realmente.
O próximo passo é criarmos a classe CEPValidator que fará a validação. Esta classe tem que obrigatóriamente implementar a interface javax.validation.ConstraintValidator:
public class CepValidator implements
ConstraintValidator<Cep, String> {
private Pattern pattern =
Pattern.compile("[0-9]{5}-[0-9]{3}");
@Override
public void initialize(Cep constraintAnnotation) {
}
@Override
public boolean isValid(String value,
ConstraintValidatorContext context) {
Matcher m = pattern.matcher(value);
return m.matches();
}
}
No método isValid verificamos e retornamos um booleano dizendo se a String passada como argumento está de acordo com a expressão regular que criamos no topo da classe. Podemos agora validar qualquer atributo de algum Java Bean, devendo apenas anotar o atributo com a nossa recém criada @CEP. Por exemplo:
public class Contato {
@Cep
private String cep;
// getters e setters
}
Para rodar esse exemplo precisamos de um servidor de aplicação compatível com o Java EE 6, como o Glassfish 3.0. A premissa do Bean Validation é que muitos outros frameworks acabem por adotar um único mecanismo de validação, sendo possível reaproveitar o código criado aqui. O Caelum Stella, que fornece diversos validadores para sistemas que envolvem o domínio brasileiro, em breve será compatível com essa especificação, possibilitando que você utilize-o em todas as diversas especificações que compões o Java EE.
Além disso precisamos de um Servidor de Aplicação compatível com a versão 6 do Java EE. Por enquanto o único servidor que implementa essa versão é o Glassfish v3.0 que por sinal também é uma implementação de referência.
No caso desse exemplo, seria mais interessante anotar @Cep com @Pattern do que implementar algo que já existe na mão. Inclusive isso mostra o poder da composição das regras na API.
Comment by Michael Nascimento Santos — February 4, 2010 @ 11:49 am
Gostei desde o inicio dessa proposta do Bean Validation [JSR 303], ótimo post!
Comment by Leandro — February 4, 2010 @ 1:14 pm
É que o intuito era mesmo mostrar como fazer sua própria anotação. Mas é perfeitamente válida sua idéia Michael.
Comment by Lucas Souza — February 4, 2010 @ 1:41 pm
Discordo do Michael, é muito mais claro você criar uma anotações como @Cep nesses casos do que espalhar Regex complicados pelo código.
Além disso foi um exemplo para “explicar o poder de Extensão das regras na API”.
Comment by Rodrigo — February 4, 2010 @ 6:35 pm
Oi Rodrigo. O Michael não quis dizer para usar a anotação
@Pattern(regexp ="")em cima de cada atributo, o que realmente não seria limpo. Ele quis dizer para usar o@Patternem cima da@interface Cep, ai nem haveria necessidade de criar uma implementação de ConstraintValidator, já que o Beans Validation permite essa sobreposição de anotações, permitindo você compor regras de maneira fácil.Comment by Paulo Silveira — February 4, 2010 @ 6:41 pm
Saquei
Comment by Rodrigo — February 5, 2010 @ 10:02 am
Exato, Paulo.
Comment by Michael Nascimento Santos — February 5, 2010 @ 10:25 am
Acredito que a versão “JBoss AS 6.0.0.M1″ implemente algumas das características do JEE 6, como “(…) CDI/Weld, BV and JSF2″.
Ref: http://www.jboss.org/feeds/post/jboss_as_6_0_0_m1
Comment by Daniel Braga — February 5, 2010 @ 1:45 pm
Lucas Souza e integrantes deste blog, gostaria de saber se vocês tiveram alguma problema na instalação do JEE 6 SDK. Pois não consegui instalar, várias outras pessoas no Brasil tiveram ou ainda tem problema de instalação.
O comportamento é que baixei o arquivo: java_ee_sdk-6-windows.exe, executei e depois que mostrou a tela de instalação, após uns 3 a 5 segundos a tela sumiu e não acontece mais nada.
Comment by Henrique Rocha — February 10, 2010 @ 4:05 pm
Olá Henrique, eu apenas baixei o GlassFish v3 no link: https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=sges-3-oth-JPR@CDS-CDS_Developer
E instalei dentro do Eclipse. Tudo funcionou normalmente.
Comment by Lucas Souza — February 22, 2010 @ 5:58 pm
Bom dia.
Excelente artigo, explica muito bem como funciona a proposta da JSR 303.
Mas tenho uma dúvida. É possível fazer esta validação usando richfaces? Por exemplo:
Creio que dessa forma a requisição não será validada, existe uma solução?
Comment by Ronildo Braga Junior — March 3, 2010 @ 11:18 am
Pessoa é o seguinte, estou tentando fazer uma Constraint de Ie, porem ela deve validar de acordo com o estado
Algem sabe fazer isto funcionar
vejo o codigo
A Constraint
view plaincopy to clipboardprint?
1. @Constraint(validatedBy = IeValidacao.class)
2. @Documented
3. @Target(ElementType.FIELD)
4. @Retention(RetentionPolicy.RUNTIME)
5. public @interface Ie {
6.
7. String message() default “Cep inválido”;
8. Class[] groups() default { };
9. Class[] payload() default { };
10. String estado() default “Sp”;
11. }
@Constraint(validatedBy = IeValidacao.class) @Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Ie { String message() default “Cep inválido”; Class[] groups() default { }; Class[] payload() default { }; String estado() default “Sp”; }
A classe de validação
view plaincopy to clipboardprint?
1. public class IeValidacao implements ConstraintValidator {
2.
3. private String estado;
4.
5. public void initialize(Ie constraintAnnotation) {
6. estado = constraintAnnotation.estado(); // não esta ENTTRANDO neste metodo
7. }
8.
9. public boolean isValid(String value, ConstraintValidatorContext context) {
10. if (estado != null && estado == “Sp” && value == “123″) {
11. return true;
12. }
13. if (estado != null && estado == “Mg” && value == “456″) {
14. return true;
15. }
16. return false;
17. }
18. }
public class IeValidacao implements ConstraintValidator { private String estado; public void initialize(Ie constraintAnnotation) { estado = constraintAnnotation.estado(); // não esta ENTTRANDO neste metodo } public boolean isValid(String value, ConstraintValidatorContext context) { if (estado != null && estado == “Sp” && value == “123″) { return true; } if (estado != null && estado == “Mg” && value == “456″) { return true; } return false; } }
Classe Pessoa
view plaincopy to clipboardprint?
1. public class Pessoa {
2. private String estado ;
3.
4. @Ie(estado= estado ) // ERRO NESTA LINHA, axo que so permite constante, como faze então?
5. private String ie;
6.
7.
8. public String getIe() {
9.
10. return ie;
11. }
12.
13. public void setIe(String ie) {
14. this.ie = ie;
15. }
16.
17. public String getEstado() {
18. return estado;
19. }
20.
21. public void setEstado(String estado) {
22. this.estado = estado;
23. }
24.
25.
26.
27. }
public class Pessoa { private String estado ; @Ie(estado= estado ) // ERRO NESTA LINHA, axo que so permite constante, como faze então? private String ie; public String getIe() { return ie; } public void setIe(String ie) { this.ie = ie; } public String getEstado() { return estado; } public void setEstado(String estado) { this.estado = estado; } }
Pessoa observe que comentei as linha que estou com dificuldade, mais seria mais ou menos isso. alguem poderia me ajudar
Deste ja agradeço
Comment by Erick — July 8, 2010 @ 2:18 pm