Java Faces

03/01/2011

Spring + Security + Hibernate

Filed under: JSF — guilhermefinotti @ 21:09

Esse post demonstra a integração do Spring 3, Spring Security 3 e Hibernate 3 numa aplicação web.
O exemplo utiliza JSF 2.0 e Primefaces.

A estrutura de pacotes é a seguinte:
* config -> a classe de conexão (datasource)
* model -> as entidades Usuario e Perfil
* services -> a classe service customizada para autenticar o usuário usando o hibernate
* web -> o managed bean acessado pela tela de login (LoginMB), um PhaseListener utilizado para capturar exceções de autenticação e uma classe utilitária para JSF.

web.xml
Aqui configuramos o filtro “org.springframework.web.filter.DelegatingFilterProxy” e definimos os arquivos lidos na inicialização (“spring-config.xml” e “spring-security.xml”).
As demais configurações são relativas ao JSF e Primefaces.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>spring + security + hibernate</display-name>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
    				/WEB-INF/spring-config.xml
    				/WEB-INF/spring-security.xml
    	</param-value>
	</context-param>
	<context-param>
		<param-name>primefaces.SKIN</param-name>
		<param-value>none</param-value>
	</context-param>
	<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>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>REQUEST</dispatcher>
	</filter-mapping>
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>
	<servlet>
		<servlet-name>Resource Servlet</servlet-name>
		<servlet-class>org.primefaces.resource.ResourceServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>Resource Servlet</servlet-name>
		<url-pattern>/primefaces_resource/*</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

faces-config.xml
Aqui configuramos o “EL Resolver” e registramos um PhaseListener responsável pelos erros de autenticação do usuário.

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

spring-config.xml
Aqui configuramos o suporte à anotações e as propriedades da sessionFactory do Hibernate.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:sec="http://www.springframework.org/schema/security"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 

	<!-- habilita a configuração por annotations -->
	<context:annotation-config />

	<!-- define os pacotes/subpacotes onde serão procurados beans do spring -->
	<context:component-scan base-package="br.com.exemploseguranca" />

	<!-- Propriedades do hibernate -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="annotatedClasses">
			<list>
				<value>br.com.exemploseguranca.model.Usuario</value>
				<value>br.com.exemploseguranca.model.Perfil</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
			</props>
		</property>
	</bean>

</beans>

spring-security.xml
Nesse arquivo definimos as regras de segurança da aplicação.
O diretório “/paginas/” é protegido e somente usuários autenticados conseguem acessá-lo.
Depois de autenticado, temos acesso às informações de autorização do usuário.
Nesse exemplo temos 2 perfis(roles) de usuário:
* Administrador (ROLE_ADM) -> possui acesso à todos os diretórios da aplicação.
* Usuário (ROLE_USER) -> possui acesso limitado ao diretório “/paginas/usuario/**”.
O acesso ao diretório “/paginas/usuario/**” é permitido aos usuários dos 2 perfis.

O fluxo é o seguinte:
O usuário acessa a aplicação e é direcionado para a página “/login.jsf”.
Após realizar o login ele é direcionado para “/paginas/inicio.jsf”, onde encontra links para as páginas internas (“/paginas/admin/admin.jsf” e “/paginas/usuario/usuario.jsf”).
A página “inicio.jsf” é liberada para qualquer usuário autenticado.

Caso ocorra algum erro na autenticação(dados incorretos), é apresentada uma mensagem de erro na própria página “/login.jsf”.
Caso um usuário não autorizado(ROLE_USER) tente acessar o diretório “/paginas/admin/**”, será direcionado para a página “/acessonegado.jsf”

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:sec="http://www.springframework.org/schema/security"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">				
	
	<sec:http auto-config="true" access-denied-page="/acessonegado.jsf">
		
		<sec:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
		<sec:intercept-url pattern="/paginas/inicio*" access="ROLE_USER, ROLE_ADM" />
		<sec:intercept-url pattern="/paginas/usuario/**" access="ROLE_USER, ROLE_ADM" />
		<sec:intercept-url pattern="/paginas/admin/**" access="ROLE_ADM" />		
		
		<sec:form-login login-page="/login.jsf"
					    login-processing-url="/j_spring_security_check" 
						default-target-url="/paginas/inicio.jsf" 
					    authentication-failure-url="/login.jsf" />
					    
		<sec:logout logout-success-url="/login.jsf" />
		
	</sec:http>

	<sec:authentication-manager>
		<sec:authentication-provider user-service-ref="hibernateUserDetailsService" ref="daoAuthenticationProvider" />
	</sec:authentication-manager>

	<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
		<property name="userDetailsService" ref="hibernateUserDetailsService" />
	</bean>

	<bean id="loggerListener" class="org.springframework.security.access.event.LoggerListener" />						
 
</beans>

HibernateUserDetailsService.java
A autenticação é feita pela interface UserDetailsService.
Para utilizar o hibernate, criamos uma implementação de UserDetailsService e sobrescrevemos o método loadUserByUsername();

package br.com.exemploseguranca.services;

import java.util.List;


import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import br.com.exemploseguranca.model.Usuario;

@Service("hibernateUserDetailsService")
public class HibernateUserDetailsService extends HibernateDaoSupport implements UserDetailsService  {
	
	@Autowired
	public HibernateUserDetailsService(SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
	}

	@SuppressWarnings("unchecked")
	@Override
	public UserDetails loadUserByUsername(String login) {
		DetachedCriteria criteria = DetachedCriteria.forClass(Usuario.class, "usuario");
		criteria.add(Restrictions.eq("usuario.login", login));
		List resultado = getHibernateTemplate().findByCriteria(criteria);
		if(resultado != null && resultado.size() == 0) {
			throw new UsernameNotFoundException("Usuário não encontrado!");
	    }	
		return (Usuario)resultado.get(0);
	}

}

LoginMB.java
Managed Bean que recebe a requisição da página “login.jsf”

package br.com.exemploseguranca.web.controle;

import javax.faces.context.FacesContext;
import javax.servlet.RequestDispatcher;


import org.springframework.stereotype.Controller;

import br.com.exemploseguranca.web.util.FacesUtil;

@Controller("loginMB")
public class LoginMB {
	
	public LoginMB() {	
	}
	
	public String logar() {
		try {
		    RequestDispatcher dispatcher = FacesUtil.getServletRequest().getRequestDispatcher("/j_spring_security_check");
		    dispatcher.forward(FacesUtil.getServletRequest(), FacesUtil.getServletResponse());
		    FacesContext.getCurrentInstance().responseComplete();
		} catch (Exception ex) {
			FacesUtil.exibirMensagemErro(ex.getMessage());
			return null;
		}
	    return null;
	}
}

LoginErrorPhaseListener.java
PhaseListener utilizado para capturar exceções de autenticação. Utiliza a classe utilitária FacesUtil para enviar mensagens de erro.

package br.com.exemploseguranca.web.seguranca;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;


import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.web.WebAttributes;

import br.com.exemploseguranca.web.util.FacesUtil;

@SuppressWarnings({"serial"})
public class LoginErrorPhaseListener implements PhaseListener {

	@Override
	public void afterPhase(PhaseEvent arg0) {
	}

	@SuppressWarnings({ "unchecked" })
	@Override
	public void beforePhase(PhaseEvent arg0) {
		Exception dadosIncorretosException = (Exception) FacesUtil.getSessionMap().get(WebAttributes.AUTHENTICATION_EXCEPTION);
		if(dadosIncorretosException instanceof BadCredentialsException) {
			FacesUtil.getSessionMap().put(WebAttributes.AUTHENTICATION_EXCEPTION, null);
			FacesUtil.exibirMensagemErro("Dados incorretos!");
		}
	}

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

FacesUtil.java
Classe utilitária para desenvolvimento JSF.

package br.com.exemploseguranca.web.util;

import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * Classe utilitária para desenvolvimento JSF
 */
public class FacesUtil {
	
   
	public static String getRequestParameter(String name) {
		return (String)FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(name);
	}

	public static void exibirMensagemSucesso(String mensagem) {
		exibirMensagem(FacesMessage.SEVERITY_INFO, mensagem);
	}

	public static void exibirMensagemAlerta(String mensagem) {
		exibirMensagem(FacesMessage.SEVERITY_WARN, mensagem);
	}
	
	public static void exibirMensagemErro(String mensagem) {
		exibirMensagem(FacesMessage.SEVERITY_ERROR, mensagem);
	}
	
	private static void exibirMensagem(FacesMessage.Severity severity, String mensagem) {
		FacesMessage facesMessage = new FacesMessage(severity, "", mensagem);
		FacesContext.getCurrentInstance().addMessage(null, facesMessage);
	}

	public static ExternalContext getExternalContext() {
		return FacesContext.getCurrentInstance().getExternalContext();
	}
	
	public static Map getSessionMap() {
		return FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
	}
	
	public static ServletContext getServletContext() {
		return (ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext();
	}
	
	public static HttpServletRequest getServletRequest() {
		return (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
	}
	
	public static HttpServletResponse getServletResponse() {
		return (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
	}
		

}

Usuario.java
A entidade Usuario implementa a interface UserDetails e deve ter o método getAuthorities().
Foi criada uma tabela de associação (usuario_perfil) para armazenar o perfil de acesso(autorização) dos usuários.

package net.finottisistemas.modelo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;

@Entity
@Table(name = "usuario")
public class Usuario implements java.io.Serializable, UserDetails {
	
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id_usuario")
	private Long id;
	
	@Column(name = "login")
	private String login;
	
	@Column(name = "senha")
	private String senha;

	private boolean ativo = true;

	@ManyToMany(fetch = FetchType.EAGER)
	@JoinTable(name = "usuario_perfil", joinColumns = @JoinColumn(name = "id_usuario"), inverseJoinColumns = @JoinColumn(name = "id_perfil"))
	private List<Perfil> perfis = new ArrayList<Perfil>();

	@Transient
	public Collection<GrantedAuthority> getAuthorities() {
		List<GrantedAuthority> lista = new ArrayList<GrantedAuthority>();
		for(Perfil perfil : getPerfis()) {
			lista.add(new GrantedAuthorityImpl(perfil.getAuthority()));
		}
		return lista;
	}

	@Transient
	public String getPassword() {
		return this.senha;
	}

	@Transient
	public String getUsername() {
		return this.login;
	}

	@Transient
	public boolean isAccountNonExpired() {
		return true;
	}

	@Transient
	public boolean isAccountNonLocked() {
		return true;
	}

	@Transient
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Transient
	public boolean isEnabled() {
		return this.ativo;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public boolean isAtivo() {
		return ativo;
	}

	public void setAtivo(boolean ativo) {
		this.ativo = ativo;
	}

	public List<Perfil> getPerfis() {
		return perfis;
	}

	public void setPerfis(List<Perfil> perfis) {
		this.perfis = perfis;
	}

}

Perfil.java
A entidade Perfil implementa a interface GrantedAuthority.

package net.finottisistemas.modelo;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.springframework.security.core.GrantedAuthority;

@Entity
@Table(name = "perfil")
public class Perfil implements java.io.Serializable, GrantedAuthority {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id_perfil")
	private Long id;
	
	@Column(name = "descricao")
	private String descricao;
	
	@ManyToMany(fetch=FetchType.EAGER)
	@JoinTable(name = "usuario_perfil", joinColumns = @JoinColumn(name = "id_perfil"), inverseJoinColumns = @JoinColumn(name = "id_usuario"))
	private List<Usuario> usuarios = new ArrayList<Usuario>();


	@Transient
	public String getAuthority() {
		return this.descricao;
	}

	@Transient
	public int compareTo(Object o) {
		return this.compareTo(o);
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	public List<Usuario> getUsuarios() {
		return usuarios;
	}

	public void setUsuarios(List<Usuario> usuarios) {
		this.usuarios = usuarios;
	}
}

Conexao.java
Propriedades da conexão com o banco de dados (datasource)

package br.com.exemploseguranca.config;

import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;


@Component("dataSource")
public class Conexao extends DriverManagerDataSource {
	
	public Conexao(){
		this.setDriverClassName("com.mysql.jdbc.Driver");
		this.setUrl("jdbc:mysql://localhost/seguranca");
		this.setUsername("root");
		this.setPassword("root");
	}
	
}

login.xhtml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:p="http://primefaces.prime.com.tr/ui"
	  xmlns:h="http://java.sun.com/jsf/html"
	  xmlns:f="http://java.sun.com/jsf/core"
	  xmlns:ui="http://java.sun.com/jsf/facelets">
	
   <h:head>
    
       <title>
       		Spring + Security + Hibernate
       </title>
       
       <link type="text/css" rel="stylesheet" href="#{facesContext.externalContext.requestContextPath}/css/redmond/skin.css" />
        
   </h:head> 
   
   <h:body>  

	   <h:form prependId="false">
	   
	   		<p:dialog header="Área restrita"
	   				  modal="true"
	   				  closable="false"
	   				  position="center"
	   				  widgetVar="modalLogin"
	   				  minWidth="300"
	   				  width="300"
	   				  showEffect="slide" 
	   		    	  draggable="false"
	   		    	  resizable="false"
	   		    	  visible="true">		 
	   				 
	   			<center>	 
	   				 
				   	<p:messages id="mensagens" showDetail="true" showSummary="false" />    				 
		   				 
		   			<h:panelGrid columns="2">   		
		     	
				      	<h:outputLabel value="Login" />
		     			<h:inputText id="j_username" size="15" />
		
				 		<h:outputLabel value="Senha" />
						<h:inputSecret id="j_password" size="15" />
										
					</h:panelGrid>
					
					<br />	
		
					<h:commandButton value="Entrar" action="#{loginMB.logar}" />		
					
				</center>
					
			</p:dialog>
				
	 	</h:form>
 	
 	</h:body>
 
</html>

/paginas/inicio.xhtml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html"
	  xmlns:f="http://java.sun.com/jsf/core"
	  xmlns:ui="http://java.sun.com/jsf/facelets"
	  xmlns:p="http://primefaces.prime.com.tr/ui">
	
   <h:head>
    
       <title>
       		Spring + Security + Hibernate
       </title>
       
       <link href="#{facesContext.externalContext.requestContextPath}/css/redmond/skin.css" rel="stylesheet" type="text/css" />
        
   </h:head> 
   
   <h:body>
   
   <h:form prependId="false">     
  		
   		<p:dialog header="Spring + Security + Hibernate"
   				  modal="true"
   				  closable="false"
   				  position="center"
   				  widgetVar="modalInicio"
   				  width="500"
   				  height="400"
   		    	  draggable="false"
   		    	  resizable="false"
   		    	  visible="true">
   		    
   		    <center>	  
   		    	<img src="#{facesContext.externalContext.requestContextPath}/img/spring_security.jpg" />
   		    </center>
   		    
   		    <br />
   		    <br />
   		    <br />	    		
  		  		
   			<a href="#{facesContext.externalContext.requestContextPath}/paginas/admin/admin.jsf">
   				Página do administrador
   			</a>
   			<br />				 
   			<a href="#{facesContext.externalContext.requestContextPath}/paginas/usuario/usuario.jsf">
   				Página do usuário
   			</a>
   		    <br />
   		    <br />  		    
	 		<a href="#{facesContext.externalContext.requestContextPath}/j_spring_security_logout"> 
   				Sair
   			</a>
   			
   		</p:dialog>	
   		
   	</h:form>					 
 	
 	</h:body>
 
</html>

/paginas/admin/admin.xhtml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html"
	  xmlns:f="http://java.sun.com/jsf/core"
	  xmlns:ui="http://java.sun.com/jsf/facelets"
	  xmlns:p="http://primefaces.prime.com.tr/ui">
	
   <h:head>
    
       <title>
       		Spring + Security + Hibernate
       </title>
       
       <link href="#{facesContext.externalContext.requestContextPath}/css/redmond/skin.css" rel="stylesheet" type="text/css" />
        
   </h:head> 
   
   <h:body>     
  		
   		<p:dialog header="Página do administrador"
   				  modal="true"
   				  closable="true"
   				  position="center"
   				  widgetVar="modalAdmin"
   				  minWidth="400"
   				  width="400"
   		    	  draggable="true"
   		    	  resizable="true"
   		    	  visible="true">
   		    	  
   		    <center>
   		    	  
   		    	<img src="#{facesContext.externalContext.requestContextPath}/img/spring_security.jpg" />
	   		    
	   		    <br />
	   		    <br />
	   		    <br />	   		    	  	 
	    		# Página do Administrador #
	   		    <br />	   		    	  	 
	   		    <br />	   		    	  	 
	   		    <br />
	   		    
	   		    <a href="#{facesContext.externalContext.requestContextPath}/paginas/inicio.jsf">
	   		     Voltar 
	   		    </a>
	   		    <br />
	   		    <br />				 
   				<a href="#{facesContext.externalContext.requestContextPath}/j_spring_security_logout">
   					Sair
   				</a>	   		    	  	 
    		 				 
   		    </center>
    				   				
 		</p:dialog>	
 
	</h:body>

</html>

/paginas/usuario/usuario.xhtml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html"
	  xmlns:f="http://java.sun.com/jsf/core"
	  xmlns:ui="http://java.sun.com/jsf/facelets"
	  xmlns:p="http://primefaces.prime.com.tr/ui">
	
   <h:head>
    
       <title>
       		Spring + Security + Hibernate
       </title>
       
       <link href="#{facesContext.externalContext.requestContextPath}/css/redmond/skin.css" rel="stylesheet" type="text/css" />
        
   </h:head> 
   
   <h:body>     
  		
   		<p:dialog header="Página do Usuário"
   				  modal="true"
   				  closable="false"
   				  position="center"
   				  widgetVar="modalUsuario"
   				  minWidth="400"
   				  width="400"
   		    	  draggable="false"
   		    	  resizable="false"
   		    	  visible="true">
   		    	  
   		    <center>
   		    	  
   		    	<img src="#{facesContext.externalContext.requestContextPath}/img/spring_security.jpg" />
	   		    
	   		    <br />
	   		    <br />
	   		    <br />	   		    	  	 
	    		# Página do usuário #
	   		    <br />	   		    	  	 
	   		    <br />	   		    	  	 
	   		    <br />
	   		    
	   		    <a href="#{facesContext.externalContext.requestContextPath}/paginas/inicio.jsf">
	   		     Voltar 
	   		    </a>
	   		    <br />
	   		    <br />				 
   				<a href="#{facesContext.externalContext.requestContextPath}/j_spring_security_logout">
   					Sair
   				</a>	   		    	  	 
    		 				 
   		    </center>				 
    				   				
 		</p:dialog>	
 
	</h:body>

</html>

/acessonegado.xhtml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html"
	  xmlns:f="http://java.sun.com/jsf/core"
	  xmlns:ui="http://java.sun.com/jsf/facelets"
	  xmlns:p="http://primefaces.prime.com.tr/ui">
	
   <h:head>
    
       <title>
       		<h:outputText value="Spring + Security + Hibernate" />
       </title>
       
       <link href="#{facesContext.externalContext.requestContextPath}/css/redmond/skin.css" rel="stylesheet" type="text/css" />
        
   </h:head> 
   
   <h:body style="font-size: 10pt">     
  		
   		<p:dialog header="Ops! Acesso Negado!"
   				  modal="true"
   				  closable="true"
   				  position="center"
   				  widgetVar="modalNegado"
   				  minWidth="500"
   				  width="400"
   				  showEffect="shake" 
   		    	  draggable="true"
   		    	  resizable="true"
   		    	  visible="true">
   		    	  
   		    <center>
   		    	  
   		    	<img src="#{facesContext.externalContext.requestContextPath}/img/acesso_negado.gif" />
	   		    
	   		    <br />
	   		    <br />
	   		    <br />	   		    	  	 
	    		# Acesso Negado #
	   		    <br />	   		    	  	 
	   		    <br />	   		    	  	 
	   		    <br />
	   		    
	   		    <a href="#{facesContext.externalContext.requestContextPath}/paginas/inicio.jsf">
	   		     Voltar 
	   		    </a>	   		    	  	 
    		 				 
   		    </center>
    				   				
 		</p:dialog>	
 
	</h:body>

</html>

seguranca.sql
Script utilizado para o banco de dados (Mysql)

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for perfil
-- ----------------------------
DROP TABLE IF EXISTS `perfil`;
CREATE TABLE `perfil` (
  `id_perfil` bigint(20) NOT NULL AUTO_INCREMENT,
  `descricao` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id_perfil`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

-- ----------------------------
-- Table structure for usuario
-- ----------------------------
DROP TABLE IF EXISTS `usuario`;
CREATE TABLE `usuario` (
  `id_usuario` bigint(20) NOT NULL AUTO_INCREMENT,
  `ativo` bit(1) NOT NULL,
  `login` varchar(255) DEFAULT NULL,
  `senha` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id_usuario`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

-- ----------------------------
-- Table structure for usuario_perfil
-- ----------------------------
DROP TABLE IF EXISTS `usuario_perfil`;
CREATE TABLE `usuario_perfil` (
  `id_perfil` bigint(20) NOT NULL,
  `id_usuario` bigint(20) NOT NULL,
  KEY `FK57CD23FD9DB9E69B` (`id_perfil`),
  KEY `FK57CD23FD5A9933B9` (`id_usuario`),
  KEY `FK57CD23FD67F47FA6` (`id_perfil`),
  KEY `FK57CD23FDD7B1BC0E` (`id_usuario`),
  KEY `FK57CD23FD28906DBF` (`id_perfil`),
  KEY `FK57CD23FD2A939115` (`id_usuario`),
  CONSTRAINT `FK57CD23FD2A939115` FOREIGN KEY (`id_usuario`) REFERENCES `usuario` (`id_usuario`),
  CONSTRAINT `FK57CD23FD28906DBF` FOREIGN KEY (`id_perfil`) REFERENCES `perfil` (`id_perfil`),
  CONSTRAINT `FK57CD23FD5A9933B9` FOREIGN KEY (`id_usuario`) REFERENCES `usuario` (`id_usuario`),
  CONSTRAINT `FK57CD23FD67F47FA6` FOREIGN KEY (`id_perfil`) REFERENCES `perfil` (`id_perfil`),
  CONSTRAINT `FK57CD23FD9DB9E69B` FOREIGN KEY (`id_perfil`) REFERENCES `perfil` (`id_perfil`),
  CONSTRAINT `FK57CD23FDD7B1BC0E` FOREIGN KEY (`id_usuario`) REFERENCES `usuario` (`id_usuario`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- ----------------------------
-- Records 
-- ----------------------------
INSERT INTO `perfil` VALUES ('1', 'ROLE_ADM');
INSERT INTO `perfil` VALUES ('2', 'ROLE_USER');
INSERT INTO `usuario` VALUES ('1', true, 'admin', '12345');
INSERT INTO `usuario` VALUES ('2', true, 'user', '12345');
INSERT INTO `usuario_perfil` VALUES ('1', '1');
INSERT INTO `usuario_perfil` VALUES ('2', '2');

O código fonte do projeto está disponível no github e no 4shared.

20/12/2010

Utilizando Spring annotations

Filed under: JSF — guilhermefinotti @ 9:38

Conforme sugerido pelo pessoal da lista javasf, reescrevi o exemplo anterior utilizando Spring annotations.
Nessa nova versão, o managed bean(UsuarioController) será gerenciado como um bean do spring.
Será necessário criar o arquivo faces-config.xml indicando que o spring é o responsável pelo “EL Resolver”.

faces-config.xml

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

</faces-config>

O arquivo web.xml também foi alterado.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">

	<display-name>exemplo jsf 2</display-name>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring.xml</param-value>
	</context-param>
	
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

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

	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>

	<servlet>
		<servlet-name>Resource Servlet</servlet-name>
		<servlet-class>org.primefaces.resource.ResourceServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>Resource Servlet</servlet-name>
		<url-pattern>/primefaces_resource/*</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	
</web-app>

ViewScope

No exemplo anterior, utilizamos o escopo view do JSF 2.0 para o managed bean UsuarioController. Porém, o spring não disponibiliza esse escopo.
Para adicioná-lo, criamos a classe ViewScope e a registramos no arquivo spring.xml.

package br.com.exemplojsf2.web.util;

import java.util.Map;
import javax.faces.context.FacesContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.stereotype.Component;

/**
 * Disponibiliza o view scope para managed beans gerenciados pelo spring
 *
 */
public class ViewScope implements Scope {

	@SuppressWarnings("unchecked")
	public Object get(String name, ObjectFactory objectFactory) {
		Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();

		if(viewMap.containsKey(name)) {
			return viewMap.get(name);
		} else {
			Object object = objectFactory.getObject();
			viewMap.put(name, object);

			return object;
		}
	}

	public Object remove(String name) {
		return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
	}

	public String getConversationId() {
		return null;
	}

	public void registerDestructionCallback(String name, Runnable callback) {
		//Not supported
	}

	public Object resolveContextualObject(String key) {
		return null;
	}
}

Obs: As informações referentes ao ViewScope foram obtidas nesse post

spring.xml

Em relação ao exemplo anterior, foram adicionadas a classe ViewScope e as seguintes tags:
* context:annotation-config => habilita a configuração por annotations
* context:component-scan base-package => indica os pacotes/sub que serão lidos em busca de spring beans.
E foram retiradas as declarações do hibernateTemplate e da classe UsuarioDAO.

Obs: O arquivo spring.xml foi movido para o diretório WEB-INF.

<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
						http://www.springframework.org/schema/context 
						http://www.springframework.org/schema/context/spring-context-3.0.xsd 
						http://www.springframework.org/schema/tx 
						http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
						http://www.springframework.org/schema/aop 
						http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

	<!-- habilita a configuração por annotations -->
	<context:annotation-config />
	
	<!-- define os pacotes/subpacotes que onde serão procurados beans do spring -->
	<context:component-scan base-package="br.com.exemplojsf2" />
	
        <!-- Parâmetros de conexão com o banco de dados -->
	<bean id="dataSource" class="br.com.exemplojsf2.config.Conexao" />
	
	<!-- Propriedades do hibernate -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    	<property name="dataSource" ref="dataSource" />
    	<property name="annotatedClasses">
    		<list>
    			<value>br.com.exemplojsf2.modelo.Usuario</value>
    		</list>
    	</property>    	
    	<property name="hibernateProperties">
    		<props>
    			<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
    			<prop key="hibernate.show_sql">true</prop>  
    			<prop key="hibernate.hbm2ddl.auto">update</prop> 			
    		</props>
    	</property>
    </bean>
    
    <!-- View scope -->
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="view">
                    <bean class="br.com.exemplojsf2.web.util.ViewScope"/>
                </entry>
            </map>
        </property>
    </bean>

</beans>

UsuarioController

A classe UsuarioController agora é um bean do spring.
A anotação @ManagedBean do JSF 2.0 dá lugar a anotação @Controller do spring.
Outra alteração importante é a maneira diferente de obter a instância da classe UsuarioDAO.
No exemplo anterior, a classe UsuarioDAO era declarada no arquivo spring.xml e recuperada através da classe BeanFactory.
Com o Spring annotations, a classe UsuarioDAO será recebida via construtor(anotado com @Autowired).

package br.com.exemplojsf2.web.controle;

import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import br.com.exemplojsf2.dao.UsuarioDAO;
import br.com.exemplojsf2.modelo.Usuario;

@Controller
@Scope("view")
public class UsuarioController {
	
	private Usuario usuario = new Usuario();
	private List<Usuario> listaUsuarios = new ArrayList<Usuario>();	
	private UsuarioDAO usuarioDAO;

	@Autowired
	public UsuarioController(UsuarioDAO usuarioDAO) {
		this.usuarioDAO = usuarioDAO;
		atualizarTela();
	}

	/**
	 * Limpa os campos input e atualiza a lista de usuários cadastrados
	 */
	private void atualizarTela() {
		usuario = new Usuario();
		listaUsuarios = usuarioDAO.buscarTodos();
	}

	/**
	 * Grava novo registro ou atualiza um registro
	 */
	public void gravar(){
		usuarioDAO.gravar(getUsuario());
		atualizarTela();
	}
	
	/**
	 * Exclui um registro da tabela usuario
	 */
	public void excluir(){
		usuarioDAO.excluir(getUsuario());
		atualizarTela();
	}
	
	public List<Usuario> getListaUsuarios() {
		return listaUsuarios;
	}
		 
	public void setListaUsuarios(List<Usuario> listaUsuarios) {
		this.listaUsuarios = listaUsuarios;
	}
		 
	public Usuario getUsuario() {
		return usuario;
	}
		 
	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}

}

UsuarioDAO

Como a classe UsuarioDAO não é mais declarada no arquivo spring.xml, será necessário anotá-la(@Repository) para o spring reconhecê-la como um bean.
A classe UsuarioDAO recebe, via construtor, um objeto sessionFactory do hibernate(declarado no arquivo spring.xml). O método construtor deve estar anotado com @Autowired.
O sessionFactory recebido será utilizado pelo método setSessionFactory(sessionFactory).

package br.com.exemplojsf2.dao;

import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import br.com.exemplojsf2.modelo.Usuario;

@Repository
public class UsuarioDAO extends HibernateDaoSupport{
	
	@Autowired
	public UsuarioDAO(SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
	}
	
	public void gravar(Usuario usuario) {
		getHibernateTemplate().saveOrUpdate(usuario);
	}
	
	public List<Usuario> buscarTodos(){
		return getHibernateTemplate().loadAll(Usuario.class);
	}
	
	public Usuario buscarPorId(Long id){
		return getHibernateTemplate().get(Usuario.class, id);		
	}
	
	public void excluir(Usuario usuario){
		getHibernateTemplate().delete(usuario);
	}
}

Os outros arquivos não foram alterados em relação ao exemplo anterior.

Obs: Os pacotes foram renomeados, acrescentando o prefixo “br.com.exemplojsf2″. Algumas bibliotecas foram adicionadas e outras atualizadas por versões mais novas.
Agradeço ao Rafael Ponte pelo envio de uma aplicação de exemplo.
O código fonte desse exemplo está disponível para download

02/12/2010

Exemplo JSF 2 + Hibernate 3 + Spring 3

Filed under: JSF — guilhermefinotti @ 16:39

Esse tutorial apresenta um exemplo simples de cadastro de usuários.
Será utilizado JSF 2 (Mojarra), Primefaces, Hibernate 3 , Spring 3, MySQL 5 e Tomcat 6.

A estrutura de pacotes do projeto é a seguinte:

* entidade -> contém a classe Usuario, que representa a tabela usuario no banco de dados.
* dao -> contém a classe UsuarioDAO, responsável pelo acesso ao banco de dados.
* controle -> contém a classe UsuarioController. É o Managed Bean do JSF
* util -> contém a classe BeanFactory, utilizada para recuperarmos os “beans” gerenciados pelo Spring.
* config -> contém o arquivo de configuração do spring (spring.xml) e a classe Conexao, que contém os parâmetros de conexão com o banco de dados.

Vamos começar pela entidade Usuario.
Essa classe possui os mesmos atributos representados na tabela usuario (id, nome, email).
Tanto a classe quanto os atributos utilizam anotações do Hibernate/JPA.

package entidade;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="usuario")
public class Usuario implements Serializable {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 @Column(name="id_usuario", unique=true, nullable=false)
 private Long id;

 @Column(name="nome")
 private String nome;

 @Column(name="email")
 private String email;

 public Usuario() {
 }

 public Long getId() {
 return id;
 }

 public void setId(Long id) {
 this.id = id;
 }

 public String getNome() {
 return nome;
 }

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

 public String getEmail() {
 return email;
 }

 public void setEmail(String email) {
 this.email = email;
 }

}

A segunda classe é UsuarioDAO.
Essa classe estende uma classe do Spring (HibernateDaoSupport) e nos fornece um objeto hibernateTemplate. Com esse objeto disponível, não precisamos nos preocupar em gerenciar a sessão do Hibernate.
Também possui os métodos que efetuam as operações no banco de dados (buscarTodos, buscarPorId, gravar, excluir) e será gerenciada como um bean do Spring.

package dao;

import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import entidade.Usuario;

public class UsuarioDAO extends HibernateDaoSupport {

 /**
 *
 * @return
 */
 public List<Usuario> buscarTodos() {
 return getHibernateTemplate().loadAll(Usuario.class);
 }

 /**
 *
 * @param id
 * @return
 * @throws Exception
 */
 public Usuario buscarPorId(Long id) {
 return (Usuario) getHibernateTemplate().get(Usuario.class, id);
 }

 /**
 * Grava novo registro ou atualiza registro já existente
 * @param usuario
 */
 public void gravar(Usuario usuario) {
 getHibernateTemplate().saveOrUpdate(usuario);
 }

 /**
 *
 * @param usuario
 */
 public void excluir(Usuario usuario) {
 getHibernateTemplate().delete(usuario);
 }

}

Classe BeanFactory.
Lê o arquivo de configuração do spring (spring.xml) e fornece métodos para recuperar os beans declarados no arquivo de configuração do spring.

package util;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanFactory {

 private static ClassPathXmlApplicationContext ctx;

 static {
 ctx = new ClassPathXmlApplicationContext("config/spring.xml");
 }

 private BeanFactory() {
 }

 public static Object getBean(String beanName) {
 return ctx.getBean(beanName);
 }

 public static Object getBean(String beanName, Class classe) {
 return ctx.getBean(beanName, classe);
 }

}

Agora o pacote config.
A classe Conexao estende DriverManagerDataSource e também será gerenciada como um bean do Spring.

package config;

import org.springframework.jdbc.datasource.DriverManagerDataSource;

public class Conexao extends DriverManagerDataSource {

 public Conexao() {

 this.setDriverClassName("com.mysql.jdbc.Driver");
 this.setUrl("jdbc:mysql://localhost/exemplojsf");
 this.setUsername("root");
 this.setPassword("root");

 }

}

E o arquivo de configuração spring.xml.
Nesse arquivo são definidos as propriedades do hibernate, as entidades utilizadas e os beans que serão gerenciados pelo spring. O arquivo está comentado e acho que é de fácil entendimento.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
				   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
				   http://www.springframework.org/schema/tx
				   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
				   http://www.springframework.org/schema/aop
				   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
		
	<!-- Classe com os parâmetros de conexão com o banco de dados -->
	<bean id="dataSource" class="config.Conexao" />
		
	<!-- Hibernate -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    	<property name="annotatedClasses">
    		<list>
    			<value>entidade.Usuario</value>
    		</list>
    	</property>
    	<property name="hibernateProperties">
    		<props>
    			<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
    			<prop key="hibernate.show_sql">true</prop>  
    			<prop key="hibernate.hbm2ddl.auto">update</prop> 			
    		</props>
    	</property>
    	<property name="dataSource" ref="dataSource" />
    </bean>

	<!-- Injeta uma sessão do hibernate -->
	<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
  		<property name="sessionFactory" ref="sessionFactory" />
	</bean> 
	 
	<!-- DAOs -->
    <bean id="usuarioDAO" class="dao.UsuarioDAO">
    	<!-- Injeta o hibernateTemplate na classe UsuarioDAO -->
    	<property name="hibernateTemplate" ref="hibernateTemplate" />
    </bean>
    
</beans>

Agora vamos ver a parte web da aplicação.
Para utilizar o primefaces, será necessário alterar o arquivo web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
 <display-name>exemplo JSF</display-name>

 <servlet>
 <servlet-name>Faces Servlet</servlet-name>
 <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
 <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
 <servlet-name>Faces Servlet</servlet-name>
 <url-pattern>*.jsf</url-pattern>
 </servlet-mapping>

 <servlet>
 <servlet-name>Resource Servlet</servlet-name>
 <servlet-class>org.primefaces.resource.ResourceServlet</servlet-class>
 </servlet>

 <servlet-mapping>
 <servlet-name>Resource Servlet</servlet-name>
 <url-pattern>/primefaces_resource/*</url-pattern>
 </servlet-mapping>

 <welcome-file-list>
 <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>

</web-app>

UsuarioController
Essa classe recebe as requisições da página, acessa o modelo e retorna para a página.
No JSF, essa classe é conhecida como Managed Bean.
Veja que ela tem anotações do JSF 2, então não é necessário criar um arquivo faces-config.xml.

@ManagedBean(name=”usuarioController”) define o nome pelo qual o managed bean será acessado.
@ViewScoped -> um dos escopos disponíveis no JSF 2. Define que o estado dos objetos será mantido enquanto estivermos na mesma página(view).

package controle;

import java.util.ArrayList;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

import util.BeanFactory;

import dao.UsuarioDAO;

import entidade.Usuario;

@ManagedBean(name="usuarioController")
@ViewScoped
public class UsuarioController {

	private Usuario usuario = new Usuario();
	private List<Usuario> listaUsuarios = new ArrayList<Usuario>();	
	private UsuarioDAO usuarioDAO = (UsuarioDAO) BeanFactory.getBean("usuarioDAO", UsuarioDAO.class);
	
	public UsuarioController(){
		atualizarTela();
	}

	/**
	 * Limpa os campos input e atualiza a lista de usuários cadastrados
	 */
	private void atualizarTela() {
		usuario = new Usuario();
		listaUsuarios = usuarioDAO.buscarTodos();
	}

	/**
	 * Grava novo registro ou atualiza um registro
	 */
	public void gravar(){
		usuarioDAO.gravar(getUsuario());
		atualizarTela();
	}
	
	/**
	 * Exclui um registro da tabela usuario
	 */
	public void excluir(){
		usuarioDAO.excluir(getUsuario());
		atualizarTela();
	}
	
	public List<Usuario> getListaUsuarios() {
		return listaUsuarios;
	}
		 
	public void setListaUsuarios(List<Usuario> listaUsuarios) {
		this.listaUsuarios = listaUsuarios;
	}
		 
	public Usuario getUsuario() {
		return usuario;
	}
		 
	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}	
}

A aplicação tem apenas 2 páginas.
index.jsp, cuja função é apenas redirecionar para a página usuario.jsf

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<jsp:forward page="/usuario.jsf"/>

usuario.xhtml.

Essa página possui um painel de cadastro e um painel que exibe os usuários cadastrados. Ao efetuar o cadastro de um usuário, o painel de exibição é atualizado via ajax.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html"
	  xmlns:f="http://java.sun.com/jsf/core"
	  xmlns:ui="http://java.sun.com/jsf/facelets"
	  xmlns:p="http://primefaces.prime.com.tr/ui">
	
	<h:head>
    
        <title>
        	<h:outputText value="JSF 2 + spring 3 + hibernate 3" />
        </title>
        
    </h:head>
       
    <h:body>

   <h:form id="usuarioForm">
    
    	<center>
    	
			<p:panel id="painelCadastro" 
    				 header="Cadastrar novo usuario" 
    				 style="text-align:left;width:700px;" >
    				 
    			<h:panelGrid columns="2">				
    			
    				<h:outputLabel value="Nome" />
    				<h:inputText id="nomeUsuario" value="#{usuarioController.usuario.nome}" size="45" />
    				
    				<h:outputLabel value="Email" />
    				<h:inputText id="emailUsuario" value="#{usuarioController.usuario.email}" size="45" />
    			
    			</h:panelGrid>
    			
    			<br />
    			
				<center>
	    			<p:commandButton value="Gravar" action="#{usuarioController.gravar}" update="painelConsulta, painelCadastro" />
	    			<p:commandButton type="reset" value="Limpar" />
    			</center>    				 
    				 
    		</p:panel> 
    		
    		<br />
    		<br />   	
    	    		
    		<!-- Lista de usuarios -->
    		<p:panel id="painelConsulta" 
    				 header="Lista de usuarios cadastrados" 
    				 style="text-align:left;width:700px;" >
    		
    		
				<p:dataTable value="#{usuarioController.listaUsuarios}"
	   					     id="tabela"
	   					     emptyMessage="Nenhum registro encontrado"
	   					     rowIndexVar="var"
	   					     paginator="true"
	   					     paginatorPosition="bottom"
	   					     firstPageLinkLabel="Primeira"
	   					     previousPageLinkLabel="Anterior"
	   					     nextPageLinkLabel="Proxima"
	   					     lastPageLinkLabel="ultima"
	   					     rows="10"
	   					     var="usuario"
	   					     width="600">
 
	   					     
	   				<p:column>
	   					<f:facet name="header">
							<h:outputText value="ID" />
						</f:facet>
						<h:outputText value="#{usuario.id}" styleClass="letra1" />	
	   				</p:column>

	   				<p:column>
	   					<f:facet name="header">
							<h:outputText value="Nome" />
						</f:facet>
						<h:outputText value="#{usuario.nome}" styleClass="letra1" />	
	   				</p:column>
	   				
	   				<p:column>
	   					<f:facet name="header">
							<h:outputText value="Email" />
						</f:facet>
						<h:outputText value="#{usuario.email}" styleClass="letra1" />	
	   				</p:column>
	   				
	   				<p:column>
		   				 <p:commandButton  value="alterar" update="usuarioForm">
	                        <f:setPropertyActionListener target="#{usuarioController.usuario}" value="#{usuario}" />
	                     </p:commandButton>
                    </p:column>
	   				
	   				<p:column>
		   				 <p:commandButton action="#{usuarioController.excluir}" value="excluir" update="usuarioForm">
	                        <f:setPropertyActionListener target="#{usuarioController.usuario}" value="#{usuario}" />
	                     </p:commandButton>
                    </p:column>

	   			</p:dataTable>
	   			    		
    		</p:panel>
    		
    	</center>
    
    </h:form>
		
	</h:body>

</html>

Para rodar esse exemplo, deve ser criado um banco de dados com o nome exemplojsf.
As tabelas serão criadas automaticamente pelo Hibernate quando a aplicação for instalada no servidor.
Apesar de ser um exemplo bastante simples, espero que seja útil à quem está com dificuldades para configurar um projeto usando essas tecnologias/frameworks.
* Esse exemplo foi desenvolvido no Eclipse e o projeto está disponível para download.
Download do projeto no Eclipse

Tema: Silver is the New Black. Blog no WordPress.com.

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.