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}