Com a crescente demanda no uso de aplicativos em quaisquer dispositivos que contenham recursos necessários para o uso de internet e o surgimento de novas tecnologias, vem aumentando repentinamente e a busca por novas soluções, permitindo uma proposta inovadora para a criação própria de ferramentas que facilitam e agilizam as aplicações web.

Apresentando um conceito inicial, Java é uma linguagem de programação que possui inúmeros recursos por ser multiplataforma: independente de arquitetura e sistema operacional e também possui características como portabilidade, orientação a objetos, além de ser concisa e simples.

Ao ser compilada, a linguagem Java gera um bytecode que consiste num resultado de comunicação, em tempo de execução, entre o sistema operacional e o lado cliente a ser executado para o usuário.

Para criar componentes utilizando JSF, é necessário um ambiente de desenvolvimento capaz de interpretar a linguagem Java, além de fazer a comunicação com o HTML, com o servidor web, com o JDBC (conector para a fonte de dados) e por fim com o banco de dados, tudo isso no formato para a web. Existem inúmeros ambientes de desenvolvimentos, mas recomendamos o NetBeans IDE que se encontra na versão 8.0.1.

Já na parte de banco de dados, há o PostgreSQL que é um banco de dados robusto e possui características simples e sua manipulação é de maneira muito facilitada. Poderíamos escolher qualquer outro banco de dados para ilustrar este artigo, mas o PostgreSQL é muito rápido quando trata das transações de dados. Além disso, o NetBeans IDE já tem em suas opções uma para anexar a biblioteca JDBC própria e pronta para o projeto a ser desenvolvido.

Primeiros Passos para a Criação do Projeto Web

Feito o download do ambiente de desenvolvimento e do gerenciador do banco de dados e ter instalado ambos, agora podemos iniciar a criação de um componente JSF. Após ter aberto o NetBeans IDE, é necessário ir na barra de menus → Arquivo → Novo Projeto. Vai aparecer uma janela de opções, como a demonstrada na Figura 1.

Janela de opções para escolha de um Novo Projeto
Figura 1. Janela de opções para escolha de um Novo Projeto

Após isso, clicar em Próximo e logo aparecerá uma janela para colocar o nome do projeto (que pode ser Componentes) e o local do mesmo (que pode ser num local seguro e de qualquer preferência) como ilustra a Figura 2.

Configuração do Nome do Projeto e a Localização do Projeto
Figura 2. Configuração do Nome do Projeto e a Localização do Projeto

Feito isso, e ter clicado em Próximo, vai aparecer uma janela para definir qual o servidor web que rodará a aplicação. Por preferência, a escolha é do Apache Tomcat (embora o GlassFish Server também seja muito bom) e a versão do Java EE web é a 7. O caminho do contexto deixa do jeito que foi criado automaticamente, como mostra a Figura 3.

Escolha do Servidor, Versão do Java EE e do Caminho do Contexto
Figura 3. Escolha do Servidor, Versão do Java EE e do Caminho do Contexto

E finalmente, após ter clicado em próximo, aparecerá a última janela de escolha de configuração: por se tratar de JSF, seleciona-se a opção JavaServer Faces e por fim clique em Finalizar, como mostra a Figura 4.

Escolha de Frameworks e Bibliotecas
Figura 4. Escolha de Frameworks e Bibliotecas

Projeto criado então agora o próximo passo é colocar em prática a idéia de como será criado o componente e onde será utilizado.

Como a linguagem Java possui recursos de JDBC suficientemente para conectar com o banco de dados, como já havia dito, o PostgreSQL será necessário para a armazenagem de dados onde a aplicação será encarregada de ter uma parte responsável de obter tais dados e mostrar para o usuário.

Adicionando Driver JDBC ao Projeto

Para adicionar o driver JDBC para o projeto, é só clicar com o botão direito em Bibliotecas, navegar até a opção Bibliotecas Disponíveis, selecionar Driver JDBC do PostgreSQL e finalmente clicar em Adicionar Biblioteca, como mostra a Figura 5.

Escolha do Driver JDBC
Figura 5. Escolha do Driver JDBC

Inicialmente, ao dar os primeiros passos com a implementação, é necessário criar uma tabela no PostgreSQL com o nome de estados,cujo princípio é ter um conjunto de dados já armazenados que possam facilitar na hora de chamar os mesmos durante a execução do projeto.

Configurando PostgreSQL

Com o PostgreSQL já instalado, agora temos que criar um banco de dados internamente,e para isso é preciso iniciar o gerenciador, que no caso é pgAdmin. Dê dois cliques em cima de PostgreSQL X.Y (onde X.Y é a versão atual instalada, provavelmente são dois números), clique com o botão direito em: Databases e depois em New Database, ou vá à barra de menu superior e selecione a opção Edit e clicar em Create. Em seguida aparecerá a seguinte tela da Figura 6.

New Database com o nome do banco de dados
Figura 6. New Database com o nome do banco de dados

Colocado um nome, é só clicar em OK. Para conectar, basta dar duplo clique em cima do banco de dados criado e para adicionar a tabela que será com o nome de uf, clique no botão SQL (Execute arbitrary SQL queries), como mostra na Figura 7logo abaixo.

Tela de orientação de propriedades, características e informações internas do banco de dados
Figura 7. Tela de orientação de propriedades, características e informações internas do banco de dados

Agora é hora de criar de fato a tabela uf e para isso, coloque o seguinte código descrito na Listagem 1 e logo após apertar a tecla F5 e a tabela será criada.


CREATE TABLE uf
(
  uf_codigo integer NOT NULL,
  uf_nome character varying(20) NOT NULL,
  uf_sigla character varying(2) NOT NULL,
  CONSTRAINT uf_pkey PRIMARY KEY (uf_codigo)
)
 
WITH (
  OIDS=FALSE
);
 
ALTER TABLE uf
  OWNER TO postgres;
  
CREATE INDEX uf_uf_codigo_idx
  ON uf
  USING btree
  (uf_codigo);
Listagem 1. Esta listagem refere-se para a criação de uma tabela de estados com nome de: uf

Após ter executado e não ter dado erro, agora temos que cadastrar alguns estados e, para isso, basta colocar esse conjunto de comandos descritos na Listagem 2 e executar que os estados ficarão armazenados.


insert into uf (uf_codigo, uf_nome, uf_sigla) values (1,'Acre','AC');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (2,'Alagoas','AL');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (3,'Amazonas','AM');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (4,'Amapá','AP');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (5,'Bahia','BA');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (6,'Ceará','CE');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (7,'Distrito Federal','DF');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (8,'Espirito Santo','ES');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (9,'Goiás','GO');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (10,'Maranhão','MA');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (11,'Minas Gerais','MG');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (12,'Mato Grosso do Sul','MS');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (13,'Mato Grosso','MT');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (14,'Pará','PA');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (15,'Paraiba','PB');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (16,'Pernambuco','PE');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (17,'Piauí','PI');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (18,'Paraná','PR');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (19,'Rio de Janeiro','RJ');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (20,'Rio Grande do Norte','RN');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (21,'Rondonia','RO');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (22,'Roraima','RR');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (23,'Rio Grande do Sul','RS');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (24,'Santa Catarina','SC');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (25,'Sergipe','SE');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (26,'São Paulo','SP');
insert into uf (uf_codigo, uf_nome, uf_sigla) values (27,'Tocantins','TO');
Listagem 2. Conjunto de comandos que servem para armazenar valores como código, nome do estado e a sigla dentro da tabela uf

Feito isso, é só fechar a janela do SQL (pode ser sem salvar mesmo), que agora temos que dar continuidade a criação do componente. Mas para isso é preciso ter em mente que antes haverá uma caixa de seleções de itens (selectItems) via HTML para buscar todos os estados cadastrados que estão dentro do banco de dados. E isso se transformará em um componente.

Criação de um Novo Projeto Java Auxiliar

Para que tenha uma comunicação com o banco de dados, é recomendado criar uma Nova Aplicação Java que contenha todos esses requisitos. O nome pode ser DAO (que costumeiramente se usa para dizer que é uma camada intermediária entre a aplicação e o acesso a dados). Após isso é preciso criar três pacotes: um para conexão com o banco de dados, o segundo para encapsular dados e o terceiro para executar comandos de SQL e tratar possíveis erros. Por fim, dentro de cada pacote, as seguintes classes deverão estar conforme demonstra aFigura 8.

Hierarquia de arquivos dentro do Novo Aplicativo Java
Figura 8. Hierarquia de arquivos dentro do Novo Aplicativo Java

Agora, é necessário implementar dentro do arquivo Conexao.java, o seguinte código da Listagem 3, pois o mesmo ficará responsável por conectar com o banco de dados.


package conexao;
 
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;
 
public final class Conexao {
 
    private static final String usuario = "postgres"; 
    //usuário do banco de dados
    private static final String senha = "postgres123"; 
    //senha do banco de dados
    private static final String url = "jdbc:postgresql:
    //127.0.0.1:5432/postgres"; //driver
 
    public static Connection open() { //abrir conexão
        try {
            Class.forName("org.postgresql.Driver");
            return DriverManager.getConnection(url, usuario, senha);
        } catch (SQLException | ClassNotFoundException ex) {
            Logger.getLogger(Conexao.class.getName())
            .log(Level.SEVERE, null, ex);
            return null;
        }
    }
 
    public static void close(ResultSet rs, Statement st, 
    Connection conn) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
            }
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
            }
        }
    } // fechar conexão
 
    public static void close(Statement st, Connection conn) {
        close(null, st, conn);
    }
 
    public static void close(Connection conn) {
        close(null, null, conn);
    }
}
Listagem 3. Classe que tem funcionalidade para estabelecer conexão com o banco de dados

Dentro do arquivo DAO.java é necessário programar uma interface e para isso utilizaremos o código descrito na Listagem 4.


package dao;
import java.util.List;
public interface DAO<T> {
    /**
     * Recupera um registro de uma tabela, baseado na sua chave primária
     *
     * @param chave a chave primária
     * @return o registro correspondente, ou
     * <code>null</code> se nada for encontrado
     */
    public T getSingle(Object... chave);
    /**
     * Recupera uma lista de registros
     *
     * @return a lista de registros, ou
     * <code>null</code> se nada for encontrado
     */
    public List<T> getList();
    /**
     * Recupera uma lista de registros, o tamanho da lista é limitado pelo
     * parâmetro
     *
     * @param top o número máximo de registros selecionados
     * @return a lista de registros, ou
     * <code>null</code> se nada for encontrado
     */
    public List<T> getList(int top);
}
Listagem 4. Código que recupera registro baseado em sua chave primária e também em uma lista de registros de acordo com o seu tamanho

Dentro do arquivo DAOException.java teremos o tratamento de erros e utilizaremos o seguinte código da Listagem 5.


package dao;
 
public class DAOException extends Exception {
    public DAOException(Throwable cause) {
        super(cause);
    }
    public DAOException(String message, Throwable cause) {
        super(message, cause);
    }
    public DAOException(String message) {
        super(message);
    }
    public DAOException() {
        super();
    }
}
Listagem 5. Classe que propõe o tratamento de erros com base em suas exceções

Agora vamos precisar de uma entidade para fazer a busca de dados, então é necessário programar dentro do arquivo UfDAO.java, o seguinte código descrito na Listagem 6.


package dao;
 
import daoentidades.UF;
import conexao.Conexao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
 
public class UfDAO
        implements DAO<UF> {
 
    @Override
    public UF getSingle(Object... chave) {
        if (chave[0] instanceof Integer) {
            Connection conn = Conexao.open();
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                ps = conn.prepareStatement("select uf_codigo, 
                uf_nome, uf_sigla from uf where uf_codigo = ?");
                ps.setInt(1, (Integer) chave[0]);
                rs = ps.executeQuery();
                if (rs.next()) {
                    return new UF(rs.getInt("uf_codigo"), 
                    rs.getString("uf_nome"), rs.getString("uf_sigla"));
                }
            } catch (SQLException ex) {
            } finally {
                Conexao.close(rs, ps, conn);
            }
        }
        return null;
    }
 
    @Override
    public List<UF> getList() {
        return getList(0);
    }
 
    @Override
    public List<UF> getList(int top) {
        if (top < 0) {
            return null;
        }
        List<UF> lista = null;
        Connection conn = Conexao.open();
        Statement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.createStatement();
            rs = ps.executeQuery("select " + (top > 0 ? 
            "top " + top : "") + "uf_codigo, uf_nome, uf_sigla from uf");
            lista = new ArrayList<>();
            while (rs.next()) {
                lista.add(new UF(rs.getInt("uf_codigo"), 
                rs.getString("uf_nome"), rs.getString("uf_sigla")));
            }
        } catch (SQLException ex) {
        } finally {
            Conexao.close(rs, ps, conn);
        }
        return lista;
    }
}
Listagem 6. Classe que faz busca de dados tanto pela chave primária quanto por uma Lista

E por fim é preciso programar uma camada de negócio responsável por receber e enviar as informações, ficando assim dentro do arquivo UF.java, descrito na Listagem 7.


package daoentidades;
 
import java.io.Serializable;
import java.util.Objects;
 
public class UF
        implements Serializable {
 
    private Integer codigo;
    private String nome;
    private String sigla;
    public UF() {
        codigo = null;
        nome = null;
        sigla = null;
    }
    public UF(Integer codigo, String nome, String sigla) {
        this.codigo = codigo;
        this.nome = nome;
        this.sigla = sigla;
    }
    public Integer getCodigo() {
        return codigo;
    }
    public void setCodigo(Integer codigo) {
        this.codigo = codigo;
    }
    public String getNome() {
        return nome;
    }
    public void setNome(String nome) {
        this.nome = nome;
    }
    public String getSigla() {
        return sigla;
    }
    public void setSigla(String sigla) {
        this.sigla = sigla;
    }
    @Override
    public int hashCode() {
        int hash = 3;
        hash = 47 * hash + Objects.hashCode(this.codigo);
        return hash;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final UF other = (UF) obj;
        return Objects.equals(this.codigo, other.codigo);
    }
}
Listagem 7. Classe responsável por enviar e obter informações de acordo com o banco de dados e o modo cliente

Adicionando o Projeto Java ao Projeto Java Web

Agora precisamos finalizar essa parte de acesso, manipulação e comunicação entre os dados e em como serão tratados daqui em diante. Para continuar com o projetoComponentes, o mesmo tem que estar aberto no Netbeans IDE e a aplicação Java DAO, que foi criada anteriormente, deve ser adicionada como parte de uma biblioteca dentro da aplicação web. Para isso, tem que fazer o mesmo que foi feito quando adicionou-se o driver JDBC, só que ao invés de adicionar uma biblioteca, tem que escolher a opção de Adicionar um Projeto, conforme mostra na Figura 9 abaixo.

Hierarquia da Aplicação web e do Arquivo Java adicionado
Figura 9. Hierarquia da Aplicação web e do Arquivo Java adicionado

Criação de Classes Auxiliares

Seguindo a Figura 9, surge a proposta de criar um pacote chamado Estados dentro do Pacote de Códigos-Fonte. Para isso, cria-se uma classe com o nome de ListaDeEstados.java cuja funcionalidade é armazenar todos os valores obtidos num Array List, de acordo com o seguinte código descrito na Listagem 8.


package Estados;
import dao.UfDAO;
import daoentidades.UF;
import java.io.Serializable;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UISelectOne;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.event.ValueChangeListener;
 
@ManagedBean
@ViewScoped
public final class ListaDeEstados implements Serializable, ValueChangeListener {
    
    private final List<UF> estadosCadastrados;
    
    public ListaDeEstados() {
        UfDAO dao = new UfDAO();
        estadosCadastrados = dao.getList();
    }
    @Override
    public void processValueChange(ValueChangeEvent vce)
            throws AbortProcessingException {
        escolheuEstado(vce);
    }
    
    public void escolheuEstado(ValueChangeEvent evt) {
        UF estado = (UF) evt.getNewValue();
    }
    
    public void selecionaEstado(AjaxBehaviorEvent event) {
        UF estado = (UF)((UISelectOne)event.getComponent()).getValue();
    }
 
    public List<UF> getEstadosCadastrados() {
        return estadosCadastrados;
    }    
}
Listagem 8. Classe responsável por trazer os estados cadastrados num Array List de modo a ficar na memória

Após isso, só é preciso criar mais uma classe, agora com o nome de UFConverter.java, de acordo com a Listagem 9.


package Estados;
 
import dao.UfDAO;
import daoentidades.UF;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
 
@FacesConverter("UFConverter")
public class UFConverter implements Converter {
 
    @Override
    public Object getAsObject(FacesContext fc, 
    UIComponent uic, String string) {
        if (string != null) {
            try {
                int codigo = Integer.parseInt(string);
                UF estado = (new UfDAO()).getSingle(codigo);
                return estado;
            } catch (NumberFormatException ex) {
                throw new ConverterException(
                        new FacesMessage("Erro convertendo UF!"));
            }
        }
        return null;
    }
 
    @Override
    public String getAsString(FacesContext fc, 
    UIComponent uic, Object objeto) {
        if (objeto == null) {
            return "";
        }
        final UF uf = (UF) objeto;
        if (uf.getCodigo() == null) {
            return null;
        }
        return uf.getCodigo().toString();
    }
 
}
Listagem 9. Classe que possui como funcionalidade converter o código do estado em String e a String em objeto a ser disponibilizado para ser visível ao componente

Criando Arquivo Componente

Finalizando essa parte de criação de classes, agora é a hora tão esperada que consiste na criação do componente JSF. Para isso, com base na Figura 9, clique com o botão direito em Páginas WebNovoOutros e depois selecione JavaServer Faces. Por fim, selecione Componente Composto do JSF. Clicando em próximo, vai aparecer a seguinte tela da Figura 10.

Tela de criação de um novo componente JSF
Figura 10. Tela de criação de um novo componente JSF
Nota: É preciso colocar um Nome do Arquivo e obrigatoriamente o componente deve estar na pasta resources\xxx, onde xxx é um nome de uma subpasta. Feito isso, basta clicar em Finalizar.
Nota: Na Figura 10 está aparecendo uma mensagem de aviso em vermelho: “estado.xhtml already exist”. Isso acontece porque há componente com esse mesmo nome já existente no projeto.

Agora a página de criação de componente está pronta para ser programada. Como a ideia inicial é buscar todos os estados cadastrados, então com o código descrito na Listagem 10 dentro do arquivo componente criado estado.xhtml, já é suficiente para que traga os dados esperados.


<?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:cc="http://xmlns.jcp.org/jsf/composite"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
 
    <cc:interface>
        <cc:attribute name="valor" required="true"/>
    </cc:interface>
    <cc:implementation>
        <h:outputLabel for="selEstado"><br/>Estados</h:outputLabel>
        <h:selectOneMenu id="selEstado" immediate="true"
                         value="#{cc.attrs.valor}"
                         required="true" requiredMessage="Escolha um estado!">
            <f:converter converterId="UFConverter"/>
            <f:selectItem itemLabel="-- Selecione um --" 
                          itemValue="#{null}"/>
            <f:selectItems value="#{listaDeEstados.estadosCadastrados}"
                           var="uf"
                           itemLabel="#{uf.nome}"
                           itemValue="#{uf}"
                           />
        </h:selectOneMenu>
    </cc:implementation>
</html>
Listagem 10. Arquivo componente estado.xhtml e sua implementação com interface e códigos HTML que servem para buscar os dados dentro de um selectItems

Chamando Componente para Página Principal

Finalmente, após ter feito a implementação da página de componente, agora a proposta é chamar o componente para a página inicial. Ao criar uma aplicação Java web, sempre é vindo automaticamente um arquivo denominado index.xhtml, que é onde tudo se inicia, pois todas as páginas devidamente configuradas e ligadas a ela serão seguidas através dessa página. Ficará da mesma forma que o código da Listagem 11o index.xhtml.


<?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://xmlns.jcp.org/jsf/html"
      xmlns:ex="http://xmlns.jcp.org/jsf/composite/ex">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form prependId="false">
          Hello from Facelets
          <ex:estado valor=""/>
        </h:form>
    </h:body>
</html>
Listagem 11. Arquivo index.xhtml onde consta o código HTML e também há a chamada do componente criado: estado

Para concluir com o projeto, é necessário salvar todos os arquivos, mas o recomendado é ir salvando arquivo por arquivo.

Agora é só executar o projeto (pode ser usando a tecla F6) e se rodar no Google Chrome, ficará de acordo com a Figura 11.

Projeto pronto sendo executado dentro do navegador Google Chrom
Figura 11. Projeto pronto sendo executado dentro do navegador Google Chrome

Lembrando que funciona na maioria dos navegadores existentes, mas o Mozilla Firefox é browser padrão do Netbeans IDE.

Para a criação de componentes JSF é preciso já ter um conhecimento básico/intermediário sobre a linguagem Java e HTML e principalmente na área de orientação a objetos. Qualquer passo ou procedimento não executado ou caso tenha se esquecido de programar qualquer passo descrito, você pode usar o código-fonte disponível pra download nesse post.