1 /* 2 * Copyright 2002-2009 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.orm.jdo; 18 19 import java.lang.reflect.InvocationHandler; 20 import java.lang.reflect.InvocationTargetException; 21 import java.lang.reflect.Method; 22 import java.lang.reflect.Proxy; 23 import javax.jdo.PersistenceManager; 24 import javax.jdo.PersistenceManagerFactory; 25 26 import org.springframework.beans.factory.FactoryBean; 27 import org.springframework.util.Assert; 28 import org.springframework.util.ClassUtils; 29 30 /** 31 * Proxy for a target JDO {@link javax.jdo.PersistenceManagerFactory}, 32 * returning the current thread-bound PersistenceManager (the Spring-managed 33 * transactional PersistenceManager or the single OpenPersistenceManagerInView 34 * PersistenceManager) on <code>getPersistenceManager()</code>, if any. 35 * 36 * <p>Essentially, <code>getPersistenceManager()</code> calls get seamlessly 37 * forwarded to {@link PersistenceManagerFactoryUtils#getPersistenceManager}. 38 * Furthermore, <code>PersistenceManager.close</code> calls get forwarded to 39 * {@link PersistenceManagerFactoryUtils#releasePersistenceManager}. 40 * 41 * <p>The main advantage of this proxy is that it allows DAOs to work with a 42 * plain JDO PersistenceManagerFactory reference, while still participating in 43 * Spring's (or a J2EE server's) resource and transaction management. DAOs will 44 * only rely on the JDO API in such a scenario, without any Spring dependencies. 45 * 46 * <p>Note that the behavior of this proxy matches the behavior that the JDO spec 47 * defines for a PersistenceManagerFactory as exposed by a JCA connector, when 48 * deployed in a J2EE server. Hence, DAOs could seamlessly switch between a JNDI 49 * PersistenceManagerFactory and this proxy for a local PersistenceManagerFactory, 50 * receiving the reference through Dependency Injection. This will work without 51 * any Spring API dependencies in the DAO code! 52 * 53 * <p>It is usually preferable to write your JDO-based DAOs with Spring's 54 * {@link JdoTemplate}, offering benefits such as consistent data access 55 * exceptions instead of JDOExceptions at the DAO layer. However, Spring's 56 * resource and transaction management (and Dependency Injection) will work 57 * for DAOs written against the plain JDO API as well. 58 * 59 * <p>Of course, you can still access the target PersistenceManagerFactory 60 * even when your DAOs go through this proxy, by defining a bean reference 61 * that points directly at your target PersistenceManagerFactory bean. 62 * 63 * @author Juergen Hoeller 64 * @since 1.2 65 * @see javax.jdo.PersistenceManagerFactory#getPersistenceManager() 66 * @see javax.jdo.PersistenceManager#close() 67 * @see PersistenceManagerFactoryUtils#getPersistenceManager 68 * @see PersistenceManagerFactoryUtils#releasePersistenceManager 69 */ 70 public class TransactionAwarePersistenceManagerFactoryProxy implements FactoryBean<PersistenceManagerFactory> { 71 72 private PersistenceManagerFactory target; 73 74 private boolean allowCreate = true; 75 76 private PersistenceManagerFactory proxy; 77 78 79 /** 80 * Set the target JDO PersistenceManagerFactory that this proxy should 81 * delegate to. This should be the raw PersistenceManagerFactory, as 82 * accessed by JdoTransactionManager. 83 * @see org.springframework.orm.jdo.JdoTransactionManager 84 */ setTargetPersistenceManagerFactory(PersistenceManagerFactory target)85 public void setTargetPersistenceManagerFactory(PersistenceManagerFactory target) { 86 Assert.notNull(target, "Target PersistenceManagerFactory must not be null"); 87 this.target = target; 88 Class[] ifcs = ClassUtils.getAllInterfacesForClass(target.getClass(), target.getClass().getClassLoader()); 89 this.proxy = (PersistenceManagerFactory) Proxy.newProxyInstance( 90 target.getClass().getClassLoader(), ifcs, new PersistenceManagerFactoryInvocationHandler()); 91 } 92 93 /** 94 * Return the target JDO PersistenceManagerFactory that this proxy delegates to. 95 */ getTargetPersistenceManagerFactory()96 public PersistenceManagerFactory getTargetPersistenceManagerFactory() { 97 return this.target; 98 } 99 100 /** 101 * Set whether the PersistenceManagerFactory proxy is allowed to create 102 * a non-transactional PersistenceManager when no transactional 103 * PersistenceManager can be found for the current thread. 104 * <p>Default is "true". Can be turned off to enforce access to 105 * transactional PersistenceManagers, which safely allows for DAOs 106 * written to get a PersistenceManager without explicit closing 107 * (i.e. a <code>PersistenceManagerFactory.getPersistenceManager()</code> 108 * call without corresponding <code>PersistenceManager.close()</code> call). 109 * @see PersistenceManagerFactoryUtils#getPersistenceManager(javax.jdo.PersistenceManagerFactory, boolean) 110 */ setAllowCreate(boolean allowCreate)111 public void setAllowCreate(boolean allowCreate) { 112 this.allowCreate = allowCreate; 113 } 114 115 /** 116 * Return whether the PersistenceManagerFactory proxy is allowed to create 117 * a non-transactional PersistenceManager when no transactional 118 * PersistenceManager can be found for the current thread. 119 */ isAllowCreate()120 protected boolean isAllowCreate() { 121 return this.allowCreate; 122 } 123 124 getObject()125 public PersistenceManagerFactory getObject() { 126 return this.proxy; 127 } 128 getObjectType()129 public Class<? extends PersistenceManagerFactory> getObjectType() { 130 return PersistenceManagerFactory.class; 131 } 132 isSingleton()133 public boolean isSingleton() { 134 return true; 135 } 136 137 138 /** 139 * Invocation handler that delegates getPersistenceManager calls on the 140 * PersistenceManagerFactory proxy to PersistenceManagerFactoryUtils 141 * for being aware of thread-bound transactions. 142 */ 143 private class PersistenceManagerFactoryInvocationHandler implements InvocationHandler { 144 invoke(Object proxy, Method method, Object[] args)145 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 146 // Invocation on PersistenceManagerFactory interface coming in... 147 148 if (method.getName().equals("equals")) { 149 // Only consider equal when proxies are identical. 150 return (proxy == args[0]); 151 } 152 else if (method.getName().equals("hashCode")) { 153 // Use hashCode of PersistenceManagerFactory proxy. 154 return System.identityHashCode(proxy); 155 } 156 else if (method.getName().equals("getPersistenceManager")) { 157 PersistenceManagerFactory target = getTargetPersistenceManagerFactory(); 158 PersistenceManager pm = 159 PersistenceManagerFactoryUtils.doGetPersistenceManager(target, isAllowCreate()); 160 Class[] ifcs = ClassUtils.getAllInterfacesForClass(pm.getClass(), pm.getClass().getClassLoader()); 161 return Proxy.newProxyInstance( 162 pm.getClass().getClassLoader(), ifcs, new PersistenceManagerInvocationHandler(pm, target)); 163 } 164 165 // Invoke method on target PersistenceManagerFactory. 166 try { 167 return method.invoke(getTargetPersistenceManagerFactory(), args); 168 } 169 catch (InvocationTargetException ex) { 170 throw ex.getTargetException(); 171 } 172 } 173 } 174 175 176 /** 177 * Invocation handler that delegates close calls on PersistenceManagers to 178 * PersistenceManagerFactoryUtils for being aware of thread-bound transactions. 179 */ 180 private static class PersistenceManagerInvocationHandler implements InvocationHandler { 181 182 private final PersistenceManager target; 183 184 private final PersistenceManagerFactory persistenceManagerFactory; 185 PersistenceManagerInvocationHandler(PersistenceManager target, PersistenceManagerFactory pmf)186 public PersistenceManagerInvocationHandler(PersistenceManager target, PersistenceManagerFactory pmf) { 187 this.target = target; 188 this.persistenceManagerFactory = pmf; 189 } 190 invoke(Object proxy, Method method, Object[] args)191 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 192 // Invocation on PersistenceManager interface coming in... 193 194 if (method.getName().equals("equals")) { 195 // Only consider equal when proxies are identical. 196 return (proxy == args[0]); 197 } 198 else if (method.getName().equals("hashCode")) { 199 // Use hashCode of PersistenceManager proxy. 200 return System.identityHashCode(proxy); 201 } 202 else if (method.getName().equals("close")) { 203 // Handle close method: only close if not within a transaction. 204 PersistenceManagerFactoryUtils.doReleasePersistenceManager( 205 this.target, this.persistenceManagerFactory); 206 return null; 207 } 208 209 // Invoke method on target PersistenceManager. 210 try { 211 return method.invoke(this.target, args); 212 } 213 catch (InvocationTargetException ex) { 214 throw ex.getTargetException(); 215 } 216 } 217 } 218 219 } 220