My conclusion has been that it does not, at least not conveniently. Here is why. Sorry for the long post.
Data shown is from an app running with the OSIV servlet filter and a JTA configuration in HBM 3.3.2.GA.
Accessing lazy-loaded child components during JSP rendering produces this exception:
Code:
ERROR [btpool0-1] LazyInitializationException.<init>(19) | could not initialize proxy - no Session
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
at com.e3.model.Form$$EnhancerByCGLIB$$a36161b9.getName(<generated>)
at ognl.OgnlRuntime.getMethodValue(OgnlRuntime.java:931)
at ognl.Ognl.getValue(Ognl.java:333)
at org.apache.jsp.WEB_002dINF.pages.e3core.caseFileForm_jsp._jspx_meth_s_property_18(org.apache.jsp.WEB_002dINF.pages.e3core.caseFileForm_jsp:1670)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:93)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at com.e3.webapp.filter.StaticFilter.doFilterInternal(StaticFilter.java:106)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55)
at org.apache.struts2.dispatcher.ActionContextCleanUp.doFilter(ActionContextCleanUp.java:102)
at org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:389)
at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:406)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:285)
at org.apache.struts2.dispatcher.ServletDispatcherResult.doExecute(ServletDispatcherResult.java:154)
at org.apache.struts2.dispatcher.StrutsResultSupport.execute(StrutsResultSupport.java:186)
at com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:361)
at com.e3.webapp.interceptor.SessionInterceptor.intercept(SessionInterceptor.java:87)
at com.e3.webapp.interceptor.AuditInterceptor.intercept(AuditInterceptor.java:97)
...
at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:52)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:468)
at org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:395)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
The SessionFactory is configured this way:
Code:
<bean id="e3SessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="e3DataSource"/>
<property name="jtaTransactionManager" ref="jotm"/>
<property name="annotatedClasses">
<list>
<value>com.e3.model.HomeAddress</value>
<!-- list trimmed for post -->
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.cache.use_second_level_cache=true
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
hibernate.current_session_context_class=jta
hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JOTMTransactionManagerLookup
hibernate.transaction.auto_close_session=false
hibernate.connection.release_mode=after_transaction
</value>
<!-- jdoc for LocalSessionFactoryBean.setJtaTransactionManager says that if setting that prop,
do not set this prop into HBM config
hibernate.transaction.manager_lookup_class=com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
-->
</property>
<property name="eventListeners">
<map>
<entry key="merge">
<bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
</entry>
</map>
</property>
</bean>
and JOTM is configured as the JTA transaction manager. Transactions are defined using AOP as:
Code:
<aop:config>
<aop:advisor id="userManagerTx" advice-ref="userManagerTxAdvice" pointcut="execution(* *..service.UserManager.*(..))" order="0"/>
<aop:advisor id="userManagerSecurity" advice-ref="userSecurityAdvice" pointcut="execution(* *..service.UserManager.saveUser(..))" order="1"/>
<aop:advisor id="actionTx" advice-ref="txRequired" pointcut="execution(* *..action.*(..))" order="1" />
<aop:advisor id="managerTx" advice-ref="txRequired" pointcut="execution(* *..service.*Manager.*(..))" order="2"/>
<aop:advisor id="daoTx" advice-ref="txSupports" pointcut="execution(* *..dao.*(..))" order="3" />
</aop:config>
<tx:advice id="txRequired">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<tx:advice id="txSupports">
<tx:attributes>
<tx:method name="*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice>
<tx:advice id="userManagerTxAdvice">
<tx:attributes>
<tx:method name="save*" rollback-for="UserExistsException"/>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
What happens is that the Hibernate property
Code:
hibernate.current_session_context_class=jta
causes the JTASessionContext class to control creation of HBM sessions. When a session is created, the following logic in JTASessionContext is executed:
Code:
/**
* Strictly provided for subclassing purposes; specifically to allow long-session
* support.
* <p/>
* This implementation always just opens a new session.
*
* @return the built or (re)obtained session.
*/
protected Session buildOrObtainSession() {
return factory.openSession(
null,
isAutoFlushEnabled(),
isAutoCloseEnabled(),
getConnectionReleaseMode()
);
}
/**
* Mainly for subclass usage. This impl always returns true.
*
* @return Whether or not the the session should be closed by transaction completion.
*/
protected boolean isAutoCloseEnabled() {
return true;
}
This is an example of its execution:
Code:
*** AutoCloseSession enabled on Session construction ***
java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Unknown Source)
at org.hibernate.impl.SessionImpl.<init>(SessionImpl.java:243)
at org.hibernate.impl.SessionFactoryImpl.openSession(SessionFactoryImpl.java:605)
at org.hibernate.context.JTASessionContext.buildOrObtainSession(JTASessionContext.java:152)
at org.hibernate.context.JTASessionContext.currentSession(JTASessionContext.java:111)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:622)
at com.e3.dao.hibernate.GenericDaoHibernate.getHbmSession(GenericDaoHibernate.java:523)
at com.e3.dao.hibernate.GenericDaoHibernate.find(GenericDaoHibernate.java:109)
at com.e3.service.impl.GenericManagerImpl.find(GenericManagerImpl.java:130)
at com.e3.service.impl.GenericManagerImpl$$FastClassByCGLIB$$e6c38d98.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:70
0)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:635)
at com.e3.service.impl.GenericManagerImpl$$EnhancerByCGLIB$$1551eeb5.find(<generated>)
at com.e3.webapp.action.CaseFileAction.list(CaseFileAction.java:178)
and after the business methods executes, the org.hibernate.transaction.CacheSynchronization class uses the auto-close property to terminate the current session
Code:
/**
* {@inheritDoc}
*/
public void afterCompletion(int status) {
if ( log.isTraceEnabled() ) {
log.trace("transaction after completion callback, status: " + status);
}
try {
jdbcContext.afterTransactionCompletion(status==Status.STATUS_COMMITTED, hibernateTransaction);
}
finally {
if ( ctx.shouldAutoClose() && !ctx.isClosed() ) {
log.trace("automatically closing session");
ctx.managedClose();
}
}
}
that we can see here
Code:
at java.lang.Thread.dumpStack(Unknown Source)
at org.hibernate.impl.SessionImpl.managedClose(SessionImpl.java:383)
at org.hibernate.transaction.CacheSynchronization.afterCompletion(CacheSynchronization.java:122)
at org.objectweb.jotm.SubCoordinator.doAfterCompletion(SubCoordinator.java:1569)
at org.objectweb.jotm.SubCoordinator.doOnePhaseCommit(SubCoordinator.java:1278)
at org.objectweb.jotm.SubCoordinator.commit_one_phase(SubCoordinator.java:451)
at org.objectweb.jotm.TransactionImpl.commit(TransactionImpl.java:239)
at org.objectweb.jotm.Current.commit(Current.java:488)
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1028)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTran
sactionManager.java:732)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransaction
Manager.java:701)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(Transact
ionAspectSupport.java:321)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:635)
at com.e3.service.impl.GenericManagerImpl$$EnhancerByCGLIB$$40735a67.find(<generated>)
at com.e3.webapp.action.CaseFileAction.list(CaseFileAction.java:178)
I found that modifying JTASessionContext to return 'false' from isAutoCloseEnabled() keeps the HBM session open beyond the normal end of the transaction, thereby allowing the view to render. The filter then closes the session.
However, to my fairly newbie understanding, a 'proper' solution to this problem is really to include the view in the transaction -- by pre-compiling the JSPs and include their class definitions in the AOP declaration.
Can anyone report they they successfully use JTA with an OSIV filter? Does this require JSP precompilation, or am I missing something?
Thanks for ideas -