001/**
002 * Copyright 2005-2018 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.web.controller;
017
018import org.springframework.beans.factory.annotation.Autowired;
019import org.springframework.beans.factory.annotation.Qualifier;
020import org.springframework.transaction.PlatformTransactionManager;
021import org.springframework.transaction.TransactionDefinition;
022import org.springframework.transaction.TransactionStatus;
023import org.springframework.transaction.support.DefaultTransactionDefinition;
024import org.springframework.web.servlet.HandlerInterceptor;
025import org.springframework.web.servlet.ModelAndView;
026
027import javax.servlet.http.HttpServletRequest;
028import javax.servlet.http.HttpServletResponse;
029
030/**
031 * Spring interceptor class that will start the Rice configured transaction on pre handle (before binding
032 * and controller) and commit after controller execution.
033 * <p/>
034 * <p>For KRAD, this interceptor should be listed first
035 * (before {@link org.kuali.rice.krad.web.controller.UifControllerHandlerInterceptor})</p>
036 *
037 * @author Kuali Rice Team (rice.collab@kuali.org)
038 * @see org.kuali.rice.core.framework.persistence.jta.Jta
039 */
040public class TransactionHandlerInterceptor implements HandlerInterceptor {
041
042    private static final ThreadLocal<TransactionStatus> context = new ThreadLocal<TransactionStatus>();
043
044    @Autowired()
045    @Qualifier("transactionManager")
046    PlatformTransactionManager txManager;
047
048    /**
049     * {@inheritDoc}
050     */
051    @Override
052    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
053            throws Exception {
054        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
055        def.setName("request");
056        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
057
058        TransactionStatus status = txManager.getTransaction(def);
059        context.set(status);
060
061        return true;
062    }
063
064    /**
065     * {@inheritDoc}
066     */
067    @Override
068    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
069            ModelAndView modelAndView) throws Exception {
070        completeTransaction(null);
071    }
072
073    /**
074     * {@inheritDoc}
075     */
076    @Override
077    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
078            throws Exception {
079        completeTransaction(ex);
080    }
081
082    /**
083     * Completes the request transaction if needed.
084     *
085     * @param ex any exception that might have been thrown, will cause a rollback
086     */
087    protected void completeTransaction(Exception ex) {
088        TransactionStatus status = context.get();
089
090        if (status == null) {
091            return;
092        }
093
094        try {
095            if (!status.isCompleted()) {
096                if (ex == null && !status.isRollbackOnly()) {
097                    txManager.commit(status);
098                } else {
099                    txManager.rollback(status);
100                }
101            }
102        } finally {
103            context.remove();
104        }
105    }
106}