001/* 002 * Copyright 2002-2013 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.lang.reflect.InvocationHandler; 020import java.lang.reflect.InvocationTargetException; 021import java.lang.reflect.Method; 022import java.lang.reflect.Proxy; 023import javax.jdo.PersistenceManager; 024import javax.jdo.PersistenceManagerFactory; 025import javax.jdo.Query; 026 027import org.springframework.beans.factory.FactoryBean; 028import org.springframework.beans.factory.InitializingBean; 029import org.springframework.orm.jdo.DefaultJdoDialect; 030import org.springframework.orm.jdo.JdoDialect; 031import org.springframework.orm.jdo.PersistenceManagerFactoryUtils; 032import org.springframework.util.Assert; 033 034/** 035 * Proxy that implements the {@link javax.jdo.PersistenceManager} interface, 036 * delegating to the current thread-bound PersistenceManager (the Spring-managed 037 * transactional PersistenceManager or the single OpenPersistenceManagerInView 038 * PersistenceManager, if any) on each invocation. This class makes such a 039 * Spring-style PersistenceManager proxy available for bean references. 040 * 041 * <p>The main advantage of this proxy is that it allows DAOs to work with a 042 * plain JDO PersistenceManager reference in JDO 3.0 style 043 * (see {@link javax.jdo.PersistenceManagerFactory#getPersistenceManagerProxy()}), 044 * while still participating in Spring's resource and transaction management. 045 * 046 * <p>The behavior of this proxy matches the behavior that the JDO 3.0 spec 047 * defines for a PersistenceManager proxy. Hence, DAOs could seamlessly switch 048 * between {@link StandardPersistenceManagerProxyBean} and this Spring-style proxy, 049 * receiving the reference through Dependency Injection. This will work without 050 * any Spring API dependencies in the DAO code! 051 * 052 * @author Juergen Hoeller 053 * @since 3.0 054 * @see StandardPersistenceManagerProxyBean 055 * @see javax.jdo.PersistenceManagerFactory#getPersistenceManagerProxy() 056 * @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager 057 * @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#releasePersistenceManager 058 */ 059public class SpringPersistenceManagerProxyBean implements FactoryBean<PersistenceManager>, InitializingBean { 060 061 private PersistenceManagerFactory persistenceManagerFactory; 062 063 private JdoDialect jdoDialect; 064 065 private Class<? extends PersistenceManager> persistenceManagerInterface = PersistenceManager.class; 066 067 private boolean allowCreate = true; 068 069 private PersistenceManager proxy; 070 071 072 /** 073 * Set the target PersistenceManagerFactory for this proxy. 074 */ 075 public void setPersistenceManagerFactory(PersistenceManagerFactory persistenceManagerFactory) { 076 this.persistenceManagerFactory = persistenceManagerFactory; 077 } 078 079 /** 080 * Return the target PersistenceManagerFactory for this proxy. 081 */ 082 protected PersistenceManagerFactory getPersistenceManagerFactory() { 083 return this.persistenceManagerFactory; 084 } 085 086 /** 087 * Set the JDO dialect to use for this proxy. 088 * <p>Default is a DefaultJdoDialect based on the PersistenceManagerFactory's 089 * underlying DataSource, if any. 090 */ 091 public void setJdoDialect(JdoDialect jdoDialect) { 092 this.jdoDialect = jdoDialect; 093 } 094 095 /** 096 * Return the JDO dialect to use for this proxy. 097 */ 098 protected JdoDialect getJdoDialect() { 099 return this.jdoDialect; 100 } 101 102 /** 103 * Specify the PersistenceManager interface to expose, 104 * possibly including vendor extensions. 105 * <p>Default is the standard {@code javax.jdo.PersistenceManager} interface. 106 */ 107 public void setPersistenceManagerInterface(Class<? extends PersistenceManager> persistenceManagerInterface) { 108 this.persistenceManagerInterface = persistenceManagerInterface; 109 Assert.notNull(persistenceManagerInterface, "persistenceManagerInterface must not be null"); 110 Assert.isAssignable(PersistenceManager.class, persistenceManagerInterface); 111 } 112 113 /** 114 * Return the PersistenceManager interface to expose. 115 */ 116 protected Class<? extends PersistenceManager> getPersistenceManagerInterface() { 117 return this.persistenceManagerInterface; 118 } 119 120 /** 121 * Set whether the PersistenceManagerFactory proxy is allowed to create 122 * a non-transactional PersistenceManager when no transactional 123 * PersistenceManager can be found for the current thread. 124 * <p>Default is "true". Can be turned off to enforce access to 125 * transactional PersistenceManagers, which safely allows for DAOs 126 * written to get a PersistenceManager without explicit closing 127 * (i.e. a {@code PersistenceManagerFactory.getPersistenceManager()} 128 * call without corresponding {@code PersistenceManager.close()} call). 129 * @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager(javax.jdo.PersistenceManagerFactory, boolean) 130 */ 131 public void setAllowCreate(boolean allowCreate) { 132 this.allowCreate = allowCreate; 133 } 134 135 /** 136 * Return whether the PersistenceManagerFactory proxy is allowed to create 137 * a non-transactional PersistenceManager when no transactional 138 * PersistenceManager can be found for the current thread. 139 */ 140 protected boolean isAllowCreate() { 141 return this.allowCreate; 142 } 143 144 @Override 145 public void afterPropertiesSet() { 146 if (getPersistenceManagerFactory() == null) { 147 throw new IllegalArgumentException("Property 'persistenceManagerFactory' is required"); 148 } 149 // Build default JdoDialect if none explicitly specified. 150 if (this.jdoDialect == null) { 151 this.jdoDialect = new DefaultJdoDialect(getPersistenceManagerFactory().getConnectionFactory()); 152 } 153 this.proxy = (PersistenceManager) Proxy.newProxyInstance( 154 getPersistenceManagerFactory().getClass().getClassLoader(), 155 new Class<?>[] {getPersistenceManagerInterface()}, new PersistenceManagerInvocationHandler()); 156 } 157 158 159 @Override 160 public PersistenceManager getObject() { 161 return this.proxy; 162 } 163 164 @Override 165 public Class<? extends PersistenceManager> getObjectType() { 166 return getPersistenceManagerInterface(); 167 } 168 169 @Override 170 public boolean isSingleton() { 171 return true; 172 } 173 174 175 /** 176 * Invocation handler that delegates close calls on PersistenceManagers to 177 * PersistenceManagerFactoryUtils for being aware of thread-bound transactions. 178 */ 179 private class PersistenceManagerInvocationHandler implements InvocationHandler { 180 181 @Override 182 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 183 // Invocation on PersistenceManager interface coming in... 184 185 if (method.getName().equals("equals")) { 186 // Only consider equal when proxies are identical. 187 return (proxy == args[0]); 188 } 189 else if (method.getName().equals("hashCode")) { 190 // Use hashCode of PersistenceManager proxy. 191 return System.identityHashCode(proxy); 192 } 193 else if (method.getName().equals("toString")) { 194 // Deliver toString without touching a target EntityManager. 195 return "Spring PersistenceManager proxy for target factory [" + getPersistenceManagerFactory() + "]"; 196 } 197 else if (method.getName().equals("getPersistenceManagerFactory")) { 198 // Return PersistenceManagerFactory without creating a PersistenceManager. 199 return getPersistenceManagerFactory(); 200 } 201 else if (method.getName().equals("isClosed")) { 202 // Proxy is always usable. 203 return false; 204 } 205 else if (method.getName().equals("close")) { 206 // Suppress close method. 207 return null; 208 } 209 210 // Invoke method on target PersistenceManager. 211 PersistenceManager pm = PersistenceManagerFactoryUtils.doGetPersistenceManager( 212 getPersistenceManagerFactory(), isAllowCreate()); 213 try { 214 Object retVal = method.invoke(pm, args); 215 if (retVal instanceof Query) { 216 PersistenceManagerFactoryUtils.applyTransactionTimeout( 217 (Query) retVal, getPersistenceManagerFactory()); 218 } 219 return retVal; 220 } 221 catch (InvocationTargetException ex) { 222 throw ex.getTargetException(); 223 } 224 finally { 225 PersistenceManagerFactoryUtils.doReleasePersistenceManager(pm, getPersistenceManagerFactory()); 226 } 227 } 228 } 229 230}