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

13 de novembro de 2012

PrimeFaces 3.4.1: GMap

Olá!
Depois de algum tempo, aqui estou eu com mais uma postagem sobre PrimeFaces, desta vez estou usando a versão 3.4.1. Peço desculpas se por acaso eu perdi a didática de escrever aqui, é que faz muito tempo que não escrevo mais.
Mas vamos lá... Hoje a postagem é sobre o GMap, pra quem ainda não sabe o PrimeFaces já tem um componente com a biblioteca GMap, pra usá-la é bem simples.
Primeiramente vamos baixar a biblioteca do Prime, basta clicar aqui.
Para este projeto estarei usando JPA, para quem ainda não sabe como usar: é só ver os primeiros links da página Nível: Intermediário.

Meu projeto ficou com essa estrutura:

Começando... adicione a biblioteca do PrimeFaces às bibliotecas do projeto. E na página index:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true" />
        <title>GMap</title>
    </h:head>
    <h:body>
        <p:gmap center="-15.565986,-54.309782" zoom="15" type="HYBRID" style="width:600px;height:400px" />  
    </h:body>
</html>

Vamos aos pontos importantes do código acima:
Linha 3: lembre-se de adicionar a taglib do prime na página
Linha 5: Ponto importante para o funcionamento do p:gmap, que é a biblioteca do Google para o maps funcionar.
Linha 9: Este código é suficiente para aparecer o mapa, e você já poderá navegar pelo mundo! Mas vamos aos detalhes dessa linha: a propriedade center se refere à latitude e longitude, esta propriedade indica qual será o ponto onde o mapa deverá abrir inicialmente; a propriedade zoom se refere ao zoom do mapa, que pelos meus testes pode ir de 0 até 20 (talvez em alguns lugares possa ir a mais ou menos que isso); a propriedade type trata da forma de visualizar o mapa, que pode ser HYBRID (mapa e satélite), ROADMAP (apenas mapa), TERRAIN (terreno: uma opção que fica abaixo do mapa) ou SATELLITE (visão do satélite, mas vi que ele habilita a opção mapa também, mas se escolher ela, o mapa não aparece, uma solução para usar apenas o satélite seria usar a propriedade mapTypeControl="false").
Resultado:

Capturar Latitude e Longitude

Seguindo ainda a mesma base usado anteriormente, irei usar para este exemplo minha classe LocalBean (vide imagem da estrutura do meu projeto lá em cima), nela faça o seguinte:
@javax.faces.bean.ManagedBean
@javax.faces.bean.ViewScoped
public class LocalBean {
    
    public void pontoSelecionado(PointSelectEvent event) {  
        LatLng latituteLongitude = event.getLatLng();  
          
        FacesContext.getCurrentInstance().addMessage(
            null,
            new FacesMessage(
                FacesMessage.SEVERITY_INFO, 
                "Ponto selecionado", 
                "Lat:" + latituteLongitude.getLat() + ", Long:" + latituteLongitude.getLng()
            )
        );  
    }  
}
Detalhes do código
Linha 5 e 6: as classes PointSelectEvent e LatLng devem ser importadas da biblioteca do PrimeFaces.


E a index deve ser editada para:
<h:form>
    <p:growl id="mensagem"  showDetail="true" />

    <p:gmap center="-15.565986,-54.309782" zoom="15" type="HYBRID" style="width:600px;height:400px">
        <p:ajax event="pointSelect" listener="#{localBean.pontoSelecionado}" update="mensagem" />
    </p:gmap>
</h:form>
Adicionamos tudo dentro de um h:form por conta do p:growl, então na linha 2 adicionamos o p:growl.
Linha 5: é adicionado a tag p:ajax, onde temos a propriedade event, listener que chama o método da nossa classe LocalBean, e por fim atualizamos o growl.
Resultando em:


Adicionando e carregando marcadores de um banco de dados
Para este exemplo estaremos gravando os marcadores em um banco de dados, e como disse anteriormente, estarei usando JPA para agilizar este desenvolvimento, então, para começo de conversa vamos fazer nossa classe model.Local:
@Entity
@Table(name="local")
public class Local implements Serializable{
    
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name="codigo", nullable=false, unique=true)
    private Long codigo;
    @Column(name="descricao")
    private String descricao;
    @Column(name="latitude")
    private double latitude;
    @Column(name="longitude")
    private double longitude;

    /*
     * Getters e Setters
     */
}

Agora vamos fazer nossa classe dao.LocalDAO:
@Stateless
public class LocalDAO {
    
    @PersistenceContext(unitName="PrimeGmapPU")
    private EntityManager em;
    
    public void gravar(Local local){
        try {
            em.persist(local);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public List<Local> listar(){
        List<Local> locais = new ArrayList<Local>();
        try {
            Query query = em.createQuery("SELECT l FROM Local l");
            locais = query.getResultList();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return locais;
    }
    
}

Lembrando que na Linha 4, o unitName, é da minha unidade de persistência, o que pode mudar de acordo como você criou sua conexão.
Agora vamos para nossa classe controller.LocalBean, eu refiz ela, ou seja tirei do projeto aquela questão do growl, ela ficou dessa forma:

@javax.faces.bean.ManagedBean
@javax.faces.bean.ViewScoped
public class LocalBean implements Serializable{

    @EJB
    private LocalDAO localDAO;
    private Local local;
    private MapModel locais;

    public void novo(PointSelectEvent event) {
        local = new Local();
        LatLng coord = event.getLatLng();
        local.setLatitude(coord.getLat());
        local.setLongitude(coord.getLng());
    }

    public void gravar() {
        localDAO.gravar(local);
        carregarLocais();
    }

    private void carregarLocais() {
        locais = new DefaultMapModel();

        List<Local> locaisList = localDAO.listar();
        for (Local l1 : locaisList) {
            locais.addOverlay(
                    new Marker(
                    new LatLng(l1.getLatitude(), l1.getLongitude()),
                    l1.getDescricao()));
        }
    }

    /*
     * Getters e Setters
     */
    public MapModel getLocais() {
        if (locais == null) {
            carregarLocais();
        }
        return locais;
    }

}

Linha 8: a classe MapModel deve ser importada da biblioteca do Primefaces;
Linha 10: método novo, responsável por pegar as coordenadas geográficas e setar no meu objeto Local;
Linha 17: método gravar, responsável por chamar o DAO e gravar o método no banco de dados;
Linha 22: método carregarLocais, este método é responsável por converter uma lista de locais em um MapModel;
Linha 37: neste caso estou mostrando apenas esse get, por ter um diferencial que é carregar os locais caso o mesmo esteja null.

E por fim nossa página index.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true" />
        <title>GMap</title>
    </h:head>
    <h:body style="font-size: 12px">
        <p:gmap id="mapa" center="-15.565986,-54.309782" zoom="15" model="#{localBean.locais}" onPointClick="dlgLocal.show()" type="HYBRID" style="width:600px;height:400px">
            <p:ajax event="pointSelect" listener="#{localBean.novo}" update="formLocal" />
        </p:gmap>

        <p:dialog widgetVar="dlgLocal" width="280" height="100">  
            <h:form id="formLocal">  
                <p:focus for="desc" />
                <h:panelGrid columns="2" style="width: 100%">
                    <h:outputLabel for="desc" value="Descrição:" />  
                    <p:inputText id="desc" value="#{localBean.local.descricao}" /> 
                </h:panelGrid>
                <p:toolbar>
                    <p:toolbarGroup align="right">
                        <p:commandButton value="Gravar" actionListener="#{localBean.gravar()}" oncomplete="dlgLocal.hide()" update=":mapa" />
                    </p:toolbarGroup>
                </p:toolbar>
            </h:form>  
        </p:dialog>
    </h:body>
</html>

Agora vamos entender o que está acontecendo nesta página:
Linha 9:  temos o gmap, com a propriedade model, que está relacionada ao MapModel do nosso controller, e a propriedade onPointClick, que abre o dialog de cadastro de um novo marcador;
Linha 10: chama o método novo quando é clicado no mapa;
Linha 13: temos o dialog que faz o cadastro de um novo marcador.
Ficando dessa forma:


Este projeto foi apenas para dar uma base de como carregar e adicionar marcadores no gmaps com o PrimeFaces. Também não tratei o dialog, ou seja, precisaria de um "trato especial" no caso dele não fechar quando o campo descrição estiver vazio. Bom eu tenho o código que faz isso, mas para não deixar essa postagem mais extensa, não coloquei aqui, provavelmente eu deva criar uma postagem sobre como controlar o dialog, até mesmo como abrir ou fechar ele pelo controller.

Até mais! :)

22 de julho de 2012

PrimeFaces 3.3: Começando...

Vamos começar criando um projeto básico do básico. Para os exemplos futuros, estarei usando o NetBeans 7.1 e o GlassFish 3.1.1. Então crie uma Aplicação Web no netBeans, caso não saiba como fazer isso, veja aqui
Para baixar o PrimeFaces, clique aqui, basta selecionar a versão que quer utilizar, estarei usando a versão 3.3, irei aguardar um pouco para testar a 3.3.1.

A estrutura do projeto será a seguinte:

Depois de criado o projeto, adicione a biblioteca do PrimeFaces no projeto (para fazer isso, botão direito em Bibliotecas e Add jar/pasta, e encontre a biblioteca que foi baixada).

Aproveitando a página index.xhtml (criada automaticamente ao criar o projeto), vamos adicionar a namespace do PrimeFaces na tag html: 

xmlns:p="http://primefaces.org/ui"

A página deverá ficar dessa forma:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Start com PrimeFaces 3.3</title>
    </h:head>
    <h:body>
        <h:form>
            <p:editor />
        </h:form>
    </h:body>
</html>

Lembrando que para o PrimeFaces funcionar, é extremamente necessário que haja a tag head. para este exemplo, apenas utilizei o componente p:editor, o resultado é o seguinte:


Como eu falei, esse é só o começo de como usar o PrimeFaces...


Temas


Quanto aos temas, podem ser visualizados aqui: Temas PrimeFaces. e podem ser baixados pelo repositório do próprio Prime, aqui. Para baixar um tema escolha-o pelo nome na galeria de temas, e o procure no repositório, aconselho utilizar a ultima versão, a maioria dos temas se encontram (até o momento da postagem) na versão 1.0.6.
Depois e escolhido o tema, adicione às bibliotecas juntamente com a biblioteca do PrimeFaces, e no WEB-INF/web.xml, adicione as seguintes tags:
<context-param>  
    <param-name>primefaces.THEME</param-name>  
    <param-value>bluesky</param-value>  
</context-param> 

Para este exemplo, escolhi a biblioteca Bluesky, por isso dentro da tag param-value deve ser colocado o nome do tema, sem a versão.
Para quem deseja ter seu próprio tema, use o JQuery UI - ThemeRoller, para saber como utilizar seu próprio tema, veja o final desta postagem.

Até mais!