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