quinta-feira, 3 de outubro de 2013

Atualizando DataTables dinamicamente (IV)

Este post faz parte de uma série que demonstra o uso de Primefaces, Spring, JPA, JSF para criar uma aplicação que atualiza as DataTables dos clientes automaticamente. Confira os outros posts da série aqui: Primefaces

Vamos agora introduzir um mecanismo que permite atualizar dinamicamente os p:dataTable em qualquer cliente da aplicação. Para isso vamos criar um segundo managed bean no pacote controle do projeto, chamado aplicacao:

package controle;

import delegate.FacadeBD;
import java.io.Serializable;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;

@ManagedBean(name = "aplicacao")
@ApplicationScoped
public class Aplicacao implements Serializable {

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

    public Aplicacao() {
    }

    public FacadeBD getFacadeBD() {
        return facadeBD;
    }

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

}

Por enquanto, a única observação relevante é que o escopo deste bean é @AplicationScoped, ou seja, todos os seus atributos e métodos estão disponíveis a qualquer cliente da aplicação, a partir do momento em que o servidor ativar a aplicação.

No escopo @ViewScoped que utilizamos no bean controlador, os atributos e métodos do bean estão disponíveis separadamente para cada cliente de cada sessão. Isso implica em várias cópias (no servidor) do bean, uma para cada cliente que estiver executando a aplicação, o que causa o efeito de um cliente não ver o que o outro fez sem recorrer a uma nova leitura do banco de dados. E o pior de tudo, se quisermos manter disponível uma lista com grande quantidade de dados, haverá uma cópia da lista para cada cliente, sobrecarregando a memória do servidor.

Então, para continuar com nosso exemplo, devemos retirar do bean controlador a lista de autores e transferi-la para o bean aplicacao:

@ManagedBean(name = "aplicacao")
@ApplicationScoped
public class Aplicacao implements Serializable {

    @ManagedProperty(value = "#{facadeBD}")
    private FacadeBD facadeBD;
    private List<Autor> autores = new ArrayList<Autor>();

    public Aplicacao() {
    }

    public List<Autor> getAutores() {
        if (autores.isEmpty()) {
            autores = facadeBD.listar(Autor.class);
        }
        return autores;
    }

    public void setAutores(List<Autor> autores) {
        this.autores = autores;
    }

É óbvio que em seguida retiramos o atributo autores e os get e set correspondentes do bean controlador. Em seguida, modificamos o bean controlador, injetando o controlador aplicacao como um atributo:

public class Controlador implements Serializable {

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

E em seguida criamos os get e set para o atributo aplicacao:

    public Aplicacao getAplicacao() {
        return aplicacao;
    }

    public void setAplicacao(Aplicacao aplicacao) {
        this.aplicacao = aplicacao;
    }

Importante: podemos injetar um bean com escopo mais amplo em um bean com escopo mais restrito, mas não podemos fazer o contrário!

Com essa mudança, os métodos gravar e excluir devem ser modificados para apontar para o novo bean:

    public void gravar(Entidade entidade) {
        facadeBD.salvar(entidade);
        aplicacao.getAutores().clear();
    }

    public void excluir(Entidade entidade) {
        entidade.setFlagRemover(true);
        facadeBD.salvar(entidade);
        aplicacao.getAutores().clear();
    }

E a página autores.xhtml também deve ler os dados usando o novo bean:

                <p:dataTable id="Tabela" 
                             widgetVar="wTabela"
                             rowIndexVar="rowNumber"
                             rows="10"
                             paginator="true"
                             value="#{aplicacao.autores}"
                             var="registro">

Nessa fase, já eliminamos o problema de ter várias cópias da lista de autores, mantendo uma única cópia durante o tempo em que o servidor estiver disponível. Mas ainda permanece a necessidade de ler o banco de dados todas as vezes em que quisermos obter as alterações mais recentes.

Vamos corrigir isso criando três métodos no bean aplicacao:

    public void atualizarLista(Entidade e, Short operacao) {
        List l = null;
        if (e instanceof Autor) {
            l = autores;
        }
        if (l != null) {
            if (e.getId() == null) {
                l.add(e);
            } else {
                Integer indice = 0;
                Object objeto = null;
                for (Object o : l) {
                    if (((Entidade) o).getId() == e.getId()) {
                        objeto = o;
                        break;
                    }
                    indice++;
                }
                if (indice <= l.size()) {
                    if (operacao == EXCLUIR) {
                        l.remove(objeto);
                    }
                    if (operacao == ATUALIZAR) {
                        l.set(indice, e);
                    }
                }
            }
        }
    }

    public void atualizarNaLista(Entidade e) {
        atualizarLista(e, ATUALIZAR);
    }

    public void removerDaLista(Entidade e) {
        atualizarLista(e, EXCLUIR);
    }

As constantes utilizadas devem ser declaradas no início da classe:

public class Aplicacao implements Serializable {

    public static Short ATUALIZAR = 1;
    public static Short EXCLUIR = 2;
    @ManagedProperty(value = "#{facadeBD}")
    private FacadeBD facadeBD;

Esses três métodos recebem um objeto que deve pertencer a uma subclasse de Entidade e decidem se o objeto passado deve ser inserido, atualizado ou excluído da lista correspondente. Com essa mudança, os métodos gravar e excluir do bean controlador devem mudar novamente:

    public void gravar(Entidade entidade) {
        aplicacao.atualizarNaLista(autor);
        facadeBD.salvar(entidade);
    }

    public void excluir(Entidade entidade) {
        entidade.setFlagRemover(true);
        aplicacao.removerDaLista(entidade);
        facadeBD.salvar(entidade);
    }

Uma observação importante no método gravar: a lista deve ser atualizada antes do banco de dados. Caso contrário, se o registro for novo, o DAO vai mudar o valor da chave primária e o bean aplicacao não vai conseguir decidir se o registro tem que ser inserido ou alterado.

Agora, estamos com a seguinte situação: os dados são atualizados na tela de qualquer cliente, assim que o cliente fizer alguma atualização na tabela (por exemplo, gravar uma edição, ou remover um autor), mas não há mais uma nova leitura de todo o conteúdo a partir do banco de dados, porque após a operação de CRUD, apenas o objeto que foi modificado é atualizado na lista de autores.

Vamos então para o último passo: automatizar a atualização dos clientes! Isso é feito colocando um componente do Primefaces na página autores.xhtml:

            </p:dialog>
            <p:poll interval="3"  update="pnlTabela" /> 
        </h:form>
    </h:body>
</html>

O componente p:poll do Primefaces, mostrado acima, executa o update no componente pnlTabela a cada 3 segundos. Como o componente Tabela, que está dentro de pnlTabela, acessa a lista do bean aplicacao, a cada 3 segundos o cliente (seja lá quem for ou em que máquina estiver) terá uma tabela atualizada!


Para ver esse efeito, basta abrir dois navegadores, reduzi-los para metade da tela e alterar um autor em um dos navegadores. Após alguns segundos, o outro navegador atualizará automaticamente seus dados.

Não vamos nos aprofundar a respeito dos locks pessimísticos, otimísticos, concorrência, etc.. Se o leitor verificar todo o mecanismo utilizado na camada DAO e a atualização automática implementada aqui, perceberá que:
  1. duas ou mais edições ao mesmo tempo: vai prevalecer a que for gravada por último.
  2. uma edição em aberto e uma exclusão do mesmo registro, antes de gravar a edição:  os dados da edição serão descartados, já que o registro foi excluído antes da gravação.
Bem, aqui termina essa série de posts destinada a mostrar a operação básica de CRUD usando Spring, JPA, JSF e Primefaces, com atualização automática independente de estação cliente.

O código deste projeto está disponibilizado aqui: http://code.google.com/p/livraria-jsf/source/browse/Livraria
Veja o vídeo com a demonstração deste aplicativo:



Atualizando DataTables dinamicamente (III)

Este post faz parte de uma série que demonstra o uso de Primefaces, Spring, JPA, JSF para criar uma aplicação que atualiza as DataTables dos clientes automaticamente. Confira os outros posts da série aqui: Primefaces

No post anterior, criamos uma página que exibe os dados de uma entidade Autor em um componente p:dataTable do Primefaces. Vamos modificar a página criada para que ofereça as opções restantes de CRUD, ou seja, cadastrar, atualizar e excluir.

Para melhorar a aparência da aplicação, vamos criar ícones para representar algumas ações. As quatro imagens abaixo serão utilizadas para editar, excluir, gravar e adicionar.


Basta baixar as imagens (ou arranjar outras - fica à escolha de cada pessoa) e colocá-las em uma pasta do projeto chamada resources - se a pasta não existir, obviamente tem que ser criada. Eu costumo separar as imagens em uma subpasta chamada imagens, dentro de resources. Para facilitar as referências, vamos renomear as imagens para editar.png, excluir.png, gravar.png e adicionar.png, respectivamente.

Com as imagens já gravadas na pasta, vamos modificar a página autores.xhtml dessa maneira:

<?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:p="http://primefaces.org/ui"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Livraria</title>
    </h:head>
    <h:body>
        <style>
            .panelGridSemBorda tr, .panelGridSemBorda td {
                border: none;
            }
            .dialogoSemScroll div.ui-dialog-content {
                overflow: hidden;
            }
        </style>
        <h:form prependId="false" id="frmPrincipal">
            <p:outputPanel id="pnlTabela">                
                <p:dataTable id="Tabela" 
                             widgetVar="wTabela"
                             rowIndexVar="rowNumber"
                             rows="10"
                             paginator="true"
                             value="#{controlador.autores}"
                             var="registro">
                    <f:facet name="header">
                        <h:outputText value="Autores"/>
                        <p:toolbar>
                            <p:toolbarGroup>
                                <p:commandLink action="#{controlador.adicionar('AUTOR')}"
                                               oncomplete="wdlgDetalhe.show()" 
                                               update=":frmPrincipal:pnlDetalhe">
                                    <p:graphicImage url="./resources/imagens/adicionar.png" 
                                                    style="background-color: transparent; vertical-align: middle; border: none" 
                                                    width="40" height="40"/>
                                </p:commandLink>
                            </p:toolbarGroup>
                        </p:toolbar>
                    </f:facet>
                    <p:column headerText="Nome">
                        #{registro.nome}
                    </p:column>
                    <p:column headerText="Sobrenome">
                        #{registro.sobrenome}
                    </p:column>
                    <p:column width="60">
                        <p:commandLink action="#{controlador.editar(registro)}" 
                                       oncomplete="wdlgDetalhe.show()" 
                                       update=":frmPrincipal:pnlDetalhe">
                            <p:graphicImage url="./resources/imagens/editar.png" 
                                            style="background-color: transparent; vertical-align: middle; border: none" 
                                            width="20" height="20"/>
                        </p:commandLink>
                        <p:spacer width="10"/>
                        <p:commandLink action="#{controlador.excluir(registro)}" 
                                       update=":frmPrincipal:pnlTabela">
                            <p:graphicImage url="./resources/imagens/excluir.png" 
                                            style="background-color: transparent; vertical-align: middle; border: none" 
                                            width="20" height="20"/>
                        </p:commandLink>
                    </p:column>
                </p:dataTable>
            </p:outputPanel>
            <p:dialog id="dlgDetalhe" 
                      widgetVar="wdlgDetalhe" 
                      modal="true" 
                      resizable="false" 
                      closable="true" 
                      header="Detalhes do autor" 
                      styleClass="dialogoSemScroll" >
                <p:panelGrid id="pnlDetalhe" 
                             columns="1" 
                             styleClass="panelGridSemBorda">
                    <p:outputLabel value="Nome:" id="lNome" for="cNome" />
                    <p:inputText id="cNome" value="#{controlador.autor.nome}"/>
                    <p:outputLabel value="Sobrenome:" id="lSobrenome" for="cSobrenome" />
                    <p:inputText id="cSobrenome" value="#{controlador.autor.sobrenome}"/>
                    <f:facet name="footer">
                        <p:toolbar>
                            <p:toolbarGroup>
                                <p:commandLink action="#{controlador.gravar(controlador.autor)}"
                                               oncomplete="wdlgDetalhe.hide()" 
                                               update=":frmPrincipal:pnlTabela">
                                    <p:graphicImage url="./resources/imagens/gravar.png" 
                                                    style="background-color: transparent; vertical-align: middle; border: none" 
                                                    width="40" height="40"/>
                                </p:commandLink>
                            </p:toolbarGroup>
                        </p:toolbar>
                    </f:facet>
                </p:panelGrid>
            </p:dialog>
        </h:form>
    </h:body>
</html>

O managed bean controlador ganhará novos atributos e métodos, como mostrado 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;
    private Autor autor = new Autor();
    private List<Autor> autores = new ArrayList<Autor>();

    public Controlador() {
    }

    public void adicionar(String entidade) {
        if (entidade.toUpperCase().equals("AUTOR")) {
            autor = new Autor();
        }
    }

    public void editar(Entidade entidade) {
        autor = (Autor) facadeBD.carregar(entidade.getClass(), entidade.getId());
    }

    public void excluir(Entidade entidade) {
        entidade.setFlagRemover(true);
        facadeBD.salvar(entidade);
        autores.clear();
    }

    public void gravar(Entidade entidade) {
        facadeBD.salvar(entidade);
        autores.clear();
    }

    public FacadeBD getFacadeBD() {
        return facadeBD;
    }

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

    public Autor getAutor() {
        return autor;
    }

    public void setAutor(Autor autor) {
        this.autor = autor;
    }

    public List<Autor> getAutores() {
        if (autores.isEmpty()) {
            autores = facadeBD.listar(Autor.class);
        }
        return autores;
    }

    public void setAutores(List<Autor> autores) {
        this.autores = autores;
    }
}

Vamos às linhas mais relevantes do arquivo xhtml, sem entrar no assunto "ciclo de vida do JSF":

  • No cabeçalho da tabela (entre as linhas 28 e 41), inserimos um commandButton que chama o método adicionar do controlador, e no final da requisição, atualiza o componente identificado por :frmPrincipal:pnlDetalhe e executa o método show do componente identificado por wdlgDetalhe.
  • O mesmo acontece entre as linhas 49 e 55, sendo que o método chamado é o editar do controlador.
  • Na linha 57, o método chamado é o excluir.
  • Entre as linhas 66 e 94 está definido o componente wdlgDetalhe, que possui os campos para edição e um commandLink que chama o método gravar do controlador na linha 83 (por enquanto não foi implementado o método cancelar - basta fechar a janela).
  • No começo do arquivo, entre as linhas 11 e 17, uma sobreposição do CSS utilizado pelo Primefaces, para tirar as bordas do p:panelGrid da linha 73 e o scroll do p:dialog da linha 66.

Já no controlador, os métodos são bem tranquilos, ressaltando que para atualizar a tabela, somos obrigados a esvaziar a lista autores (linhas 38 e 43), o que automaticamente causa uma nova leitura dos dados a partir do banco (linhas 63, 64 e 65). Essa implementação funciona, mas é justamente essa nova leitura do banco de dados que vamos evitar, na sequência dessa série de posts. Também há um efeito indesejado que ocorre entre os clientes que estiverem acessando a aplicação: os dados atualizados só aparecem se ocorrer uma operação de CRUD na aplicação, ou seja, após uma edição, ou após excluir um registro. Esse comportamento será substituído pela atualização automática, no próximo post.

Atualizando DataTables dinamicamente (II)

Este post faz parte de uma série que demonstra o uso de Primefaces, Spring, JPA, JSF para criar uma aplicação que atualiza as DataTables dos clientes automaticamente. Confira os outros posts da série aqui: Primefaces

A configuração do Primefaces em um projeto é bem simples, são apenas três etapas:

  1. Bibliotecas: basta acrescentar o arquivo .jar no projeto (no momento, estou usando esse: primefaces-3.6-20130315.085445-6.jar) e baixar um arquivo .jar de tema de sua escolha, deste site: http://repository.primefaces.org/org/primefaces/themes/ (no momento, estou usando o redmond 1.0.10), acrescentando esse arquivo no projeto, também.

  2. Configuração do arquivo web.xml:
        <!-- Primefaces -->
        <context-param>
            <param-name>com.sun.faces.writeStateAtFormEnd</param-name>
            <param-value>false</param-value>
        </context-param>
        <context-param>
            <param-name>com.sun.faces.allowTextChildren</param-name>
            <param-value>true</param-value>
        </context-param>
        <context-param>
            <param-name>primefaces.THEME</param-name>
            <param-value>redmond</param-value>
        </context-param>
        <context-param>
            <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
            <param-value>.xhtml</param-value>
        </context-param>
        <context-param>
            <description>The default for this parameter is false. Fixes IE xhtml content-type restriction.</description>
            <param-name>com.sun.faces.preferXHTML</param-name>
            <param-value>false</param-value>
        </context-param>
        <!-- Fim Primefaces -->

    Uma atenção para a linha 12, que indica qual o nome do tema que será usado.

  3. Configuração nas páginas xhtml: todas as páginas que vão usar componentes do Primefaces deverão ter uma declaração de namespace como destacado abaixo:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:p="http://primefaces.org/ui"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

Pronto. Um exemplo de uma página que usa Primefaces é a página de login que já foi mostrada aqui: Configurando o Spring (II).

Em seguida, vamos demonstrar o uso de p:dataTable em uma página, lembrando que é preciso:


private List<Autor> autores = new ArrayList<Autor>();


    public List<Autor> getAutores() {
        if (autores.isEmpty()) {
            autores = facadeBD.listar(Autor.class);
        }
        return autores;
    }

    public void setAutores(List<Autor> autores) {
        this.autores = autores;
    }

Depois de inserir alguns dados na tabela pessoa, podemos criar uma nova página chamada de autores.xhtml, conforme o código abaixo:

<?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:p="http://primefaces.org/ui"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Livraria</title>
    </h:head>
    <h:body>
        <h:form prependId="false" id="frmPrincipal">
            <p:outputPanel id="pnlTabela">                
                <p:dataTable id="Tabela" 
                             widgetVar="wTabela"
                             rowIndexVar="rowNumber"
                             rows="10"
                             paginator="true"
                             value="#{controlador.autores}"
                             var="registro">
                    <f:facet name="header">
                        <h:outputText value="Autores"/>
                    </f:facet>
                    <p:column headerText="Nome">
                        #{registro.nome}
                    </p:column>
                    <p:column headerText="Sobrenome">
                        #{registro.sobrenome}
                    </p:column>
                </p:dataTable>
            </p:outputPanel>
        </h:form>
    </h:body>
</html>
Apesar de ser um código bem pequeno, há muitas coisas importantes acontecendo aqui:

  1. Primeiro, é bom observar que a lista de autores é carregada do banco de dados apenas uma vez pelo controlador, somente na primeira vez que o getAutores() é executado.
  2. O formulário tem o atributo prependId="false". Isto evita que os ids dos componentes carreguem o sufixo dos componentes-pais, o que em alguns casos vai facilitar para escrever o código.
  3. Dentro do formulário há um outputPanel que envolve a tabela (linha 12). Ele vai ser utilizado para a atualização automática da página.
  4. Os dados da tabela vêm do controlador, pelo atributo autores (linha 18). Cada elemento da lista autores é referenciado na tabela pelo identificador registro (linha 19).
  5. A tabela possui um id e um widgetVar (linhas 13 e 14). Ambos são usados quando vamos executar métodos javaScript relativos ao componente dataTable do Primefaces (widgetVar) ou quando vamos atualizar (Tabela). O importante é que o nome usado não pode ser igual para os dois atributos - eu costumo colocar um "w" como prefixo no widgetVar.
  6. Os atributos rowIndexVar e rows (linhas 14 e 15) sempre devem ser definidos. Há situações em que eles são exigidos (apesar de serem opcionais...).
  7. Há muitas outras propriedades que podem ser (e mais adiante serão) configuradas, mas não vamos discutir isso aqui - a documentação do Primefaces e o showcase deles já demonstra todas elas - vamos citar conforme forem aparecendo.

Bem, esta página já produz resultados, como mostra a figura acima. Para acessá-la enquanto não temos um menu, execute a aplicação e mude o endereço no navegador de index.xhtml para autores.xhtml. No próximo post, vamos mostrar como implementar o CRUD, antes de mostrar a atualização automática nos clientes.

quarta-feira, 2 de outubro de 2013

Atualizando DataTables dinamicamente (I)

Nesta nova série de posts vou mostrar como utilizar JSF, JPA, Spring e Primefaces para criar um mecanismo que atualiza os dados de DataTables de uma mesma aplicação automaticamente, mesmo que estejam executando em máquinas diferentes (ou seja, em sessões de navegadores diferentes). Confira a série toda aqui: Primefaces

Como pré-requisito, toda a estrutura em camadas que foi mostrada na série anterior (vejam neste link: Spring + JPA).

Começaremos com a lista de bibliotecas que eu usei no projeto:

antlr-2.7.7.jar
aopalliance-1.0.jar
asm-attrs.jar
asm.jar
cglib-2.1.3.jar
commons-beanutils-1.8.2.jar
commons-collections-3.2.1.jar
commons-digester-1.7.jar
commons-fileupload-1.2.2.jar
commons-io-2.0.1.jar
commons-javaflow-20060411.jar
commons-lang-2.6.jar
commons-lang3-3.1.jar
commons-logging-1.1.jar
commons-logging.jar
dom4j-1.6.1.jar
groovy-all-2.0.1.jar
hibernate-commons-annotations-4.0.1.Final.jar
hibernate-core-4.1.8.Final.jar
hibernate-entitymanager-4.1.8.Final.jar
hibernate-jpa-2.0-api-1.0.1.Final.jar
iText-2.1.7.jar
jasperreports-4.5.1.jar
javassist-3.15.0-GA.jar
javax.faces-2.1.4.jar
jaxb-api-2.1.jar
jaxb-impl-2.1.3.jar
jboss-logging-3.1.0.GA.jar
jboss-transaction-api_1.1_spec-1.0.0.Final.jar
joda-time-2.1.jar
jstl.jar
jxl-2.6.10.jar
log4j-1.2.16.jar
mysql-connector-java-5.1.6-bin.jar
org.springframework.aop-3.1.2.RELEASE.jar
org.springframework.asm-3.1.2.RELEASE.jar
org.springframework.aspects-3.1.2.RELEASE.jar
org.springframework.beans-3.1.2.RELEASE.jar
org.springframework.context-3.1.2.RELEASE.jar
org.springframework.context.support-3.1.2.RELEASE.jar
org.springframework.core-3.1.2.RELEASE.jar
org.springframework.expression-3.1.2.RELEASE.jar
org.springframework.instrument-3.1.2.RELEASE.jar
org.springframework.instrument.tomcat-3.1.2.RELEASE.jar
org.springframework.jdbc-3.1.2.RELEASE.jar
org.springframework.jms-3.1.2.RELEASE.jar
org.springframework.orm-3.1.2.RELEASE.jar
org.springframework.oxm-3.1.2.RELEASE.jar
org.springframework.test-3.1.2.RELEASE.jar
org.springframework.transaction-3.1.2.RELEASE.jar
org.springframework.web-3.1.2.RELEASE.jar
org.springframework.web.portlet-3.1.2.RELEASE.jar
org.springframework.web.servlet-3.1.2.RELEASE.jar
org.springframework.web.struts-3.1.2.RELEASE.jar
poi-3.7-20101029.jar
postgresql-9.2-1000.jdbc4.jar
primefaces-3.6-20130315.085445-6.jar
primefaces-extensions-0.7.0.jar
redmond-1.0.10.jar
spring-ldap-1.3.1.RELEASE-all.jar
spring-security-3.0.0.RELEASE.zip
spring-security-acl-3.1.2.RELEASE.jar
spring-security-aspects-3.1.2.RELEASE.jar
spring-security-cas-3.1.2.RELEASE.jar
spring-security-config-3.1.2.RELEASE.jar
spring-security-core-3.1.2.RELEASE.jar
spring-security-crypto-3.1.2.RELEASE.jar
spring-security-ldap-3.1.2.RELEASE.jar
spring-security-openid-3.1.2.RELEASE.jar
spring-security-remoting-3.1.2.RELEASE.jar
spring-security-taglibs-3.1.2.RELEASE.jar
spring-security-web-3.1.2.RELEASE.jar
standard.jar
xstream-1.3.1.jar

É claro que nem todas serão necessárias no momento, mas é o que eu uso com mais frequência e vou deixar a lista assim, porque com todos esses arquivos o funcionamento é garantido. Algumas versões já são um pouco antigas, e provavelmente uma migração exija alguns ajustes.

O ambiente é o JBoss as 7.1.1, NetBeans 7.3.1, Java 7, Hibernate 4, JPA 2, MySQL ou PostgreSQL, Primefaces 3.6 ou superior.

Devemos começar configurando o JBoss:
  1. Para instalar a versão 7.1.1, basta fazer o download, descompactar, e copiar os arquivos jar do MySQL e/ou do PostgreSQL para a pasta <pasta_do_JBoss>/standalone/deployments e o próprio servidor se encarregará de instalar os módulos.
  2. Há um script na pasta bin do JBoss chamado add-user.sh (ou add-user.bat, para Windows). É necessário executar esse script e seguir as indicações para cadastrar um usuário e senha de administrador.
  3. Em seguida, instalar o JBoss no NetBeans (a versão 7.1.1 do JBoss só é suportada a partir da versão 7.3.1 do NetBeans).
  4. Depois de iniciar o JBoss pelo NetBeans, é preciso acessar o Console Admin (em serviços - botão direito do mouse sobre o JBoss, escolher Visualizar Console Admin ou entrando pelo endereço localhost:9990, geralmente).
  5. Já na tela de administração,é preciso configurar um datasource:
  • No canto direito superior escolher Profile, em seguida no menu à esquerda escolher Datasources, e na tela central escolher add.
  • Preencher Name e JNDI Name, lembrando que o JNDI Name deve coincidir com o jndi-name usado no jboss-web.xml (no meu caso, java:/fonteDeDados).
  • Na tela seguinte escolher o banco de dados (MySQL, PostgreSQL).
  • Na tela seguinte preencher com os dados da conexão (jdbc:postgresql://meuServidor:5432/meuBancoDeDados, usuário e senha do banco).
  • Quando a configuração voltar para a lista de Datasources, é preciso selecionar o Datasource que acabou de ser instalado e clicar em enable para habilitar. Se acontecer algum erro, é só remover e fazer tudo novamente.
Depois do JBoss configurado, a estrutura mostrada na série de posts anterior vai se conectar ao datasource fonteDeDados, e qualquer alteração de servidor ou tecnologia de banco de dados será feita diretamente no console admin do JBoss. Ou seja: para a aplicação, a configuração da conexão com um banco de dados é totalmente delegada ao datasource.


Neste ponto, já é possível executar o aplicativo, fazer a autenticação e visualizar a página index.xhtml padrão criada pelo NetBeans. É claro que todos os passos da configuração do Spring e Spring Security mostradas anteriormente devem ser seguidos.

No próximo post, a configuração do Primefaces e a criação da primeira página do projeto.