Pesquisar este blog

segunda-feira, 26 de outubro de 2015

Testes de código no Java


Dependências

As dependênicias que devem ser adicionadas ao projeto para usar as ferramentas apresentadas neste post são:

<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>

Testes no desenvolvimento de software

Todos sabem que o desenvolvimento de software muitas vezes não atende plenamente o que foi especificado pelo cliente.
Existem muitos processos de desenvolvimento de software, cada um com suas vantagens e desvantagens.
Existem vários artigos científicos explicando que o custo de conserto de um bug é exponencial em relação ao tempo que ele foi descoberto. Quanto mais cedo for descoberto um bug, mas fácil é de se arrumar ele. Isto acontece devido a memória do programador, se o bug é descoberto durante o desenvolvimento, o programador tem na cabeça o funcionamento daquele código e com isso fica bem fácil de arrumar o problema. Quanto mais tempo passa, após o desenvolvimento, mais difícil será para o programador lembrar do código, com isso ele leva algum tempo até ler e entender o funcionamento do código.
Por mais legível que seja o código, haverá uma perda de tempo para ler e entender o código.
Bugs nos softwares podem ser classificados em diversas formas:
  • atende aos requisitos especificados na documentação da análise
  • responde corretamente aos dados de entrada
  • realiza suas operações em um período aceitável
  • é utilizável
  • pode ser instalado e rodar nos ambientes planejados
  • atinge os resultados que os clientes desejam
Existem várias formas de se encontrar um bug no software e também existem várias intensidades de um bug, por exemplo uma cor errada em um botão é certamente menos grave que um bug que calcula errado o saldo da sua conta no banco.
A intensidade do bug varia de negócio para negócio, mas certamente um dos objetivos de quem desenvolve software é diminuir ao máximo a quantidade de bugs nos seus sistemas.
E como fazer isso? Existem uma série de ferramentas que nos ajudam a validar a qualidade do código, e as farramentas de teste unitários são uma delas.

Teste de unidade

O teste de unidade testa as assinaturas, as entradas e saídas de uma unidade de software. Por unidade de software entenda como a menor parte testável de um software. Por exemplo em um programa orientado a objeto, um método pode ser uma unidade a ser testada.
As entradas e saídas, podem ser os parâmetros do seu método e as respostas que o seu método retorna após ser executado. 

Ferramentas de Teste Unitário para o Java

JUnit

Uma das ferramentas mais conhecidas para a execução de testes unitários é o JUnit. Esta ferramenta fornece métodos e anotações que facilitam a execução de códigos de testes.
Por exemplo 
O processo de desenvolvimento de software exerce um papel bem importante na quantidade de bugs. Existe uma técnica conhecida como Desenvolvimento Guiado por Testes, ou Test Driven Development (TDD) que prega que devemos criar primeiro um teste para depois escrevermos o código.
Por exemplo, imagina que você esteja desenvolvendo uma calculadora e o seu código está assim:
public class Calculadora {
  public int operacao(String expressao) {
    int resultado = 0;
    for (String soma: expressao.split("\\+"))
      resultado += Integer.valueOf(suma);
    return soma;
  }
}
Por enquanto esta sua calculadora só faz a soma, e claro como ela é muito simples não estamos aplicando técnicas de orientação a objeto nela, então um teste válido para ela seria:
import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CalculadoraTest {
  @Test
  public void avaliaAExpressao() {
    Calculadora calculadora = new Calculadora();
    int soma = calculadora.operacao("1+2+3");
    assertEquals(6, soma);
  }
}
A classe de teste é uma classe java normal, a única diferença aqui é que a anotação @Teste marca quais são os métodos que vão testar o código, o que neste caso estamos avaliando uma operação de soma. O método assertEquals valida se o resultado retornado pelo método soma é igual a 6, assertEquals valida se o primeiro atributo é igual ao segundo.
Mas não temos o == para validar a igualdade, por que então é que usamos o assertEquals?
O JUnit provê vários métodos que inciam como assert e nos ajudam a validar dados, e usar eles facilita a nossa vida, pois quando uma validação falha, ele automaticamente gera um relatório daquela falha.

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CalculadoraTest {
  @Test
  public void avaliaAExpressao() {
    Calculadora calculadora = new Calculadora();
    int soma = calculadora.operacao("1+2+3");
    assertEquals(6, soma,"Resultado da soma inválido");
  }
}
Os métodos de assert possuem um argumento de mensagem, que será colocada no log dos testes e pode nos ajudar a achar o problema no caso de uma falha.

Mock de objetos

O JUnit é muito bom para testar as regras do código que não dependem de nenhum serviço externo, como uma classe de outra camada, um banco de dados, algum webservice ou mesmo uma api de terceiros.
Mas e quando o código a ser testado depende de algo externo?
Um dos intuitos dos testes unitários é testar apenas uma parte do código, eles fazem parte do processo de desenvolvimento e devem rodar bem rápido. Quando o código depende de algo externo, um banco de dados, ou de um serviço externo, pode impactar bastante  na performance de execução de um teste de unidade. 
E como fazemos para resolver este tipo de problema?
Uma das formas de resolver isso, é usando um objeto Mock, um Mock nada mais é que um simulador de resultados, com um mock podemos simular qual vai ser o resultado de um select no banco, sem precisarmos nos conectar a ele, com isso conseguimos simular o funcionamento da parte do código que realiza operações no banco de dados, sem ter que acessá-lo.
EasyMock e JMock são dois exemplos de frameworks de mock para Java.
Chamamos de mock a instância de uma classe que vamos simular, por exemplo ao testar o validador, apresentado aqui, usariamos o mock para mockar os objetos que não são usados no teste:

public class ValidadorDeSenhaTest extends EasyMockSupport{

   private MessageFactory msg = createNiceMock(MessageFactory.class);

   @TestSubject
   private final ValidadorDeSenha validador = new ValidadorDeSenha(msg);

   @Rule
   public ExpectedException excecao= ExpectedException.none();

   @Mock
   private FacesContext context;
   @Mock
   private UIComponent uiComponent;

   @Test()
   public void testSenhaErrada() {
       String mensagem = "Mensgem";
       excecao.expect(ValidatorException.class);
       excecao.expectMessage(mensagem);
       String valor = "1234A";      
       expect(msg.getMessage("senhaInvalida")).andReturn(mensagem);
       replay(msg);
       validador.validate(context, uiComponent, valor);
   }
Esta implementação foi feita através do EasyMock, e podemos notar isso logo na extensão da classe EasyMockSupport, esta classe irá ajudar a trabalhar com os mocks.
O primeiro ponto deste teste é a utilização do método createNiceMock na classe MessageFactory, este método é utilizado para criarmos um mock da classe MessageFactory, esta classe será criada sem nenhum parametro, ou método implementado, por enquanto.
Esta classe será utilizada no construtor da classe ValidadorDeSenha, que vai ser o nosso sujeito do teste (@TestSubject), esta anotação está nos dizendo onde é que os objetos mockados serão utilizados.
O método de validação dispara uma exceção quando a validação falha, por isso usamos a anotação @Rule para criar uma regra de exceção. Dentro do método de teste este objeto, excecao, será configurado para esperar uma exceção do tipo ValidatorException, com a mensagem "Mensagem". Estas configurações foram adicionadas para validar se realmente o objeto mock foi utilizado, já que a mensagem disparada pela implementação é a mensagem configurada no arquivo de properties.
Para mockar um objeto, basta adicionarmos a anotação @Mock sobre os objetos mockados, e como o método validador recebe um objeto FacesContext e um UIComponent, que não serão utilizados na sua implementação, podemos mocka-los sem problemas.
Repare que o método expect é utilizado para dizer ao mock que haverá uma chamada ao método getMessage, com o parametro "senhaInvalida", e este método quando chamado irá retornar a mensagem "Mensagem".
Quando um mock é criado ele não possui nenhuma implementação de método e se algum método do mock for executado, ele irá disparar uma mensagem. O método expect server para simular chamadas aos métodos, podendo determinar o seu retorno e isso é que torna possível emular uma camada do nosso software, como um banco de dados.
Neste exemplo há apenas uma chamada a um método, mas poderiam ser várias, o único detalhe é chamar o método replay depois de configurar todas as chamadas a métodos, com isso o easymock implementa todas as expectativas no objeto mockado e o teste pode ser executado.

Teste de integração

O teste de integração é feito em sistemas que possuem conexões com outros sistemas, este teste visa validar se a troca de informações entre diversos sistemas acontecem como o esperado.
Imagine um sistema de banco, que deve-se conectar com o sistema de outros bancos para trocar informações das transferências interbancárias realizadas.
Estas informações trocadas entre os sistemas, devem obedecer a um padrão estabelecido pelo Banco Central, porém para o banco, é muito importante que esta troca de informações seja testada, para evitar problemas na contabilização destas operações entre bancos.

Teste de regressão

Ao trocarmos a versão de um software, algumas funcionalidades podem sofrer alterações e é pra isso que servem os testes de regressão, eles irão testar se as funcionalidades de uma versão, continuam funcionando da mesma forma, em uma versão futura do software, e ele garante que uma nova versão não irá quebrar o que já funcionava bem na versão anterior.

Processo de Desenvolvimento

Não há dúvidas que os testes são uma parte muito importante no desenvolvimento de software, mas eles em si, não garantem que todos os bugs serão eliminados. 
Em um processo de desenvolvimento de software, quando um bug for encontrado, deve-se arrumá-lo e criar um teste para aquele caso, que provavelmente não estava sendo testado antes, senão o bug não existiria. Mas se o bug já foi arrumado, por que deve-se criar um teste para ele? Assim como o software foi para produção com aquele bug uma vez, nada impede que no futuro um desenvolvedor volte a alterar aquele código, de forma a recriar o bug. O teste criado durante a correção do bug, garante que ele não vá acontecer em uma versão futura, e se acontecer o teste irá falhar gerando um alerta para o desenvolvedor.

Test Driven Development (TDD)

O TDD prega que devemos implementar primeiro os testes e depois o código, com isso o desenvolvedor consegue planejar e pensar melhor no código a ser implementado naquale funcionalidade do sistema. 
No primeiro exemplo deste post, há uma calculadora, mas esta calculadora só executa a soma. Assumindo que esta calculadora irá implementar somente as operações básicas, soma, subtração, divisão e multiplicação, usando o TDD, o primeiro passo seria implementar um teste para cada uma destas operações. Com isso todos os testes falhariam no começo, já que a implementação do código da calculadora, ainda nem começou.
Passo a passo o desenvolvedor vai implementado os métodos, de maneira que os testes passem e o seu trabalho só termina quando todos os testes passarem.
Se todas as regras de negócio da calculadora, o que neste caso eram as operações aritiméticas básicas, estiverem sendo testadas, ao final deste processo ele garante que as regras foram implementadas.
Desenvolver software usando TDD, obriga o desenvolvedor a pensar nos cenários de teste antes de implmentar a regra de negócio e com isso, quando ele for implementar as regras, ele vai fazer isto pensando em evitar os erros pré planejados nos testes. Isto faz com que a qualidade do software implementado por ele aumente.
Mas o TDD não é feito somente de vantagens, e ele também não se encaixa em todos os projetos.
Em alguns casos, quando o projeto as regras são alteradas constantemente, usar TDD pode atrapalhar, já que ficar refazendo os testes toda hora terá um custo de desenvolvimento. Além das regras, pode ser que o design do projeto também se altere algumas vezes.É claro que um projeto que as regras ou o design são alterados constantemente tende a não ser um bom projeto.
Testes de algoritmos muito complexos serão difíceis de implementar, o que pode atrazar o desenvolvimento do produto.
Alguns autores citam que o tempo adicional de se escrever o código do teste é uma desvantagem deste processo, mas isto é muito discutido uma vez que a cada bug evitado pelo teste, estará evitando um grande e moroso processo de abertura de um problema no sistema, podendo impactar na experiência do usuário.

Usar ou não?

Há uma grande discussão na comunidade de desenvolvimento des software sobre usar ou não usar testes unitários. Existem vários argumentos contra e vários argumentos a favor.
Muitas pessoas argumentam que desenvolver testes é uma perda de tempo, pois "perde-se"  muito tempo desenvolvendo os testes, quando este tempo poderia ser usado para desenvolver mais funcionalidades.
É certo que ao desenvolver testes junto com o código, gasta-se mais tempo no desenvolvimento, mas entenda que este tempo gasto a mais, certamente será compensado ao evitar erros.
Não há dúvida que fazer testes unitários poupa tempo no desenvolvimento de software, entenda que o ciclo de desenvolvimento de um software é um processo longo, e garantir que as regras de negócio estão funcionando, e continuam funcionando a cada build, é um passo importante para garantir a qualidade no processo de desenvolvimento.
O tempo gasto para se corrigir um bug, assim com o seu custo, é exponencialmente maior, quanto mais tempo se passou do desenvolvimento daquela funcionalidade, assim quanto antes descobrimos um bug, mais fácil será para arrumá-lo.
Criar testes que exercitam as regras de negócio implementadas, ajuda o desenvolvedor a descobrir erros em sua implementação e como a implementação dos testes é feita junto ao desenvolvimento do software, os bugs encontrados serão corrigidos bem mais facilmente.
Mesmo que o teste implementado não ache nenhum problema, seu tempo de implementação será compensado pela garantia de que aquela regra continuará funcionando, uma vez que uma modificação futura poderia quebrar aquela regra, o teste garante que isso não vai acontecer, e caso aconteça, o desenvolvedor será avisado.

Ferramentas de análise de código

Existem várias ferramentas de análise de código, como o PMD. Estas ferramentas fazem busca por códigos que nunca são utilizados, possíveis bugs, como as NullPointerExceptions, complexidade ciclomática das classes, relativo a quantidade de caminhos possíveis dentro do código, ifs e whiles vazios, código duplicado.
FindBugs é uma outra ferramenta de análise de código que visa encontrar problemas na implementação, como acesso de índices fora de um array, objetos não utilizados, código fora de padrão.
Este tipo de análise contribui para a qualidade do código desenvolvido por um time, ajudando na padronização do código desenvolvido. Estas ferramentas também podem calcular quanto tempo será gasto para tornar arrumar código do software analisado, como acontece com o Sonar, que é uma plataforma que une diversas destas ferramentas afim de facilitar a delas no processo de desenvolvimento. O sonar cobre desde arquitetura, estilo de código, quanto bugs, possíveis bugs e comentários.

Cobertura de código

Um outro tipo de ferramenta bastante utilizado junto com os testes, são as ferramentas de cobertura de código.
Como exemplo temos o emma, o coverage, o cobertura e o JaCoCo. Estas ferramentas marcam quais partes do código foram testadas, fazendo com que fique fácil vizualizar o que não está sendo testado e assim contruir um teste para passar por aquela parte.
Mas devemos ter 100% de cobertura de código, ou seja, os nossos testes devem passar obrigatoriamente por todo o código desenvolvido? Isso garante que o software será livre de bugs?
Não, nenhum, nem outro, quanto maior for a cobertura de código por testes, mais tempo será gasto no desenvolvimento deles. Não há um número exato de cobertura que garanta que o codigo está bom, e nem devemos perseguir este número.
Estas ferramentas não garantem que todas as possibilidades do software foram testados, elas só mostram para o desenvolvedor qual parte do código ainda não foi testada. A grande vantagem destas ferramentas é apontar ao desenvolvedor quando uma parte do código, que deveria ter sido testada, ainda não foi. Assim fica fácil vizualizar e construir um teste para cobrir o que deveria ter sido testado mas não foi.

segunda-feira, 19 de outubro de 2015

Evitando exceções ao gravar com JPA e Hibernate - um guia prático

Muitos usuários tem problemas ao gravar dados de múltiplas tabelas, neste post serão explicados como evitar este tipo de problema.

Exceção de Detached Object

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: br.unip.dsd.modelos.Usuario
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141) 
Este é um erro muito comum de acontecer com objetos java. Esta exception ocorre quando tentamos gravar um objeto que já está no banco, ou seja tem um id definido (no caso dos objetos em que o id for gerado pelo banco). Mas estamos tentando gravar ele em uma outra conexão, por exemplo você buscou o objeto no banco de dados, fechou a conexão ou transferiu ele para um outro bean, como estamos fazendo no caso da gravação do usuário aqui, usando este código:
<h:commandButton value="Continuar"
 action="#{usuarioDetalheBean.registrar(usuarioBean.usuario)}" class="btn btn-default btn-lg"
        ajax="false">
  <f:param name="includeInSalvar" value="true" />
 </h:commandButton> 
Repare que neste código  que o valor da entidade usuario, é repassada do usuarioBean para o usuarioDetalheBean. Com isto este objeto ficará desligado do banco, uma vez que o objeto que inseriu ele no banco, o usuarioBean, é diferente do objeto que irá gravá-lo novamente. Lembre-se que ao gravar um objeto relacionado com o usuário, no caso o usuário bean, o hibernate irá tentar gravar também o objeto usuário, já que este objetos são relacionados pela chave primária:
        @OneToOne
 @PrimaryKeyJoinColumn
 @MapsId
 private Usuario usuario;
Esta exceção é um tipo de proteção do hibernate, pois ele não sabe há quanto tempo este objeto foi pego do banco, e muito menos se houve alguma alteração, por isso ele dispara esta exception.
Para resolver este problema, vamos reatachar este objeto ao banco, para fazer isto muitas pessoas recomendam ou atualizar, ou fazer um merge deste objeto. Estes métodos fazem uma operação de atualização no banco de dados, mas como no nosso caso este objeto não deve ter sido alterado, então vamos buscá-lo novamente no banco, assim:

  Usuario usuarioBanco = repositorioUsuario.findOne(usuario.getId());
  usuarioDetalhe.setUsuario(usuarioBanco);
Desta forma, ao gravarmos o objeto usuário detalhe, o hibernate não irá disprar nenhuma exceção.

TransientPropertyException ao salvar relacionamentos

Uma outra exception bem comum de acontecer é a de TransientException, como mostrado abaixo:
org.hibernate.TransientPropertyValueException: object references an unsaved 
transient instance - save the transient instance before flushing
Esta exception acontece quando tentamos salvar um dado de algum relacionamento, geralemente onetomany ou manytoone, e o dado filho não está gravado.
Por exemplo, em nossa estrutura mostrada nesta figura ao salvarmos o UsuarioDetalhe, sem antes termos salvo os dados do Endereço, esta exception será disparada.
E como resolver este problema?
Existem duas formas, a primeira é alterar o tipo de cascada do relacionamento:

@Entity
public class UsuarioDetalhe {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;

 @OneToOne(cascade=CascadeType.ALL)
 @PrimaryKeyJoinColumn
 private Endereco endereco;
Veja que ao setarmos o tipo de relacionamento para cascade=CascadeType.ALL todas as operações que forem realizadas no objeto pai, neste caso o UsuarioDetalhe, serão também realizadas no objeto filho. Portanto ao salvarmos o objeto UsuarioDetalhe, o objeto Endereco também será salvo.
Existem algumas ressalvas a este tipo de solução:
Este tipo de cascata é bem perigoso, pois ao configuar isso em todos os seus relacionamentos, um simples delete em uma tabela de alta hierarquia, com muitos relacionamentos filhos, podem apagar muitos dados do seu banco de dados. É bem fácil de perder o controle com este tipo de configuração.
A outra forma de evitarmos esta exception é:
@Transactional()
public void gravaDetalhe(Usuario usuario,UsuarioDetalhe usuarioDetalhe, Endereco endereco, Rua rua, Cidade cidade, 
  Estado estado, TipoLogradouro tipoLogradouro, CEP cep) {
 Usuario usuarioBanco = repositorioUsuario.findOne(usuario.getId());
 usuarioDetalhe.setUsuario(usuarioBanco);
 repositorioCidade.save(cidade);
 cep.setCidade(cidade);
 repositorioRua.save(rua);
 cep.setRua(rua);
 repositorioEstado.save(estado);
 cep.setEstado(estado);
 repositorioTipoLogradouro.save(tipoLogradouro);
 cep.setTipoLogradouro(tipoLogradouro);
 repositorioCEP.save(cep);
 endereco.setCep(cep);
 repositorioEndereco.save(endereco);
 usuarioDetalhe.setEndereco(endereco);
 repositorioUsuarioDetalhe.save(usuarioDetalhe);  
}
Aqui neste método todos os objetos são salvos em uma sequência lógica, obedecendo a ordem, primeiro salva-se os filhos, para depois salvar os objetos com hierarquia mais alta:
Usuário -> Endereço ->  Cidade, Estado, Rua e Tipo de Logradouro -> CEP
Com isto, todos os objetos já estarão salvos ao salvarmos o UsuarioDetalhe, que é o objeto pai de toda esta hierarquia.
Todas estas operações estarão dentro de uma mesma transação, garantidos pela anotação @Transaction, conforme mostrado aqui.

Exemplo de cadastro de Usuários com o JSF

Como fazer um cadastro de usuários com o JSF?
Seguindo na sequência de como desenvolver um aplicativo web completo com Java e JSF
Todo o código referente a estes posts estão no github.

Estrutura do banco

Como dito anteriormente, o primeiro passo para se construir um sistema é a definição da estrutura do seu banco de dados, a estrutura escolhida para este passo do sistema está na Figura 1.
Figura1: Estrutura do banco de dados
A tabela de usuário só possui o nome do usuário e o email dele, note que neste caso o sistema só permite um email por usuário, se o seu sistema necessita de mais de um email por usuário, você deverá criar uma tabela específica para os emails do usuário.
Foi criada uma tabela separada para a senha do usuário afim de garantir uma maior segurança dos dados. Como a senha está isolada em uma outra tabela, ela só será pesquisada pelo sistema quando o usuário logar, ou quando houver alguma alteração da senha.
Caso a senha estivesse na mesma tabela do usuário, os dados da senha ficariam trafegando pelo aplicativo toda hora em que os dados do usuário fossem requisitados, o que aumenta a possibilidade de alguém coletar esta informação, e mesmo que criptografada, pode ser que esta senha seja descoberta.
Na parte do endereço do usuário, houve uma normalização dos dados de cidades, estados e ruas. Note que nem sempre esta normalização é necessária, poderíamos ter uma tabela de endereço com todos estes dados, mas a normalização deles nos traz a vantagem de não termos que repetir o cadastro de dados que já existam.
Todas estas tabelas estão ligadas a tabela CEP, para que com isso seja possível localizarmos estes dados pelo CEP, assim que o usuário digitar um CEP que já existe no sistema ele deve trazer os valores daqueles campos, facilitando o cadastro das informações do endereço.
Porém isto também levanta um alerta, e se o usuário estiver cadastrando um CEP e aquele valor já existir para uma certa Rua de um município? Bom alguns municípios pequenos possuem apenas um CEP, com isso todas as ruas deles estão em um mesmo valor do CEP, assim podemos ter dados de CEPs que só possuem o estado e a cidade.
Para tratarmos estes casos, devemos ter uma certa lógica de negócios ao utilizarmos o cadastro do CEP. Uma das atitudes que podem ser tomadas é nunca cadastrar todos os dados do CEP, quando ele tiver sendo inserido pela primeira vez em uma cidade. Se uma cidade nunca foi cadastrada no sistema, o CEP só vai conter os valores do estado e da Cidade, e quando houver um segundo cadastro, com a mesma rua, ai sim completa-se a informação do CEP. Mas fazer isso durante o cadastro do endereço do usuário não é uma boa opção, já que o sistema deve fazer algumas queries para realizar estas operações, entõa o ideal é deixar isto para um sistema de batch que rode de tempos em tempos.
E o que fazer quando duas ruas foram cadastradas para um CEP de uma cidade, mas esta cidade não possui CEP único? Neste caso, você pode tentar tratar pela maioria, ou mesmo fazer uma análise manual, isto vai depender da quantidade de dados que a sua base tem neste caso.

Validações e regras de negócio

Existem algumas validações que podemos e devemos fazer durante este cadastro, por exemplo se a senha possui no mínimo 6 caracteres e 2 dígitos. Para fazer este tipo de validação iremos usar uma classe validadora do JSF, para isso deve-se adicionar a tag validator no seu componente, como mostrado a seguir em destaque:
<h:inputSecret id="senha"
                        value="#{usuarioBean.usuarioSenha.password}"
                        validator="validadorDeSenha"
                        required="#{not empty param.includeInSalvar}"
                        pt:placeholder="Qual é a sua senha?" />
O código de validação será implementado em um bean que implementa a interface Validator do faces. O único método que esta interface define é o validate, como mostrado abaixo:
public void validate(FacesContext context, UIComponent iuComponent, Object valor)
            throws ValidatorException {
        if(!padraoSenha.matcher(valor.toString()).matches()){
            MessageFactory msg = new MessageFactory();
             throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
                            msg.getMessage("senhaInvalida"), null));             
        }
    }
FacesContext é o contexto faces e podemos usar ele para, por exemplo redirecionar o usuário para uma outra página de erro. O componente é o objeto referente ao componente da tela que está sendo validado e finalmente o Objeto será o valor digitado pelo usuário.
Neste caso estamos apenas validando o valor digitado, usando REGEX (regular expression), nossa REGEX de validação de senha verifica se os valores digitados estão entre 6 e 20 caracteres de tamanho, e se ela contém pelo menos 2 letras e pelo menos 2 números, abaixo segue a implementação do REGEX:
((?=.{2,}\\d)(?=.{2,}[a-zA-Z]).{6,20})
(
   (?=.{2,}\\d) verifica se contém pelo menos 2 ({2,}) dígitos \\d
   (?=.{2,}[a-zA-Z]) verifica se contém pelo menos 2 ({2,})   letras(a-zA-Z) de a a z minúsculo e A a Z maiúsculo
   .{6,20} verificacao de tamanho;
)
Repare que para o regex a definição de números é dada por \d mas como \ no java é um caracter especial, devemos utilizar o caracter de escape que também é uma \, por isso temos \\d.
Para utilizar o regex em java usa-se a classe chamada Pattern:
private Pattern padraoSenha=  Pattern.compile("((?=.{2,}\\d)(?=.{2,}[a-zA-Z]).{6,20})");
Regex é um tema muito amplo que não cabe ao escopo deste post, por isso não vamos discutí-lo aqui.package br.unip.dsd.bean.validador;
Basicamente a classe validadora segue os mesmos princípios do ManagedBean e para definirmos ele, usamos a anotação @FacesValidator sobre a classe. Com isso ele poderá ser acessado nas suas páginas usando o nome da classe, no mesmo padrão dos beans, primeira letra minúscula e demais de acordo com o nome da classe.
Quando o usuário digitar uma senha que não atende aos padrões do nosso regex, uma mensagem de erro será mostrada para ele ao lado do componente.
Porém esta ação de validação acontece nas ações da tela, por exemplo, quando o usuário clicar no botão de salvar.
Quando a validação falha uma exceção do tipo ValidatorException é disparada, e este é o comportamento padrão dos validadores. Repare que nada deverá ser feito se o valor digitado pelo usuário estiver correto.
Lembre-se que cada pacote de beans adicionado ao projeto, deverá ser adicionado a propriedade basePackages, da anotação @ComponentScan, na classe de configuração, que no nosso caso é a JPAConfig.

Usando enums internacionalizados para os dados tipados

É comum o uso de enumerations para dados tipados, que raramente terão os valores alterados. Os enums basicamente são uma lista de valores introduzidos no código e que são gravados em um campo. Para gravarmos os valores dos enums em um campo existem duas possibilidades:
  1. usar um id no enum, valor numérico
  2. usar o toString do enum
Abaixo segue um exemplo de como usar um enum no modo string:
        public List<SelectItem> getEstadoCivil() {
           List<SelectItem> estadoCivil = new ArrayList<SelectItem>();
           MessageFactory msg = new MessageFactory();
       
           estadoCivil.add(new SelectItem(EstadoCivil.CASADO.toString(), msg.getMessage("casado")));   
           estadoCivil.add(new SelectItem( EstadoCivil.SOLTEIRO.toString(), msg.getMessage("solteiro")));
           estadoCivil.add(new SelectItem(EstadoCivil.DIVORCIADO.toString(), msg.getMessage("divorciado")));
           estadoCivil.add(new SelectItem(EstadoCivil.VIUVO.toString(), msg.getMessage("viuvo")));
           return estadoCivil;
    }
O código é bem simples, o primeiro ponto a observar é a utilização de um MessageFactory, que está sendo usado aqui para a internacionalização dos valores do enumeration.
SelectItem é um objeto utilizado para mostrarmos dados como uma lista, em uma combobox, para que o usuário selecione o valor desejado.
Através do SelectItem são enviados dois valores de String para a tela, primeiro o valor do enum internacionalizado, que irá aparecer na lista para o usuário. Depois o valor do enum transformado em String, que será usado para gravarmos no banco.

Amarrando os dados 

No próximo post será explicado como evitar exceções ao usar o jpa e hibernate.

segunda-feira, 12 de outubro de 2015

Camada de Serviço em um aplicativo Web com Java e Spring

Continuando com os posts de desenvolvimento de um sistema web, aqui será descrita a utilização da camada de serviços.
Para um sistema simples as regras de negócio podem ser colocadas no controller, mas quando o sistema começa a ficar um pouco mais complexo, o ideal é criarmos uma camada de serviço em nossa aplicação.
Esta camada nada mais é do que um local onde ficarão concentradas as regras de negócio do sistema.
Validações simples dos dados, como o tamanho de um campo (senha com no mínimo 6 caracteres, etc) são feitas no controller. Já validações como, este usuário já está inserido no banco?, transações e alterações em múltiplas tabelas do banco de dados, verificações específicas de um sistema, como validação de o aluno tirou nota mínima para ser aprovado ou não. Este tipo de regra fica na camada de serviço.
Em nosso sistema a demarcação desta camada é feita pela anotação @Service. Esta anotação define um bean da camada de serviço.
Toda parte do sistema que tiver uma regra mais complexa, como a inserção de usuários, que inserem valores em duas tabelas, são feitas nesta camada.
No sistema desenvolvido aqui, esta camada ficará nesta parte do repositório.

Controlando Transações do Banco de dados no Java com o Spring Transaction

Seguindo a construção do nosso sistema de cadastro de usuários, devido a estrutura normalizada do nosso banco de dados, ao registrar um usuário, deveremos gravar os dados da entidade usuário (nome e email) em uma tabela, e da entidade usuarioSenha (senha do usuário) em outra tabela. Mas como fazer isso sem que tenhamos algum problema no meio do caminho?
Um problema clássico ao gravarmos dados em duas tabelas dentro de uma mesma operação é acontecer algum problema (exceção), após os dados terem sido salvos na primeira tabela. Com isso, ao não transacionar a gravação, os dados serão salvos na primeira tabela e não serão salvos na segunda, fazendo com a que a base de dados fique inconsistente, uma vez que os dados como nome e email do usuário foram gravados, porém a sua senha não. E o primeiro sintoma disto é a impossibilidade do usuário logar no seu sistema.
E como resolver este problema?

@Transações em Banco de Dados

O conceito das transações em banco de dados é descrito pela sigla ACID (Atomicidade, Consistência, Isolamento e Durabilidade)
  • Atomicidade: A transação deve ser considerada como uma sequência de operações única, o que significa que ou todos os dados serão gravados de uma vez só, executando as operações nas múltiplas tabelas, ou nenhum dado será gravado no banco, quando houver algum problema, ou caso alguma validação falhe.
  • Consistência: Que representa a consistência dos dados gravados no banco, como a unicidade de uma chave primária, a existência das referências e chaves estrangeiras, etc
  • Isolamento: Podem existir muitas transações sendo processadas ao mesmo tempo no banco de dados, porém elas devem estar isoladas de maneira que uma não interfira na outra.
  • Durabilidade: Assim que uma transação completa, os seus dados devem estar armazenados no banco, de maneira que eles não podem mais ser apagados/corrompidos, devido a uma falha do sistema do banco.
Um sistema de armazenamendo RDMS garante todas as propriedades descritas acima. No SQL os comandos usados para transação são:
  • Begin: inicia uma transação
  • Commit: grava os dados de todas as operações da transação no banco
  • Rollback: desfaz as operações da transação e o banco retorna ao estado em que ele estava antes de a transação iniciar

Transações Globais e Locais

As transações locais, envolvem apenas uma conexão com o banco de dados, enquanto uma transação global pode afetar múltiplos sistemas, como um banco distribuído. 
Um sistema de transação local, possui apenas uma máquina com o banco de dados e por isso são mais fáceis de serem administradas, já um sistema global pode ter múltiplas máquinas espalhadas por várias localidades diferentes.

Transacionando os seus métodos

E como fazer para transacionar as operações com o banco de dados?
O Spring Transaction possui uma anotação chamada @Transactional esta anotação deverá ser colocada nos métodos que contém o código com a regra de negócio de alteração do banco.
        @Transactional
public void gravaUsuario(Usuario usuario, UsuarioSenha usuarioSenha) {
usuarioSenha.setUsuario(usuario); // Por que esta linha?
repositorioUsuarioSenha.save(usuarioSenha);
}
Com isso todas as operações dentro deste método serão encapsuladas dentro de uma transação no banco de dados.
Repare que há uma pequena regra de negócio neste método que é a alteração do id do usuário senha depois do usuário ter sido inserido no banco de dados. Por que esta linha é necessária?
Esta linha está criando o relacionamento das duas entidades.
Mas por que não estamos gravando a entidade usuário?
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
private Usuario usuario;
Na definição do relacionamento das duas entidades estamos marcando o tipo de cascata (cascade = CascadeType.ALL) ou seja, todas as operaçoes realizadas em uma entidade, também serão realizadas na outra, portanto, ao gravar os dados de usuarioSenha, os dados do usuário também serão gravados.
Entenda que todas as operações realizadas dentro do método gravaUsuario, serão feitas dentro da mesma transação, portanto, uma outra opção de gravação dos dados seria:
        @Transactional
public void gravaUsuario(Usuario usuario, UsuarioSenha usuarioSenha) {
repositorioUsuario.save(usuario);
usuarioSenha.setUsuario(usuario); // Por que esta linha?
repositorioUsuarioSenha.save(usuarioSenha);
}
Mesmo fazendo assim, devido a anotação @Transactional, todas as operações deste método estariam dentro da mesma transação.

Transações encapsuladas

Tudo certo até agora, e transacionar uma operação com o spring é bem tranquilo, mas o que acontece quando um método transacionado, chama um outro método transacionado?
Isto pode acontecer quando um método que cria uma outra entidade e também criará um usuário, o que pode acontecer por exemplo em uma integração de sistemas. Este caso é chamado de nested transaction e existem algumas formas de tratarmos isto.
O tratamento destes casos é feito pela propriedade propagation, e existem alguns tipos de propagação de transação:

  • Mandatória: Propagation.MANDATORY - necessita que uma transação já tenha sido criada, caaso não tenha nenhuma transação ele dispara uma exceção TransactionRequiredException. Só deve ser usado em métodos que serão utilizados somente dentro de outros métodos transacionais.
  • Aninhada: Propagation.NESTED - O bloco de código será executado dentro de uma nova transação aninhada, caso uma transação já exista, caso contrário uma nova transação será criada. Esta transação aninhada será uma parte da transação já existente e será criado um ponto de checagem antes da criação da transação aninhada. Caso a transação aninhada falhe, o banco volta ao estado anterior a criação da transação aninhada. Os dados desta transação aninhada só será comitada ao final da transação existente.
  • Necessita de uma nova - Propagation.REQUIRES_NEW - uma transação nova e independente será criada ao início deste método. A transação anterior será pausada antes do início deste método e ela irá continuar assim que a transação corrente terminar.
  • Nunca propague: Propagation.NEVER - O método não suporta uma transação e dispara uma exceção caso uma transação anterior exista.
  • Não suportada: Propagation.NOT_SUPPORTED - Este método não necessita de transação e nenhuma transação será criada nele. Caso uma transação exista, ela será suspensa durante a execução deste método.
  • Suporta: Propagation.SUPPORTS - Uma transação não é necessária neste método, se alguma transação já existir ela será usada, caso contrário não.
  • Necessária: Propagation.REQUIRED - A transação é necessária para este método, caso não tenha nenhuma transação criada, uma nova será criada, caso já exista uma transação ele utilizará ela. Este é o valor padrão de uma transação.

Somente Leitura:

Podemos utilizar uma transação somente leitura, geralmente usada para métodos de select, e para fazer isto usamos o atributo readonly:
@Transactional(readOnly=true)




    sexta-feira, 18 de setembro de 2015

    Introdução ao Linux, Sistemas Operacionais Abertos

    Introdução ao Linux

    Aqui vão ser explicados alguns comandos básicos do linux, para testá-los na sua casa você deve acessar o terminal. Cada distribuição do linux possui uma configuração de um sistema de janelas diferentes, mas todos vem com um terminal instalado. Para acessá-lo, encontre o sistema de busca de programas, e digite terminal. O logo do terminal geralmente é um quadrado com o fundo preto, ou vermelho, abra ele e pronto, você já está apto a usar os comandos que serão mostrados aqui.
    Uma outra forma de acessar o terminal é usando as teclas ctrl + alt + 1 isso te leva ao modo texto do linux. Agora é só digitar o seu usuário e senha e pronto, você já estará acessando o terminal.
    No linux a caixa da letra (maiúscula e minúscula) fazem a diferença, então tudo o que será mostrado aqui, deve ser seguido a risca.

    Top

    O comando top mostra uma lista dos processos que estão sendo executado no seu sistema. Em cima da tela ele exibe um cabeçalho, onde estão informações como o número total de Tasks, quantas estão rodando, quantas estão dormindo, quantas estão paradas. Qual é a porcentagem da cpu que está sendo utilizada, qual é a quantidade da memória está sendo utilizada, incluindo o SWAP (memória virtual).
    Abaixo do cabeçalho, temos uma lista dos processos, ordenados pela ocupação da CPU, ou seja, os que mais estão consumindo processamento, serão mostrados primeiro.
    Dentre as informações importantes que temos no top, estão o PID (proccess id), que é o id do processo no sistema. O pid será usado mais afrente neste post, na definição do comando kill.
    Quanto cada processo está ocupando do processador, memória e ha quanto tempo ele está rodando, quem é o usuário dono do processo (aquele que iniciou o processo).
    Ele se atualiza a cada 3 segundos, por padrão, mas para alterar isso basta usar o argumento -d
    top -d 10
    Faz com que a lista se atualize a cada 10 segundos.

    Aptitude (apt-get)

    No início, a instalação de um software no Linux era uma tarefa complicada, todo software que era instalado, deveria ser compilado pelo usuário, e no seu processo de compilação, ele checava as dependências daquele software. Por exemplo, se o software A depende do B, você deve instalar primeiro o B, para depois instalar o A. Esta lista inicialmente era bem pequena, mas depois de algum tempo, administrar todas estas dependências tornou-se um processo complicado.
    Algumas distribuições como o SuSe (da novel), o RedHat, o Debian e o e algumas outras, criaram a sua forma de facilitar o processo de instalação de softwares no Linux.
    O apt-get é o administrador de pacotes das distribuições baseadas no Debian, dentre elas o Ubuntu, o Mint, mas ele também pode ser instalado nas outras distribuições.
    O funcionamento do apt-get se dá da seguinte maneira. Cada distribuição possui uma lista de servidores onde ficam armazenados os softwares que estarão disponíveis para serem utilizados. Estes servidores também possuem uma lista de quais softwares e quais versões estão disponíveis nele.
    Toda configuração de onde o apt-get irá procurar e baixar os softwares ficam aramazenadas no arquvo:
    /etc/apt/sources.list  (em algumas distribuições este arquivos pode ter um nome ou uma localização diferente desta)
    Existem diversas fontes diferentes de softwares, como pendrives, cdroms, dvds e tudo isso pode ser configurado neste arquivo:
    deb http://sítio.exemplo.com/debian distribuição componente1 componente2 componente3
    deb-src http://sítio.exemplo.com/debian distribuição componente1 componente2 componente3
    Distribuição é onde será configurada o nome da sua distribuição, e os componentes são subdivisões de como os softwares são categorizados, e elas geralmente são:
    • main 
    • contrib
    • non-free
    Cada distribuição faz a sua subdivisão de quais softwares estarão em cada categoria.
    Os comandos do apt-get são:
    • update que atualiza a lista de pacotes disponíveis para instalação
    • upgrade atualiza os softwares instalados
    • install instala os softwares listados
    • dist-upgrade atualiza a versão da distribuição
    • remove remove um pacote
    • clean apaga os arquivos baixados para instalação
    Um exemplo de instalação seria:
    apt-get install htop

    Usuário Root e comando sudo

    Toda instalação do linux, possui um super usuário, também conhecido como root. Este usuário é quem consegue instalar, alterar as configurações e apagar os arquivos do sistema.
    Nunca devemos usar um usuário em uma estação como root, por exemplo, caso haja um vírus, se você estiver com o seu usuário, não root, o máximo que este vírus conseguirá fazer de estrado, é apagar as coisas do seu usuário e nunca, apagar um software instalado no sistema, já que isso necessita da permissão de root.
    Uma das formas de fazer com o que seu usuário tenha a permissão de root, momentâneamente, é utilizando o comando sudo.
    Para usarmos o apt-get devemos ter permissão de root, então como exemplo teríamos que usá-lo assim:
    sudo apt-get update
    Com isso o seu usuário se torna root, por alguns minutos, e o apt-get consegue rodar a instalar os softwares que você pediu.
    Um usuário só consegue usar o sudo, se ele estiver no grupo de sudo.

    Adicionando usuário ao grupo

    Existem duas formas de se adicionar um usuário a um grupo, a primeira delas é através do comando:
      useradd -G grupo usuario
    No caso para adicionar o usuário dirceu ao grupo sudo faça:
    useradd -G sudo dirceu
    A outra forma de fazer isso é editando o arquivo:
    /etc/group
    E adicionar o nome do usuário na linha do grupo do sudo:
     sudo:x:27:dirceu
    Para isso deve-se usar um editor de texto

    Editor de texto nano

    O nano é um dos editores que vem instalado na distribuição debian e vamos utilizá-lo para alterar os arquivos.
    A maioria dos comandos do nano são acessados através da tecla ctrl.
    A sua utilização é bem simples, para acessar o arquivo de grupos digite:
    suto nano /etc/group
    Com isso ele abrirá o arquivo na sua tela.
    ctrl+w
    é o comando que usamos para busca, e como queremos achar o grupo sudo, basta digitar ctrl+w sudo
    Ao achar o texto, seu cursor será direcionado para o texto encontrado, da mesma forma como acontece nos softwares que estamos acostumados.
    Os comandos básicos do nano são:
    • ctrl+x sair
    • ctrl+a tecla home
    • ctrl+e tecla end
    • ctrl+y page down
    • ctrl+v page up

    Cat

    Cat é o comando responsável por mostrar o conteúdo de um arquivo na tela.
    cat /etc/group
    Irá mostrar o conteúdo do arquivo /etc/group na sua tela, com isso podemos ver se a nossa alteração foi realmente salva no arquivo.

    Comando PS

    O comando top nos mostra os processos em exeucção no momento, já o comando ps, nos mostra uma lista com todos os processos sendo executados.
    • A mostra todos os processos
    • a todos os processos daquele terminal
    O ps é um comando bem útil quando queremos saber qual é o PID de um certo processo. Como a saída do ps -A é bem grande, vamos explicar uma forma de limitar o resultado apresentado.

    Comando | grep

    O pipe, ou | serve para encadear processos no linux. Com o pipe podemos direcionar a saída de um comando, para outro.
    Grep é o comando utilizado para realizar buscas em arquivos dentro de um diretório.
    grep dirceu *
    Procura a palavra dirceu em todos os arquivos daquele diretório.
    ps -A | grep dirceu
    Vai procurar a palavra dirceu na saída gerada pelo comando ps -A, portanto se houver algum processo chamado dirceu rodando na sua máquina, a linha dela será mostrada e com isso teremos o PID daquele processo.

    Matando um processo com o kill

    Kill é um comando que envia um sinal para um processo, e como o seu nome diz, este sinal geralmente é o de terminar o processo. Quando algo trava, podemos usar o kill para matar aquele processo e evitar termos que reiniciar a máquina. Reiniciar o Linux é uma tarefa muito rara.
    Existem mais de 60 tipos de sinais que podem ser enviados pelo comando kill, mas o que estamos mais interessados é o 9
    kill -9 1234
    Irá mandar um sinal de SIGKILL para o processo com id 1234. Lembre para saber o PID de um processo usamos o comando ps, descrito acima. SIGKILL é o sinal responsável por matar aquele processo. Este é o comando mais forte do kill. Por padrão, se nenhum comando for especificado, um comando -15 será usado SIGTERM, caso ele falhe, deveremos utilizar o kill -9 para matar o processo.
    Para matar um processo (kill -15) usando o seu nome, podemos usar o comando killall, mas saiba que o comando killall matará todos os processos que tiverem aquele nome.
    killall nano
    Envia um comando SIGTERM para todas as instancias do nano que estão sendo executadas naquela máquina.

    Tarefas em background

    O terminal do Linux é multitarefas, ou seja, conseguimos executar vários softwares nele ao mesmo tempo.
    Uma das formas de se colocar um processo em background, ou seja, executando mas sem ocupar a tela, é através da utilização do caracter &
    nano /etc/group &
    Irá fazer com que o nano seja executado em background.
    Para parar um processo que está sendo executado, e jogá-lo em background pausado, usamos as teclas ctrl+z
    Com isso o processo vai para background, mas fica parado. Podemos fazer isso com vários programas e para listá-los podemo suar o comando jobs, que irá te mostrar a lista de processos e a situação deles.
    Como o nano é um programa que precisa da tela, toda vez que jogarmos ele para background ele ficará parado.
    Exemplo de saída o jobs:
    [1]+  Stopped                 nano /etc/group
    [2]   Running                 tail -f /etc/group &
    [3]-  Stopped                 tail -f /etc/group &
    Aqui ele mostrou uma lista com três processos, cada um deles, com seu número, estado e o comando.
    Como o processo 3 está parado, caso seja necessário deixá-lo rodando em background  deve-se usar o comando bg. Bg é o comando que joga um processo para background.
    bg 3
    Fará com que o status do comando 3 mude de Stopped para Running
    [1]+  Stopped                 nano /etc/group
    [2]   Running                 tail -f /etc/group &
    [3]-  Running                 tail -f /etc/group &
     O último comando deste post é o fg, mas e se você quiser ver o resultado, ou trazer para a tela um dos processos que estão sendo executados em background? Basta usar o comando fg, que funciona da mesma forma que o bg:
    fg 3
    Coloca na tela (foreground) o processo tail -f /etc/group

    segunda-feira, 14 de setembro de 2015

    Spring Data com anotações e JSF

    No último post foi mostrado um padrão de MVC push, ou seja, a interface enviava os dados e realizava as chamadas aos controllers. Aqui será mostrado uma maneira de desenvolver uma aplicaçao Web com o MVC pull, ou seja, a interface pucha os dados da camada de serviço.

    Java Server Faces

    JSF é uma especificação do Java, baseada em componentes, para se construir a interface de um aplicativo Web. Na primeira versão do JSF as páginas eram construidas utilizando a tecnologia JSP, depois da versão 2.0 do JSF elas passaram a ser construidas sobre uma tecnologia chamada Facelets.
    Facelet é uma tecnologia da apache de modelos das interfaces web.

    Configuração de um aplicativo Web com JSF e Spring Data

    Aqui serão apresentados os detalhes da configuração do web.xml de uma aplicação JSF com Spring Data
    A primeira coisa que devemos nos preoupar, ao configurar um aplicativo web, é a configuração do seu servlet, para o JSF utilizamos o FacesServlet, que será responsável por mapear todas as requisições das páginas a camada de serviço, a sua configuração é feita por:
        <servlet>
            <servlet-name>Faces Servlet</servlet-name>
            <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
            <async-supported>true</async-supported>
        </servlet>
    A tag servlet-mapping é de extrema importância, pois é nela que declaramos onde estarão localizados os nossos arquivos de interface, o que neste caso serão todos os arquivos .xhtml da pasta raiz:

        <servlet-mapping>
            <servlet-name>Faces Servlet</servlet-name>
            <url-pattern>*.xhtml</url-pattern>
        </servlet-mapping>
    A configuração do spring por anotações é realizada pela classe AnnotationConfigWebApplicationContext, e introduzindo o texto a seguir no seu web.xml irá configurar o spring para ler as anotações do projeto e configurar os Beans a partir delas.
        <context-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </context-param>

    Depois de definir que o projeto será configurado por anotações deveremos especificar qual é a classe de configuração dos nossos beans:

    <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>br.unip.dsd.config.JPAConfig</param-value>
        </context-param>
    A última configuração a ser adicionada é a configuração dos listeners. O ContextLoaderListener é responsável por ler e parsear todas as configurações referentes ao contexto da aplicação, como a instanciação dos beans. Já o ConfigureListener irá ler e parsear todas as configurações do JSF, e a sua configuração no web.xml é assim:
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <listener>
            <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
        </listener>
    Com isso o nosso aplicativo está pronto para ser executado, agora serão mostradas as configurações do JSF.

    Configuração do JSF

    Todas as configurações dos aplicativos Web ficam em uma pasta WEB-INF, o arquivo de configuração do JSF chama-se faces-config.xml. Nele serão definidas as configurações dos beans do JSF e da classe responsável por enviar mensagens ao nosso aplicativo.
    No nosso post de MVC, as classes que recebiam as chamadas da interface eram chamadas de Controllers, aqui no JSF elas são chamadas de Managed-Bean. Um managed-bean é um Bean do Java que pode ser acessado através dos componentes do JSF, e sua configuração define o nome, que será utilizado na interface, e a sua respectiva classe:
        <managed-bean>
            <managed-bean-name>usuarioBean</managed-bean-name>
            <managed-bean-class>br.unip.dsd.bean.UsuarioBean</managed-bean-class>
            <managed-bean-scope>request</managed-bean-scope>
        </managed-bean>
    Também podemos configurar as mensagens da nossa interface, por mensagens entenda qualquer texto que será utilizado em nossa interface. Nesta configuração podemos usar a internacionalizaçao do Java. Mas o que é isto?
    Nesta configuração pode-se definir quais são as linguas suportadas pelo nosso aplicativo, e cada lingua suportada, terá um arquivo de mensagens específico. O browser detecta automaticamente a lingua do cliente, e exibe as mensagens na sua lingua específica, caso a lingua do usuário não seja suportada, ele mostrará a lingua padrão.
    Nesta configuração defini-se como lingua padrão o br (de portugues do Brasil) e en (de English, ou Inglês) como segunda lingua suportada.

      <application>
        <message-bundle>br.unip.dsd.mensagens.MensagensAplicacao</message-bundle>
        <locale-config>
           <default-locale>br</default-locale>
           <supported-locale>en</supported-locale>
        </locale-config>
        <el-resolver>
                org.springframework.web.jsf.el.SpringBeanFacesELResolver
            </el-resolver>
       
      </application>
    A tag message-bundle, define a localização dos arquivos de mensagem do aplicativo, o que será um arquivo localizado no pacote  br.unip.dsd.mensagens, chamado MensagensAplicacao.properties (para o ingles) e MensagensAplicacao_br.properties (para o portugues). Aqui é possível adicionar quantas linguas você quiser, para isso basta adicionar um supported-locale, e um arquivo de propriedades.
    Um arquivo de propriedades é um arquivo de chave, valor, ou seja, ele possui uma coluna com as chaves, nomes usados no nosso aplicativo e que deverão estar definidos em todos os arquivos utilizados aqui, e um valor, que é o texto mostrado para o usuário:
    erroEmailExiste=Email já cadastrado
    erroConfirmacaoEmail=Por favor verifique se os emails digitados são idênticos
    Acima temos um exemplo de duas mensagens de erro, sendo que as chaves, que serão utilizadas pelo aplicativo para recuperar as mensagens, estão grifadas, e os valores, que serão mostrados ao usuário estão definidos logo a frente.
    No aplicativo, para acessar estas mensagens vamos precisar de uma Factory, que é definida assim:
      private ResourceBundle bundle;
      private Locale locale;
     
      public MessagemFactory() {
        locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
          bundle = ResourceBundle.getBundle("br.unip.dsd.mensagens.MensagensAplicacao", locale);
      }
    Nestes caso o bundle é onde foi definido as nossas mensagens, e locale é onde está localizado o nosso cliente.
    Para recuperar uma mensagem basta usar um bundle.getString(valor) e pronto, ele irá cuidar de mostrar a mensagem correta ao nosso cliente.

    Managed Bean

    A definição de um bean é feita através da anotação @ManagedBean, conforme mostrado abaixo:
    @Component
    @ManagedBean
    public class UsuarioBean
    Todos os valores que forem acessados pela sua interface, devem ser definidos como propriedades do seu Bean, e eles devem ter seus métodos de get e set, para que o JSF consiga acessar e alterar os seus valores.
    Então você pode estar pensando que aqui você vai por os seus modelos? Bem não é exatamente isto o que deve ser feito. Nos beans colocamos regras de negócios, por exemplo se o seu bean for um bean de gravação de usuário, aqui podemos colocar as regras de validação dos valores digitados, assim como uma instância do repositório dos usuuários.
    Para colocar qualquer instância de qualquer bean definido no contexto do seu aplicativo, basta usar a anotação @Autowired, que toda aquela configuração colocada no web.xml irá cuidar de encontrar o bean e instanciá-lo:
    @Autowired
     private RepositorioUsuario repositorioUsuario;
    Irá prover uma instância do repositório do usuário, definido pela interface RepositorioUsuario.
    Com isso conseguimos gravar os dados recebidos da tela no banco de dados.

    Interface JSF

    Bom, já foi mostrada toda a integração e a parte de backend, mas e os componentes do JSF, como utilizá-los?
    A definição dos componentes JSF podem ser feitos em um arquivo HTML, aqui neste projeto estamos usando arquivos .xhtml. Precisamos definir as configurações do JSF nos metadados do nosso html e isso é feito usando:
    <html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:h="http://java.sun.com/jsf/html">
    Repare que definimos xmlns:h e isso determina que os componentes do jsf serão definidos como h:componente.
    Para definirmos um campo que acessa o valor de nome do nosso bean do usuário, basta fazermos:
    <h:inputText id="nome" value="#{usuarioBean.usuario.nome}"
                required="true" />
    Para que isto funcione, o nosso bean de usuário, deverá ter uma propriedade usuario, que por sua vez terá uma propriedade nome. Com isso acessamos a propriedade nome, do usuario do usuarioBean e o valor digitado no nosso campo texto será automaticamente setado nele.
    Aqui devemos considerar que para que isso funcione, o usuário não pode ser nulo, do contrário teremos uma NullPointerException sendo disparada.
    Todos os arquivos deste exemplo estão disponíveis no meu repositório e a apresentação.