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);
}
}
}