Bom a imagem abaixo representa a estrutura de pacotes em que se divide nossa aplicação. De cima para baixo temos as pastas META-INF e WEB-INF a primeira delas não tem muita relevância nesse caso já a segunda é reponsável por armazenar os arquivos de configuração de nossa aplicação como faces.config.xml que armazena as configurações do JSF, o web.xml que mapeia listeners, servlets e demais configurações da aplicação e por fim temos o spring-security.xml que armazena as configurações de segurança de nossa aplicação. Um pouco abaixo temos as pastas public, restrict e resources. A pasta public no nosso projeto conterá as páginas que poderão ser acessadas sem a necessidade de autenticação por parte do usuário a restrict por sua vez armazena as páginas que só serão acessadas por usuários autenticados e autorizados. A pasta resources contém outras duas pastas public e restrict onde são armazenados arquivos de css, javascript e templates Facelets correspondentes as páginas publicas e restritas.
Logo abaixo temos nossa estrutura de pacotes. Inicialmente temos o pacote br.com.semeru.controller que armazena as classes da camada de controle de nossa aplicação. Logo abaixo temos o pacote br.com.semeru.conversores que armazena as classes responsáveis por converter dados, como por exemplo criptografar uma senha. Logo abaixo temos o pacote br.com.semeru.exceptions que armazena as classes responsáveis por tratar as exceções ocorridas na aplicação. Logo abaixo temos o pacote br.com.semeru.i18n que é responsável por prover internacionalização da aplicação, entretanto isso ainda não foi aplicado no projeto atual. Mais abaixo temos o pacote br.com.semeru.dao que armazena a uma interface do DAO e uma classe de DAO Genérico que é responsável por acessar os a base de dados. Um pouco mais abaixo temos o pacote br.com.semeru.entities que armazena as entidades que representam as tabelas do banco de dados da nossa aplicação. Vale destacar que nossa arquitetura segue o padrão MVC onde as páginas representam a camada de visão (V) os DAO’s e as Entidades representam o modelo (M) e o controle é representado pelos nossos ManagedBeans. Logo abaixo temos o pacote br.com.semeru.suport que armazena os BackingBeans de nossa aplicação essas classes são reponsáveis por oferecer suporte à página não processando nada e raramente contendo regras de negócio. Por fim temos o pacote br.com.semeru.validators que armazena as classes responsáveis por realizar validações de dados no projeto.
Temos ainda no pacote default o arquivo hibernate.cfg.xml, a fim de manter o projeto levemente mais simples vamos mantê-lo no pacote default. Pouco depois temos o pacote ScriptsSQL que contem o backup de alguns dados do nosso banco e depois o arquivo pom.xml que é o responsável por armazenar as configurações do Maven. Não irei me aprofundar muito em explicações do Maven para entender melhor sobre o maven acesse este link, este e este.
05 – Curso De JSF Do Zero À Nuvem – Definindo A Estrutura De Pacotes
Nesse vídeo iremos montar toda a estrutura de pacotes onde é explicado com mais detalhe todo o projeto.
Aqui temos a classe HibernateUtil que é responsável por criar a SessionFactory que nos permitirá acessar a base de dados. Inicialmente declaramos uma instância de SessionFactory e depois uma constante com a sessão do Hibernate. Logo abaixo temos um bloco estático que é responsável por tentar criar uma SessionFactory. Inicialmente declaramos uma instância de Configuration. Posteriormente tentamos registrar uma SessionFactory para isso passamos como parâmetro no método applySettings as informações do nosso hibernate.cfg.xml se mudarmos esse arquivo para um pacote diferente do default deveremos passar sua localização como parâmetro configuration.getProperties, caso contrário o Hibernate não saberá se localizar. Caso ocorra alguma falha nesse processo uma exception será lançada. Por fim temos o o método getSessionFactory que é o responsável por permitir a obtenção da SessionFactory pelo nosso PhaseListener, que será abordado mais abaixo.
package br.com.semeru.util; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; public class HibernateUtil { private static final SessionFactory sessionFactory; public static final String HIBERNATE_SESSION = "hibernate_session"; static{ try { Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder(). applySettings(configuration.getProperties()).buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); } catch (Exception ex) { throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
06 – Curso De JSF Do Zero À Nuvem – Criando A Classe HibernateUtil
Aqui temos o processo de criação da classe HibernateUtil que detalha um pouco mais essas informações.
07 – Curso De JSF Do Zero À Nuvem – Removendo Dependência Duplicada E slf4j
Nesse vídeo removemos uma dependência duplicada e o slf4j que estavam impedindo a execução de nosso projeto.
Aqui temos as nossa InterfaceDAO que define os métodos que o nosso DAO genérico deverá implementar. São eles save, update, remove, merge responsáveis por salvar, atualizar, remover e tanto salvar quanto atualizar respectivamente. Temos também os métodos de leitura como por exemplo o getEntity que obtém um objeto no banco, o getEntityByDetachedCriteria que obtém um objeto no banco através de uma DetachedCriteria, o getEntities que obtém uma lista de objetos no banco e getListByDetachedCriteria que obtém uma lista de objetos no banco através de uma DetachedCriteria.
package br.com.semeru.model.dao; import java.io.Serializable; import java.util.List; import org.hibernate.criterion.DetachedCriteria; public interface InterfaceDAO<t> { void save (T entity); void update (T entity); void remove (T entity); void merge (T entity); T getEntity(Serializable id); T getEntityByDetachedCriteria(DetachedCriteria criteria); List<t> getEntities(); List<t> getListByDetachedCriteria(DetachedCriteria criteria); }
Aqui temos a nossa classe HibernateDAO que é de tipagem genérica, o que siginfica que o nosso DAO possibilitará a persistencia de todas as entidades da nossa aplicação. No nosso construtor obrigamos a sempre passar o tipo da entidade que queremos persistir e a sessão do hibernate para que o DAO faça o serviço. Além disso toda a implementação é bastante simples, sendo necessário apenas usar a session do hibernate e executar seus métodos passando a entidade envolvida como parâmetro.
package br.com.semeru.model.dao; import java.io.Serializable; import java.util.List; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.criterion.DetachedCriteria; public class HibernateDAO<t> implements InterfaceDAO<t>, Serializable { private static final long serialVersionUID = 1L; private Class<t> classe; private Session session; public HibernateDAO(Class<t> classe, Session session) { super(); this.classe = classe; this.session = session; } @Override public void save(T entity) { session.save(entity); } @Override public void update(T entity) { session.update(entity); } @Override public void remove(T entity) { session.delete(entity); } @Override public void merge(T entity) { session.merge(entity); } @Override public T getEntity(Serializable id) { T entity = (T)session.get(classe, id); return entity; } @Override public T getEntityByDetachedCriteria(DetachedCriteria criteria) { T entity = (T)criteria.getExecutableCriteria(session).uniqueResult(); return entity; } @Override public List<t> getListByDetachedCriteria(DetachedCriteria criteria) { return criteria.getExecutableCriteria(session).list(); } @Override public List<t> getEntities() { List<t> enties = (List<t>) session.createCriteria(classe).list(); return enties; } }
08 – Curso De JSF Do Zero À Nuvem – Criação Do DAO Genérico
Nesse vídeo nós criamos a interface e o DAO genérico, neles você terá informações com maiores detalhes.
Aqui temos a classe FacesContextUtil que é responsável por colocar na sessão do usuário (FacesContext) do JSF, a sessão do Hibernate. Como se pode perceber ela possui um atributo estático que é a chave da sessão do Hibernate e dois métodos set e get. O primeiro deles captura o RequestMap do FacesContext e atribui a ele a sessão do Hibernate. O segundo método obtém de volta a sessão do Hibernate para que o PhaseListenerSemeru possa realizar o commit na transação como será explicado mais abaixo.
package br.com.semeru.util; import javax.faces.context.FacesContext; import org.hibernate.Session; public class FacesContextUtil { private static final String HIBERNATE_SESSION = "hibernate_session"; public static void setRequestSession(Session session){ FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put(HIBERNATE_SESSION, session); } public static Session getRequestSession() { return (Session)FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get(HIBERNATE_SESSION); } }
Antes de partir para a próxima etapa recomendo que você leia este post que explica como funciona o ciclo de vida do JSF. Agora aproveite para assistir também a parte teórica do video abaixo em que isso é abordado com mais detalhes.
09 – Curso De JSF Do Zero À Nuvem – O Ciclo De Vida De Uma Request No JSF E O FacesContextUtil
Atuando junto à classe FacesContextUtil temos a classe PhaseListenerSemeru é a responsável por auxiliar o sistema de login e por implementar o padrão Open Session in View do Hibernate. Open Session in View consiste em iniciar a transação quando é feita uma requisição e encerrá-la quando a página é renderizada. O PhaseListenerSemeru implementa a interface PhaseListener do JSF e possui três métodos beforePhase, afterPhase e getPhaseId. O método beforePhase verifica se a fase está antes da Restore View (Restaurar a Visão), se estiver ele abre a sessão do Hibernate e coloca-a na requisição do usuário passando-a para o FacesContext. O segundo método, verifica se a fase Render Response (Renderizar a Resposta) foi executada, se sim, ele tenta commitar a transação. Se ocorrer algum erro ele imprime uma mensagem no log, nesse momento, caso a sessão ainda esteja ativa ele executa um rollback e finalmente fecha a sessão. Caso não ocorram erros ele encerra a transação e fecha a sessão. Já o terceiro método, o getPhaseId é responsável por especificar em que fase o PhaseListener deve ser acionado, no nosso exemplo ele é acionado em todas as fases. Dessa forma, a classe PhaseListenerSemeru implementa o Open Session in View de forma transparente para o desenvolvedor. Essa abordagem torna desnecessário, ao desenvolvedor, se preocupar com abrir e fechar a sessão. Toda a responsabilidade pelo controle de sessão passa a ser feito pelo PhaseListenerSemeru através da verificação das fases da requisição no FacesContext, do JSF.
package br.com.semeru.util; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; import org.hibernate.Session; public class PhaseListenerSemeru implements PhaseListener { //Antes da Fase @Override public void beforePhase(PhaseEvent fase) { System.out.println("Antes da fase: "+ fase.getPhaseId()); if (fase.getPhaseId().equals(PhaseId.RESTORE_VIEW)) { Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); FacesContextUtil.setRequestSession(session); } } //Depois da Fase @Override public void afterPhase(PhaseEvent fase) { System.out.println("Depois da fase: "+ fase.getPhaseId()); if (fase.getPhaseId().equals(PhaseId.RENDER_RESPONSE)) { Session session = FacesContextUtil.getRequestSession(); try { session.getTransaction().commit(); } catch (Exception e) { if (session.getTransaction().isActive()) { session.getTransaction().rollback(); } } finally{ session.close(); } } } @Override public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } }
Para reforçar os conceitos sobre o PhaseListener assista mais essa vídeo aula.
10 – Curso De JSF Do Zero À Nuvem – O PhaseListener
Agora que temos uma visão geral mais clara botaremos para quebrar nos próximos posts. Lembrando que os comentários são importantes para que eu possa ir melhorando gradativamente os posts. Quero destacar também que os fontes desse curso podem ser baixados do GitHub. Serão feitos posts correspondentes às demais vídeo aulas e assim que possível irei realizando posts para complementar o que foi falado nos vídeos. É isso aí bons estudos.
Treinamentos relacionados com este post
ao criar a sessao
Session session = FacesContextUtil.getRequestSession();
da o erro
java.lang.NullPointerException
at br.com.indicacao.util.FacesContextUtil.getRequestSession(FacesContextUtil.java:15)
at br.com.indicacao.security.UserDetailsService.findUser(UserDetailsService.java:66)
at br.com.indicacao.security.UserDetailsService.mapUserFromContext(UserDetailsService.java:148)
at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:84)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:192)
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:93)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:670)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Grande Marcus beleza, caso seja possível coloque o código no Github para que possamos te ajudar com a solução.