001/*
002 * Copyright 2002-2015 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 java.io.IOException;
020import javax.jdo.PersistenceManager;
021import javax.jdo.PersistenceManagerFactory;
022import javax.servlet.FilterChain;
023import javax.servlet.ServletException;
024import javax.servlet.http.HttpServletRequest;
025import javax.servlet.http.HttpServletResponse;
026
027import org.springframework.orm.jdo.PersistenceManagerFactoryUtils;
028import org.springframework.orm.jdo.PersistenceManagerHolder;
029import org.springframework.transaction.support.TransactionSynchronizationManager;
030import org.springframework.web.context.WebApplicationContext;
031import org.springframework.web.context.support.WebApplicationContextUtils;
032import org.springframework.web.filter.OncePerRequestFilter;
033
034/**
035 * Servlet Filter that binds a JDO PersistenceManager to the thread for the
036 * entire processing of the request. Intended for the "Open PersistenceManager in
037 * View" pattern, i.e. to allow for lazy loading in web views despite the
038 * original transactions already being completed.
039 *
040 * <p>This filter makes JDO PersistenceManagers available via the current thread,
041 * which will be autodetected by transaction managers. It is suitable for service
042 * layer transactions via {@link org.springframework.orm.jdo.JdoTransactionManager}
043 * or {@link org.springframework.transaction.jta.JtaTransactionManager} as well
044 * as for non-transactional read-only execution.
045 *
046 * <p>Looks up the PersistenceManagerFactory in Spring's root web application context.
047 * Supports a "persistenceManagerFactoryBeanName" filter init-param in {@code web.xml};
048 * the default bean name is "persistenceManagerFactory".
049 *
050 * @author Juergen Hoeller
051 * @since 1.1
052 * @see OpenPersistenceManagerInViewInterceptor
053 * @see org.springframework.orm.jdo.JdoTransactionManager
054 * @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager
055 * @see org.springframework.transaction.support.TransactionSynchronizationManager
056 */
057public class OpenPersistenceManagerInViewFilter extends OncePerRequestFilter {
058
059        public static final String DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME = "persistenceManagerFactory";
060
061        private String persistenceManagerFactoryBeanName = DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME;
062
063
064        /**
065         * Set the bean name of the PersistenceManagerFactory to fetch from Spring's
066         * root application context. Default is "persistenceManagerFactory".
067         * @see #DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME
068         */
069        public void setPersistenceManagerFactoryBeanName(String persistenceManagerFactoryBeanName) {
070                this.persistenceManagerFactoryBeanName = persistenceManagerFactoryBeanName;
071        }
072
073        /**
074         * Return the bean name of the PersistenceManagerFactory to fetch from Spring's
075         * root application context.
076         */
077        protected String getPersistenceManagerFactoryBeanName() {
078                return this.persistenceManagerFactoryBeanName;
079        }
080
081
082        /**
083         * Returns "false" so that the filter may re-bind the opened {@code PersistenceManager}
084         * to each asynchronously dispatched thread and postpone closing it until the very
085         * last asynchronous dispatch.
086         */
087        @Override
088        protected boolean shouldNotFilterAsyncDispatch() {
089                return false;
090        }
091
092        /**
093         * Returns "false" so that the filter may provide an {@code PersistenceManager}
094         * to each error dispatches.
095         */
096        @Override
097        protected boolean shouldNotFilterErrorDispatch() {
098                return false;
099        }
100
101        @Override
102        protected void doFilterInternal(
103                        HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
104                        throws ServletException, IOException {
105
106                PersistenceManagerFactory pmf = lookupPersistenceManagerFactory(request);
107                boolean participate = false;
108
109                if (TransactionSynchronizationManager.hasResource(pmf)) {
110                        // Do not modify the PersistenceManager: just set the participate flag.
111                        participate = true;
112                }
113                else {
114                        logger.debug("Opening JDO PersistenceManager in OpenPersistenceManagerInViewFilter");
115                        PersistenceManager pm = PersistenceManagerFactoryUtils.getPersistenceManager(pmf, true);
116                        TransactionSynchronizationManager.bindResource(pmf, new PersistenceManagerHolder(pm));
117                }
118
119                try {
120                        filterChain.doFilter(request, response);
121                }
122
123                finally {
124                        if (!participate) {
125                                PersistenceManagerHolder pmHolder = (PersistenceManagerHolder)
126                                                TransactionSynchronizationManager.unbindResource(pmf);
127                                logger.debug("Closing JDO PersistenceManager in OpenPersistenceManagerInViewFilter");
128                                PersistenceManagerFactoryUtils.releasePersistenceManager(pmHolder.getPersistenceManager(), pmf);
129                        }
130                }
131        }
132
133        /**
134         * Look up the PersistenceManagerFactory that this filter should use,
135         * taking the current HTTP request as argument.
136         * <p>Default implementation delegates to the {@code lookupPersistenceManagerFactory}
137         * without arguments.
138         * @return the PersistenceManagerFactory to use
139         * @see #lookupPersistenceManagerFactory()
140         */
141        protected PersistenceManagerFactory lookupPersistenceManagerFactory(HttpServletRequest request) {
142                return lookupPersistenceManagerFactory();
143        }
144
145        /**
146         * Look up the PersistenceManagerFactory that this filter should use.
147         * The default implementation looks for a bean with the specified name
148         * in Spring's root application context.
149         * @return the PersistenceManagerFactory to use
150         * @see #getPersistenceManagerFactoryBeanName
151         */
152        protected PersistenceManagerFactory lookupPersistenceManagerFactory() {
153                if (logger.isDebugEnabled()) {
154                        logger.debug("Using PersistenceManagerFactory '" + getPersistenceManagerFactoryBeanName() +
155                                        "' for OpenPersistenceManagerInViewFilter");
156                }
157                WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
158                return wac.getBean(getPersistenceManagerFactoryBeanName(), PersistenceManagerFactory.class);
159        }
160
161}