domingo, 29 de setembro de 2013

A camada de visão - o managed bean do JSF

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Finalizando essa série de posts sobre Spring, JPA e JSF, vou mostrar finalmente como juntar todas as camadas e executar uma pequena aplicação para testar tudo o que foi feito até aqui.

Relembrando que a integração do JSF ocorre sem problemas desde que sejam feitas as alterações no obsoleto arquivo faces-config.xml, como já foi dito aqui: A camada de visão - Business Delegate.

Antes de executar o exemplo, é interessante deixar o arquivo persistence.xml configurado para apagar e criar as entidades, pois assim o teste pode ser refeito quantas vezes forem necessárias.

Vamos usar uma classe Pessoa,  como foi mostrado aqui: Criando entidades de bancos de dados e uma classe Autor conforme o código a seguir:

package entidades;

import java.io.Serializable;
import javax.persistence.Entity;

@Entity
public class Autor extends Pessoa implements Serializable {

    public Autor() {
    }
}

É apenas uma herança direta, já que a classe Pessoa é abstrata e não pode ser instanciada. Em seguida criamos um pacote chamado controle e dentro dele uma classe Controlador.java conforme o código abaixo:

package controle;

import delegate.FacadeBD;
import entidades.Entidade;
import entidades.Autor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;

@ManagedBean(name = "controlador")
@ViewScoped
public class Controlador implements Serializable {

    @ManagedProperty(value = "#{facadeBD}")
    private FacadeBD facadeBD;

    public Controlador() {
    }

    public String listaGravada() {
        String retorno = "";
        List<Autor> la = facadeBD.listar(Autor.class);
        retorno += "\n\nLista gravada:\n\n";
        if (la != null) {
            for (Autor autor : la) {
                retorno += "("+autor.getId()+") "+autor.getNome() + "\n";
            }
        }
        return retorno;
    }

    public String getResultado() {
        String retorno = "";
        Autor autor1 = new Autor();
        autor1.setNome("Stephen King");
        Autor autor2 = new Autor();
        autor2.setNome("Dan Brown");
        List<Entidade> listaAutores = new ArrayList<Entidade>();
        listaAutores.add(autor1);
        listaAutores.add(autor2);
        retorno += "\nInserindo dois autores\n";
        facadeBD.salvarLista(listaAutores);
        retorno += listaGravada();
        autor1.setFlagRemover(Boolean.TRUE);
        facadeBD.salvar(autor1);
        retorno += "\nExcluindo ("+autor1.getId()+") "+autor1.getNome()+"\n";
        retorno += listaGravada();
        Autor autor3 = new Autor();
        autor3.setNome("ray bradbury");
        listaAutores.add(autor3);
        retorno += "\nInserindo novo autor "+autor3.getNome()+"\n";
        facadeBD.salvarLista(listaAutores);
        retorno += listaGravada();
        retorno += "\nAlterando "+autor3.getNome()+"\n";
        autor3.setNome("Ray Bradbury");
        facadeBD.salvarLista(listaAutores);
        retorno += listaGravada();
        return retorno;
    }

    public FacadeBD getFacadeBD() {
        return facadeBD;
    }

    public void setFacadeBD(FacadeBD facadeBD) {
        this.facadeBD = facadeBD;
    }
}

Esta classe possui um atributo facadeBD, injetado pelo JSF, ou seja, é uma instância da classe FacadeBD. Podemos verificar na linha 13 que essa classe é colocada no contexto do JSF com o nome "controlador" (em minúsculas), e é como será referenciada logo a seguir, no página que será construída. Temos ainda um método listaGravada para recuperar o conteúdo do banco de dados correspondente à tabela pessoas. Podemos observar, na linha 25, o uso do método listar.

Finalmente, um método acessor getResultado na linha 35. Apesar de não termos um atributo resultado, verificamos aqui que o JSF só precisa do método acessor, e não do atributo, como já era esperado. Esse método faz várias chamadas aos métodos DAO por meio de facadeBD. É importante ressaltar que não há preocupação com conexões, transações, etc.. Vemos também que para excluir um registro do banco de dados, mudamos o atributo flagRemover para true e salvamos. No final do processamento, temos uma String com o histórico das operações, que vai ser acessada na página a seguir:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Livraria</title>
    </h:head>
    <h:body>
        <h:inputTextarea cols="100" rows="50" value="#{controlador.resultado}" />
    </h:body>
</html>

Na linha 9, temos a referência ao "atributo" resultado, acessado por meio do método getResultado do managed bean controlador. Ao executar essa aplicação, há um redirecionamento para a página de autenticação, e em seguida, ocorre o processamento nas camadas e o histórico de operações é exeibido no inputTextArea:

Inserindo dois autores


Lista gravada:

(1) Stephen King
(2) Dan Brown

Excluindo (1) Stephen King


Lista gravada:

(2) Dan Brown

Inserindo novo autor ray bradbury


Lista gravada:

(2) Dan Brown
(3) ray bradbury

Alterando ray bradbury


Lista gravada:

(2) Dan Brown
(3) Ray Bradbury

Outra observação importante é que também não há uma preocupação com as classes que vão ser gravadas. Pode ser uma lista com apenas um tipo de classe (autores) ou com mais de um tipo (autores mesclados com publicações, por exemplo). A única restrição é que as classes devem ser subclasses de Entidade.

Ainda, se observarmos as chamadas que foram feitas ao método salvarLista, veremos que em nenhum momento removemos da lista listaAutores o autor que foi excluído do banco. A classe GenericDAO já prevê tudo isso, justamente para que a camada de visão fique ainda mais desacoplada da camada de serviços, deixando para o desenvolvedor apenas a preocupação com o fluxo da aplicação e a disposição dos dados nas páginas.

E assim termina a série de posts sobre uso de JPA, Spring, JSF, MVC. Temos um projeto básico completo que será usado na próxima série de posts, ode serão abordados os relacionamentos entre entidades e como utilizar o Primefaces para construção de uma aplicação completa.

quinta-feira, 26 de setembro de 2013

A camada de visão - Business Delegate

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Quando utilizamos Business Delegate (BD), temos como principal objetivo reduzir o acoplamento entre a camada de visão propriamente dita e os serviços. Neste exemplo que venho construindo desde alguns posts anteriores essa abordagem talvez não seja necessária, mas a aplicação já ficará preparada para evoluir, se for necessário.

Vamos criar um pacote chamado delegate e duas classes semelhantes às classes da camada de controle (ou serviços):

package delegate;

import entidades.Entidade;
import java.io.Serializable;
import java.util.List;
import service.AbstractService;

public abstract class AbstractBD<T extends Entidade> {

    protected AbstractService<T> service;

    public abstract void setService(AbstractService<T> service);

    public void salvar(T entidade) {
        service.salvar(entidade);
    }

    public void salvarLista(List<T> lista) {
        service.salvarLista(lista);
    }

    public T carregar(Class classe, Serializable codigo) {
        return service.carregar(classe, codigo);
    }

    public List<T> listar(Class classe) {
        return service.listar(classe);
    }

    public List<T> consultaPersonalizada(String consulta) {
        return service.consultaPersonalizada(consulta);
    }

    public List<T> consultaNativa(String consulta) {
        return service.consultaNativa(consulta);
    }

}

package delegate;

import java.io.Serializable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import service.AbstractService;

@Component(value = "bd")
public class GenericBD extends AbstractBD implements Serializable {

    @Override
    @Value("#{genericService}")
    public void setService(AbstractService service) {
        this.service = service;
    }
}

Não temos muito o que comentar aqui. Como aconteceu com as classes "Service", temos os métodos que serão utilizados, injeção de dependências dos componentes do Spring que serão necessários, e só. O único ponto relevante é o baixo acoplamento. Mudanças podem ocorrer nos serviços sem que a camada de visão sofra qualquer alteração, e vice-versa. Além do fato de que a camada de serviços trabalha com transações e a camada de visão não percebe isso. Esse é mais um motivo para ressaltar a importância da programação voltada a interfaces (ou voltada a classes abstratas).

Com essa tecnologia, podemos ter mais de um tipo de serviço (serviços para bancos de dados e serviços para arquivos xml, por exemplo) controlados por vários business delegates. Isso permitiria mudar a maneira como se faz a persistência dos dados sem modificar nada na camada de visão. Mas é preciso ainda mais um nível de desacoplamento que pode ser obtido implementando um facade (fachada), que será responsável por reduzir a complexidade dos BDs e fornecer uma classe com métodos que a camada de visão pode entender com maior facilidade. Por exemplo, com base em uma escolha do usuário ou por meio de um arquivo de configuração, o facade poderia decidir se o método "salvar" deve instanciar a gravação em arquivos xml ou em bancos de dados, mas a visão continuaria pedindo apenas para "salvar" os dados, sem saber como isto seria feito.

Vou mostrar a seguir o meu modelo de classe facade, que já é utilizado em vários projetos. No mesmo pacote delegate, criamos a classe facadeBD:

package delegate;

import entidades.Entidade;
import java.io.Serializable;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import org.primefaces.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("facadeBD")
@ManagedBean(name = "facadeBD")
@ViewScoped
public class FacadeBD implements Serializable {

    public static final short SALVAR = 1;
    public static final short SALVARLISTA = 2;
    public static final short LISTAR = 3;
    public static final short CARREGAR = 4;
    public static final short CONSULTAPERSONALIZADA = 5;
    public static final short CONSULTANATIVA = 6;
    private Class classe;
    private Serializable chavePrimaria;
    private Entidade objetoRetorno;
    private Entidade entidade;
    private List listaRetorno;
    private String consulta;
    private List listaEntidades;
    @Autowired
    private GenericBD bd;

    public FacadeBD() {
    }

    public void salvar(Entidade entidade) {
        this.entidade = entidade;
        executar(SALVAR);
    }

    public void salvarLista(List listaEntidades) {
        this.listaEntidades = listaEntidades;
        executar(SALVARLISTA);
    }

    public List listar(Class classe) {
        this.classe = classe;
        executar(LISTAR);
        return listaRetorno;
    }

    public Entidade carregar(Class classe, Serializable chavePrimaria) {
        this.classe = classe;
        this.chavePrimaria = chavePrimaria;
        executar(CARREGAR);
        return objetoRetorno;
    }

    public List consultaPersonalizada(String consulta) {
        this.consulta = consulta;
        executar(CONSULTAPERSONALIZADA);
        return listaRetorno;
    }

    public List consultaNativa(String consulta) {
        this.consulta = consulta;
        executar(CONSULTANATIVA);
        return listaRetorno;
    }

    public Boolean mensagemNova(FacesMessage mensagem) {
        Boolean adicionar = true;
        List<FacesMessage> lm = FacesContext.getCurrentInstance().getMessageList();
        if (lm != null && lm.size() > 0) {
            for (FacesMessage fm : lm) {
                if (fm.getDetail().trim().equals(mensagem.getDetail().trim())) {
                    adicionar = false;
                }
            }
        }
        return adicionar;
    }

    public void executar(short metodo) {
        FacesMessage mens;
        String mensagem = "";
        try {
            if (metodo == SALVAR) {
                bd.salvar(entidade);
            }
            if (metodo == SALVARLISTA) {
                bd.salvarLista(listaEntidades);
            }
            if (metodo == LISTAR) {
                listaRetorno = bd.listar(classe);
            }
            if (metodo == CARREGAR) {
                objetoRetorno = bd.carregar(classe, chavePrimaria);
            }
            if (metodo == CONSULTAPERSONALIZADA) {
                listaRetorno = bd.consultaPersonalizada(consulta);
            }
            if (metodo == CONSULTANATIVA) {
                listaRetorno = bd.consultaNativa(consulta);
            }
        } catch (Exception e) {
            System.out.println("===== erro ====");
            if (classe != null) {
                System.out.println("Classe: " + classe.getSimpleName());
            } else {
                System.out.println("Classe: null");
            }
            System.out.println("Método: " + metodo);
            System.out.println("Consulta: " + consulta);
            System.out.println("Chave: " + chavePrimaria);
            e.printStackTrace();
            System.out.println("===============");
            mens = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Erro!", e.getMessage());
            if (mensagemNova(mens)) {
                FacesContext.getCurrentInstance().addMessage(null, mens);
            }
            RequestContext.getCurrentInstance().update("growl");
        } finally {
            if (!mensagem.isEmpty()) {
                mens = new FacesMessage(FacesMessage.SEVERITY_INFO, "Sucesso", mensagem);
                if (mensagemNova(mens)) {
                    FacesContext.getCurrentInstance().addMessage(null, mens);
                }
                RequestContext.getCurrentInstance().update("growl");
            }
        }
    }

    public GenericBD getBd() {
        return bd;
    }

    public void setBd(GenericBD bd) {
        this.bd = bd;
    }

}

  • Como podemos ver na anotação da linha 15, facadeBD é um managed bean, e poderá ser injetado por meio do JSF em outro managed bean com o mesmo escopo (@ViewScoped).
  • facadeBD também é um componente no contexto do Spring, e pode receber uma injeção do GenericBD (linhas 32 e 33). Aqui cabe uma observação: para integrar o Spring com o JSF permitindo injeção de dependências entre os contextos, utilizamos uma pequena configuração no arquivo faces-config.xml:

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>
</faces-config>

  • No trecho compreendido entre as linhas 38 e 71, temos a implementação dos métodos que serão utilizados pela camada de visão. Para salvar uma entidade que foi instanciada no objeto autor, por exemplo, a visão só saberá invocar "salvar(autor)". Todo o processamento de transações, conexão com o banco de dados, decisão sobre inserir ou atualizar, e outros detalhes, serão processados em suas respectivas camadas, de maneira transparente.
  • O método executar que inicia na linha 86, desvia para o business delegate bd que foi injetado pelo Spring, todo o restante do processamento.
O restante do código resume-se aos gets e sets e processamento de mensagens do JSF.

No próximo post, a criação de um controlador do JSF que fará a integração entre as páginas e o facadeBD.

quarta-feira, 25 de setembro de 2013

Transações e a camada de controle

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Como foi mostrado nos dois posts anteriores, Temos um pacote entidades onde serão descritas as tabelas do banco de dados e um pacote dao, onde estão as classes que fazem o acesso aos dados do banco, por meio de JPA+Hibernate. Esses dois pacotes formam uma camada que frequentemente é chamada de camada de Modelo (o M do MVC).

Manter essa camada separada das demais facilita a mudança de uma tecnologia para outra. Por exemplo, suponhamos que o desenvolvedor precisar mudar de Hibernate para EclipseLink. Somente essa camada precisará ser alterada, além de algumas bibliotecas e configurações do Spring, sem afetar o restante do sistema.

Da mesma maneira, uma camada de Controle (o C do MVC) onde as transações são configuradas, deve ser implementada separadamente das outras camadas, para que se possa jogar para ela toda essa responsabilidade. A camada de controle muitas vezes é citada como camada de serviço, por isso as denominações "service" que acrescentaremos a seguir.

Vamos ao código de uma classe abstrata que vai direcionar como os métodos DAO da camada de modelo devem ser controlados:

package service;

import dao.InterfaceDAO;
import java.io.Serializable;
import java.util.List;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public abstract class AbstractService<T> {

    protected InterfaceDAO<T> dao;

    public void setDao(InterfaceDAO<T> dao) {
        this.dao = dao;
    }

    public void salvar(T entity) {
        dao.salvar(entity);
    }

    public void salvarLista(List<T> lista) {
        dao.salvarLista(lista);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public T carregar(Class classe, Serializable chave) {
        return dao.carregar(classe, chave);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<T> listar(Class classe) {
        return dao.listar(classe);
    }


    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<T> consultaPersonalizada(String consulta) {
        return dao.consultaPersonalizada(consulta);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<T> consultaNativa(String consulta) {
        return dao.consultaNativa(consulta);
    }
}

Foi criado um pacote chamado service e uma classe chamada AbstractService. Nela está centralizado todo o controle das transações que ocorrerão. É óbvio que isso só funciona se todas as operações relativas ao CRUD das entidades passar por essa camada.

Atenção para as linhas 9 e 26. Na linha 9 determinamos que todos os métodos descritos na classe exigirão uma transação, a menos que outra anotação interna determine outra configuração. Já na linha 26, estamos dizendo que o método carregar, em particular, suporta a utilização de uma transação já em andamento, mas não precisa de uma transação para ser utilizado. Não vou entrar em detalhes sobre esse assunto, mas a documentação do Spring explica muito bem essas anotações.

As outras linhas com anotações semelhantes servem para o mesmo propósito. Dessa maneira, nenhuma outra parte do sistema deve abordar controle de transações. Essa classe é a única responsável por essa tarefa, e deve interceptar todas as operações de CRUD.

Em seguida, no mesmo pacote service, criamos uma classe GenericService (agora concreta) para disponibilizar esse serviço, já que a classe abstrata não pode ser instanciada:

package service;

import dao.InterfaceDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service("genericService")
public class GenericService extends AbstractService {

    @Override
    @Value("#{genericDAO}")
    public void setDao(InterfaceDAO dao) {
        this.dao = dao;
    }

}

Esta classe é bastante simples, e tem poucas coisas relevantes para se comentar. Ela utiliza uma injeção de dependência na linha 11, trazendo ao atributo chamado de dao (que foi herdado de AbstractService - linha 12) o objeto genericDAO (identificador reconhecido pelo Spring - confira a linha 11 da classe no post anterior). Podemos notar também que há uma anotação na linha 7, nomeando essa classe no contexto (do mesmo modo que foi feito na classe GenericDAO) para que o Spring possa injetar uma instância dessa classe em outra camada, como veremos no próximo post.

Essa construção possibilita que a camada seguinte dependa apenas da classe GenericService. Qualquer mudança no contexto transacional será feita na classe abstrata, sem atingir o restante das classes da aplicação!

Uma observação a respeito da injeção de dependências: se o atributo dao e o identificador genericDAO tiverem uma correspondência única, ou seja, se houver apenas um objeto no contexto do Spring do tipo AbstractDAO, podemos utilizar uma abordagem com anotação @Autowired, e basta definir um atributo com os mesmos nome e tipo da classe desejada, anotá-lo com @Autowired e declarar seus get e set. Essas variações dependem do objetivo da classe - uma classe que vai manipular vários componentes do contexto do Spring que tenham o mesmo tipo mas nomes de classe diferentes, pode ser implementada para trabalhar strings, obter em tempo de execução o nome da classe e instanciá-la. Nesse caso, utiliza-se @value referenciando uma EL com o nome da classe, como foi feito aqui. Mas se os objetos forem únicos, pode-se utilizar apenas a abordagem com @Autowired. Já foi utilizada uma abordagem dessas quando foi implementada a classe GenericDAO, com o atributo jpaTemplate (Como criar uma camada DAO).

No próximo post, a camada de Visão.

terça-feira, 24 de setembro de 2013

Como criar uma camada DAO

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Criar uma camada DAO (Data Access Objects) que possa ser reutilizada em outros projetos não é uma tarefa muito difícil, embora envolva vários conceitos de OOP (Object Oriented Programming) e Design Patterns.

Este exemplo mostrará uma construção que vai disponibilizar ao desenvolvedor um ambiente de CRUD (Create, Retrieve, Update, Delete) com algumas características interessantes:

  1. decisão automática por inserção ou atualização, baseada no valor da chave primária;
  2. recuperação por consulta total, personalizada ou nativa do banco de dados;
  3. exclusão direta ou por meio do sinalizador da classe Entidade (flagRemover);
  4. processamento de uma única entidade ou de uma lista de entidades, pertencentes a qualquer subclasse da classe Entidade.
A última característica é a mais interessante, pois possibilita enviar várias entidades diferentes de uma só vez, garantindo o processamento dentro da mesma transação, o que garante um rollback total em caso de erro.

Dentro de um pacote chamado de dao, vamos criar uma interface chamada InterfaceDAO. É muito importante a programação voltada a interfaces (ou classes abstratas, quando for interessante), pois traz ao projeto uma versatilidade muito grande, permitindo que sejam agregados outros mecanismos, sem interferir naquilo que já foi desenvolvido.

Segue o código da classe InterfaceDAO:

package dao;

import java.io.Serializable;
import java.util.List;

public interface InterfaceDAO<T> {

    public void salvar(T entidade);

    public void salvarLista(List<T> lista);

    public T carregar(Class<T> classe, Serializable chavePrimaria);

    public List<T> listar(Class<T> classe);

    public List<T> consultaPersonalizada(String consulta);

    public List<T> consultaNativa(String consulta);

}

Essa interface orienta o desenvolvedor a respeito dos métodos que serão necessários ao projeto e que podem ser utilizados em outras camadas, já que são métodos públicos. Não há método de atualização e exclusão na interface, pois a atualização depende apenas do valor da chave primária, e a exclusão, como foi visto anteriormente, será feita por meio da sinalização do atributo flagRemover. É importante observar que a interface não fixa o tipo das classes que serão processadas. Em vez disso, usa o tipo genérico, representado por T.

A seguir, a implementação da classe  que o projeto utilizará para as operações CRUD:

package dao;

import entidades.Entidade;
import java.io.Serializable;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.orm.jpa.support.SharedEntityManagerBean;
import org.springframework.stereotype.Repository;

@Repository("genericDAO")
@Scope(value = "singleton")
public class GenericDAO<T extends Entidade> implements InterfaceDAO<T> {

    @Autowired
    private SharedEntityManagerBean jpaTemplate;

    @Override
    public void salvar(T entidade) {
        if (entidade != null) {
            salvarSemFlush(entidade);
            jpaTemplate.getObject().flush();
        }
    }

    private void salvarSemFlush(T entidade) {
        if (entidade != null) {
            try {
                if (entidade.getFlagRemover()) {
                    if (entidade.getId() != null) {
                        if (jpaTemplate.getObject().find(entidade.getClass(), entidade.getId()) != null) {
                            jpaTemplate.getObject().remove(jpaTemplate.getObject().getReference(entidade.getClass(), entidade.getId()));
                        }
                    }
                } else {
                    if (entidade.getId() == null) {
                        jpaTemplate.getObject().persist(entidade);
                    } else {
                        if (jpaTemplate.getObject().find(entidade.getClass(), entidade.getId()) != null) {
                            jpaTemplate.getObject().merge(entidade);
                        }
                    }
                }
            } catch (Exception e) {
                System.out.println(e.getLocalizedMessage());;
            }
        }
    }

    @Override
    public void salvarLista(List<T> lista) {
        Integer i = 0;
        if (lista != null) {
            for (T entidade : lista) {
                salvarSemFlush(entidade);
                i++;
                // sincroniza a cada 500 registros
                if ((i % 500) == 0) {
                    try {
                        jpaTemplate.getObject().flush();
                    } catch (Exception e) {
                        System.out.println(e.getLocalizedMessage());;
                    }
                }
            }
            // sincroniza os ultimos registros
            if (i % 500 != 0) {
                try {
                    jpaTemplate.getObject().flush();
                } catch (Exception e) {
                    System.out.println(e.getLocalizedMessage());;
                }
            }
        }
    }

    @Override
    public T carregar(Class<T> classe, Serializable chave) {
        if (classe == null || chave == null) {
            return null;
        }
        return jpaTemplate.getObject().find(classe, chave);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<T> listar(Class<T> classe) {
        if (classe == null) {
            return null;
        }
        return jpaTemplate.getObject().createQuery("select a from " + classe.getSimpleName() + " a ").getResultList();
    }

    @Override
    public List<T> consultaPersonalizada(String consulta) {
        if (consulta == null) {
            return null;
        }
        return jpaTemplate.getObject().createQuery(consulta).getResultList();
    }

    @Override
    public List<T> consultaNativa(String consulta) {
        if (consulta == null) {
            return null;
        }
        return jpaTemplate.getObject().createNativeQuery(consulta).getResultList();
    }

    public SharedEntityManagerBean getJpaTemplate() {
        return jpaTemplate;
    }
}

Vamos à análise das linhas mais relevantes:

  • linhas 11 e 12: configurações do Spring. Essa classe será injetada pelo Spring posteriormente, na camada de controle.
  • linha 13:Essa classe restringe o uso às subclasses de Entidade e utiliza os métodos definidos em InterfaceDAO.
  • linhas 15 e 16: modelo JPA injetado pelo Spring, e que foi definido em AppConfig (Configurando o Spring (II)).
  • linhas 19 e 26: aqui a camada DAO possibilita ao desenvolvedor chamar o método salvar (para apenas uma entidade, com flush no final) ou chamar o método salvarLista (linha 51), que vai gerenciar o flush conforme o processamento vai atingindo blocos de 500 registros.
  • demais linhas: implementação dos demais métodos.

No próximo post, a implementação de um serviço que faz o gerenciamento de transações para chamar os métodos DAO.

segunda-feira, 23 de setembro de 2013

Criando entidades de bancos de dados

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Uma boa alternativa para quem está desenvolvendo um projeto e precisa agilizar os testes durante a modelagem dos dados e a criação da camada DAO é deixar a unidade de persistência e o Hibernate se encarregarem da criação das tabelas das entidades no banco de dados.

Para que isso ocorra, é necessária uma propriedade no arquivo persistence.xml, mais precisamente na linha 13, mostrada abaixo:


  
    org.hibernate.ejb.HibernatePersistence
    java:/fonteDeDados
    entidades.Pessoa
    false
    
      
      
      
      
      
      
    
  


Essa propriedade indica ao Hibernate para apagar todas as tabelas e recriá-las, a cada vez que a aplicação for executada. Depois que todos os testes terminarem, a propriedade pode ser deixada com o valor update, que atualiza ou cria automaticamente qualquer atributo novo ou tabela que for acrescentada ao projeto.


  
    org.hibernate.ejb.HibernatePersistence
    java:/fonteDeDados
    entidades.Pessoa
    false
    
      
      
      
      
      
      
    
  


Caso não haja mais necessidade de alterar a estrutura do banco de dados, a propriedade pode ser simplesmente retirada do arquivo.

Uma pequena observação: se a propriedade estiver configurada para create-drop e ocorrer erro no servidor de aplicações, é preciso remover manualmente as tabelas do banco de dados.

Para demonstrar estas configurações, vou criar uma série de posts com a construção das camadas MVC que serão usadas em um projeto simples para uma livraria.

O primeiro passo é preparar uma classe abstrata que servirá de modelo para todas as entidades que forem criadas no projeto. O objetivo é que todas as entidades atendam os requisitos abaixo:

  1. um método chamado getId() que retorne o valor de sua chave primária;
  2. um atributo temporário chamado flagRemover, que sinalizará ao DAO que o objeto que chegou até ele deve ser removido do banco de dados;

A importância desta classe será demonstrada quando for implementado o CRUD do projeto. Vamos ao código:

package entidades;

import java.io.Serializable;
import javax.persistence.Transient;

public abstract class Entidade implements Serializable {

    @Transient
    public boolean flagRemover;

    public abstract Serializable getId();

    public Boolean getFlagRemover() {
        return flagRemover;
    }

    public void setFlagRemover(Boolean flagRemover) {
        this.flagRemover = flagRemover;
    }
}

Numa rápida análise, o pacote entidades conterá essa classe abstrata que tem  um atributo flagRemover, com seus métodos de acesso (get e set) e um método público abstrato que simula o método get de um atributo chamado id. Esse método possibilita generalizar a obtenção da chave primária de qualquer classe de entidade que herde essa classe, já que ele será implementado na subclasse, e poderá retornar justamente o atributo da chave primária da subclasse. Observamos também que o atributo flagRemover não será escrito no banco de dados, pois está anotado com @Transient. Ele será utilizado apenas para marcar uma entidade que deve ser removida, antes de enviá-la ao DAO.

É bastante clara a vantagem de usar uma classe abstrata em vez de uma interface, pois podemos deixar alguns métodos prontos já para a subclasse, o que não poderia ser feito com o uso de uma interface.

Um rápido exemplo:

package entidades;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public abstract class Pessoa extends Entidade implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer reg;
    @Column(length=50)
    private String nome;
    @Column(length=50)
    private String sobrenome;

    public Integer getReg() {
        return reg;
    }

    public void setReg(Integer reg) {
        this.reg = reg;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getSobrenome() {
        return sobrenome;
    }

    public void setSobrenome(String sobrenome) {
        this.sobrenome = sobrenome;
    }

    @Override
    public Serializable getId() {
        return reg;
    }

}

A classe Pessoa (que também é uma classe abstrata!) herda a estrutura da classe Entidade (linha 11), ou seja, automaticamente possui um atributo flagRemover com get e set. Essa classe possui sua própria chave primária reg, anotada devidamente com @Id, e que não tem, inicialmente, uma ligação com o método getId.  No final do código, podemos ver a implementação (que é obrigatória) do método getId, que está retornando o atributo reg (chave primária da classe Pessoa). Portanto, outras classes poderão acessar o valor da chave primária da classe Pessoa, utilizando o método getId(), sem saber que o atributo original é chamado de reg, ou seja, deixando o atributo reg encapsulado.

No próximo post, a construção das classes DAO.

domingo, 22 de setembro de 2013

Configurando fontes de dados com o JBoss (II)

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Vamos agora abordar a criação da unidade de persistência. Já vimos que a unidade de persistência será chamada de "PU", como está declarado no arquivo AppConfig (veja a linha 22 do arquivo, neste post).

Vamos usar Hibernate, JPA 2, JTA, além do Spring, é claro. Vai ser um pouco trabalhoso, mas que renderá muitos benefícios e poderá ser totalmente reaproveitado em outros projetos.

Criamos o arquivo persistence.xml no NetBeans e configuramos seu código (é melhor que configurar pelo wizard...) da seguinte maneira:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="PU" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:/fonteDeDados</jta-data-source>
    <class>entity.classe1</class>
    <class>entity.classe2</class>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <validation-mode>NONE</validation-mode>
    <properties>
      <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
      <property name="hibernate.show_sql" value="false"/>
      <property name="hibernate.format_sql" value="false"/>
      <property name="javax.persistence.validation.mode" value="none"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
    </properties>
  </persistence-unit>
</persistence>

  • Na linha 3, temos o nome da unidade de persistência, como foi comentado no primeiro parágrafo. Na linha 5, a ligação da unidade de persistência com o famigerado identificador que já apareceu em vários arquivos de configuração. Aqui percebemos que em um novo projeto, o que muda é apenas o arquivo persistence.xml - todo o restante da configuração aponta para "fonteDeDados", inclusive o próprio persistence.xml. Até o JBoss referencia fonteDeDados, e essas dependências ajudam a realizar alterações de ambiente (mudar servidor, mudar plataforma do banco de dados, etc.) com pouquíssimas alterações no projeto.
  • Nas linhas 6, 7 e 8 temos a configuração das classes (que serão criadas em outro post).
  • Na linha 15 temos um parâmetro que indica ao Hibernate para criar as tabelas de acordo com as anotações das classes declaradas nas linhas 6 e 7. Percebemos que só é necessário criar um banco de dados, conseguir uma conexão, configurar o Spring e a unidade de persistência. Quando executarmos o projeto e ele necessitar de acesso ao banco de dados, será feita a verificação das classes e as tabelas serão criadas automaticamente. Esse assunto veremos com mais detalhes em outro post.
Aqui termina a configuração do projeto básico usando Spring + JPA. Todas as configurações necessárias para trabalhar com as camadas de dados já estão contempladas nestes arquivos de configuração que foram mostrados até aqui. Agora precisamos criar os métodos genéricos para realizar as operações CRUD (Create, Read, Update e Delete), e é nesse instante que o conhecimento de OOP (Object Oriented Programming) e Spring faz a diferença.

Conforme avançarmos na construção das camadas (elaboradas com base no design pattern MVC), surgirão os comentários a respeito das configurações utilizadas até aqui. Modificações deverão ser feitas sempre que quisermos alterar comportamento de logs, recriar tabelas, e etc..

No próximo post, como gerar as tabelas pelo próprio projeto do NetBeans.

sábado, 21 de setembro de 2013

Configurando fontes de dados com o JBoss (I)

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Pra começo de conversa, por que usar o JBoss?

O JBoss é um robusto servidor de aplicações, baseado na mesma arquitetura do Apache TomCat, e suporta muito bem o gerenciamento de transações. Já o TomCat não pode ser chamado de servidor de aplicações - seria apenas um "container web". Um ambiente ideal seria desenvolver usando o TomCat e distribuir usando o JBoss. O TomCat não deve ser utilizado em produção, mas pode perfeitamente ser utilizado durante o desenvolvimento, já que é bem mais fácil de configurar e bem mais rápido para efetuar os testes. Mas as coisas não acontecem da mesma maneira no TomCat e no Jboss... Como o TomCat precisa de bibliotecas de terceiros para suportar transações, já tive bastante dor de cabeça para ajustar um sistema inteiro depois de simplesmente mudar de TomCat para Jboss, e depois dessas experiências, agora faço os testes no JBoss, também. Mesmo que seja mais demorado, tenho a garantia de distribuir imediatamente meu projeto assim que os testes terminam.

Depois de baixar o JBoss (eu uso nestes exemplos a versão 6.1.0.Final), é importante editar o arquivo /jboss-6.1.0.Final/bin/run.conf e fazer a modificação a seguir:

#
# Specify options to pass to the Java VM.
#
if [ "x$JAVA_OPTS" = "x" ]; then
   JAVA_OPTS="-Xms2G -Xmx6G -XX:MaxPermSize=2G -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=36000 -Dsun.rmi.dgc.server.gcInterval=36000"
fi

Os valores que aparecem na frente dos Xms, Xmx e MaxPermSize devem ser os mais altos que a memória do seu servidor comportar (faça quantos testes forem necessários, vale a pena). Quanto mais altos estes valores, menos o servidor irá travar e menos problemas de "Permgen" surgirão. E isto não é uma característica do JBoss, não. Ao implantar/desimplantar um projeto várias vezes, seja no TomCat, no Glassfish, etc., sempre chega um momento em que o servidor não consegue gerenciar mais a memória que lhe foi reservada, e aí, só reiniciando.

No caso de usar o NetBeans, como eu, pode ser necessário fazer a alteração nas propriedades do servidor, como mostrado abaixo:

Depois do JBoss instalado e configurado, voltamos ao projeto. É necessário criar um arquivo que chamaremos de jboss-web.xml, na pasta WEB-INF:


O conteúdo deste arquivo é bastante simples:

<?xml version="1.0" encoding="UTF-8"?>
<!-- jboss-web.xml -->
<jboss-web>
  <context-root>/livraria</context-root>
  <resource-ref>
    <res-ref-name>jdbc/fonteDeDados</res-ref-name>
    <jndi-name>java:/fonteDeDados</jndi-name>
  </resource-ref>
</jboss-web>

O contexto, mostrado na linha 4, deve coincidir com o contexto do projeto. No NetBeans, para definir o contexto e o URL relativo, basta acessar as propriedades do projeto e a opção executar:

Percebemos, nas linhas 6 e 7, o famoso identificador "fonteDeDados" que apareceu nos arquivos de configuração do Spring. Esta fonte de dados deve ser referenciada nos arquivos de configuração do servidor JBoss (no TomCat não é necessário...), mais especificamente, em um arquivo da pasta /jboss-6.1.0.Final/server/default/deploy com sufixo "-ds". Uma boa prática é eliminar todos os arquivos desta pasta que tenham o sufixo "-ds" e centralizar em um único arquivo, por exemplo, "config-ds.xml", mostrado a seguir:

<datasources>  
  
   <local-tx-datasource>       
      <jndi-name>fonteDeDados</jndi-name>    
      <connection-url>jdbc:postgresql://meuServidor:5432/meuBancoDeDados</connection-url>   
      <driver-class>org.postgresql.Driver</driver-class>  
      <user-name>postgres</user-name>  
      <password>minhaSenha</password>  
      <min-pool-size>5</min-pool-size>  
      <max-pool-size>300</max-pool-size>  
      <idle-timeout-minutes>0</idle-timeout-minutes>    
   </local-tx-datasource>  
  
   <local-tx-datasource>          
      <jndi-name>outraFonteDeDados</jndi-name>    
      <connection-url>jdbc:postgresql://meuOutroServidor:5432/meuOutroBancoDeDados</connection-url>   
      <driver-class>org.postgresql.Driver</driver-class>  
      <user-name>postgres</user-name>  
      <password>minhaSenha</password>  
      <min-pool-size>5</min-pool-size>  
      <max-pool-size>300</max-pool-size>  
      <idle-timeout-minutes>0</idle-timeout-minutes>    
   </local-tx-datasource>  
  
</datasources> 

Podemos verificar que o arquivo é formado por uma coleção de "local-tx-datasource", e que o primeiro deles está associado com o famigerado identificador "fonteDeDados" (linha 4). As linhas 5, 7, e 8 mostram a configuração da conexão ao banco de dados, que no exemplo é PostgreSQL. Como já enfrentei alguns problemas relativos ao número máximo de conexões, deixo sempre um número alto em max-pool-size (linha 10). Lembrando que o servidor de aplicações (JBoss) e o servidor de banco de dados devem ser máquinas com boa quantidade de memória.

Percebemos que o segundo datasource pode se conectar a outro banco de dados totalmente diferente, e nem precisa ser a mesma tecnologia - pode ser MySQL, por exemplo.

Aqui termina a configuração do JBoss. No próximo post, a criação da unidade de persistência.

quinta-feira, 19 de setembro de 2013

Configurando o Spring (III)

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

A utilização do Spring Security é bastante tranquila. Já uso em vários projetos e posso autenticar tanto por meio de um banco de dados, quanto por meio de uma base LDAP.

Vou mostrar com mais detalhes a autenticação por meio de um banco de dados. Vamos assumir que as tabelas com os usuários e as regras de acesso estão no mesmo banco de dados que será utilizado pelo sistema, e referenciado pelo identificador "fonteDeDados" que já apareceu em vários arquivos dos outros posts.

O arquivo security-config.xml é o último arquivo de configuração do Spring que vamos abordar, veja o código abaixo:

<?xml version="1.0" encoding="ISO-8859-1"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans 
             http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
             http://www.springframework.org/schema/security 
             http://www.springframework.org/schema/security/spring-security-3.1.xsd
             http://www.springframework.org/schema/context 
             http://www.springframework.org/schema/context/spring-context-3.1.xsd">
    <http use-expressions="true">
        <intercept-url pattern="/web/index.xhtml" access="isAuthenticated()" />
        <intercept-url pattern="/web/resources/**" access="permitAll" />
        <intercept-url pattern="/web/acessoNegado.xhtml" access="permitAll" />
        <intercept-url pattern="/web/login.xhtml" access="permitAll" />
        <intercept-url pattern="/web/ajaxDialog.xhtml" access="permitAll" />
        <intercept-url pattern="/web/resources/imagens/ajaxloading.gif" access="permitAll" />
        <intercept-url pattern="/web/menu.xhtml" access="isAuthenticated()" />
        <access-denied-handler error-page="/web/acessoNegado.xhtml" />
        <form-login login-page="/web/login.xhtml"
                    authentication-failure-url="/web/login.xhtml?login_error=1"/>
        <logout logout-success-url="/web/index.xhtml" />
    </http> 

    
    <authentication-manager>  
        <authentication-provider>  
            <!--password-encoder hash="md5"/-->  
            <jdbc-user-service data-source-ref="fonteDeDados"/>  
        </authentication-provider>  
    </authentication-manager>     
    
</beans:beans>

O código mais relevante do arquivo é o trecho entre as linhas 26 e 31. Percebemos aqui a presença do identificador "fonteDeDados", e depois da configuração do banco de dados que será utilizado pelo sistema, criamos neste mesmo banco as tabelas para os usuários e regras de autorização.

Caso seja necessária a utilização de criptografia, podemos descomentar a linha 28, mas as senhas deverão ser armazenadas já criptografadas no banco de dados. Para o exemplo, vamos armazenar as senhas em claro.

O trecho entre as linhas 20 e 23 determina quais os arquivos responsáveis pelo controle da autenticação. Podemos perceber claramente que devem ser criados os arquivos acessoNegado.xhtml e login.xhtml.

As linhas iniciadas por "intercept-url" determinam como vai ser autorizado o acesso a uma página ou a uma pasta. Podemos perceber que algumas páginas estão autorizadas se  for satisfeita a condição isAuthenticated(), e outras podem ser acessadas mesmo sem autenticação. Voltaremos a este assunto mais tarde, quando precisarmos restringir um conjunto de páginas administrativas, por exemplo. Caso o leitor precise de mais detalhes, a literatura sobre o Spring é muito ampla, um exemplo está neste link: http://docs.spring.io/spring-security/site/docs/3.0.x/reference/springsecurity-single.html

Uma grande vantagem da utilização do Spring para autenticação e autorização é que para deixar de utilizar um banco de dados e passar a utilizar uma base LDAP, precisamos modificar somente o trecho entre as linhas 26 e 31. Segue um exemplo:

    <ldap-server id="ldapServer" url="ldap://xxx.xxx.xxx.xxx:389" />
    <authentication-manager>        
        <!-- Trecho para autenticacao via ldap -->  
        <ldap-authentication-provider 
            user-search-base="ou=People,dc=meudominio,dc=com,dc=br" 
            group-search-base="ou=Groups,dc=meudominio,dc=com,dc=br">
        </ldap-authentication-provider>
    </authentication-manager>

Percebemos claramente a facilidade desta mudança. Apenas precisamos do IP do servidor LDAP e o restante da configuração continua o mesmo. Vamos encerrar mostrando o código dos arquivos acessoNegado.xhtml e login.xhtml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- acessoNegado.xhtml -->
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <body>
        <div align="center">
            <h:panelGroup>
                <span style="color:red">Acesso negado.</span>
                < br/>
                < br/>
                <h:outputText value="Motivo: #{SPRING_SECURITY_LAST_EXCEPTION.message}" rendered="#{!SPRING_SECURITY_LAST_EXCEPTION.message==''}" />
            </h:panelGroup>

            <form id="login" method="post" action="#{facesContext.externalContext.request.contextPath}/web/index.xhtml">
                <h:commandButton type="submit" value="Voltar à página inicial"/>
            </form>
        </div>
    </body>
</html>


<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- login.xhtml -->
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <body>
        <div align="center">
            <p:fieldset style="width:300px" >
                <h:panelGroup rendered="#{!empty param.login_error}">
                    <span style="color:red">Erro na autenticação.</span>
                    < br/>
                    < br/>
                    <h:outputText value="Confira o nome do usuário e a senha." />
                </h:panelGroup>
                <h1><h:outputText value="Autenticação" style="text-decoration: underline" /></h1>
                <form id="login" method="post" action="${request.contextPath}/j_spring_security_check">
                    <h:panelGrid columns="2">
                        <h:outputText value="Usuário: " />
                        <p:inputText id="j_username" />
                        <h:outputText value="Senha: " />
                        <p:password id="j_password" />
                    </h:panelGrid>
                    < br/>
                    <p:commandButton onclick="submit()" type="submit" value="Autenticar" global="false" />
                    <script>
                        document.getElementById("login").j_username.value = "#{SPRING_SECURITY_LAST_USERNAME}";
                    </script>
                </form>
            </p:fieldset>
        </div>
    </body>
</html>

Não há muito o que comentar quanto aos arquivos acima. Apenas as linhas 17, 20 e 22 do login.xhtml, que contêm identificadores que são esperados pelas classes de autenticação, e não devem ser modificados.

Uma última consideração quanto ao método de autenticação: se o login falhar, o arquivo login.xhtml é chamado novamente, com um parâmetro para indicar que houve erro (authentication-failure-url="/web/login.xhtml?login_error=1").

Para criar os usuários e as regras no banco de dados, podemos criar as tabelas com o sql a seguir:

    create table users(  
          username varchar(50) not null primary key,  
          password varchar(50) not null,  
          enabled boolean not null);  
      
    create table authorities (  
          username varchar(50) not null,  
          authority varchar(50) not null,  
          constraint fk_authorities_users foreign key(username) references users(username));  
          create unique index ix_auth_username on authorities (username,authority);  

Em seguida, basta inserir um usuário, senha, colocar verdadeiro no campo enabled e inserir uma regra, por exemplo, "admin".

No próximo post, a configuração da fonte de dados e do Jboss.

quarta-feira, 18 de setembro de 2013

Configurando o Spring (II)

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Continuando o post anterior, vou mostrar agora o arquivo web-application-config.xml. Tive alguns problemas com a localização do arquivo, o que foi resolvido mantendo o seguinte padrão: os arquivos de configuração do Spring referenciados no arquivo web.xml devem ser sempre colocados em uma subpasta /WEB-INF/classes. Observe na imagem abaixo:


O conteúdo do arquivo é bem simples:

<?xml version="1.0" encoding="UTF-8"?><br/><beans xmlns="http://www.springframework.org/schema/beans"<br/>       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br/>       xmlns:context="http://www.springframework.org/schema/context"<br/>       xsi:schemaLocation="<br/>           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd<br/>           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"<br/>       default-autowire="byName"><br/>    <context:component-scan base-package="configuracao" /><br/>    <import resource="security-config.xml" /><br/></beans>

Uma atenção especial para as linhas 9 e 10:

Na linha 9, é declarado um pacote onde o Spring vai procurar por arquivos de configuração, que mostrarei logo a seguir.

Na linha 10, é declarado um arquivo de configuração para autenticação, que será mostrado em outro post. Se não houver necessidade de autenticação, ou se for preciso testar o projeto inicialmente sem preocupação com autorização de acesso, esta linha pode ser temporariamente comentada.

Seguindo a declaração da linha 9, o projeto deve ter um pacote chamado "configuracao":


Neste pacote foi criada uma classe AppConfig.java, que substitui a implementação xml da configuração do Spring. Qual a vantagem disso? É que um arquivo de classe pode ser facilmente reaproveitado em um projeto novo, simplesmente criando um arquivo jar.

Vamos analisar rapidamente a classe AppConfig:

package configuracao;

import java.io.Serializable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.support.SharedEntityManagerBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = { "controle", "dao", "delegate", "entidades", "service"})
public class AppConfig implements Serializable {

    @Bean @Scope(value="singleton")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        bean.setPersistenceUnitName("PU");
        return bean;
    }

    @Bean @Scope(value="singleton")
    public SharedEntityManagerBean jpaTemplate() {
        SharedEntityManagerBean bean = new SharedEntityManagerBean();
        bean.setEntityManagerFactory(entityManagerFactory().getObject());
        return bean;
    }

    @Bean @Scope(value="singleton")
    public JtaTransactionManager transactionManager() {
        JtaTransactionManager bean = new JtaTransactionManager();
        return bean;
    }

    @Bean @Scope(value="singleton")
    public JndiObjectFactoryBean fonteDeDados() {
        JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
        bean.setJndiName("java:comp/env/jdbc/fonteDeDados");
        return bean;
    }
}

Novamente, a classe deve ser estudada e o leitor deve buscar na documentação do Spring as explicações para todas as anotações mostradas aqui. Mas vamos destacar as linhas mais importantes da classe:

  • Na linha 14, uma anotação que vai sinalizar ao Spring para que use o conteúdo deste arquivo como configuração. Logo em seguida, na linha 16, uma lista com os pacotes onde o Spring deve procurar por classes com anotações. Isto será muito importante quando chegar o momento de construir a camada de acesso aos dados (DAO). É óbvio que os pacotes devem ser criados com os mesmos nomes declarados na linha 16.
  • Na linha 22, podemos identificar o nome da unidade de persistência que vai ser utilizada ("PU"). Vamos criar esta unidade de persistência durante a construção da camada de acesso aos dados.
  • E nas linhas 40 e 42, podemos ver a mesma referência que foi utilizada na configuração do Spring contida no arquivo web.xml.

Importante: esta configuração é para uso do Spring com Jboss. Há diferenças nas configurações se o servidor de aplicações for TomCat, ou Glassfish. Em um próximo artigo, vou mostrar como configurar o Jboss para este projeto.

No próximo post, a configuração do Spring Security.

terça-feira, 17 de setembro de 2013

Configurando o Spring (I)

Este post faz parte de uma série sobre Spring, JPA e JSF. Caso algum assunto abordado aqui não esteja claro, consulte este link: Spring + JPA.

Eu uso o Spring há algum tempo, e tem sido muito útil. Me ajuda muito a manter a integridade de meus projetos, principalmente por causa do controle de transações, aliado a uma arquitetura em camadas.

Não é fácil compreender todo o funcionamento do Spring, nem é meu objetivo aqui. Acredito que outros desenvolvedores se beneficiarão mais dos exemplos funcionando, e para que isso aconteça, é preciso configurar cuidadosamente o ambiente de trabalho.

A IDE utilizada será o NetBeans.

1 - Obtendo as bibliotecas


Eu utilizo as seguintes bibliotecas:


É óbvio que versões mais recentes podem já estar disponíveis, mas ressalto novamente que esse é um modelo que funciona muito bem para as minhas necessidades.

2 - O arquivo web.xml

O trecho a seguir deve ser colocado no arquivo web.xml:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/classes/web-application-config.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class> org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
    </filter>
  
    <filter-mapping>  
        <filter-name>springSecurityFilterChain</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping> 
 
    <resource-ref>
        <description>
            Resource reference to a factory for java.sql.Connection
            instances that may be used for talking to a particular
            database that is configured in the
            configurartion for the web application.
        </description>
        <res-ref-name>jdbc/fonteDeDados</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

Algumas observações importantes:

  1.  Esta configuração será sempre assim, já tenho vários projetos que usam o mesmo trecho de código.
  2.  Há um arquivo adicional que vamos criar em seguida, como se pode observar na linha 4.
  3.  Uma fonte de dados será configurada posteriormente, como se pode observar na linha 38.

Compreender o restante do código fica por conta do leitor. São muitas horas de leitura sobre o Spring que não podem ser resumidas aqui.

No próximo post vou mostrar o arquivo web-application-config.xml.

Blogando novamente!

Depois de um longo tempo me aventurando por vários frameworks, servidores de aplicativos, e depois testar muitas tecnologias para trabalhar com Java para Web, cheguei a um padrão de programação que satisfaz minhas necessidades. 

Acredito que este padrão também seja útil para muitos programadores que ainda têm dúvidas sobre o uso de JSF, Primefaces e JPA. 

Esta série de posts vai abranger a configuração de um ambiente JBoss + JSF + (MySQL ou PostGreSQL) + Primefaces + Spring. 

Não é uma tarefa simples, mas também não é o fim do mundo. Na realidade há muito trabalho, que será recompensado com uma arquitetura robusta e versátil, podendo ser reaproveitada facilmente em novos projetos.