001
002 /*
003 * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004 *
005 * $Id: OpenTransactionInViewFilter.java 2 2011-02-05 21:51:43Z archie.cobbs $
006 */
007
008 package org.dellroad.stuff.servlet;
009
010 import java.io.IOException;
011
012 import javax.servlet.FilterChain;
013 import javax.servlet.ServletException;
014 import javax.servlet.http.HttpServletRequest;
015 import javax.servlet.http.HttpServletResponse;
016
017 import org.slf4j.Logger;
018 import org.slf4j.LoggerFactory;
019 import org.springframework.transaction.PlatformTransactionManager;
020 import org.springframework.transaction.TransactionStatus;
021 import org.springframework.transaction.support.DefaultTransactionDefinition;
022 import org.springframework.transaction.support.TransactionCallback;
023 import org.springframework.transaction.support.TransactionTemplate;
024 import org.springframework.web.context.WebApplicationContext;
025 import org.springframework.web.context.support.WebApplicationContextUtils;
026 import org.springframework.web.filter.OncePerRequestFilter;
027
028 /**
029 * Servlet filter that wraps execution in a transaction. A {@link TransactionTemplate} must exist in the
030 * associated {@link WebApplicationContext} and is identified by name via {@link #setTransactionManagerBeanName
031 * setTransactionManagerBeanName()} (by default, {@link #DEFAULT_TRANSACTION_MANAGER_BEAN_NAME}).
032 *
033 * <p>
034 * Transaction properties are configurable via filter <code><init-param></code>'s {@code isolation},
035 * {@code propagation}, and {@code readOnly}.
036 * </p>
037 */
038 public class OpenTransactionInViewFilter extends OncePerRequestFilter {
039
040 public static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
041
042 protected final Logger log = LoggerFactory.getLogger(getClass());
043
044 private final DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
045
046 private WebApplicationContext webApplicationContext;
047 private String transactionManagerBeanName = DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
048 private PlatformTransactionManager transactionManager;
049
050 @Override
051 protected void initFilterBean() throws ServletException {
052 super.initFilterBean();
053 log.debug("finding containing WebApplicationContext");
054 try {
055 this.webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
056 } catch (IllegalStateException e) {
057 throw new ServletException("could not locate containing WebApplicationContext");
058 }
059 }
060
061 public void setIsolation(String isolation) {
062 this.transactionDefinition.setIsolationLevelName(isolation);
063 }
064
065 public void setPropagation(String propagation) {
066 this.transactionDefinition.setPropagationBehaviorName(propagation);
067 }
068
069 public void setReadOnly(boolean readOnly) {
070 this.transactionDefinition.setReadOnly(readOnly);
071 }
072
073 public void setTransactionManagerBeanName(String transactionManagerBeanName) {
074 this.transactionManagerBeanName = transactionManagerBeanName;
075 }
076
077 protected synchronized PlatformTransactionManager getTransactionManager() {
078 if (this.transactionManager == null) {
079 this.transactionManager = this.webApplicationContext.getBean(
080 this.transactionManagerBeanName, PlatformTransactionManager.class);
081 }
082 return this.transactionManager;
083 }
084
085 @Override
086 protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
087 final FilterChain filterChain) throws ServletException, IOException {
088 TransactionTemplate transactionTemplate = new TransactionTemplate(getTransactionManager(), this.transactionDefinition);
089 try {
090 transactionTemplate.execute(new TransactionCallback<Void>() {
091
092 @Override
093 public Void doInTransaction(TransactionStatus status) {
094 try {
095 filterChain.doFilter(request, response);
096 } catch (IOException e) {
097 throw new RuntimeException(e);
098 } catch (ServletException e) {
099 throw new RuntimeException(e);
100 }
101 return null;
102 }
103 });
104 } catch (RuntimeException e) {
105 Throwable nested = e.getCause();
106 if (nested instanceof IOException)
107 throw (IOException)nested;
108 if (nested instanceof ServletException)
109 throw (ServletException)nested;
110 throw e;
111 }
112 }
113 }
114