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.jpa.support; 018 019import java.io.IOException; 020import javax.persistence.EntityManager; 021import javax.persistence.EntityManagerFactory; 022import javax.persistence.PersistenceException; 023import javax.servlet.FilterChain; 024import javax.servlet.ServletException; 025import javax.servlet.http.HttpServletRequest; 026import javax.servlet.http.HttpServletResponse; 027 028import org.springframework.dao.DataAccessResourceFailureException; 029import org.springframework.orm.jpa.EntityManagerFactoryUtils; 030import org.springframework.orm.jpa.EntityManagerHolder; 031import org.springframework.transaction.support.TransactionSynchronizationManager; 032import org.springframework.util.StringUtils; 033import org.springframework.web.context.WebApplicationContext; 034import org.springframework.web.context.request.async.WebAsyncManager; 035import org.springframework.web.context.request.async.WebAsyncUtils; 036import org.springframework.web.context.support.WebApplicationContextUtils; 037import org.springframework.web.filter.OncePerRequestFilter; 038 039/** 040 * Servlet Filter that binds a JPA EntityManager to the thread for the 041 * entire processing of the request. Intended for the "Open EntityManager in 042 * View" pattern, i.e. to allow for lazy loading in web views despite the 043 * original transactions already being completed. 044 * 045 * <p>This filter makes JPA EntityManagers available via the current thread, 046 * which will be autodetected by transaction managers. It is suitable for service 047 * layer transactions via {@link org.springframework.orm.jpa.JpaTransactionManager} 048 * or {@link org.springframework.transaction.jta.JtaTransactionManager} as well 049 * as for non-transactional read-only execution. 050 * 051 * <p>Looks up the EntityManagerFactory in Spring's root web application context. 052 * Supports an "entityManagerFactoryBeanName" filter init-param in {@code web.xml}; 053 * the default bean name is "entityManagerFactory". As an alternative, the 054 * "persistenceUnitName" init-param allows for retrieval by logical unit name 055 * (as specified in {@code persistence.xml}). 056 * 057 * @author Juergen Hoeller 058 * @since 2.0 059 * @see OpenEntityManagerInViewInterceptor 060 * @see org.springframework.orm.jpa.JpaTransactionManager 061 * @see org.springframework.orm.jpa.SharedEntityManagerCreator 062 * @see org.springframework.transaction.support.TransactionSynchronizationManager 063 */ 064public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { 065 066 /** 067 * Default EntityManagerFactory bean name: "entityManagerFactory". 068 * Only applies when no "persistenceUnitName" param has been specified. 069 * @see #setEntityManagerFactoryBeanName 070 * @see #setPersistenceUnitName 071 */ 072 public static final String DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME = "entityManagerFactory"; 073 074 075 private String entityManagerFactoryBeanName; 076 077 private String persistenceUnitName; 078 079 private volatile EntityManagerFactory entityManagerFactory; 080 081 082 /** 083 * Set the bean name of the EntityManagerFactory to fetch from Spring's 084 * root application context. 085 * <p>Default is "entityManagerFactory". Note that this default only applies 086 * when no "persistenceUnitName" param has been specified. 087 * @see #setPersistenceUnitName 088 * @see #DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME 089 */ 090 public void setEntityManagerFactoryBeanName(String entityManagerFactoryBeanName) { 091 this.entityManagerFactoryBeanName = entityManagerFactoryBeanName; 092 } 093 094 /** 095 * Return the bean name of the EntityManagerFactory to fetch from Spring's 096 * root application context. 097 */ 098 protected String getEntityManagerFactoryBeanName() { 099 return this.entityManagerFactoryBeanName; 100 } 101 102 /** 103 * Set the name of the persistence unit to access the EntityManagerFactory for. 104 * <p>This is an alternative to specifying the EntityManagerFactory by bean name, 105 * resolving it by its persistence unit name instead. If no bean name and no persistence 106 * unit name have been specified, we'll check whether a bean exists for the default 107 * bean name "entityManagerFactory"; if not, a default EntityManagerFactory will 108 * be retrieved through finding a single unique bean of type EntityManagerFactory. 109 * @see #setEntityManagerFactoryBeanName 110 * @see #DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME 111 */ 112 public void setPersistenceUnitName(String persistenceUnitName) { 113 this.persistenceUnitName = persistenceUnitName; 114 } 115 116 /** 117 * Return the name of the persistence unit to access the EntityManagerFactory for, if any. 118 */ 119 protected String getPersistenceUnitName() { 120 return this.persistenceUnitName; 121 } 122 123 124 /** 125 * Returns "false" so that the filter may re-bind the opened {@code EntityManager} 126 * to each asynchronously dispatched thread and postpone closing it until the very 127 * last asynchronous dispatch. 128 */ 129 @Override 130 protected boolean shouldNotFilterAsyncDispatch() { 131 return false; 132 } 133 134 /** 135 * Returns "false" so that the filter may provide an {@code EntityManager} 136 * to each error dispatches. 137 */ 138 @Override 139 protected boolean shouldNotFilterErrorDispatch() { 140 return false; 141 } 142 143 @Override 144 protected void doFilterInternal( 145 HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 146 throws ServletException, IOException { 147 148 EntityManagerFactory emf = lookupEntityManagerFactory(request); 149 boolean participate = false; 150 151 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 152 String key = getAlreadyFilteredAttributeName(); 153 154 if (TransactionSynchronizationManager.hasResource(emf)) { 155 // Do not modify the EntityManager: just set the participate flag. 156 participate = true; 157 } 158 else { 159 boolean isFirstRequest = !isAsyncDispatch(request); 160 if (isFirstRequest || !applyEntityManagerBindingInterceptor(asyncManager, key)) { 161 logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter"); 162 try { 163 EntityManager em = createEntityManager(emf); 164 EntityManagerHolder emHolder = new EntityManagerHolder(em); 165 TransactionSynchronizationManager.bindResource(emf, emHolder); 166 167 AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(emf, emHolder); 168 asyncManager.registerCallableInterceptor(key, interceptor); 169 asyncManager.registerDeferredResultInterceptor(key, interceptor); 170 } 171 catch (PersistenceException ex) { 172 throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex); 173 } 174 } 175 } 176 177 try { 178 filterChain.doFilter(request, response); 179 } 180 181 finally { 182 if (!participate) { 183 EntityManagerHolder emHolder = (EntityManagerHolder) 184 TransactionSynchronizationManager.unbindResource(emf); 185 if (!isAsyncStarted(request)) { 186 logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewFilter"); 187 EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager()); 188 } 189 } 190 } 191 } 192 193 /** 194 * Look up the EntityManagerFactory that this filter should use, 195 * taking the current HTTP request as argument. 196 * <p>The default implementation delegates to the {@code lookupEntityManagerFactory} 197 * without arguments, caching the EntityManagerFactory reference once obtained. 198 * @return the EntityManagerFactory to use 199 * @see #lookupEntityManagerFactory() 200 */ 201 protected EntityManagerFactory lookupEntityManagerFactory(HttpServletRequest request) { 202 if (this.entityManagerFactory == null) { 203 this.entityManagerFactory = lookupEntityManagerFactory(); 204 } 205 return this.entityManagerFactory; 206 } 207 208 /** 209 * Look up the EntityManagerFactory that this filter should use. 210 * <p>The default implementation looks for a bean with the specified name 211 * in Spring's root application context. 212 * @return the EntityManagerFactory to use 213 * @see #getEntityManagerFactoryBeanName 214 */ 215 protected EntityManagerFactory lookupEntityManagerFactory() { 216 WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); 217 String emfBeanName = getEntityManagerFactoryBeanName(); 218 String puName = getPersistenceUnitName(); 219 if (StringUtils.hasLength(emfBeanName)) { 220 return wac.getBean(emfBeanName, EntityManagerFactory.class); 221 } 222 else if (!StringUtils.hasLength(puName) && wac.containsBean(DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME)) { 223 return wac.getBean(DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME, EntityManagerFactory.class); 224 } 225 else { 226 // Includes fallback search for single EntityManagerFactory bean by type. 227 return EntityManagerFactoryUtils.findEntityManagerFactory(wac, puName); 228 } 229 } 230 231 /** 232 * Create a JPA EntityManager to be bound to a request. 233 * <p>Can be overridden in subclasses. 234 * @param emf the EntityManagerFactory to use 235 * @see javax.persistence.EntityManagerFactory#createEntityManager() 236 */ 237 protected EntityManager createEntityManager(EntityManagerFactory emf) { 238 return emf.createEntityManager(); 239 } 240 241 private boolean applyEntityManagerBindingInterceptor(WebAsyncManager asyncManager, String key) { 242 if (asyncManager.getCallableInterceptor(key) == null) { 243 return false; 244 } 245 ((AsyncRequestInterceptor) asyncManager.getCallableInterceptor(key)).bindSession(); 246 return true; 247 } 248 249}