Java tutorial
/* * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package uk.co.unclealex.persistence.jdo.spring; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.datanucleus.api.jdo.JDOPersistenceManagerFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.orm.jdo.JdoTemplate; import org.springframework.orm.jdo.PersistenceManagerFactoryUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * Note that this class has been altered so that the * {@link PersistenceManagerFactory} it creates returns concrete subclasses of * the {@link PersistenceManager} it returns and not just a class that * implements the {@link PersistenceManager} interface. * <p> * Proxy for a target JDO {@link javax.jdo.PersistenceManagerFactory}, returning * the current thread-bound PersistenceManager (the Spring-managed transactional * PersistenceManager or the single OpenPersistenceManagerInView * PersistenceManager) on <code>getPersistenceManager()</code>, if any. * </p> * * <p> * Essentially, <code>getPersistenceManager()</code> calls get seamlessly * forwarded to {@link PersistenceManagerFactoryUtils#getPersistenceManager}. * Furthermore, <code>PersistenceManager.close</code> calls get forwarded to * {@link PersistenceManagerFactoryUtils#releasePersistenceManager}. * * <p> * The main advantage of this proxy is that it allows DAOs to work with a plain * JDO PersistenceManagerFactory reference, while still participating in * Spring's (or a J2EE server's) resource and transaction management. DAOs will * only rely on the JDO API in such a scenario, without any Spring dependencies. * * <p> * Note that the behavior of this proxy matches the behavior that the JDO spec * defines for a PersistenceManagerFactory as exposed by a JCA connector, when * deployed in a J2EE server. Hence, DAOs could seamlessly switch between a JNDI * PersistenceManagerFactory and this proxy for a local * PersistenceManagerFactory, receiving the reference through Dependency * Injection. This will work without any Spring API dependencies in the DAO * code! * * <p> * It is usually preferable to write your JDO-based DAOs with Spring's * {@link JdoTemplate}, offering benefits such as consistent data access * exceptions instead of JDOExceptions at the DAO layer. However, Spring's * resource and transaction management (and Dependency Injection) will work for * DAOs written against the plain JDO API as well. * * <p> * Of course, you can still access the target PersistenceManagerFactory even * when your DAOs go through this proxy, by defining a bean reference that * points directly at your target PersistenceManagerFactory bean. * * @author Juergen Hoeller * @since 1.2 * @see javax.jdo.PersistenceManagerFactory#getPersistenceManager() * @see javax.jdo.PersistenceManager#close() * @see PersistenceManagerFactoryUtils#getPersistenceManager * @see PersistenceManagerFactoryUtils#releasePersistenceManager */ public class TransactionAwarePersistenceManagerFactoryProxy implements FactoryBean<PersistenceManagerFactory> { private JDOPersistenceManagerFactory target; private boolean allowCreate = true; private PersistenceManagerFactory proxy; /** * Set the target JDO PersistenceManagerFactory that this proxy should * delegate to. This should be the raw PersistenceManagerFactory, as accessed * by JdoTransactionManager. * * @see org.springframework.orm.jdo.JdoTransactionManager */ public void setTargetPersistenceManagerFactory(JDOPersistenceManagerFactory target) { Assert.notNull(target, "Target PersistenceManagerFactory must not be null"); this.target = target; Class[] ifcs = ClassUtils.getAllInterfacesForClass(target.getClass(), target.getClass().getClassLoader()); this.proxy = (PersistenceManagerFactory) Proxy.newProxyInstance(target.getClass().getClassLoader(), ifcs, new PersistenceManagerFactoryInvocationHandler()); } /** * Return the target JDO PersistenceManagerFactory that this proxy delegates * to. */ public JDOPersistenceManagerFactory getTargetPersistenceManagerFactory() { return this.target; } /** * Set whether the PersistenceManagerFactory proxy is allowed to create a * non-transactional PersistenceManager when no transactional * PersistenceManager can be found for the current thread. * <p> * Default is "true". Can be turned off to enforce access to transactional * PersistenceManagers, which safely allows for DAOs written to get a * PersistenceManager without explicit closing (i.e. a * <code>PersistenceManagerFactory.getPersistenceManager()</code> call without * corresponding <code>PersistenceManager.close()</code> call). * * @see PersistenceManagerFactoryUtils#getPersistenceManager(javax.jdo.PersistenceManagerFactory, * boolean) */ public void setAllowCreate(boolean allowCreate) { this.allowCreate = allowCreate; } /** * Return whether the PersistenceManagerFactory proxy is allowed to create a * non-transactional PersistenceManager when no transactional * PersistenceManager can be found for the current thread. */ protected boolean isAllowCreate() { return this.allowCreate; } public PersistenceManagerFactory getObject() { return this.proxy; } public Class<? extends PersistenceManagerFactory> getObjectType() { return PersistenceManagerFactory.class; } public boolean isSingleton() { return true; } /** * Invocation handler that delegates getPersistenceManager calls on the * PersistenceManagerFactory proxy to PersistenceManagerFactoryUtils for being * aware of thread-bound transactions. */ private class PersistenceManagerFactoryInvocationHandler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Invocation on PersistenceManagerFactory interface coming in... if (method.getName().equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0]); } else if (method.getName().equals("hashCode")) { // Use hashCode of PersistenceManagerFactory proxy. return System.identityHashCode(proxy); } else if (method.getName().equals("getPersistenceManager")) { JDOPersistenceManagerFactory target = getTargetPersistenceManagerFactory(); PersistenceManager pm = PersistenceManagerFactoryUtils.doGetPersistenceManager(target, isAllowCreate()); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(pm.getClass()); enhancer.setCallback(new PersistenceManagerMethodInterceptor(pm, target)); return enhancer.create(new Class[] { target.getClass(), String.class, String.class }, new Object[] { target, target.getConnectionUserName(), target.getConnectionPassword() }); } // Invoke method on target PersistenceManagerFactory. try { return method.invoke(getTargetPersistenceManagerFactory(), args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } /** * Invocation handler that delegates close calls on PersistenceManagers to * PersistenceManagerFactoryUtils for being aware of thread-bound * transactions. */ private static class PersistenceManagerMethodInterceptor implements MethodInterceptor { private final PersistenceManager target; private final PersistenceManagerFactory persistenceManagerFactory; public PersistenceManagerMethodInterceptor(PersistenceManager target, PersistenceManagerFactory pmf) { this.target = target; this.persistenceManagerFactory = pmf; } /** * {@inheritDoc} */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // Invocation on PersistenceManager interface coming in... if (method.getName().equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0]); } else if (method.getName().equals("hashCode")) { // Use hashCode of PersistenceManager proxy. return System.identityHashCode(proxy); } else if (method.getName().equals("close")) { // Handle close method: only close if not within a transaction. PersistenceManagerFactoryUtils.doReleasePersistenceManager(this.target, this.persistenceManagerFactory); return null; } // Invoke method on target PersistenceManager. try { return method.invoke(this.target, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } }