Filter Hibernate - Falhando ao Salvar ("não tem transaction ativa") [RESOLVIDO]

Comunidade,

Embora minha aplicação suba sem nenhuma exception (inclusive montando o db com base nas annotations), quando tento salvar um registro no banco, dá a seguinte exception: “org.hibernate.HibernateException: save is not valid without active transaction”

AVISO: #{usuarioBean.salvar()}: org.hibernate.HibernateException: save is not valid without active transaction
javax.faces.FacesException: #{usuarioBean.salvar()}: org.hibernate.HibernateException: save is not valid without active transaction
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
	at javax.faces.component.UICommand.broadcast(UICommand.java:315)
	at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
	at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
	at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:409)
	at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1534)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
	at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
	at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
	at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:326)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:227)
	at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:170)
	at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:822)
	at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:719)
	at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1013)
	at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
	at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
	at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
	at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
	at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
	at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
	at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
	at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
	at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
	at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
	at java.lang.Thread.run(Thread.java:662)
Caused by: javax.faces.el.EvaluationException: org.hibernate.HibernateException: save is not valid without active transaction
	at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
	... 32 more
Caused by: org.hibernate.HibernateException: save is not valid without active transaction
	at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:297)
	at $Proxy132.save(Unknown Source)
	at model.UsuarioDAOImp.salvar(UsuarioDAOImp.java:29)
	at model.UsuarioRN.salvar(UsuarioRN.java:26)
	at model.UsuarioBean.salvar(UsuarioBean.java:23)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at javax.el.BeanELResolver.invokeMethod(BeanELResolver.java:737)
	at javax.el.BeanELResolver.invoke(BeanELResolver.java:467)
	at javax.el.CompositeELResolver.invoke(CompositeELResolver.java:254)
	at com.sun.el.parser.AstValue.invoke(AstValue.java:228)
	at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:297)
	at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
	at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
	... 33 more

Até pensei uma coisa, mas não tenho certeza se faz sentido…
Se o doFilter é ativado sempre que requisito uma página jsf, pode ser que minha session não está iniciando pq não há nenhum apontamento para uma página .jsf (faz sentido?!).
Como eu estou começando agora, estou fazendo tudo no meu “index.xhtml”, que está mapeado na minha web.xml como welcome-file

Será que se eu criar um index.html com um

mas será que funcionaria? confesso que até tentei, mas deu “requested resource () is not available” e eu deduzi que não funcionaria.

Meu web.xml está assim:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <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>/faces/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
    <filter>
        <filter-name>conexaoFilter</filter-name>
        <filter-class>util.filter.ConexaoHibernateFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>conexaoFilter</filter-name>
        <url-pattern>*.jsf</url-pattern>
    </filter-mapping>
</web-app>

Meu Filter:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package util.filter;

import util.HibernateUtil;
import javax.servlet.*;
import org.hibernate.SessionFactory;

/**
 *
 * @author root
 */
public class ConexaoHibernateFilter implements Filter{
    private SessionFactory sf;

    @Override
    public void init(FilterConfig config) throws ServletException {
        this.sf = HibernateUtil.getSessionFactory();
    }
    
    @Override
    public void doFilter(ServletRequest servletRequest,
            ServletResponse servletResponse, 
            FilterChain chain) throws ServletException{
            try{
                this.sf.getCurrentSession().beginTransaction();
                chain.doFilter(servletRequest, servletResponse);
                this.sf.getCurrentSession().getTransaction().commit();
                this.sf.getCurrentSession().close();
            } catch (Throwable ex){
                try{
                    if(this.sf.getCurrentSession().getTransaction().isActive()){
                       this.sf.getCurrentSession().getTransaction().rollback();
                    }
                } catch (Throwable t){
                    t.printStackTrace();
                }
                throw new ServletException(ex);
            }
    }

    @Override
    public void destroy(){}

    public SessionFactory getSf() {
        return sf;
    }

    public void setSf(SessionFactory sf) {
        this.sf = sf;
    }
    
}

Estou usando NetBeans 7

Como a própria exceção ja indica,vc precisa de uma transação ativa pra poder fazer o insert.

Ago como:

getEntityManager().getTransaction()begin();
save();
getEntityManager().getTransaction()commit();

Cara,

Pq vc nao usar EJB ou Spring, pq com isso vc consegue deixar o controle da transação por controle do container.

t+

@raf4ever
Fazendo dessa forma, provavelmente funcionaria… na verdade funcionaria mesmo…
… mas daí não teria sentido nehum trabalhar com o Filter… teria que repetir muito código e eu quero que minhas classes fiquem bem clean.
Na última das hipóteses… terei que fazer isso.

@alissonvla
Honestamente sou cabação com persistencia… eu estava misturando muitas responsabilidades da minha aplicação e pensei em fazer utilizando o Filter e reorganizar tudo (e estou fazendo isso) pq tinha muito código repetido.

Não saberia trabalhar com EJB + Spring de cara… posteriormente, vou gerenciar o acesoo com o Spring… mas acho que seria um passo maior que perna fazer isso agora… estou com os alicerces, apenas.

Tem noção de como conseguiria abrir a transação usando os filters? obrigado aos dois pela contribuição!

Abs

[quote=rCalastro]@raf4ever
Fazendo dessa forma, provavelmente funcionaria… na verdade funcionaria mesmo…
… mas daí não teria sentido nehum trabalhar com o Filter… teria que repetir muito código e eu quero que minhas classes fiquem bem clean.
Na última das hipóteses… terei que fazer isso.
[/quote]

Implementa um esquema com DAO genérico,assim vc n precisa repetir código.

Exemplo:

public class GenericDAOImp<T,ID extends Serializable>  implements GenericDAO<T, ID> {
	@PersistenceContext
	private EntityManager entityManager;
	private final Class<T> classePersistente;

	public GenericDAOImp(){
		this.classePersistente = (Class<T>)
		((ParameterizedType)getClass().getGenericSuperclass())
		.getActualTypeArguments()[0];
	}

	public Class<T> getClassePersistente() {return classePersistente;}
	
	protected final Criteria criaCriteria() {
		return criaSession().createCriteria(getClassePersistente());
	}

	public final Session criaSession() {
		return  (Session)getEntityManager().getDelegate();
	}
	public EntityManager getEntityManager() {return entityManager;}

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	@Override
	public Class<T> getObjectClass() {return this.classePersistente;}

	@Override 
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED) 
	public T salvar(T object)  {
		getEntityManager().clear();
		try {
			getEntityManager().persist(object);
		}catch(Exception e){
			e.printStackTrace();
		}
		return object;
	}

	@SuppressWarnings("unchecked")       
	public List<T> todos(String ordem){ 
		StringBuffer queryS = new StringBuffer("SELECT obj FROM "+classePersistente.getSimpleName()+" obj "); 
		if(ordem!=null){
			queryS.append("order by "+ordem);
		}
		Query query = getEntityManager().createQuery(queryS.toString()); 
		return query.getResultList(); 
	}

	@Override 
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED) 
	public T atualizar(T object) {
		getEntityManager().merge(object);
		return null;
	}

	@Override 
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED) 
	public T excluir(T object) {
		try {
			object = getEntityManager().merge(object);
			getEntityManager().remove(object);
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED) 
	public T findById(ID id) {
		return getEntityManager().find(getClassePersistente(), id);
	}

}

cara,

trabalhar com EJB ou Spring é tao simplessss, acho que nao vale apenas vc ficar reinventando a roda nao.
da uma lida nesta apostila, acho q vc vai mudar de ideia. http://www.k19.com.br/downloads/apostilas-java

t+

Vou digerir essa sua classe.
Vi muita gente falando sobre o uso de GenericDAOs como sendo uma coisa negativa que nem procurei muito a fundo.
Notei que em nenhum momento se tem um beginTransaction(). Não deveria ter não?
se eu fosse usar essa classe para salvar um objeto Livro, como faria?

[quote]trabalhar com EJB ou Spring é tao simplessss, acho que nao vale apenas vc ficar reinventando a roda nao.
da uma lida nesta apostila, acho q vc vai mudar de ideia. http://www.k19.com.br/downloads/apostilas-java [/quote]
Show de bola esse site! vou dar uma olhada nas apostilas com calma. Não conhecia esse site não! Muito matarial em portugues e gratis! obrigadão

Resolvido!

Agradeço a colaboração e aproveito para divulgar a solução.

Tive um norte olhando um artigo “Open Session In View” que tem no http://community.jboss.org/wiki/OpenSessioninView

Tive que fazer uma alteração no meu web.xml, mapeando a classe Filter para atuar sobre todas as páginas e não apenas para .jsf.

Como era:

    <filter-mapping>
        <filter-name>conexaoFilter</filter-name>
        <url-pattern>*.jsf</url-pattern>
    </filter-mapping>

Como ficou:

    <filter-mapping>
        <filter-name>conexaoFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Espero estar ajudando as pessoas que tiverem essa mesma dificuldade. Procurei em muitos fóruns (inclusive alguns tópicos aqui) e encontrei gente com o mesmo problema, mas sem solução.

PS.

Volto a pensar que talvez se eu fizesse algo como o redirecionamento (de forma correta) para minha pagina xhtml talvez funcionasse.

Boa noite, rCalastro , obrigado pelo esclarecimento, me ajudou muito, valeu …

Ajudou aqui também, valeu!