001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019package org.apache.isis.persistence.jdo.spring.support;
020
021import javax.jdo.PersistenceManager;
022import javax.jdo.PersistenceManagerFactory;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026
027import org.springframework.dao.DataAccessException;
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
033import org.apache.isis.persistence.jdo.spring.integration.PersistenceManagerFactoryUtils;
034import org.apache.isis.persistence.jdo.spring.integration.PersistenceManagerHolder;
035
036/**
037 * Spring web request interceptor that binds a JDO PersistenceManager to the
038 * thread for the entire processing of the request. Intended for the "Open
039 * PersistenceManager in View" pattern, i.e. to allow for lazy loading in
040 * web views despite the original transactions already being completed.
041 *
042 * <p>This interceptor makes JDO PersistenceManagers available via the current thread,
043 * which will be autodetected by transaction managers. It is suitable for service
044 * layer transactions via {@link org.apache.isis.persistence.jdo.spring.integration.JdoTransactionManager}
045 * or {@link org.springframework.transaction.jta.JtaTransactionManager} as well
046 * as for non-transactional read-only execution.
047 *
048 * <p>In contrast to {@link OpenPersistenceManagerInViewFilter}, this interceptor
049 * is set up in a Spring application context and can thus take advantage of
050 * bean wiring.
051 *
052 * @see OpenPersistenceManagerInViewFilter
053 * @see org.apache.isis.persistence.jdo.spring.integration.JdoTransactionManager
054 * @see org.apache.isis.persistence.jdo.spring.integration.PersistenceManagerFactoryUtils#getPersistenceManager
055 * @see org.springframework.transaction.support.TransactionSynchronizationManager
056 */
057public class OpenPersistenceManagerInViewInterceptor implements WebRequestInterceptor {
058
059        /**
060         * Suffix that gets appended to the PersistenceManagerFactory toString
061         * representation for the "participate in existing persistence manager
062         * handling" request attribute.
063         * @see #getParticipateAttributeName
064         */
065        public static final String PARTICIPATE_SUFFIX = ".PARTICIPATE";
066
067
068        protected final Log logger = LogFactory.getLog(getClass());
069
070        private PersistenceManagerFactory persistenceManagerFactory;
071
072
073        /**
074         * Set the JDO PersistenceManagerFactory that should be used to create
075         * PersistenceManagers.
076         */
077        public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
078                this.persistenceManagerFactory = pmf;
079        }
080
081        /**
082         * Return the JDO PersistenceManagerFactory that should be used to create
083         * PersistenceManagers.
084         */
085        public PersistenceManagerFactory getPersistenceManagerFactory() {
086                return persistenceManagerFactory;
087        }
088
089
090        @Override
091        public void preHandle(WebRequest request) throws DataAccessException {
092                if (TransactionSynchronizationManager.hasResource(getPersistenceManagerFactory())) {
093                        // Do not modify the PersistenceManager: just mark the request accordingly.
094                        String participateAttributeName = getParticipateAttributeName();
095                        Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
096                        int newCount = (count != null ? count + 1 : 1);
097                        request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
098                }
099                else {
100                        logger.debug("Opening JDO PersistenceManager in OpenPersistenceManagerInViewInterceptor");
101                        PersistenceManager pm =
102                                        PersistenceManagerFactoryUtils.getPersistenceManager(getPersistenceManagerFactory(), true);
103                        TransactionSynchronizationManager.bindResource(
104                                        getPersistenceManagerFactory(), new PersistenceManagerHolder(pm));
105                }
106        }
107
108        @Override
109        public void postHandle(WebRequest request, ModelMap model) {
110        }
111
112        @Override
113        public void afterCompletion(WebRequest request, Exception ex) throws DataAccessException {
114                String participateAttributeName = getParticipateAttributeName();
115                Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
116                if (count != null) {
117                        // Do not modify the PersistenceManager: just clear the marker.
118                        if (count > 1) {
119                                request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
120                        }
121                        else {
122                                request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
123                        }
124                }
125                else {
126                        PersistenceManagerHolder pmHolder = (PersistenceManagerHolder)
127                                        TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory());
128                        logger.debug("Closing JDO PersistenceManager in OpenPersistenceManagerInViewInterceptor");
129                        PersistenceManagerFactoryUtils.releasePersistenceManager(
130                                        pmHolder.getPersistenceManager(), getPersistenceManagerFactory());
131                }
132        }
133
134        /**
135         * Return the name of the request attribute that identifies that a request is
136         * already filtered. Default implementation takes the toString representation
137         * of the PersistenceManagerFactory instance and appends ".FILTERED".
138         * @see #PARTICIPATE_SUFFIX
139         */
140        protected String getParticipateAttributeName() {
141                return getPersistenceManagerFactory().toString() + PARTICIPATE_SUFFIX;
142        }
143
144}