diff --git a/domain/src/main/java/org/acegisecurity/domain/dao/DetachmentContextHolder.java b/domain/src/main/java/org/acegisecurity/domain/dao/DetachmentContextHolder.java new file mode 100644 index 0000000000..c4a53e2a34 --- /dev/null +++ b/domain/src/main/java/org/acegisecurity/domain/dao/DetachmentContextHolder.java @@ -0,0 +1,70 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * 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 net.sf.acegisecurity.domain.dao; + +import net.sf.acegisecurity.context.SecurityContextImpl; + + +/** + * InheritableThreadLocal which indicates whether a {@link Dao} + * implementation should be forced to return a detached instance. + * + *

A detached instance is one which is no longer associated with the ORM + * mapper and changes will therefore not be transparently persisted to the database. + * + *

Not all Dao implementations support the concept of detached + * instances. + * + * @author Ben Alex + * @version $Id$ + * + * @see java.lang.InheritableThreadLocal + */ +public class DetachmentContextHolder { + //~ Static fields/initializers ============================================= + + private static InheritableThreadLocal contextHolder = new InheritableThreadLocal(); + + //~ Methods ================================================================ + + /** + * Specifies whether or not detached in SecurityContext with the current thread of + * execution. + * + * @param + */ + public static void setForceReturnOfDetachedInstances(boolean alwaysReturnDetached) { + contextHolder.set(alwaysReturnDetached); + } + + /** + * Obtains the SecurityContext associated with the current + * thread of execution. If no SecurityContext has been + * associated with the current thread of execution, a new instance of + * {@link SecurityContextImpl} is associated with the current thread and + * then returned. + * + * @return the current SecurityContext (guaranteed to never be + * null) + */ + public static boolean isForceReturnOfDetachedInstances() { + if (contextHolder.get() == null) { + contextHolder.set(Boolean.FALSE); + } + + return contextHolder.get(); + } +} diff --git a/domain/src/main/java/org/acegisecurity/domain/hibernate/DaoHibernate.java b/domain/src/main/java/org/acegisecurity/domain/hibernate/DaoHibernate.java index 21fb9481b3..5a368b037b 100644 --- a/domain/src/main/java/org/acegisecurity/domain/hibernate/DaoHibernate.java +++ b/domain/src/main/java/org/acegisecurity/domain/hibernate/DaoHibernate.java @@ -22,6 +22,7 @@ import java.util.List; import net.sf.acegisecurity.domain.PersistableEntity; import net.sf.acegisecurity.domain.dao.Dao; +import net.sf.acegisecurity.domain.dao.DetachmentContextHolder; import net.sf.acegisecurity.domain.dao.EvictionCapable; import net.sf.acegisecurity.domain.dao.InitializationCapable; import net.sf.acegisecurity.domain.dao.PaginatedList; @@ -41,6 +42,7 @@ import org.hibernate.metadata.ClassMetadata; import org.hibernate.type.Type; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.orm.hibernate3.HibernateCallback; +import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.util.Assert; import org.springframework.validation.BindException; @@ -56,7 +58,7 @@ import org.springframework.validation.BindException; public class DaoHibernate extends HibernateDaoSupport implements Dao, EvictionCapable, InitializationCapable { //~ Instance fields ======================================================== - + /** The class that this instance provides services for */ private Class supportsClass; @@ -74,6 +76,31 @@ public class DaoHibernate extends HibernateDaoSuppo //~ Methods ================================================================ + /** + * Obtains a HibernateTemplate that uses the appropriate Session + * based on the value of {@link DetachmentContextHolder}. + * + *

Specifically, if DetachmentContextHolder requires detached instances, + * the method will build a new Session (ignore the current thread-bound + * Session) and use that new Session in the HibernateTemplate. + * If DetachmentContextHolder is at its fault false value, the + * returned HibernateTemplate will simply use the Session obtained + * from the superclass, which is generally the same Session as used for the + * transaction. + * + * @return the template, containing the correct Session based on the + * DetachmentContactHolder request + */ + protected HibernateTemplate doGetHibernateTemplate() { + if (DetachmentContextHolder.isForceReturnOfDetachedInstances()) { + HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory()); + hibernateTemplate.setAlwaysUseNewSession(true); + return hibernateTemplate; + } else { + return super.getHibernateTemplate(); + } + } + public void setSupportsClass(Class supportClass) { this.supportsClass = supportClass; } @@ -93,7 +120,7 @@ public class DaoHibernate extends HibernateDaoSuppo public E create(E value) { Assert.notNull(value); validate(value); - getHibernateTemplate().save(value); + doGetHibernateTemplate().save(value); return readId(value.getInternalId()); } @@ -106,47 +133,49 @@ public class DaoHibernate extends HibernateDaoSuppo } } - public E createOrUpdate(E value) { - Assert.notNull(value); - validate(value); - getHibernateTemplate().saveOrUpdate(value); - - return readId(value.getInternalId()); - } - public void delete(E value) { Assert.notNull(value); validate(value); - getHibernateTemplate().delete(value); + doGetHibernateTemplate().delete(value); } public void evict(PersistableEntity entity) { Assert.notNull(entity); - getHibernateTemplate().evict(entity); + doGetHibernateTemplate().evict(entity); } public List findAll() { - return getHibernateTemplate().loadAll(supportsClass); + return doGetHibernateTemplate().loadAll(supportsClass); } public List findId(Collection ids) { Assert.notNull(ids, "Collection of IDs cannot be null"); Assert.notEmpty(ids, "There must be some values in the Collection list"); - return (List) getHibernateTemplate().execute(getFindByIdCallback(ids)); + return (List) doGetHibernateTemplate().execute(getFindByIdCallback(ids)); + } + + private E readId(final Serializable id, final boolean populate) { + Assert.notNull(id); + return (E) doGetHibernateTemplate().execute(new HibernateCallback() { + public Object doInHibernate(Session session) throws HibernateException { + E obj = (E) session.get(supportsClass, id); + if (populate) { + initializeAllZeroArgumentGetters(obj); + } + return obj; + } + }, true); } public E readId(Serializable id) { Assert.notNull(id); - - return (E) getHibernateTemplate().get(supportsClass, id); + return readId(id, false); } public E readPopulatedId(Serializable id) { Assert.notNull(id); - E result = readId(id); - initializeAllZeroArgumentGetters(result); - return result; + return readId(id, true); } /** @@ -156,7 +185,7 @@ public class DaoHibernate extends HibernateDaoSuppo * *

For example, a Foo object might provide a getBar() method. * Passing the Foo instance to this method will guarantee getBar() is - * available to the services layer. However, it getBar() returned a Bar + * available to the services layer. However, if getBar() returned a Bar * which in turn provided a getCar() method, there is NO GUARANTEE * the getCar() method will be initialized. * @@ -176,28 +205,28 @@ public class DaoHibernate extends HibernateDaoSuppo public PaginatedList scroll(E value, int firstElement, int maxElements, String orderByAsc) { validateScrollMethod(value, firstElement, maxElements, orderByAsc); - return (PaginatedList) getHibernateTemplate().execute(getFindByValueCallback( + return (PaginatedList) doGetHibernateTemplate().execute(getFindByValueCallback( value.getClass(), false, value, firstElement, maxElements, Order.asc(orderByAsc))); } public PaginatedList scrollWithSubclasses(E value, int firstElement, int maxElements, String orderByAsc) { validateScrollMethod(value, firstElement, maxElements, orderByAsc); - return (PaginatedList) getHibernateTemplate().execute(getFindByValueCallback( + return (PaginatedList) doGetHibernateTemplate().execute(getFindByValueCallback( this.supportsClass, false, value, firstElement, maxElements, Order.asc(orderByAsc))); } public PaginatedList scrollPopulated(E value, int firstElement, int maxElements, String orderByAsc) { validateScrollMethod(value, firstElement, maxElements, orderByAsc); - return (PaginatedList) getHibernateTemplate().execute(getFindByValueCallback( + return (PaginatedList) doGetHibernateTemplate().execute(getFindByValueCallback( value.getClass(), true, value, firstElement, maxElements, Order.asc(orderByAsc))); } public PaginatedList scrollPopulatedWithSubclasses(E value, int firstElement, int maxElements, String orderByAsc) { validateScrollMethod(value, firstElement, maxElements, orderByAsc); - return (PaginatedList) getHibernateTemplate().execute(getFindByValueCallback( + return (PaginatedList) doGetHibernateTemplate().execute(getFindByValueCallback( this.supportsClass, true, value, firstElement, maxElements, Order.asc(orderByAsc))); } @@ -217,7 +246,7 @@ public class DaoHibernate extends HibernateDaoSuppo public E update(E value) { Assert.notNull(value); validate(value); - getHibernateTemplate().update(value); + doGetHibernateTemplate().update(value); return readId(value.getInternalId()); } diff --git a/domain/src/main/java/org/acegisecurity/domain/validation/ValidationManagerImpl.java b/domain/src/main/java/org/acegisecurity/domain/validation/ValidationManagerImpl.java index dc60842640..c730a1fd95 100644 --- a/domain/src/main/java/org/acegisecurity/domain/validation/ValidationManagerImpl.java +++ b/domain/src/main/java/org/acegisecurity/domain/validation/ValidationManagerImpl.java @@ -15,6 +15,8 @@ package net.sf.acegisecurity.domain.validation; +import net.sf.acegisecurity.domain.dao.DetachmentContextHolder; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -134,6 +136,7 @@ public class ValidationManagerImpl implements InitializingBean, Object currentDomainObject = iter.next(); Class clazz = currentDomainObject.getClass(); + DetachmentContextHolder.setForceReturnOfDetachedInstances(true); try { // Call bindSupport() if this class wishes BindBeforeValidationUtils.bindIfRequired(currentDomainObject); @@ -172,6 +175,8 @@ public class ValidationManagerImpl implements InitializingBean, logger.debug("Could not locate validator for class '" + clazz + "'; skipping without error"); } + } finally { + DetachmentContextHolder.setForceReturnOfDetachedInstances(false); } } }