Mostrando postagens com marcador JavaEE 6. Mostrar todas as postagens
Mostrando postagens com marcador JavaEE 6. Mostrar todas as postagens

9 de julho de 2013

Login customizado com Spring Security 3.1.4

Olá Pessoal!

Depois de muito tempo sem postar (diríamos meses...), resolvi escrever sobre um tema que a algum tempo muitas pessoas vinham me pedindo sobre isso, e como eu consegui fazer uma solução para este "problema" resolvi escrever aqui, (não sei se é a forma certa, mas é funcional)!
O tema de hoje é sobre como ter um campo a mais no form de login utilizando o Spring Security, imagino eu que tenha uma forma mais simples de fazer... mas enquanto não descubro, vamos àquela que funciona.

Antes de começar o nosso projeto, é de total importância que você já conheça ou já tenha utilizado o Spring Security, caso não conheça, leia esta postagem.

* Começando *

Inicialmente a minha estrutura do banco de dados (básico):

 
Neste caso, eu tenho duas tabelas: uma de empresa onde a coluna regra será para dizer se é ROLE_ADM, ROLE_USER... entre outras, e tenho o usuário que está ligado diretamente a empresa, fiz o mais básico possível apenas para mostrar como que pode estar colocando um campo a mais no login, neste caso: o usuário terá que colocar seu login, senha e escolher a qual empresa pertence.
Para entender direito, esta será a nossa tela de login:


O projeto completo terá esta estrutura:


Não dê tanta atenção quanto ao pacotes que se encontram as classes, pois esta apenas foi uma organização minha, acredito que cada um tenha uma forma melhor de organizar.
A primeira coisa a se fazer é baixar a biblioteca do Spring Security na versão 3.1.4 (pode baixar clicando aqui), juntamente com o Spring Framework 3.1.4. As bibliotecas que eu uso são:


*Códigos*

Inicialmente é necessário fazer as classes de modelo, que estão no pacote javasemcafe.model, observo que fiz este projeto utilizando JPA, mas pra quem quiser usar apenas JDBC é só fazer as adaptações, utilizei o JPA por ser mais rápido, então de acordo com a estrutura de banco de dados teremos duas classes, uma é Empresa e a outra é Usuário:
Classe Empresa.java:
@Entity
@Table(name = "empresa")
public class Empresa implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @Column(name = "codigo")
    private Integer codigo;
    @Size(max = 45)
    @Column(name = "descricao")
    private String descricao;
    @Size(max = 45)
    @Column(name = "regra")
    private String regra;

    // Getters e Setters
}

Classe Usuario.java:
@Entity
@Table(name = "usuario")
public class Usuario implements Serializable {
    @Id
    @Basic(optional = false)
    @NotNull
    @Column(name = "codigo")
    private Integer codigo;
    @Size(max = 45)
    @Column(name = "login")
    private String login;
    @Size(max = 45)
    @Column(name = "senha")
    private String senha;
    @JoinColumn(name = "empresa_codigo", referencedColumnName = "codigo")
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    private Empresa empresa;

    // Getters e Setters
}

No pacote javasemcafe.dao, temos a classe EmpresaDAO.java:
@Stateless
public class EmpresaDAO {

    @PersistenceContext(unitName = "LoginCustomizadoPU")
    private EntityManager em;

    public List<Empresa> listar() {
        List<Empresa> empresas = null;

        try {
            Query query = em.createQuery("Select e from Empresa e order by e.descricao");

            empresas = query.getResultList();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return empresas;
    }
}

Nesta classe, apenas temos um método que retorna uma lista de empresas que será utilizada com o EmpresaBean.java para popular o combo de empresas na página de login.
Classe EmpresaBean.java:
@ManagedBean
@ViewScoped
public class EmpresaBean implements Serializable {

    @EJB
    private EmpresaDAO empresaDAO;
    private List<Empresa> empresas;

    public List<Empresa> getEmpresas() {
        empresas = empresaDAO.listar();

        return empresas;
    }

    public void setEmpresas(List<Empresa> empresas) {
        this.empresas = empresas;
    }
}
Neste caso apenas preenche uma Collection que aqui é o List.

Voltando ao pacote javasemcafe.dao, temos praticamente a classe mais importante:
AutenticacaoFilter.java, é nela que fazemos a verificação de login, senha e se o usuário pertence ou não a empresa selecionada no login, abaixo explico os detalhes dela:
public class AutenticacaoFilter extends UsernamePasswordAuthenticationFilter {

    private EntityManagerFactory factory = Persistence.createEntityManagerFactory("LoginCustomizadoPU");
    private EntityManager em;
    private String mensagem;

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, BadCredentialsException {
        String login = request.getParameter("j_login");
        String senha = request.getParameter("j_senha");
        Integer codigoEmpresa = Integer.parseInt(request.getParameter("j_empresa"));

        try {
            Usuario usuario = buscarUsuario(login, senha);
            if (usuario != null) {

                if (usuario.getEmpresa().getCodigo().equals(codigoEmpresa)) {

                    Collection<GrantedAuthority> regras = new ArrayList<GrantedAuthority>();
                    regras.add(new SimpleGrantedAuthority(usuario.getEmpresa().getRegra()));

                    request.getSession().setAttribute("usuarioLogado", usuario);
                    mensagem = "Bem vindo: " + usuario.getLogin();
                    return new UsernamePasswordAuthenticationToken(usuario.getLogin(), usuario.getSenha(), regras);

                } else {
                    mensagem = "Acesso negado a empresa selecionada!";
                    throw new BadCredentialsException(mensagem);
                }

            } else {
                mensagem = "Dados Incorretos";
                throw new BadCredentialsException(mensagem);
            }

        } catch (Exception e) {
            throw new BadCredentialsException(e.getMessage());
        }
    }

    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
        SecurityContextHolder.getContext().setAuthentication(authResult);
        request.getSession().setAttribute("msg", mensagem);
        response.sendRedirect("index.jsf");
    }

    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        request.getSession().setAttribute("msg", mensagem);
        response.sendRedirect("login.jsf");
    }

    /**
     * Metodos de acesso ao BD
     *
     */
    public Usuario buscarUsuario(String login, String senha) {
        em = factory.createEntityManager();
        Usuario usuario = null;
        try {
            Query query = em.createQuery("SELECT u FROM Usuario u WHERE u.login = :login AND u.senha = :senha");
            query.setParameter("login", login);
            query.setParameter("senha", senha);

            usuario = (Usuario) query.getSingleResult();

        } catch (NoResultException e) {
            System.out.println("DAO: Não foi encontrado resultado!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            em.close();
        }

        return usuario;
    }
}

Vamos entender o que acontece nessa classe: os métodos attemptAuthentication. successfulAuthentication e unsuccessfulAuthentication são métodos criados automaticamente quando se utiliza o extends UsernamePasswordAuthenticationFilter. O primeiro método é onde ocorre a verificação do usuário, é o comando executado quando o usuário clica no botão de "Acessar" (conforme tela no início da postagem), nesse método tem um detalhe importante, que é a linha 23 onde ele dá o return com os dados do usuário, no caso das regras são aqueles ROLE_ADM, ROLE_USER.... isso é necessário para o applicationContext.xml saber se deve liberar a pagina ou não, veremos mais pra frente isso, o que você precisa saber é que, dependendo a lógica que você utilizar para montar a estrutura do BD, será possível que um usuário pertença a diferentes filiais com diferentes regras para cada uma, mas aí teria que fazer uma tabela N-N, o que prologaria ainda mais essa postagem que já vai ficar enorme. Só mais um detalhe antes de continuar, na linha 21, nós jogamos o usuário para a sessão da aplicação, assim podemos recuperar ele a qualquer momento. Continuando, se no primeiro método ocorrer o return ele vai para o método de sucesso (successfulAuthentication), onde eu digo para o Spring Security que há uma autenticação e redireciono o usuário para a página inicial. No caso de não ocorrer um return, por dados incorretos, pelo usuário não ter acesso a empresa, automaticamente cairá no método unsuccessfulAuthentication, onde eu redireciono ele para a página de login novamente.
Vamos a uma observação nessa classe: percebam que eu tenho uma String mensagem, pois bem, ela não seria necessária se caso não quisesse mostrar ao usuário os avisos de dados incorretos e outros... então eu coloco essa String na sessão da aplicação, e recupero por uma outra classe que é a AutenticacaoPhaseListener.java, que se encontra no pacote javasemcafe.controller. 

public class AutenticacaoPhaseListener implements PhaseListener {

    @Override
    public void afterPhase(PhaseEvent event) {
        //não implementado
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        FacesContext context = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
        if (session != null) {
            String mensagem = (String) session.getAttribute("msg");

            if (mensagem != null) {
                context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, mensagem, null));
                session.setAttribute("msg", null);
            }
        }
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }
}

Esta classe implementa um PhaseListener que vai pegar a mensagem na sessão da aplicação e vai mandá-la por por um FacesMessage para o JSF.

Utilizando a classe UsuarioBean.java, do pacote javasemcafe.controller(esta classe não está aparecendo na imagem da estrutura do projeto, mas deve estar no projeto), vamos pegar o usuário na sessão:

@ManagedBean
@ViewScoped
public class UsuarioBean {

    private Usuario usuarioLogado;

    public Usuario getUsuario() {
        FacesContext context = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
        usuarioLogado = (Usuario) session.getAttribute("usuarioLogado");

        return usuarioLogado;
    }

    public void setUsuario(Usuario usuario) {
        this.usuarioLogado = usuario;
    }
}

Veja que é utilizado o atributo "usuarioLogado" que foi setado lá na classe AutenticacaoFilter.java, é através dela que conseguimos pegar esse usuário. Ou você pode estar pegando o usuário diretamente do Spring Security, porém neste caso ele não te trás muitas informações de usuário, mas sei que o login é uma delas, veja um exemplo (isto não está no projeto, é apenas uma outra forma de um outro projeto meu):
public ConfiguracaoBean() {
    Authentication authentication = (Authentication) SecurityContextHolder.getContext().getAuthentication();
    if (authentication != null) {
       usuario = authentication.getName();
    }
}

Agora vamos para as páginas do JSF, começando pela index.xhtml (também conhecida: index.jsf) que será a página para onde o usuário será redirecionado se logar o sistema:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:outputText value="Olá #{usuarioBean.usuario.login}!"/><br/>
        <h:outputText value="Você está na empresa: #{usuarioBean.usuario.empresa.descricao}!"/><br/>
        
        <a href="./logout">Sair</a>
    </h:body>
</html>

Não tem nada de mais, só estou usando a classe UsuarioBean.java para mostrar o usuário que está logado na sessão da aplicação. Ficando desta forma:


Página de login, login.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Login Customizado</title>
    </h:head>
    <h:body>
        <form action="j_spring_security_check" method="post" style="text-align: right;">
            <fieldset>
                <legend>Login</legend>
                <h:panelGrid columns="2">
                    <h:outputText value="Login:" />
                    <h:inputText id="j_login" />
                    <h:outputText value="Senha:" />
                    <h:inputSecret id="j_senha" />
                    <h:outputText value="Empresa:" />
                    <h:selectOneMenu id="j_empresa" style="width: 155px" >
                        <f:selectItems value="#{empresaBean.empresas}" var="e" itemLabel="#{e.descricao}" itemValue="#{e.codigo}" />
                    </h:selectOneMenu>
                    <h:outputText />
                    <h:commandButton value="Acessar" id="btnAcessar" />
                </h:panelGrid>
                <h:message for="btnAcessar" />
            </fieldset>    
        </form>

    </h:body>
</html>

A única coisa diferente do normal das páginas de login do Spring Security, é o caso de ter adicionado o h:selectOneMenu para fazer o combo, fora isso, o restante já foi explicado na postagem que eu indico para leitura no começo do post. No caso da h:message serve para mostrar as mensagens enviadas pelo FacesMessage, aparecendo dessa forma:


Vamos ver agora os arquivos de configuração que ficam dentro do WEB-INF.
Começando com a configuração básica no web.xml para o Spring Security ser identificado (já explicado na postagem indicada):
<!-- Spring security -->
    <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>

<!-- Fim spring security --> 

Bom, como devem saber na versão JEE 6, o faces-config.xml é opcional, ou seja é utilizado em alguns casos, como o caso agora, onde eu preciso "registrar" o PhaseListener (a classe AutenticacaoPhaseListener.java): 

<faces-config version="2.1"
              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_1.xsd">
    <lifecycle>
        <phase-listener>javasemcafe.controller.AutenticacaoPhaseListener</phase-listener>
    </lifecycle>
</faces-config>

Agora vamos para toda a "mágica" da customização do login, o applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:p="http://www.springframework.org/schema/p"
       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">

    <security:http auto-config="false" use-expressions="true" access-denied-page="/index.jsf" entry-point-ref="authenticationEntryPoint" >
        <security:intercept-url pattern="/index.jsf" access="hasRole('ROLE_ADM')"/>
     
        <security:logout invalidate-session="true" logout-success-url="/login.jsf" logout-url="/logout"/>

        <security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
    </security:http>

    <bean id="authenticationFilter" class="javasemcafe.dao.AutenticacaoFilter" p:authenticationManager-ref="authenticationManager"/>

    <bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint" p:loginFormUrl="/login.jsf"/>

    <security:authentication-manager alias="authenticationManager" />
</beans>

O que vemos de diferente na configuração do applicationContext, é a partir da 15º linha, onde de uma forma geral, estou dizendo que a autenticação deverá ser feita pela classe: javasemcafe.dao.AutenticacaoFilter, como pode ver na linha 18.

Bom pessoal, acabei deixando de explicar muitas classes (do Spring Security) porque senão essa postagem iria ficar bem mais longa!

Até a próxima :)
andii.brunetta

7 de junho de 2011

JSF 2.0: Componentes PrimeFaces 2.2.1 - Parte 5

Depois de vermos sobre menus do PrimeFaces, nessa postagem vamos ver alguns outros componentes. Vamos precisar de algumas umas imagens e algumas bibliotecas extras, baixe por aqui.

Estrutura do projeto
Caso não saiba como montar a estrutura do projeto veja aqui.
Depois de pronto o projeto fica com essa estrutura:


O primeiro componente que veremos hoje é o dataExporter que serve para exportar os dados para arquivos PDF, XLS, XML ou CSV de uma determinada dataTable, então para isso eu preciso de uma dataTable:


Código-fonte:
Para este exemplo vamos precisar de uma classe Carro (pojo), então dentro de pacote de códigos fonte crie um novo pacote com o nome de model, e dentro dele crie uma nova classe Java com o nome de Carro:
public class Carro {

    private String marca;
    private int ano;
    private String placa;
    private String cor;
    private String modelo;

    public Carro(String marca, int ano, String placa, String cor, String modelo) {
        this.marca = marca;
        this.ano = ano;
        this.placa = placa;
        this.cor = cor;
        this.modelo = modelo;
    }

   //GETTERS e SETTERS
}

Agora vamos criar o nosso bean que vai dar suporte à nossa dataTable, para isso crie um pacote com o nome de controle, dentro desse pacote crie  uma nova classe Java com o nome de CarroBean:
@Named
@RequestScoped
public class CarroBean {

    private List<Carro> carros;

    public CarroBean() {
        listarCarros();
    }

    private void listarCarros() {
        carros = new ArrayList<Carro>();
        carros.add(new Carro("Fiat", 2011, "DFV 3455", "Verde", "Novo Uno"));
        carros.add(new Carro("Toyota", 2009, "ADV 3245", "Prata", "Prado"));
        carros.add(new Carro("Volks", 2010, "AAC 2351", "Vermelho", "Gol"));
        carros.add(new Carro("Volks", 2011, "EGF 1245", "Preto", "Cross Fox"));
    }

    //Gerar GET e SET
}
Bom nem tem o que explicar o código acima... Para este exemplo vamos precisar das imagens (do link que disponibilizei lá no inicio), por enquanto só irei precisar de: xsl.png e pdf.png, dentro de páginas web, crie um novo diretório com o nome de imagens, e coloque as imagens dentro dela. Agora vamos para a página index:
<h:form>
    <p:dataTable id="tabelaCarros" value="#{carroBean.carros}" var="c">
        <p:column>
            <f:facet name="header">Marca</f:facet>
            <h:outputText value="#{c.marca}" />
        </p:column>
        <p:column>
            <f:facet name="header">Modelo</f:facet>
            <h:outputText value="#{c.modelo}" />
        </p:column>
        <p:column>
            <f:facet name="header">Ano</f:facet>
            <h:outputText value="#{c.ano}" />
        </p:column>
        <p:column>
            <f:facet name="header">Cor</f:facet>
            <h:outputText value="#{c.cor}" />
        </p:column>
        <p:column>
            <f:facet name="header">Placa</f:facet>
            <h:outputText value="#{c.placa}" />
        </p:column>
    </p:dataTable>

    <h:commandLink>
        <p:graphicImage value="/imagens/xls.png" />
        <p:dataExporter type="xls" target="tabelaCarros" fileName="carros" />
    </h:commandLink>
    <h:commandLink>
        <p:graphicImage value="/imagens/pdf.png" />
        <p:dataExporter type="pdf" target="tabelaCarros" fileName="carros"/>
    </h:commandLink>
</h:form>
No código acima eu tenho uma dataTable normal (vista em postagens anteriores) e logo abaixo dela eu tenho os dataExporter, o detalhe mais importante é quanto a propriedade target, veja que ela corresponde ao id da tabela.

Nosso segundo componente é o pickList:


Vamos direto ao código, então dentro do pacote controle (criado anteriormente) vamos criar uma classe com o nome de PicklistBean:
@Named
@RequestScoped
public class PicklistBean {

    private DualListModel<String> cores;

    public PicklistBean() {
        listarCores();
    }

    private void listarCores() {
        List<String> lista = new ArrayList<String>();
        List<String> selecionados = new ArrayList<String>();

        lista.add("Azul");
        lista.add("Amarelo");
        lista.add("Vermelho");
        lista.add("Verde");
        lista.add("Roxo");

        cores = new DualListModel<String>(lista, selecionados);
    }

    public void mostrarCoresSelecionadas() {
        for (String cor : cores.getTarget()) {
            System.out.println("Esta cor é: " + cor);
        }
    }

    //Gerar GET e SET
}
Veja que usamos um DualListModel (do próprio PrimeFaces), para inicializar ele eu preciso informar duas listas, uma é dos os objetos a serem selecionados (chamada de source) e a outra representará o objetos selecionados(target), o método mostrarCoresSelecionadas() percorre a lista de cores selecionadas, repare que ele dá um getTarget() ... para pegar a lista original é só dar um getSource().
<h:form>
    <p:pickList value="#{picklistBean.cores}" var="cor" iconOnly="true" itemLabel="#{cor}" itemValue="#{cor}">
        <f:facet name="sourceCaption">Lista</f:facet>
        <f:facet name="targetCaption">Selecionadas</f:facet>
    </p:pickList>
    <p:commandButton value="Ver cores selecionadas" actionListener="#{picklistBean.mostrarCoresSelecionadas}" />
</h:form>
Ao final do código eu coloquei um commandButton que chama o método para listar as cores selecionadas.

O nosso próximo componente é o printer, ele é um componente que chama a função de impressão do navegador, com a diferença que o programador define qual a parte da página será impressa, para usar ele eu vou precisar da imagem printer.png (no arquivo que eu passei), coloque-o dentro do diretório imagens. Vou mostra-lo junto com o próximo componente, mas antes segue o código:
<h:outputLink id="lnk" value="#">
    <p:printer target="quebraCabeca" />
    <p:graphicImage value="/imagens/print.png" />
</h:outputLink>
Repare que o target se refere a um elemento quebraCabeca, ele corresponde a uma parte da página que eu quero que seja impresso, nesse caso este quebraCabeca será o próximo elemento a ser feito.

Para finalizar vamos ver o componente dashboard, que em um sistema comercial eu não vi muita utilidade, então para este exemplo eu resolvi criar um quebra cabeça meio desengonçado com ele!


Quanto a impressora que aparece acima na imagem, esta corresponde ao componente printer. Vamos começar então com o nosso quebra-cabeça, primeiramente precisamos pegar as imagens que eu passei lá no inicio e coloca-las no projeto. Primeiro dentro do diretório imagens crie um novo diretório com o nome de jogo: dentro dele coloque as imagens a.jpg, b.jpg até a imagem i.jpg... Abaixo segue a classe Java QuebraCabecaBean que deve ser criado dentro do pacote controle:
@Named
@RequestScoped
public class QuebraCabecaBean {

    private DashboardModel pecas;

    public QuebraCabecaBean() {
        listarPecas();
    }

    private void listarPecas() {
        pecas = new DefaultDashboardModel();
        DashboardColumn coluna1 = new DefaultDashboardColumn();
        DashboardColumn coluna2 = new DefaultDashboardColumn();
        DashboardColumn coluna3 = new DefaultDashboardColumn();

        coluna1.addWidget("f");
        coluna1.addWidget("c");
        coluna1.addWidget("g");

        coluna2.addWidget("h");
        coluna2.addWidget("a");
        coluna2.addWidget("d");

        coluna3.addWidget("i");
        coluna3.addWidget("e");
        coluna3.addWidget("b");

        pecas.addColumn(coluna1);
        pecas.addColumn(coluna2);
        pecas.addColumn(coluna3);
    }

    //Gerar GET e SET
}
Veja que eu incluo as letras embaralhadas dentro do DashboardColumn, realmente para iniciarem embaralhadas, só temos um detalhe: para cada letra que eu coloquei nessa lista eu preciso de um componente com o id do mesmo nome dentro do componente dashboard, apenas colocar os painéis no xhtml com as imagens não é suficiente eu preciso ter esse espelho entre o bean e a página:
<h:form id="quebraCabeca">
    <p:dashboard model="#{quebraCabecaBean.pecas}">
        <p:panel id="f">
            <p:graphicImage url="/imagens/jogo/f.jpg" />
        </p:panel>
        <p:panel id="c">
            <p:graphicImage url="/imagens/jogo/c.jpg" />
        </p:panel>
        <p:panel id="g">
            <p:graphicImage url="/imagens/jogo/g.jpg" />
        </p:panel>

        <p:panel id="h">
            <p:graphicImage url="/imagens/jogo/h.jpg" />
        </p:panel>
        <p:panel id="a">
            <p:graphicImage url="/imagens/jogo/a.jpg" />
        </p:panel>
        <p:panel id="d">
            <p:graphicImage url="/imagens/jogo/d.jpg" />
        </p:panel>

        <p:panel id="i">
            <p:graphicImage url="/imagens/jogo/i.jpg" />
        </p:panel>
        <p:panel id="e">
            <p:graphicImage url="/imagens/jogo/e.jpg" />
        </p:panel>
        <p:panel id="b">
            <p:graphicImage url="/imagens/jogo/b.jpg" />
        </p:panel>
    </p:dashboard>
</h:form>

A solução do quebra cabeça é este:


OBS: Quanto ao quebra cabeça este foi improvisado, já que a postagem original era para ser falado sobre os gráficos do PrimeFaces, mas que infelizmente não consegui fazer funcionar.
Mas pensem comigo, antes isso do que nada né!? :P

1 de junho de 2011

JSF 2.0: Componentes PrimeFaces 2.2.1 - Parte 4

Continuando com a série de postagens de componentes PrimeFaces 2.2.1, hoje vamos ver a utilização de layout e menus! Teremos menus para todas as necessidades ;)
Lembrando, que na postagem anterior vimos alguns componentes do tipo dialog, confirmDialog, entre outros: clique aqui para acessar.
Para esse projeto, vamos precisar de alguns ícones... estou disponibilizando-os aqui, mas se quiser use seus próprios ícones: baixar aqui.

Estrutura do projeto
Caso não saiba como montar a estrutura do projeto veja aqui.
Depois de pronto o projeto fica com essa estrutura:


Começando...
Nossa tela final ficará assim:


Nessa imagem temos o layout e os menus do PrimeFaces...

Nesse exemplo vamos usar o layout de tela inteira, e aproveitando este layout vamos encaixando os menus nele:

Código-fonte do componente:
<p:layout fullPage="true">
    <p:layoutUnit position="top" height="50">
    </p:layoutUnit>
    <p:layoutUnit position="left" width="180" header="Menu" collapsible="true" >
    </p:layoutUnit>
    <p:layoutUnit position="center" header="Sistema">
    </p:layoutUnit>
</p:layout>
Veja que quem cria as divisões é o componente layoutUnit, aconselho ver o link acima para ver as outras possibilidades de uso do layout.

Agora começamos com os menus, estes pouca coisa diferem entre si...

O primeiro que vamos usar é o breadCrumb, ele é um menu na horizontal:


Código-fonte do componente:
<p:breadCrumb>
    <p:menuitem value="Home" url="http://www.google.com.br" />
    <p:menuitem value="Documentos" url="#" />
    <p:menuitem value="Musicas" url="#" />
    <p:menuitem value="Imagens" url="#" />
    <p:menuitem value="Zip" url="#" />
</p:breadCrumb>
Veja que ele é composto por menuitem... na realidade todos os menus do PrimeFaces  são compostos de menuitem, por isso nos outros menus vamos os mesmos do exemplo acima. Nos menus quem faz a diferença é o componente-pai, no caso acima é a tag breadCrumb, o detalhe é que pra esse componente ele não aceita ícones no menuitem.
Obs: pode-se abrir dialogs(visto na parte 3) com os menuitem, para isso use a propriedade onclick deste...

O outro menu é na vertical, este tem também submenus, e com a diferença que ele aceita icones nos menuitem:


Obs: essa é uma das formas de usar ele, no link do menu tem outras formas bem interessantes de menu na posição vertical.
Código-fonte do componente:
<p:menu style="width: 98%">
    <p:submenu label="Contas...">
        <p:menuitem value="... a pagar" url="#" />
        <p:menuitem value="... a receber" url="#" />
    </p:submenu>
    <p:submenu label="Outros">
        <p:menuitem value="Home" url="http://www.google.com.br" />
        <p:menuitem value="Documentos" url="#" />
        <p:menuitem value="Musicas" url="#" />
        <p:menuitem value="Imagens" url="#" />
        <p:menuitem value="Zip" url="#" />
    </p:submenu>
    <p:submenu>
        <p:menuitem value="Sair" url="#" icon="ui-icon ui-icon-power" />
    </p:submenu>
</p:menu>
veja que eu defini o style na tag menu, fiz isto pois quero que este menu se adapte ao layout que ele se encontra e conforme redimensionar a janela, ele também se redimensionará (isso também pode ser feito em outros componentes que tenham a propriedade style)

O próximo menu é um menubar, na horizontal que também aceita ícones, mas este permite que tenha submenus:


Código-fonte do componente:
<p:menubar>
    <p:submenu label="Contas...">
        <p:menuitem value="... a pagar" url="#" />
        <p:menuitem value="... a receber" url="#" />
    </p:submenu>
    <p:submenu label="Outros">
        <p:menuitem value="Home" url="http://www.google.com.br" />
        <p:menuitem value="Documentos" url="#" />
        <p:menuitem value="Musicas" url="#" />
        <p:menuitem value="Imagens" url="#" />
        <p:menuitem value="Zip" url="#" />
    </p:submenu>
    <p:menuitem value="Sair" url="#" icon="ui-icon ui-icon-power" />
</p:menubar>

Agora pra quem não tem espaço na página, temos um botão menu que abre na posição vertical e também aceita ícones... que é o menuButton.
Fechado:

Clicado:


Código-fonte do componente:
<h:form>
    <p:menuButton value="Menu...">
        <p:menuitem value="Home" url="http://www.google.com.br" />
        <p:menuitem value="Documentos" url="#" />
        <p:menuitem value="Musicas" url="#" />
        <p:menuitem value="Imagens" url="#" />
        <p:menuitem value="Zip" url="#" />
        <p:menuitem value="Sair" url="#" icon="ui-icon ui-icon-power" />
    </p:menuButton>
</h:form>

Agora vamos para os menus bonitinhos e queridinhos com imagens!
Primeiramente vamos colocar aquelas imagens que eu passei lá no início, dentro do projeto... no meu caso eu estou colocando elas dentro do projeto de forma organizada, ou seja... dentro de Páginas Web, crie um novo diretório com o nome de imagens e dentro deste diretório coloque as imagens (.png).

Vamos começar pelo dock que funciona como o menu do Mac OS:


Obs: ele é um menu um pouco mais limitado, ou fica na parte superior da página ou na parte inferior da página (padrão)... mas dá um efeito muito legal :)
Código-fonte do componente:
<p:dock>
    <p:menuitem value="Home" url="http://www.google.com.br" icon="/imagens/home.png" />
    <p:menuitem value="Documentos" url="#" icon="/imagens/documentos.png" />
    <p:menuitem value="Musicas" url="#" icon="/imagens/musicas.png" />
    <p:menuitem value="Imagens" url="#" icon="/imagens/imagens.png" />
    <p:menuitem value="Zip" url="#" icon="/imagens/zip.png" />
</p:dock>

O outro é o stack, esse é outro menu com imagens, mas bem intessante... eu peguei a imagem padrão dele no site do PrimeFaces pra usar como base, mas você pode colocar a imagem que quiser.
Fechado:
Clicado:


Vamos as limitações... ele é um menu que aceita poucos menuitem, pois como pode ver na imagem acima, ele vai pendendo pro lado direito (deve ser o peso dos icones hahaha), e outro detalhe... ele só fica no canto inferior direito da página... pois é, não dá pra colocá-lo em qualquer lugar!
Código-fonte do componente:
<p:stack icon="/imagens/stack.png">
    <p:menuitem value="Home" url="http://www.google.com.br" icon="/imagens/home.png" />
    <p:menuitem value="Documentos" url="#" icon="/imagens/documentos.png" />
    <p:menuitem value="Musicas" url="#" icon="/imagens/musicas.png" />
    <p:menuitem value="Imagens" url="#" icon="/imagens/imagens.png" />
    <p:menuitem value="Zip" url="#" icon="/imagens/zip.png" />
</p:stack>

Bom... esses são os menus que eu selecionei para falar hoje do PrimeFaces, quando a esses dois últimos por serem mais limitados até coloquei-os fora do layout da página, para que funcionem melhor...

Enfim, o código fonte completo:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.prime.com.tr/ui">
    <h:head>
        <title>Primefaces - Parte 4</title>
    </h:head>
    <h:body>
        <p:layout fullPage="true">
            <p:layoutUnit position="top" height="50">
                <p:breadCrumb>
                    <p:menuitem value="Home" url="http://www.google.com.br" />
                    <p:menuitem value="Documentos" url="#" />
                    <p:menuitem value="Musicas" url="#" />
                    <p:menuitem value="Imagens" url="#" />
                    <p:menuitem value="Zip" url="#" />
                    <p:menuitem value="Sair" url="#" icon="ui-icon ui-icon-power" />
                </p:breadCrumb>
            </p:layoutUnit>
            <p:layoutUnit position="left" width="180" header="Menu" collapsible="true" >
                <p:menu style="width: 98%">
                    <p:submenu label="Contas...">
                        <p:menuitem value="... a pagar" url="#" />
                        <p:menuitem value="... a receber" url="#" />
                    </p:submenu>
                    <p:submenu label="Outros">
                        <p:menuitem value="Home" url="http://www.google.com.br" />
                        <p:menuitem value="Documentos" url="#" />
                        <p:menuitem value="Musicas" url="#" />
                        <p:menuitem value="Imagens" url="#" />
                        <p:menuitem value="Zip" url="#" />
                    </p:submenu>
                    <p:submenu>
                        <p:menuitem value="Sair" url="#" icon="ui-icon ui-icon-power" />
                    </p:submenu>
                </p:menu>
            </p:layoutUnit>
            <p:layoutUnit position="center" header="Sistema">
                <p:menubar>
                    <p:submenu label="Contas...">
                        <p:menuitem value="... a pagar" url="#" />
                        <p:menuitem value="... a receber" url="#" />
                    </p:submenu>
                    <p:submenu label="Outros">
                        <p:menuitem value="Home" url="http://www.google.com.br" />
                        <p:menuitem value="Documentos" url="#" />
                        <p:menuitem value="Musicas" url="#" />
                        <p:menuitem value="Imagens" url="#" />
                        <p:menuitem value="Zip" url="#" />
                    </p:submenu>
                    <p:menuitem value="Sair" url="#" icon="ui-icon ui-icon-power" />
                </p:menubar>
                <h:form>
                    <p:menuButton value="Menu...">
                        <p:menuitem value="Home" url="http://www.google.com.br" />
                        <p:menuitem value="Documentos" url="#" />
                        <p:menuitem value="Musicas" url="#" />
                        <p:menuitem value="Imagens" url="#" />
                        <p:menuitem value="Zip" url="#" />
                        <p:menuitem value="Sair" url="#" icon="ui-icon ui-icon-power" />
                    </p:menuButton>
                </h:form>
            </p:layoutUnit>
        </p:layout>

        <p:dock>
            <p:menuitem value="Home" url="http://www.google.com.br" icon="/imagens/home.png" />
            <p:menuitem value="Documentos" url="#" icon="/imagens/documentos.png" />
            <p:menuitem value="Musicas" url="#" icon="/imagens/musicas.png" />
            <p:menuitem value="Imagens" url="#" icon="/imagens/imagens.png" />
            <p:menuitem value="Zip" url="#" icon="/imagens/zip.png" />
        </p:dock>

        <p:stack icon="/imagens/stack.png">
            <p:menuitem value="Home" url="http://www.google.com.br" icon="/imagens/home.png" />
            <p:menuitem value="Documentos" url="#" icon="/imagens/documentos.png" />
            <p:menuitem value="Musicas" url="#" icon="/imagens/musicas.png" />
            <p:menuitem value="Imagens" url="#" icon="/imagens/imagens.png" />
            <p:menuitem value="Zip" url="#" icon="/imagens/zip.png" />
        </p:stack>

    </h:body>
</html>

Por hoje, ficamos por aqui! Ao final da série de componentes do PrimeFaces, crio a postagem de como usar o layout do PrimeFaces com os templates do Facelets.!

:)

25 de maio de 2011

JasperReports 4.0.1: JDBC datasource - abrir relatório em projeto web

Dando continuidade na postagem: JasperReports 4.0.1: Utilizando JDBC datasource, agora vamos usar o .jasper gerado no iReport, em um projeto Java EE 6.

Começando...
Para começar, no NetBeans crie uma nova Aplicação Web com o nome de RelatorioJDBC (seguindo os passos já vistos em postagens anteriores), o estrutura final é este aqui:


Feito isso, vamos começar a estruturar nosso projeto para usarmos .jasper (criado na postagem do link). Como estamos usando JDBC, precisamos ter uma conexão com o banco de dados, então em pacotes de código-fonte crie um novo pacote com o nome de dao, dentro desse pacote crie a classe Java com o nome de Conexao (já foi utilizada em outras postagem que não utilizava JPA), segue o código-fonte:
public class Conexao {
    private String driver = "com.mysql.jdbc.Driver";
    private String URL = "jdbc:mysql://localhost/5tads";
    private String USER = "root";
    private String SENHA = "admin";
    private Connection conn;

    public Conexao() {
        try {
            Class.forName(driver);
            conn = (Connection) DriverManager.getConnection(URL, USER, SENHA);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Connection getConn() {
        return conn;
    }

    public void fecharConexao() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
Obs: só lembrando que na URL, o 5tads corresponde à minha database.
Como nosso foco não é a conexão com o banco de dados, vamos prosseguir... agora no mesmo pacote dao, vamos criar mais uma classe Java com o nome de ClienteDAO:
public class ClienteDAO {

    private Conexao conexao;
    private Statement stmt;

    public ClienteDAO() {
        conexao = new Conexao();
        try {
            stmt = (Statement) conexao.getConn().createStatement();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }

    public ResultSet clientesResultSet() {
        ResultSet rs = null;
        try {
            rs = stmt.executeQuery("SELECT * FROM cliente ORDER BY nome");
        } catch (Exception e) {
            e.printStackTrace();
        } 

        return rs;
    }
}
Veja que o nosso método clientesResultSet, retorna um ResultSet mesmo e não uma lista de clientes, ao usarmos a metodologia de JDBC datasource, é isto que precisamos passar para o .jasper, e não uma lista como na JavaBean datasource.

Agora vamos colocar o .jasper dentro do nosso projeto, para isso em Páginas Web, dentro de WEB-INF crie um novo diretório com o nome de relatorios, e dentro dele coloque o arquivo .jasper, no meu caso tem o nome de relatorioJDBC.jasper (em caso de dúvidas volte na imagem acima da estrutura do projeto).
O próximo passo é colocar as bibliotecas necessárias no projeto, caso não tenha baixado as bibliotecas do JasperReports, baixe por aqui. De todas as bibliotecas disponibilizadas no link, para este tipo de relatório iremos usar apenas algumas delas, acabei filtrando e chegando as que realmente precisavam, então adicione ao projeto as seguintes bibliotecas:


* quanto a biblioteca do MySQL é do próprio NetBeans (mas pode ser encontrada facilmente no Google).

Agora que já temos nosso dao pronto, vamos criar um novo pacote para armazenar nossos beans esse pacote será o controle: depois de criado, crie uma nova classe Java com o nome de ClienteBean:
@Named
@RequestScoped
public class ClienteBean {

    private ClienteDAO dao;

    public void gerarRelatorio() {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        ServletContext context = (ServletContext) externalContext.getContext();
        String arquivo = context.getRealPath("WEB-INF/relatorios/relatorioJDBC.jasper");

        dao = new ClienteDAO();
        JRDataSource jrRS = new JRResultSetDataSource(dao.clientesResultSet());

        gerarRelatorioWeb(jrRS, null, arquivo);
    }

    private void gerarRelatorioWeb(JRDataSource jrRS, Map<Object, Object> parametros, String arquivo) {
        ServletOutputStream servletOutputStream = null;
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();

        try {
            servletOutputStream = response.getOutputStream();
            JasperRunManager.runReportToPdfStream(new FileInputStream(new File(arquivo)), response.getOutputStream(), parametros, jrRS);
            response.setContentType("application/pdf");
            servletOutputStream.flush();
            servletOutputStream.close();
            context.renderResponse();
            context.responseComplete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Vamos ver o que esses métodos fazem:

public void gerarRelatorio()
Inicialmente este método será chamado pela nossa página JSF, nele nós precisamos pegar o caminho exato do nosso arquivo .jasper, que lá no começo da postagem colocamos dentro de WEB-INF/relatorios, para isso usamos o context.getRealPath.
Quanto ao jrRS: por termos utilizado JDBC datasource para gerar nosso .jasper, nos precisamos passar para nosso relatório um JRDataSource, que é formado por um Result Set  que é retornado pelo nosso método lá do ClienteDAO.
Ao chamar o gerarRelatorioWeb, estamos passando um parâmetro null, esse corresponderia a uma Map, que serviria para passarmos parâmetros(que vai ficar pra outra postagem) para o nosso relatório...
Agora vamos ver o que acontece no:
gerarRelatorioWeb(JRDataSource jrRS, Map<Object, Object> parametros, String arquivo)
Neste método nós dependemos do response para mostrar o nosso relatório no navegador, quanto a essa parte não vou dar muitas explicações, pois sairia do nosso foco que é o JasperReports...
Bom, no final o responsável por gerar o que vai ser o nosso relatório, é essa linha aqui:
JasperRunManager.runReportToPdfStream... 
esse método não cria um arquivo .pdf fisicamente no computador, apenas gera ele como se fosse um arquivo temporário, e quem decide se quer salvar o relatório ou não é o usuário, depois disso, o response entra em campo novamente dizendo ao navegador que o que será enviado é do tipo application/pdf, cada navegador reage de uma forma quando se trata de PDF, no caso do Google Chrome, mostra nele mesmo, se não me engano o Firefox, já dá a opção de fazer download do PDF...
e por fim o servletOutputStream, é quem manda o relatório para o navegador!


Depois de entendermos como funciona essa classe, vamos alterar nossa página index.xhtml, para que ela chame o método e mostre o relatório:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:commandButton value="Ver relatorio" actionListener="#{clienteBean.gerarRelatorio}" onclick="this.form.target='_blank'" />
        </h:form>
    </h:body>
</html

Quanto ao onclick do commandButton, eu uso para poder abrir o relatório em outra aba do navegador. Visualmente a página index, fica assim:


Quem estiver usando o Google Chrome para fazer estes testes, terá o seguinte resultado ao clicar no botão:


E por aqui finalizamos a postagem de como usar um .jasper feito com base em JDBC, em um projeto web (Java EE 6)  =)

23 de maio de 2011

JSF 2.0: Componentes PrimeFaces 2.2.1 - Parte 3

Vamos dar continuidade nas postagens sobre os componentes do PrimeFaces na versão 2.2.1. Na postagem anterior tratamos mais a parte de painéis, hoje será tratado de componentes um pouco mais úteis do que aqueles.

Estrutura do projeto
Caso não saiba como montar a estrutura do projeto veja aqui.
Depois de pronto o projeto fica com essa estrutura:

Antes de começar vamos ver o código fonte do nosso bean TesteBean.java que irá auxiliar os componentes (aos poucos vamos entendendo como essa classe será utilizada):
@Named
@SessionScoped
public class TesteBean implements Serializable {

    private int numeroSlider;
    private double valorSpinner;
    private List<String> animais;

    public void confirmar() {
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Confirmação", "Você confirmou alguma coisa!");
        FacesContext.getCurrentInstance().addMessage(null, message);
    }

    public int getNumeroSlider() {
        return numeroSlider;
    }

    public void setNumeroSlider(int numeroSlider) {
        this.numeroSlider = numeroSlider;
    }

    public double getValorSpinner() {
        return valorSpinner;
    }

    public void setValorSpinner(double valorSpinner) {
        this.valorSpinner = valorSpinner;
    }

    public List<String> getAnimais() {
        animais = new ArrayList<String>();
        animais.add("Girafa");
        animais.add("Pato");
        animais.add("Leopardo");
        animais.add("Elefante");
        animais.add("Zebra");
        return animais;
    }

    public void setAnimais(List<String> animais) {
        this.animais = animais;
    }

}

Começando...
Nossa tela irá ficar assim:


Bom... na imagem não aparecem todas os componentes, alguns deles serão chamados pelos botões "Confirmar" e "Abrir janela".

Agora vamos começar de uma vez, com um componente que é bem util, é o p:confirmDialog ele é um janelinha que geralmente é utilizada em casos de confirmação de exclusão ou confirmar alguma outra coisa:

Ao clicar no botão "Sim", a janela se fecha e mostra um mensagem a partir do componente p:messages (visto na primeira postagem), assim:


Código-fonte do componente:
<p:messages id="mensagens" showDetail="true" />
<p:commandButton value="Confirmar" onclick="confirmacao.show()" type="button"/>
<h:form>
    <p:confirmDialog widgetVar="confirmacao" header="Confirmar" message="Deseja confirmar alguma coisa?" severity="alert" modal="true">
        <p:commandButton value="Sim" update="mensagens" oncomplete="confirmacao.hide()" actionListener="#{testeBean.confirmar}" />
        <p:commandButton value="Não" onclick="confirmacao.hide()" type="button" />
    </p:confirmDialog>
</h:form>
Vamos entender o que está acontecendo neste código, inicialmente eu tenho um p:messages que será atualizado quando eu clicar no commandButton correspondente ao "Sim", esse commandButton chama o método confirmar que retorna uma mensagem ao JSF pelo FacesContext. A propriedade modal do confirmDialog serve pra bloquear que possa ser feito outras ações com este componente em funcionamento, ou seja ele faz tipo um painel de vidro, não deixando a possibilidade de mexer nos componentes atrás dele.

O segundo componente é bem parecido com o anterior, porém mais maleável, é o p:dialog, ele é praticamente um painel só que não é fixo na tela, ele realmente funciona como uma janelinha independente que abre, fecha e que pode ser movida de lugar:

Código-fonte do componente:
<p:commandButton value="Abrir janela" onclick="janela.show()" type="button" />
<p:dialog widgetVar="janela" header="Novo..." modal="true" width="500" height="230">
 ...
</p:dialog>
No código fonte completo (final da postagem) eu coloquei alguns outros componentes dentro deste dialog, apenas para mostrar que os componentes podem ser usados juntos (mas todos eles podem funcionar muito bem separadamente).

O componente p:wizard é algo até complicado de explicar para o que serve, pois depende da criatividade de quem for usar, mas o que eu posso dizer é que funciona quase como a tabView (vista na segunda postagem) mas ao invés de clicar na tab para abri-la, existem dois botões, um de avançar e outro de voltar uma tab:


Código-fonte do componente:
<p:wizard nextLabel="Avançar" backLabel="Voltar">
    <p:tab title="Slider">
        <p:panel header="Testando o slider">
        </p:panel>
    </p:tab>
    <p:tab title="Spinner">
        <p:panel header="Testando o spinner">
        </p:panel>
    </p:tab>
</p:wizard>
Veja que o mesmo é composto da mesma tag p:tab, já utilizada por outros componentes (na segunda postagem), quanto ao p:panel, não é obrigado ter ele alí, eu só coloquei para dar uma entendida melhor que se pode fazer com esse componente.

p:slider é um componente para números utilizado juntamente com um inputText (talvez com outros elementos também, não testei isso), o interessante dele é que pode-se definir intervalos numéricos:


Código-fonte do componente:
<h:outputText value="Numero:" />
<p:inputText id="numero1" value="#{testeBean.numeroSlider}"/>
<p:slider for="numero1" minValue="-100" maxValue="100" style="width: 127px"/>

Bom, como nem tudo é perfeito... a versão 2.2.1 do PrimeFaces também não é perfeita, e sim.. ela tem bugs, por exemplo o componente p:spinner não renderiza corretamente (veja no link a renderização correta dele), mas mesmo assim vou mostrar ele, é bom saber que ele existe, pode ser que em outra versão ele funcione perfeitamente:


Código-fonte do componente:
<h:outputText value="Valor:" />
<p:spinner value="#{testeBean.valorSpinner}" min="-10" max="10" stepFactor="0.10" showOn="hover"/>
Assim como o slider, este componente permite definir um intervalo numérico e também a quantidade de quanto ele deve somar ou subtrair do numero atual, isso é configurado na propriedade stepFactor. Para não ficar devendo: no Blog tem uma postagem sobre ajax nativo do JSF 2.0, onde o primeiro componente funciona meio parecido com este. Caso encontre uma solução para este bug eu volto aqui e atualizo esta postagem (se alguém souber essa solução, será bem vinda).

Agora sim vamos ver um componente bem útil, é a p:dataTable que é uma tabela que pode ser paginada e tudo mais, no link tem muitas formas de utilização dela, aconselho dar uma olhada, aqui vou mostrar o básico do básico dela:


Código-fonte do componente:
<p:dataTable value="#{testeBean.animais}" var="a" paginator="true" rows="4">
    <p:column headerText="Nome dos animais">
        <h:outputText value="#{a}" />
    </p:column>
    <p:column style="width: 30px">
        <p:commandButton image="ui-icon-wrench" />
    </p:column>
</p:dataTable>
Vou levar em consideração que você que está lendo já tenha conhecimento da h:dataTable do JSF puro, assim posso pular os detalhes... neste caso eu estou usando ela com paginação, então eu preciso dizer quantas linhas eu quero que apareça em cada página, para isso eu uso a propriedade rows.
Obs: é importante saber que ao utilizar um h:column em uma p:dataTable, esta não renderizará corretamente, por isso eu uso o p:column.

E pra finalizar, um componente que funciona bem parecido com o componente acima, porém é mais maleável também que é o p:dataGrid:


Veja que ele usa paginação da mesma forma que a p:dataTable, abaixo do código explicarei alguns detalhes.
Código-fonte do componente:
<p:dataGrid value="#{testeBean.animais}" var="a" paginator="true" columns="2" rows="4">
    <p:column>
        <p:panel header="Animal">
            <h:outputText value="Este animal é um #{a}" />
        </p:panel>
    </p:column>
</p:dataGrid>
Veja que eu estou usando a mesma lista que usei na p:dataTable, a diferença é que, na p:dataGrid é utilizada apenas um p:column, essa unica coluna funciona como um laço de repetição. Vamos aos detalhes importantes: a propriedade columns serve para definir quantas colunas devem ser mostradas, já a propriedade rows à primeira impressão ela engana, pois é, quando eu vi ela, pensava que era como a rows da p:dataTable que seria a quantidade de linhas que teria minha dataGrid, mas não é... na realidade ela corresponde a quantidade de elementos que devem ser mostrados na página: como eu defini que teria apenas duas colunas, por exemplo eu não posso querer que mostre apenas três elementos na dataGrid (ficaria meio furada) para isso não acontecer, a dataGrid preenche todos os lugares, não deixando apenas três elementos e um espaço em branco, as vezes mesmo eu explicando fica difícil de entender, por isso aconselho fazer testes com ela.

Enfim, o código fonte completo:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.prime.com.tr/ui">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <p:messages id="mensagens" showDetail="true" />
        <p:commandButton value="Confirmar" onclick="confirmacao.show()" type="button"/>
        <h:form>
            <p:confirmDialog widgetVar="confirmacao" header="Confirmar" message="Deseja confirmar alguma coisa?" severity="alert" modal="true">
                <p:commandButton value="Sim" update="mensagens" oncomplete="confirmacao.hide()" actionListener="#{testeBean.confirmar}" />
                <p:commandButton value="Não" onclick="confirmacao.hide()" type="button" />
            </p:confirmDialog>
        </h:form>

        <p:separator />

        <p:commandButton value="Abrir janela" onclick="janela.show()" type="button" />
        <p:dialog widgetVar="janela" header="Novo..." modal="true" width="500" height="230">
            <h:form>
                <p:wizard nextLabel="Avançar" backLabel="Voltar">
                    <p:tab title="Slider">
                        <p:panel header="Testando o slider">
                            <h:outputText value="Numero:" /><br />
                            <p:inputText id="numero1" value="#{testeBean.numeroSlider}"/>
                            <p:slider for="numero1" minValue="-100" maxValue="100" style="width: 127px"/>
                        </p:panel>
                    </p:tab>
                    <p:tab title="Spinner">
                        <p:panel header="Testando o spinner">
                            <h:outputText value="Valor:" /><br />
                            <p:spinner value="#{testeBean.valorSpinner}" min="-10" max="10" stepFactor="0.10" showOn="hover"/>
                        </p:panel>
                    </p:tab>
                </p:wizard>
            </h:form>
        </p:dialog>

        <p:separator />

        <h:form>
            <p:dataTable value="#{testeBean.animais}" var="a" paginator="true" rows="4">
                <p:column headerText="Nome dos animais">
                    <h:outputText value="#{a}" />
                </p:column>
            </p:dataTable>
        </h:form>

        <p:separator />

        <h:form>
            <p:dataGrid value="#{testeBean.animais}" var="a" paginator="true" columns="2" rows="4">
                <p:column>
                    <p:panel header="Animal">
                        <h:outputText value="Este animal é um #{a}" />
                    </p:panel>
                </p:column>
            </p:dataGrid>
        </h:form>

    </h:body>
</html>

Acredito que por hoje chega... =)

15 de maio de 2011

JSF 2.0: Componentes PrimeFaces 2.2.1 - Parte 2

Bom lá vamos para a segunda postagem sobre componentes PrimeFaces 2.2.1, caso não tenha visto a primeira parte: veja aqui, nessa postagem eu vou tratar mais a parte de painéis.

Estrutura do projeto
Caso não saiba como montar a estrutura do projeto veja aqui.
Depois de pronto o projeto fica com essa estrutura:


Quando chegar a hora eu explico de onde pegar os .jpg que estão dentro de Páginas Web. Como na outra postagem iremos apenas usar a página index.

Começando...
Nossa tela irá ficar assim:


Assim como na postagem anterior, aparentemente não tem nada de mais, mas novamente engana-se quem pensa isso! hehe
O primeiro componente que eu irei falar é o p:toolbar, ele funciona como se fosse uma barrinha para se colocar botões, menus... ele realmente é mais pra design da página, mas o interessante é que pode-se definir o alinhamento dos elementos:

Código-fonte do componente:
<p:toolbar>
     <p:toolbarGroup align="left">
         <p:commandButton value="Buscar" image="ui-icon-search" />
         <p:divider />
         <p:commandButton value="Novo" image="ui-icon-folder-open"/>
         <p:commandButton value="Salvar" disabled="true" image="ui-icon-disk"/>
         <p:commandButton value="Excluir" image="ui-icon-trash"/>
     </p:toolbarGroup>
     <p:toolbarGroup align="right">
         <p:commandButton value="Sair" image="ui-icon-power"/>
     </p:toolbarGroup>
</p:toolbar>
Repare que o alinhamento dos botões é feito por uma tag chamada p:toolbarGroup, na propriedade align é definido o alinhamento.

Agora vem o nosso componente base: o p:accordionPanel, ele mostra apenas uma área de cada vez, por exemplo (ver imagem abaixo) se clicar na tab Painel ela abrirá e a tab Passar imagens! se fechará, dentro dessas tabs pode-se colocar o que bem entender, tanto é que nessa postagem todos os outros componentes estarão dentro das tabs desse accordion.

Código-fonte do componente:
<p:accordionPanel>
    <p:tab title="Passar imagens!">
    </p:tab>
    <p:tab title="Painel">
    </p:tab>
    <p:tab title="Conjunto de tabs...">
    </p:tab>
    <p:tab title="Show Imagem">
    </p:tab>
</p:accordionPanel>

ATENÇÃO: A partir de agora os componentes estarão dentro do accordion, mas isso não quer dizer que obrigatoriamente eles devam estar dentro de algum outro componente, só fiz isso para mostrar os componentes meio que interligados!

Dentro da primeira tab do accordion nós teremos um p:imageSwitch, ele é um componente para passar imagens, nesse exemplo ele fica passando as imagens automaticamente:

Para testar esse componente clique aqui para baixar as imagens (são quatro imagens), ou coloque as suas próprias imagens, depois de baixado e extraído, coloque essas imagens dentro de Páginas Web do projeto, ou onde desejar, mas tome cuidado na hora de se referenciar a elas por causa do caminho.

Código-fonte do componente:
<p:imageSwitch effect="shuffle" widgetVar="imagens" slideshowAuto="true">
    <p:graphicImage value="nature1.jpg" />
    <p:graphicImage value="nature2.jpg" />
    <p:graphicImage value="nature3.jpg" />
    <p:graphicImage value="nature4.jpg" />
</p:imageSwitch>

O componente de agora, vou mostrar ele funcionando juntamente com o componente acima, para isso eu preciso que eles estejam no mesmo h:form, estou falando do p:hotkey, ele serve para capturar um evento do teclado, e fazer alguma coisa a partir disso, nesse caso não tem imagem, para testar basta usar as setas do teclado: direita e esquerda, assim avança uma imagem ou volta outra, pra ter um efeito melhor sete a propriedade slideShowAuto do imageSwitch para false.
Código-fonte do componente:
<p:hotkey bind="left" oncomplete="imagens.previous();" />
<p:hotkey bind="right" oncomplete="imagens.next();" />

O próximo componente é o p:panel, que é simples como o nome: ele é apenas um painel onde pode-se colocar o que bem entender dentro dele, geralmente é utilizado em telas de cadastro como mostra o exemplo:

 
Código-fonte do componente:
<p:panel header="Teste Cadastro" toggleable="true" closable="true">
   ....
</panel>
Só postei o código do próprio panel, ao final tem o código completo da página.

O p:tabView, é apenas um conjunto de abas, funciona da mesma forma que um p:accordion (visto lá no começo), veja:

Código-fonte do componente:
<p:tabView>
    <p:tab title="Tab 1">
        <h:outputText value="Aqui é a tab 1" />
    </p:tab>
    <p:tab title="Tab 2">
        <h:outputText value="Aqui é a tab 2" />
    </p:tab>
    <p:tab title="Tab 3">
        <h:outputText value="E essa é a tab 3" />
    </p:tab>
</p:tabView>

E pra finalizar vamos ver um componente "invisível", é o p:outputPanel, ele serve para auxilar outros componentes, nesse exemplo abaixo, eu tenho uma imagem que deve se esconder quando eu clicar em "Esconder" e mostrar quando eu clicar em "Mostrar", porém, eu não consigo renderizar ela diretamente, se eu mandar atualizar ela, não vai funcionar, por isso eu coloco ela dentro de um outputPanel, pois eu mando atualizar ele e não a imagem:

Para esse exemplo eu vou precisar de um bean para auxiliar, pra isso, dentro de Pacotes de código-fonte, crie um novo pacote com o nome de controle e dentro dele crie uma classe java com o nome de TesteBean:
@Named
@SessionScoped
public class TesteBean implements Serializable {

    private boolean mostra = false;

    public void mostrar(){
        mostra = true;
    }

    public void esconder(){
        mostra = false;
    }

    public boolean isMostra() {
        return mostra;
    }

    public void setMostra(boolean mostra) {
        this.mostra = mostra;
    }   
}
Acredito que não precise explicar nada da classe acima, então vamos continuar:
Código-fonte do componente:
<h:form>
    <p:commandButton value="Mostra" actionListener="#{testeBean.mostrar}" update="painel"/>
    <p:commandButton value="Esconde" actionListener="#{testeBean.esconder}" update="painel" />
    <br/>
    <p:outputPanel id="painel">
        <p:graphicImage value="nature1.jpg" rendered="#{testeBean.mostra}" />
    </p:outputPanel>
</h:form>
Os detalhes que devem ser observados são os actionListener e update dos commandButton, e o rendered  do graphicImage. Só explicando melhor, o rendered diz se o componente deve ser renderizado ou não, é por isso que se eu mandar atualizar diretamente a imagem na hora que eu mando atualizar ela e o rendered estiver como false, ele não vai encontrar a tal da imagem pra atualizar, pois ela não foi renderizada, por isso eu preciso renderizar um componente acima dela.

Para finalizar, o código-fonte completo da página index.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.prime.com.tr/ui" >
    <h:head>
        <title>PrimeFaces 2.2.1</title>
    </h:head>
    <h:body>
        <h:form>
            <p:toolbar>
                <p:toolbarGroup align="left">
                    <p:commandButton value="Buscar" image="ui-icon-search" />
                    <p:divider />
                    <p:commandButton value="Novo" image="ui-icon-folder-open"/>
                    <p:commandButton value="Salvar" disabled="true" image="ui-icon-disk"/>
                    <p:commandButton value="Excluir" image="ui-icon-trash"/>
                </p:toolbarGroup>
                <p:toolbarGroup align="right">
                    <p:commandButton value="Sair" image="ui-icon-power"/>
                </p:toolbarGroup>
            </p:toolbar>
        </h:form>
        <br />

        <p:accordionPanel autoHeight="false">
            <p:tab title="Passar imagens!">
                <center>
                    <h:form>
                        <p:imageSwitch effect="shuffle" widgetVar="imagens" slideshowAuto="true">
                            <p:graphicImage value="nature1.jpg" />
                            <p:graphicImage value="nature2.jpg" />
                            <p:graphicImage value="nature3.jpg" />
                            <p:graphicImage value="nature4.jpg" />
                        </p:imageSwitch>
                        <p:hotkey bind="left" oncomplete="imagens.previous();" />
                        <p:hotkey bind="right" oncomplete="imagens.next();" />
                    </h:form>
                </center>
            </p:tab>
            <p:tab title="Painel">
                <p:panel header="Teste Cadastro" toggleable="true" closable="true">
                    <h:outputText value="Aqui pode-se colocar o que quiser, por exemplo:" />
                    <h:form>
                        <h:panelGrid columns="2">
                            <h:outputText value="Nome:"/>
                            <p:inputText />
                        </h:panelGrid>
                        <p:commandButton value="Salvar" />
                        <p:commandButton value="Cancelar" />
                    </h:form>
                </p:panel>
            </p:tab>
            <p:tab title="Conjunto de tabs...">
                <p:tabView>
                    <p:tab title="Tab 1">
                        <h:outputText value="Aqui é a tab 1" />
                    </p:tab>
                    <p:tab title="Tab 2">
                        <h:outputText value="Aqui é a tab 2" />
                    </p:tab>
                    <p:tab title="Tab 3">
                        <h:outputText value="E essa é a tab 3" />
                    </p:tab>
                </p:tabView>
            </p:tab>
            <p:tab title="Show Imagem">
                <h:form>
                    <p:commandButton value="Mostra" actionListener="#{testeBean.mostrar}" update="painel"/>
                    <p:commandButton value="Esconde" actionListener="#{testeBean.esconder}" update="painel" />
                    <br/>
                    <p:outputPanel id="painel">
                        <p:graphicImage value="nature1.jpg" rendered="#{testeBean.mostra}" />
                    </p:outputPanel>
                </h:form>
            </p:tab>
        </p:accordionPanel>
    </h:body>
</html>

Aqui ficamos com mais uma postagem! =)