001/*
002 * Copyright 2002-2013 the original author or authors.
003 *
004 * Licensed under the Apache 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 *      https://www.apache.org/licenses/LICENSE-2.0
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 */
016
017package org.springframework.orm.jdo.support;
018
019import javax.jdo.PersistenceManager;
020import javax.jdo.PersistenceManagerFactory;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.dao.DataAccessException;
026import org.springframework.orm.jdo.PersistenceManagerFactoryUtils;
027import org.springframework.orm.jdo.PersistenceManagerHolder;
028import org.springframework.transaction.support.TransactionSynchronizationManager;
029import org.springframework.ui.ModelMap;
030import org.springframework.web.context.request.WebRequest;
031import org.springframework.web.context.request.WebRequestInterceptor;
032
033/**
034 * Spring web request interceptor that binds a JDO PersistenceManager to the
035 * thread for the entire processing of the request. Intended for the "Open
036 * PersistenceManager in View" pattern, i.e. to allow for lazy loading in
037 * web views despite the original transactions already being completed.
038 *
039 * <p>This interceptor makes JDO PersistenceManagers available via the current thread,
040 * which will be autodetected by transaction managers. It is suitable for service
041 * layer transactions via {@link org.springframework.orm.jdo.JdoTransactionManager}
042 * or {@link org.springframework.transaction.jta.JtaTransactionManager} as well
043 * as for non-transactional read-only execution.
044 *
045 * <p>In contrast to {@link OpenPersistenceManagerInViewFilter}, this interceptor
046 * is set up in a Spring application context and can thus take advantage of
047 * bean wiring.
048 *
049 * @author Juergen Hoeller
050 * @since 1.1
051 * @see OpenPersistenceManagerInViewFilter
052 * @see org.springframework.orm.jdo.JdoTransactionManager
053 * @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager
054 * @see org.springframework.transaction.support.TransactionSynchronizationManager
055 */
056public class OpenPersistenceManagerInViewInterceptor implements WebRequestInterceptor {
057
058        /**
059         * Suffix that gets appended to the PersistenceManagerFactory toString
060         * representation for the "participate in existing persistence manager
061         * handling" request attribute.
062         * @see #getParticipateAttributeName
063         */
064        public static final String PARTICIPATE_SUFFIX = ".PARTICIPATE";
065
066
067        protected final Log logger = LogFactory.getLog(getClass());
068
069        private PersistenceManagerFactory persistenceManagerFactory;
070
071
072        /**
073         * Set the JDO PersistenceManagerFactory that should be used to create
074         * PersistenceManagers.
075         */
076        public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
077                this.persistenceManagerFactory = pmf;
078        }
079
080        /**
081         * Return the JDO PersistenceManagerFactory that should be used to create
082         * PersistenceManagers.
083         */
084        public PersistenceManagerFactory getPersistenceManagerFactory() {
085                return persistenceManagerFactory;
086        }
087
088
089        @Override
090        public void preHandle(WebRequest request) throws DataAccessException {
091                if (TransactionSynchronizationManager.hasResource(getPersistenceManagerFactory())) {
092                        // Do not modify the PersistenceManager: just mark the request accordingly.
093                        String participateAttributeName = getParticipateAttributeName();
094                        Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
095                        int newCount = (count != null ? count + 1 : 1);
096                        request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
097                }
098                else {
099                        logger.debug("Opening JDO PersistenceManager in OpenPersistenceManagerInViewInterceptor");
100                        PersistenceManager pm =
101                                        PersistenceManagerFactoryUtils.getPersistenceManager(getPersistenceManagerFactory(), true);
102                        TransactionSynchronizationManager.bindResource(
103                                        getPersistenceManagerFactory(), new PersistenceManagerHolder(pm));
104                }
105        }
106
107        @Override
108        public void postHandle(WebRequest request, ModelMap model) {
109        }
110
111        @Override
112        public void afterCompletion(WebRequest request, Exception ex) throws DataAccessException {
113                String participateAttributeName = getParticipateAttributeName();
114                Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
115                if (count != null) {
116                        // Do not modify the PersistenceManager: just clear the marker.
117                        if (count > 1) {
118                                request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
119                        }
120                        else {
121                                request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
122                        }
123                }
124                else {
125                        PersistenceManagerHolder pmHolder = (PersistenceManagerHolder)
126                                        TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory());
127                        logger.debug("Closing JDO PersistenceManager in OpenPersistenceManagerInViewInterceptor");
128                        PersistenceManagerFactoryUtils.releasePersistenceManager(
129                                        pmHolder.getPersistenceManager(), getPersistenceManagerFactory());
130                }
131        }
132
133        /**
134         * Return the name of the request attribute that identifies that a request is
135         * already filtered. Default implementation takes the toString representation
136         * of the PersistenceManagerFactory instance and appends ".FILTERED".
137         * @see #PARTICIPATE_SUFFIX
138         */
139        protected String getParticipateAttributeName() {
140                return getPersistenceManagerFactory().toString() + PARTICIPATE_SUFFIX;
141        }
142
143}