浏览代码

Automatically ensure Validators accessing DAOs receive detached instances. Also provide DetachmentContextHolder ThreadLocal, so that this behaviour can be specified whenever required.

Ben Alex 20 年之前
父节点
当前提交
ddd69c751f

+ 70 - 0
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;
+
+
+/**
+ * <code>InheritableThreadLocal</code> which indicates whether a {@link Dao}
+ * implementation should be forced to return a detached instance.
+ * 
+ * <p>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.
+ * 
+ * <p>Not all <code>Dao</code> 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<Boolean> contextHolder = new InheritableThreadLocal<Boolean>();
+
+    //~ Methods ================================================================
+
+    /**
+     * Specifies whether or not detached in <code>SecurityContext</code> with the current thread of
+     * execution.
+     *
+     * @param 
+     */
+    public static void setForceReturnOfDetachedInstances(boolean alwaysReturnDetached) {
+        contextHolder.set(alwaysReturnDetached);
+    }
+
+    /**
+     * Obtains the <code>SecurityContext</code> associated with the current
+     * thread of execution. If no <code>SecurityContext</code> 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 <code>SecurityContext</code> (guaranteed to never be
+     *         <code>null</code>)
+     */
+    public static boolean isForceReturnOfDetachedInstances() {
+        if (contextHolder.get() == null) {
+            contextHolder.set(Boolean.FALSE);
+        }
+
+        return contextHolder.get();
+    }
+}

+ 54 - 25
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<E extends PersistableEntity> extends HibernateDaoSupport implements Dao<E>,
     EvictionCapable, InitializationCapable {
     //~ Instance fields ========================================================
-
+	
     /** The class that this instance provides services for */
     private Class supportsClass;
 	
@@ -74,6 +76,31 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
 	
     //~ Methods ================================================================
 
+	/**
+	 * Obtains a <code>HibernateTemplate</code> that uses the appropriate <code>Session</code>
+	 * based on the value of {@link DetachmentContextHolder}.
+	 * 
+	 * <p>Specifically, if <code>DetachmentContextHolder</code> requires detached instances,
+	 * the method will build a new <code>Session</code> (ignore the current thread-bound
+	 * <code>Session</code>) and use that new <code>Session</code> in the <code>HibernateTemplate</code>.
+	 * If <code>DetachmentContextHolder</code> is at its fault <code>false</code> value, the
+	 * returned <code>HibernateTemplate</code> will simply use the <code>Session</code> obtained
+	 * from the superclass, which is generally the same <code>Session</code> as used for the
+	 * transaction.
+	 * 
+	 * @return the template, containing the correct <code>Session</code> based on the
+	 * <code>DetachmentContactHolder</code> 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<E extends PersistableEntity> 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<E extends PersistableEntity> 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<E> findAll() {
-        return getHibernateTemplate().loadAll(supportsClass);
+        return doGetHibernateTemplate().loadAll(supportsClass);
     }
 
     public List<E> findId(Collection<Serializable> 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<E extends PersistableEntity> extends HibernateDaoSuppo
 	 * 
 	 * <p>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<E extends PersistableEntity> extends HibernateDaoSuppo
     public PaginatedList<E> 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<E> 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<E> 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<E> 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<E extends PersistableEntity> extends HibernateDaoSuppo
     public E update(E value) {
         Assert.notNull(value);
 		validate(value);
-        getHibernateTemplate().update(value);
+        doGetHibernateTemplate().update(value);
 
         return readId(value.getInternalId());
     }

+ 5 - 0
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);
             }
         }
     }