Skip to content

Commit

Permalink
Polish (major) MVC async processing interceptors
Browse files Browse the repository at this point in the history
New afterTimeout and afterCompletion callbacks

afterTimeout can provide a concurrent result to be used instead of the
one that could not be set or returned on time

Interceptor exceptions cause async processing to resume treating the
exception as the concurrent result

Adapter classes for convenient implementation of the interfaces

Issue: SPR-9914
  • Loading branch information
rstoyanchev committed Oct 26, 2012
1 parent 06e34f0 commit f036ed6
Showing 23 changed files with 846 additions and 319 deletions.
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
@@ -318,7 +318,7 @@ private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, Str
/**
* Bind and unbind the Hibernate {@code Session} to the current thread.
*/
private static class SessionBindingCallableInterceptor implements CallableProcessingInterceptor {
private static class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {

private final SessionFactory sessionFactory;

@@ -329,16 +329,18 @@ public SessionBindingCallableInterceptor(SessionFactory sessionFactory, SessionH
this.sessionHolder = sessionHolder;
}

public void preProcess(NativeWebRequest request, Callable<?> task) {
@Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}

private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}

public void postProcess(NativeWebRequest request, Callable<?> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
}

Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@
import org.springframework.web.context.request.AsyncWebRequestInterceptor;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;

@@ -276,24 +276,26 @@ private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, Str
/**
* Bind and unbind the Hibernate {@code Session} to the current thread.
*/
private class SessionBindingCallableInterceptor implements CallableProcessingInterceptor {
private class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {

private final SessionHolder sessionHolder;

public SessionBindingCallableInterceptor(SessionHolder sessionHolder) {
this.sessionHolder = sessionHolder;
}

public void preProcess(NativeWebRequest request, Callable<?> task) {
@Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}

private void initializeThread() {
TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}

public void postProcess(NativeWebRequest request, Callable<?> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
private void initializeThread() {
TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
}
}
}
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
@@ -215,7 +215,7 @@ private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, Str
/**
* Bind and unbind the Hibernate {@code Session} to the current thread.
*/
private static class SessionBindingCallableInterceptor implements CallableProcessingInterceptor {
private static class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {

private final SessionFactory sessionFactory;

@@ -226,16 +226,18 @@ public SessionBindingCallableInterceptor(SessionFactory sessionFactory, SessionH
this.sessionHolder = sessionHolder;
}

public void preProcess(NativeWebRequest request, Callable<?> task) {
@Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}

private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}

public void postProcess(NativeWebRequest request, Callable<?> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
}
}
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@
import org.springframework.web.context.request.AsyncWebRequestInterceptor;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;

@@ -215,24 +215,26 @@ private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, Str
/**
* Bind and unbind the Hibernate {@code Session} to the current thread.
*/
private class SessionBindingCallableInterceptor implements CallableProcessingInterceptor {
private class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {

private final SessionHolder sessionHolder;

public SessionBindingCallableInterceptor(SessionHolder sessionHolder) {
this.sessionHolder = sessionHolder;
}

public void preProcess(NativeWebRequest request, Callable<?> task) {
@Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}

private void initializeThread() {
TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}

public void postProcess(NativeWebRequest request, Callable<?> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
private void initializeThread() {
TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@@ -34,7 +34,7 @@
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
@@ -242,7 +242,7 @@ private boolean applyEntityManagerBindingInterceptor(WebAsyncManager asyncManage
/**
* Bind and unbind the {@code EntityManager} to the current thread.
*/
private static class EntityManagerBindingCallableInterceptor implements CallableProcessingInterceptor {
private static class EntityManagerBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {

private final EntityManagerFactory emFactory;

@@ -254,16 +254,18 @@ public EntityManagerBindingCallableInterceptor(EntityManagerFactory emFactory, E
this.emHolder = emHolder;
}

public void preProcess(NativeWebRequest request, Callable<?> task) {
@Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}

private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.emFactory, this.emHolder);
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.emFactory);
}

public void postProcess(NativeWebRequest request, Callable<?> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.emFactory);
private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.emFactory, this.emHolder);
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2012 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.
@@ -31,7 +31,7 @@
import org.springframework.web.context.request.AsyncWebRequestInterceptor;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;

@@ -163,7 +163,7 @@ private boolean applyCallableInterceptor(WebAsyncManager asyncManager, String ke
/**
* Bind and unbind the Hibernate {@code Session} to the current thread.
*/
private class EntityManagerBindingCallableInterceptor implements CallableProcessingInterceptor {
private class EntityManagerBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {

private final EntityManagerHolder emHolder;

@@ -172,16 +172,18 @@ public EntityManagerBindingCallableInterceptor(EntityManagerHolder emHolder) {
this.emHolder = emHolder;
}

public void preProcess(NativeWebRequest request, Callable<?> task) {
@Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}

private void initializeThread() {
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), this.emHolder);
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
}

public void postProcess(NativeWebRequest request, Callable<?> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
private void initializeThread() {
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), this.emHolder);
}
}

Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@

package org.springframework.orm.hibernate3.support;

import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.*;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
@@ -39,6 +39,7 @@
import javax.servlet.ServletResponse;
import javax.transaction.TransactionManager;

import org.easymock.EasyMock;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
@@ -63,8 +64,8 @@
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.async.AsyncWebRequest;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.StaticWebApplicationContext;


@@ -177,6 +178,8 @@ public void testOpenSessionInViewInterceptorAsyncScenario() throws Exception {

AsyncWebRequest asyncWebRequest = createStrictMock(AsyncWebRequest.class);
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
asyncWebRequest.setTimeoutHandler((Runnable) anyObject());
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
asyncWebRequest.startAsync();
replay(asyncWebRequest);

@@ -491,6 +494,8 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo

AsyncWebRequest asyncWebRequest = createMock(AsyncWebRequest.class);
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
asyncWebRequest.setTimeoutHandler(EasyMock.<Runnable>anyObject());
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
asyncWebRequest.startAsync();
expect(asyncWebRequest.isAsyncStarted()).andReturn(true).anyTimes();
replay(asyncWebRequest);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@@ -155,6 +155,8 @@ public void testOpenEntityManagerInViewInterceptorAsyncScenario() throws Excepti

AsyncWebRequest asyncWebRequest = createStrictMock(AsyncWebRequest.class);
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
asyncWebRequest.setTimeoutHandler((Runnable) anyObject());
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
asyncWebRequest.startAsync();
replay(asyncWebRequest);

@@ -344,6 +346,8 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo

AsyncWebRequest asyncWebRequest = createMock(AsyncWebRequest.class);
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
asyncWebRequest.setTimeoutHandler((Runnable) anyObject());
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
asyncWebRequest.startAsync();
expect(asyncWebRequest.isAsyncStarted()).andReturn(true).anyTimes();
replay(asyncWebRequest);
Original file line number Diff line number Diff line change
@@ -27,9 +27,9 @@

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.servlet.DispatcherServlet;
@@ -48,6 +48,8 @@
@SuppressWarnings("serial")
final class TestDispatcherServlet extends DispatcherServlet {

private static final String KEY = TestDispatcherServlet.class.getName() + "-interceptor";

/**
* Create a new instance with the given web application context.
*/
@@ -70,19 +72,15 @@ private CountDownLatch registerAsyncInterceptors(HttpServletRequest request) {

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

asyncManager.registerCallableInterceptor("mockmvc", new CallableProcessingInterceptor() {
public void preProcess(NativeWebRequest request, Callable<?> task) throws Exception { }
public void postProcess(NativeWebRequest request, Callable<?> task, Object value) throws Exception {
asyncManager.registerCallableInterceptor(KEY, new CallableProcessingInterceptorAdapter() {
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object value) throws Exception {
asyncResultLatch.countDown();
}
});

asyncManager.registerDeferredResultInterceptor("mockmvc", new DeferredResultProcessingInterceptor() {
public void preProcess(NativeWebRequest request, DeferredResult<?> result) throws Exception { }
public void postProcess(NativeWebRequest request, DeferredResult<?> result, Object value) throws Exception {
asyncManager.registerDeferredResultInterceptor(KEY, new DeferredResultProcessingInterceptorAdapter() {
public <T> void postProcess(NativeWebRequest request, DeferredResult<T> result, Object value) throws Exception {
asyncResultLatch.countDown();
}
public void afterExpiration(NativeWebRequest request, DeferredResult<?> result) throws Exception { }
});

return asyncResultLatch;
Loading

0 comments on commit f036ed6

Please sign in to comment.