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}