/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.orm.hibernate3.support;

import java.io.IOException;
import java.sql.Connection;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.transaction.TransactionManager;

import junit.framework.TestCase;
import org.easymock.MockControl;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.hibernate.engine.SessionFactoryImplementor;

import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.mock.web.PassThroughFilterChain;
import org.springframework.orm.hibernate3.HibernateAccessor;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;

/**
 * @author Juergen Hoeller
 * @since 05.03.2005
 */
public class OpenSessionInViewTests extends TestCase {

	public void testOpenSessionInViewInterceptorWithSingleSession() throws Exception {
		MockControl sfControl = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf = (SessionFactory) sfControl.getMock();
		MockControl sessionControl = MockControl.createControl(Session.class);
		Session session = (Session) sessionControl.getMock();

		OpenSessionInViewInterceptor rawInterceptor = new OpenSessionInViewInterceptor();
		rawInterceptor.setSessionFactory(sf);
		HandlerInterceptor interceptor = new WebRequestHandlerInterceptorAdapter(rawInterceptor);

		MockServletContext sc = new MockServletContext();
		MockHttpServletRequest request = new MockHttpServletRequest(sc);
		MockHttpServletResponse response = new MockHttpServletResponse();

		sf.openSession();
		sfControl.setReturnValue(session, 1);
		session.getSessionFactory();
		sessionControl.setReturnValue(sf, 2);
		session.isOpen();
		sessionControl.setReturnValue(true, 1);
		session.setFlushMode(FlushMode.NEVER);
		sessionControl.setVoidCallable(1);
		sfControl.replay();
		sessionControl.replay();
		interceptor.preHandle(request, response, "handler");
		assertTrue(TransactionSynchronizationManager.hasResource(sf));

		// check that further invocations simply participate
		interceptor.preHandle(request, response, "handler");

		assertEquals(session, SessionFactoryUtils.getSession(sf, false));

		interceptor.preHandle(request, response, "handler");
		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		interceptor.preHandle(request, response, "handler");
		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		sfControl.verify();
		sessionControl.verify();

		sfControl.reset();
		sessionControl.reset();
		sfControl.replay();
		sessionControl.replay();
		interceptor.postHandle(request, response, "handler", null);
		assertTrue(TransactionSynchronizationManager.hasResource(sf));
		sfControl.verify();
		sessionControl.verify();

		sfControl.reset();
		sessionControl.reset();
		session.close();
		sessionControl.setReturnValue(null, 1);
		sfControl.replay();
		sessionControl.replay();
		interceptor.afterCompletion(request, response, "handler", null);
		assertFalse(TransactionSynchronizationManager.hasResource(sf));
		sfControl.verify();
		sessionControl.verify();
	}

	public void testOpenSessionInViewInterceptorWithSingleSessionAndJtaTm() throws Exception {
		MockControl sfControl = MockControl.createControl(SessionFactoryImplementor.class);
		final SessionFactoryImplementor sf = (SessionFactoryImplementor) sfControl.getMock();
		MockControl sessionControl = MockControl.createControl(Session.class);
		Session session = (Session) sessionControl.getMock();

		MockControl tmControl = MockControl.createControl(TransactionManager.class);
		TransactionManager tm = (TransactionManager) tmControl.getMock();
		tm.getTransaction();
		tmControl.setReturnValue(null, 2);

		OpenSessionInViewInterceptor rawInterceptor = new OpenSessionInViewInterceptor();
		rawInterceptor.setSessionFactory(sf);
		HandlerInterceptor interceptor = new WebRequestHandlerInterceptorAdapter(rawInterceptor);

		MockServletContext sc = new MockServletContext();
		MockHttpServletRequest request = new MockHttpServletRequest(sc);
		MockHttpServletResponse response = new MockHttpServletResponse();

		sf.getTransactionManager();
		sfControl.setReturnValue(tm, 2);
		sf.openSession();
		sfControl.setReturnValue(session, 1);
		session.isOpen();
		sessionControl.setReturnValue(true, 1);
		session.setFlushMode(FlushMode.NEVER);
		sessionControl.setVoidCallable(1);

		tmControl.replay();
		sfControl.replay();
		sessionControl.replay();

		interceptor.preHandle(request, response, "handler");
		assertTrue(TransactionSynchronizationManager.hasResource(sf));

		// check that further invocations simply participate
		interceptor.preHandle(request, response, "handler");

		assertEquals(session, SessionFactoryUtils.getSession(sf, false));

		interceptor.preHandle(request, response, "handler");
		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		interceptor.preHandle(request, response, "handler");
		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		sfControl.verify();
		sessionControl.verify();

		sfControl.reset();
		sessionControl.reset();
		sfControl.replay();
		sessionControl.replay();
		interceptor.postHandle(request, response, "handler", null);
		assertTrue(TransactionSynchronizationManager.hasResource(sf));
		sfControl.verify();
		sessionControl.verify();

		sfControl.reset();
		sessionControl.reset();
		session.close();
		sessionControl.setReturnValue(null, 1);
		sfControl.replay();
		sessionControl.replay();
		interceptor.afterCompletion(request, response, "handler", null);
		assertFalse(TransactionSynchronizationManager.hasResource(sf));
		sfControl.verify();
		sessionControl.verify();
	}

	public void testOpenSessionInViewInterceptorWithSingleSessionAndFlush() throws Exception {
		MockControl sfControl = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf = (SessionFactory) sfControl.getMock();
		MockControl sessionControl = MockControl.createControl(Session.class);
		Session session = (Session) sessionControl.getMock();

		OpenSessionInViewInterceptor rawInterceptor = new OpenSessionInViewInterceptor();
		rawInterceptor.setSessionFactory(sf);
		rawInterceptor.setFlushMode(HibernateAccessor.FLUSH_AUTO);
		HandlerInterceptor interceptor = new WebRequestHandlerInterceptorAdapter(rawInterceptor);

		MockServletContext sc = new MockServletContext();
		MockHttpServletRequest request = new MockHttpServletRequest(sc);
		MockHttpServletResponse response = new MockHttpServletResponse();

		sf.openSession();
		sfControl.setReturnValue(session, 1);
		session.getSessionFactory();
		sessionControl.setReturnValue(sf);
		sfControl.replay();
		sessionControl.replay();
		interceptor.preHandle(request, response, "handler");
		assertTrue(TransactionSynchronizationManager.hasResource(sf));
		sfControl.verify();
		sessionControl.verify();

		sfControl.reset();
		sessionControl.reset();
		session.flush();
		sessionControl.setVoidCallable(1);
		sfControl.replay();
		sessionControl.replay();
		interceptor.postHandle(request, response, "handler", null);
		assertTrue(TransactionSynchronizationManager.hasResource(sf));
		sfControl.verify();
		sessionControl.verify();

		sfControl.reset();
		sessionControl.reset();
		session.close();
		sessionControl.setReturnValue(null, 1);
		sfControl.replay();
		sessionControl.replay();
		interceptor.afterCompletion(request, response, "handler", null);
		assertFalse(TransactionSynchronizationManager.hasResource(sf));
		sfControl.verify();
		sessionControl.verify();
	}

	public void testOpenSessionInViewInterceptorAndDeferredClose() throws Exception {
		MockControl sfControl = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf = (SessionFactory) sfControl.getMock();
		MockControl sessionControl = MockControl.createControl(Session.class);
		Session session = (Session) sessionControl.getMock();

		OpenSessionInViewInterceptor rawInterceptor = new OpenSessionInViewInterceptor();
		rawInterceptor.setSessionFactory(sf);
		rawInterceptor.setSingleSession(false);
		HandlerInterceptor interceptor = new WebRequestHandlerInterceptorAdapter(rawInterceptor);

		MockServletContext sc = new MockServletContext();
		MockHttpServletRequest request = new MockHttpServletRequest(sc);
		MockHttpServletResponse response = new MockHttpServletResponse();

		sf.openSession();
		sfControl.setReturnValue(session, 1);
		session.getSessionFactory();
		sessionControl.setReturnValue(sf, 1);
		session.setFlushMode(FlushMode.NEVER);
		sessionControl.setVoidCallable(1);
		sfControl.replay();
		sessionControl.replay();

		interceptor.preHandle(request, response, "handler");
		org.hibernate.Session sess = SessionFactoryUtils.getSession(sf, true);
		SessionFactoryUtils.releaseSession(sess, sf);

		// check that further invocations simply participate
		interceptor.preHandle(request, response, "handler");

		interceptor.preHandle(request, response, "handler");
		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		interceptor.preHandle(request, response, "handler");
		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		sfControl.verify();
		sessionControl.verify();
		sfControl.reset();
		sessionControl.reset();

		session.close();
		sessionControl.setReturnValue(null, 1);
		sfControl.replay();
		sessionControl.replay();

		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);
		sfControl.verify();
		sessionControl.verify();
	}

	public void testOpenSessionInViewFilterWithSingleSession() throws Exception {
		MockControl sfControl = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf = (SessionFactory) sfControl.getMock();
		MockControl sessionControl = MockControl.createControl(Session.class);
		Session session = (Session) sessionControl.getMock();

		sf.openSession();
		sfControl.setReturnValue(session, 1);
		session.getSessionFactory();
		sessionControl.setReturnValue(sf);
		session.setFlushMode(FlushMode.NEVER);
		sessionControl.setVoidCallable(1);
		session.close();
		sessionControl.setReturnValue(null, 1);
		sfControl.replay();
		sessionControl.replay();

		MockControl sf2Control = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf2 = (SessionFactory) sf2Control.getMock();
		MockControl session2Control = MockControl.createControl(Session.class);
		Session session2 = (Session) session2Control.getMock();

		sf2.openSession();
		sf2Control.setReturnValue(session2, 1);
		session2.getSessionFactory();
		session2Control.setReturnValue(sf);
		session2.setFlushMode(FlushMode.AUTO);
		session2Control.setVoidCallable(1);
		session2.close();
		session2Control.setReturnValue(null, 1);
		sf2Control.replay();
		session2Control.replay();

		MockServletContext sc = new MockServletContext();
		StaticWebApplicationContext wac = new StaticWebApplicationContext();
		wac.setServletContext(sc);
		wac.getDefaultListableBeanFactory().registerSingleton("sessionFactory", sf);
		wac.getDefaultListableBeanFactory().registerSingleton("mySessionFactory", sf2);
		wac.refresh();
		sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
		MockHttpServletRequest request = new MockHttpServletRequest(sc);
		MockHttpServletResponse response = new MockHttpServletResponse();

		MockFilterConfig filterConfig = new MockFilterConfig(wac.getServletContext(), "filter");
		MockFilterConfig filterConfig2 = new MockFilterConfig(wac.getServletContext(), "filter2");
		filterConfig2.addInitParameter("sessionFactoryBeanName", "mySessionFactory");
		filterConfig2.addInitParameter("flushMode", "AUTO");

		final OpenSessionInViewFilter filter = new OpenSessionInViewFilter();
		filter.init(filterConfig);
		final OpenSessionInViewFilter filter2 = new OpenSessionInViewFilter();
		filter2.init(filterConfig2);

		final FilterChain filterChain = new FilterChain() {
			public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) {
				assertTrue(TransactionSynchronizationManager.hasResource(sf));
				servletRequest.setAttribute("invoked", Boolean.TRUE);
			}
		};

		final FilterChain filterChain2 = new FilterChain() {
			public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
			    throws IOException, ServletException {
				assertTrue(TransactionSynchronizationManager.hasResource(sf2));
				filter.doFilter(servletRequest, servletResponse, filterChain);
			}
		};

		FilterChain filterChain3 = new PassThroughFilterChain(filter2, filterChain2);

		assertFalse(TransactionSynchronizationManager.hasResource(sf));
		assertFalse(TransactionSynchronizationManager.hasResource(sf2));
		filter2.doFilter(request, response, filterChain3);
		assertFalse(TransactionSynchronizationManager.hasResource(sf));
		assertFalse(TransactionSynchronizationManager.hasResource(sf2));
		assertNotNull(request.getAttribute("invoked"));

		sfControl.verify();
		sessionControl.verify();
		sf2Control.verify();
		session2Control.verify();

		wac.close();
	}

	public void testOpenSessionInViewFilterWithSingleSessionAndPreBoundSession() throws Exception {
		MockControl sfControl = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf = (SessionFactory) sfControl.getMock();
		MockControl sessionControl = MockControl.createControl(Session.class);
		Session session = (Session) sessionControl.getMock();

		sf.openSession();
		sfControl.setReturnValue(session, 1);
		session.getSessionFactory();
		sessionControl.setReturnValue(sf);
		session.setFlushMode(FlushMode.NEVER);
		sessionControl.setVoidCallable(1);
		session.close();
		sessionControl.setReturnValue(null, 1);
		sfControl.replay();
		sessionControl.replay();

		MockServletContext sc = new MockServletContext();
		StaticWebApplicationContext wac = new StaticWebApplicationContext();
		wac.setServletContext(sc);
		wac.getDefaultListableBeanFactory().registerSingleton("sessionFactory", sf);
		wac.refresh();
		sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
		MockHttpServletRequest request = new MockHttpServletRequest(sc);
		MockHttpServletResponse response = new MockHttpServletResponse();

		MockFilterConfig filterConfig = new MockFilterConfig(wac.getServletContext(), "filter");
		MockFilterConfig filterConfig2 = new MockFilterConfig(wac.getServletContext(), "filter2");
		filterConfig2.addInitParameter("sessionFactoryBeanName", "mySessionFactory");

		OpenSessionInViewInterceptor rawInterceptor = new OpenSessionInViewInterceptor();
		rawInterceptor.setSessionFactory(sf);
		HandlerInterceptor interceptor = new WebRequestHandlerInterceptorAdapter(rawInterceptor);

		interceptor.preHandle(request, response, "handler");

		final OpenSessionInViewFilter filter = new OpenSessionInViewFilter();
		filter.init(filterConfig);

		final FilterChain filterChain = new FilterChain() {
			public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) {
				assertTrue(TransactionSynchronizationManager.hasResource(sf));
				servletRequest.setAttribute("invoked", Boolean.TRUE);
			}
		};

		assertTrue(TransactionSynchronizationManager.hasResource(sf));
		filter.doFilter(request, response, filterChain);
		assertTrue(TransactionSynchronizationManager.hasResource(sf));
		assertNotNull(request.getAttribute("invoked"));

		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		sfControl.verify();
		sessionControl.verify();

		wac.close();
	}

	public void testOpenSessionInViewFilterWithDeferredClose() throws Exception {
		MockControl sfControl = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf = (SessionFactory) sfControl.getMock();
		final MockControl sessionControl = MockControl.createControl(Session.class);
		final Session session = (Session) sessionControl.getMock();

		sf.openSession();
		sfControl.setReturnValue(session, 1);
		session.getSessionFactory();
		sessionControl.setReturnValue(sf);
		session.getFlushMode();
		sessionControl.setReturnValue(FlushMode.NEVER, 1);
		session.setFlushMode(FlushMode.NEVER);
		sessionControl.setVoidCallable(1);
		sfControl.replay();
		sessionControl.replay();

		MockControl sf2Control = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf2 = (SessionFactory) sf2Control.getMock();
		final MockControl session2Control = MockControl.createControl(Session.class);
		final Session session2 = (Session) session2Control.getMock();
		MockControl txControl = MockControl.createControl(Transaction.class);
		Transaction tx = (Transaction) txControl.getMock();
		MockControl conControl = MockControl.createControl(Connection.class);
		Connection con = (Connection) conControl.getMock();

		sf2.openSession();
		sf2Control.setReturnValue(session2, 1);
		session2.beginTransaction();
		session2Control.setReturnValue(tx, 1);
		session2.connection();
		session2Control.setReturnValue(con, 2);
		tx.commit();
		txControl.setVoidCallable(1);
		session2.isConnected();
		session2Control.setReturnValue(true, 1);
		con.isReadOnly();
		conControl.setReturnValue(false, 1);
		session2.setFlushMode(FlushMode.NEVER);
		session2Control.setVoidCallable(1);

		sf2Control.replay();
		session2Control.replay();
		txControl.replay();
		conControl.replay();

		MockServletContext sc = new MockServletContext();
		StaticWebApplicationContext wac = new StaticWebApplicationContext();
		wac.setServletContext(sc);
		wac.getDefaultListableBeanFactory().registerSingleton("sessionFactory", sf);
		wac.getDefaultListableBeanFactory().registerSingleton("mySessionFactory", sf2);
		wac.refresh();
		sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
		MockHttpServletRequest request = new MockHttpServletRequest(sc);
		MockHttpServletResponse response = new MockHttpServletResponse();

		MockFilterConfig filterConfig = new MockFilterConfig(wac.getServletContext(), "filter");
		MockFilterConfig filterConfig2 = new MockFilterConfig(wac.getServletContext(), "filter2");
		filterConfig.addInitParameter("singleSession", "false");
		filterConfig2.addInitParameter("singleSession", "false");
		filterConfig2.addInitParameter("sessionFactoryBeanName", "mySessionFactory");

		final OpenSessionInViewFilter filter = new OpenSessionInViewFilter();
		filter.init(filterConfig);
		final OpenSessionInViewFilter filter2 = new OpenSessionInViewFilter();
		filter2.init(filterConfig2);

		final FilterChain filterChain = new FilterChain() {
			public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) {
				HibernateTransactionManager tm = new HibernateTransactionManager(sf);
				TransactionStatus ts = tm.getTransaction(
						new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_SUPPORTS));
				org.hibernate.Session sess = SessionFactoryUtils.getSession(sf, true);
				SessionFactoryUtils.releaseSession(sess, sf);
				tm.commit(ts);

				sessionControl.verify();
				sessionControl.reset();

				session.close();
				sessionControl.setReturnValue(null, 1);
				sessionControl.replay();

				servletRequest.setAttribute("invoked", Boolean.TRUE);
			}
		};

		final FilterChain filterChain2 = new FilterChain() {
			public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
			    throws IOException, ServletException {

				HibernateTransactionManager tm = new HibernateTransactionManager(sf2);
				TransactionStatus ts = tm.getTransaction(new DefaultTransactionDefinition());
				tm.commit(ts);

				session2Control.verify();
				session2Control.reset();

				session2.close();
				session2Control.setReturnValue(null, 1);
				session2Control.replay();

				filter.doFilter(servletRequest, servletResponse, filterChain);
			}
		};

		FilterChain filterChain3 = new PassThroughFilterChain(filter2, filterChain2);

		filter2.doFilter(request, response, filterChain3);
		assertNotNull(request.getAttribute("invoked"));

		sfControl.verify();
		sessionControl.verify();
		sf2Control.verify();
		session2Control.verify();
		txControl.verify();
		conControl.verify();

		wac.close();
	}

	public void testOpenSessionInViewFilterWithDeferredCloseAndAlreadyActiveDeferredClose() throws Exception {
		MockControl sfControl = MockControl.createControl(SessionFactory.class);
		final SessionFactory sf = (SessionFactory) sfControl.getMock();
		final MockControl sessionControl = MockControl.createControl(Session.class);
		final Session session = (Session) sessionControl.getMock();

		sf.openSession();
		sfControl.setReturnValue(session, 1);
		session.getSessionFactory();
		sessionControl.setReturnValue(sf);
		session.getFlushMode();
		sessionControl.setReturnValue(FlushMode.NEVER, 1);
		session.setFlushMode(FlushMode.NEVER);
		sessionControl.setVoidCallable(1);
		sfControl.replay();
		sessionControl.replay();

		MockServletContext sc = new MockServletContext();
		StaticWebApplicationContext wac = new StaticWebApplicationContext();
		wac.setServletContext(sc);
		wac.getDefaultListableBeanFactory().registerSingleton("sessionFactory", sf);
		wac.refresh();
		sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
		MockHttpServletRequest request = new MockHttpServletRequest(sc);
		MockHttpServletResponse response = new MockHttpServletResponse();

		MockFilterConfig filterConfig = new MockFilterConfig(wac.getServletContext(), "filter");
		MockFilterConfig filterConfig2 = new MockFilterConfig(wac.getServletContext(), "filter2");
		filterConfig.addInitParameter("singleSession", "false");
		filterConfig2.addInitParameter("singleSession", "false");
		filterConfig2.addInitParameter("sessionFactoryBeanName", "mySessionFactory");

		OpenSessionInViewInterceptor rawInterceptor = new OpenSessionInViewInterceptor();
		rawInterceptor.setSessionFactory(sf);
		rawInterceptor.setSingleSession(false);
		HandlerInterceptor interceptor = new WebRequestHandlerInterceptorAdapter(rawInterceptor);

		interceptor.preHandle(request, response, "handler");

		final OpenSessionInViewFilter filter = new OpenSessionInViewFilter();
		filter.init(filterConfig);
		final OpenSessionInViewFilter filter2 = new OpenSessionInViewFilter();
		filter2.init(filterConfig2);

		final FilterChain filterChain = new FilterChain() {
			public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) {
				HibernateTransactionManager tm = new HibernateTransactionManager(sf);
				TransactionStatus ts = tm.getTransaction(
						new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_SUPPORTS));
				org.hibernate.Session sess = SessionFactoryUtils.getSession(sf, true);
				SessionFactoryUtils.releaseSession(sess, sf);
				tm.commit(ts);

				sessionControl.verify();
				sessionControl.reset();
				try {
					session.close();
				}
				catch (HibernateException ex) {
				}
				sessionControl.setReturnValue(null, 1);
				sessionControl.replay();

				servletRequest.setAttribute("invoked", Boolean.TRUE);
			}
		};

		FilterChain filterChain2 = new FilterChain() {
			public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
			    throws IOException, ServletException {
				filter.doFilter(servletRequest, servletResponse, filterChain);
			}
		};

		filter.doFilter(request, response, filterChain2);
		assertNotNull(request.getAttribute("invoked"));

		interceptor.postHandle(request, response, "handler", null);
		interceptor.afterCompletion(request, response, "handler", null);

		sfControl.verify();
		sessionControl.verify();

		wac.close();
	}

}
