[seam-commits] Seam SVN: r10650 - in modules/trunk: persistence and 9 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Mon Apr 27 08:48:53 EDT 2009


Author: shane.bryzak at jboss.com
Date: 2009-04-27 08:48:53 -0400 (Mon, 27 Apr 2009)
New Revision: 10650

Added:
   modules/trunk/persistence/
   modules/trunk/persistence/pom.xml
   modules/trunk/persistence/src/
   modules/trunk/persistence/src/main/
   modules/trunk/persistence/src/main/java/
   modules/trunk/persistence/src/main/java/org/
   modules/trunk/persistence/src/main/java/org/jboss/
   modules/trunk/persistence/src/main/java/org/jboss/seam/
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/AbstractPersistenceProvider.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Entity.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerFactory.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerProxy.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerProxyInterceptor.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Filter.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/FullTextEntityManagerProxy.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/FullTextHibernateSessionProxy.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernatePersistenceProvider.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionFactory.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionProxy.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionProxyInterceptor.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedEntityInterceptor.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedEntityWrapper.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedHibernateSession.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedPersistenceContext.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Model.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceContextManager.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceContexts.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceProvider.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/QueryParser.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/annotations/
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/annotations/FlushModeType.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Naming.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Reflections.java
   modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Strings.java
Log:
initial import of persistence module

Added: modules/trunk/persistence/pom.xml
===================================================================
--- modules/trunk/persistence/pom.xml	                        (rev 0)
+++ modules/trunk/persistence/pom.xml	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,44 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" 
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">   
+   <parent>
+      <artifactId>seam-parent</artifactId>
+      <groupId>org.jboss.seam</groupId>      
+      <version>3.0.0-SNAPSHOT</version>
+   </parent>    
+  
+   <modelVersion>4.0.0</modelVersion>   
+   <groupId>org.jboss.seam</groupId>
+   <artifactId>seam-persistence</artifactId>
+   <packaging>jar</packaging>
+   <version>3.0.0-SNAPSHOT</version>
+   <name>Seam Persistence</name>
+         
+   <dependencies>
+      <dependency>
+         <groupId>org.jboss.webbeans</groupId>
+         <artifactId>jsr299-api</artifactId>
+      </dependency>              
+      <dependency>
+         <groupId>org.jboss.webbeans</groupId>
+         <artifactId>webbeans-logging</artifactId>
+      </dependency>
+      <dependency>
+         <groupId>javax.persistence</groupId>
+         <artifactId>persistence-api</artifactId>
+      </dependency>   
+      <dependency>
+         <groupId>javax.transaction</groupId>
+         <artifactId>jta</artifactId>
+      </dependency>
+      <dependency>
+         <groupId>org.hibernate</groupId>
+         <artifactId>hibernate</artifactId>
+      </dependency>            
+      <dependency>
+         <groupId>org.jboss.seam</groupId>
+         <artifactId>seam-el</artifactId>
+      </dependency>
+   </dependencies>
+
+</project>

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/AbstractPersistenceProvider.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/AbstractPersistenceProvider.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/AbstractPersistenceProvider.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,145 @@
+package org.jboss.seam.persistence;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+
+import javax.persistence.EntityManager;
+import javax.persistence.OptimisticLockException;
+import javax.transaction.Synchronization;
+
+/**
+ * Provides a default implementation of PersistenceProvider methods where possible
+ * 
+ * Other methods must be implemented
+ * 
+ * @author Pete Muir
+ *
+ */
+public abstract class AbstractPersistenceProvider
+{
+
+   /**
+    *  Set the flush mode to manual-only flushing. Called when
+    *  an atomic persistence context is required.
+    */
+   public abstract void setFlushModeManual(EntityManager entityManager);
+
+   /**
+    * Does the persistence context have unflushed changes? If
+    * it does not, persistence context replication can be
+    * optimized.
+    * 
+    * @return true to indicate that there are unflushed changes
+    */
+   public abstract boolean isDirty(EntityManager entityManager);
+
+   /**
+    * Get the value of the entity identifier attribute.
+    * 
+    * @param bean a managed entity instance
+    */
+   public Object getId(Object bean, EntityManager entityManager)
+   {
+      return Entity.forClass( bean.getClass() ).getIdentifier(bean);
+   }
+
+   /**
+    * Get the name of the entity
+    * 
+    * @param bean
+    * @param entityManager
+    * 
+    * @throws IllegalArgumentException if the passed object is not an entity
+    */
+   public String getName(Object bean, EntityManager entityManager) throws IllegalArgumentException
+   {
+      return Entity.forClass(bean.getClass()).getName();
+   }
+
+   /**
+    * Get the value of the entity version attribute.
+    * 
+    * @param bean a managed entity instance
+    */
+   public Object getVersion(Object bean, EntityManager entityManager)
+   {
+      return Entity.forClass( bean.getClass() ).getVersion(bean);
+   }
+
+   public void checkVersion(Object bean, EntityManager entityManager, Object oldVersion, Object version)
+   {
+      boolean equal;
+      if (oldVersion instanceof Date)
+      {
+         equal = ( (Date) oldVersion ).getTime() == ( (Date) version ).getTime();
+      }
+      else
+      {
+         equal = oldVersion.equals(version);
+      }
+      if ( !equal )
+      {
+         throw new OptimisticLockException("current database version number does not match passivated version number");
+      }
+   }
+
+   /**
+    * Enable a Filter. This is here just especially for Hibernate,
+    * since we well know that other products don't have such cool
+    * features. 
+    */
+   public abstract void enableFilter(Filter filter, EntityManager entityManager);
+
+   /**
+    * Register a Synchronization with the current transaction.
+    */
+   public abstract boolean registerSynchronization(Synchronization sync, EntityManager entityManager);
+
+   /**
+    * Wrap the delegate before returning it to the application
+    */
+   public Object proxyDelegate(Object delegate)
+   {
+      return delegate;
+   }
+
+   /**
+    * Wrap the entityManager before returning it to the application
+    */
+   public EntityManager proxyEntityManager(EntityManager entityManager)
+   {
+      return new EntityManagerProxy(entityManager);
+   }
+
+   /**
+    * Returns the class of an entity bean instance
+    * 
+    * @param bean The entity bean instance
+    * @return The class of the entity bean
+    */
+   public Class getBeanClass(Object bean)
+   {
+      return Entity.forClass(bean.getClass()).getBeanClass();
+   }
+
+   public Method getPostLoadMethod(Class beanClass, EntityManager entityManager)
+   {
+      return Entity.forClass(beanClass).getPostLoadMethod();      
+   }
+
+   public Method getPrePersistMethod(Class beanClass, EntityManager entityManager)
+   {
+      return Entity.forClass(beanClass).getPrePersistMethod();
+   }
+
+   public Method getPreUpdateMethod(Class beanClass, EntityManager entityManager)
+   {
+      return Entity.forClass(beanClass).getPreUpdateMethod();
+   }
+
+   public Method getPreRemoveMethod(Class beanClass, EntityManager entityManager)
+   {
+      return Entity.forClass(beanClass).getPreRemoveMethod();
+   }
+
+}
\ No newline at end of file

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Entity.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Entity.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Entity.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,281 @@
+package org.jboss.seam.persistence;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.context.ApplicationScoped;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Id;
+import javax.persistence.PostLoad;
+import javax.persistence.PrePersist;
+import javax.persistence.PreRemove;
+import javax.persistence.PreUpdate;
+import javax.persistence.Version;
+
+import org.jboss.seam.init.EjbDescriptor;
+import org.jboss.seam.init.EjbEntityDescriptor;
+import org.jboss.seam.persistence.util.Reflections;
+
+/**
+ * Metamodel class for entity classes.
+ * 
+ * A class will be identified as an entity class if it has an @Entity annotation.
+ * 
+ * @author Gavin King
+ * 
+ */
+ at ApplicationScoped
+public class Entity extends Model
+{  
+   public Entity(Class<?> beanClass) 
+   {
+      super(beanClass);
+   }
+
+   private Method preRemoveMethod;
+   private Method prePersistMethod;
+   private Method preUpdateMethod;
+   private Method postLoadMethod;
+   private Method identifierGetter;
+   private Field identifierField;
+   private Method versionGetter;
+   private Field versionField;
+   private String name;
+
+   public Method getPostLoadMethod()
+   {
+      return postLoadMethod;
+   }
+
+   public Method getPrePersistMethod()
+   {
+      return prePersistMethod;
+   }
+
+   public Method getPreRemoveMethod()
+   {
+      return preRemoveMethod;
+   }
+
+   public Method getPreUpdateMethod()
+   {
+      return preUpdateMethod;
+   }
+
+   public Object getIdentifier(Object entity)
+   {
+      if (identifierGetter != null)
+      {
+         return Reflections.invokeAndWrap(identifierGetter, entity);
+      }
+      else if (identifierField != null)
+      {
+         return Reflections.getAndWrap(identifierField, entity);
+      }
+      else
+      {
+         throw new IllegalStateException("@Id attribute not found for entity class: " + getBeanClass().getName());
+      }
+   }
+
+   public Object getVersion(Object entity)
+   {
+      if (versionGetter != null)
+      {
+         return Reflections.invokeAndWrap(versionGetter, entity);
+      }
+      else if (versionField != null)
+      {
+         return Reflections.getAndWrap(versionField, entity);
+      }
+      else
+      {
+         return null;
+      }
+   }
+
+   public String getName()
+   {
+      return name;
+   }
+   
+   public static Entity forBean(Object bean)
+   {
+      return forClass(bean.getClass());
+   }
+   
+   public static Entity forClass(Class clazz)
+   {
+      Class entityClass = Seam.getEntityClass(clazz);
+      
+      if (entityClass == null)
+      {
+         throw new NotEntityException("Not an entity class: " + clazz.getName());
+      }
+      String name = getModelName(entityClass);
+      Model model = modelCache.get(name);
+      if (model == null || !(model instanceof Entity))
+      {
+         Entity entity = new Entity(entityClass);
+         modelCache.put(name, entity);
+         return entity;
+      }
+      else
+      {
+         return (Entity) model;
+      }
+   }
+
+   private void mergeAnnotationAndOrmXml(EjbEntityDescriptor descriptor)
+   {
+      // Lookup the name of the Entity from XML, annotation or default
+      this.name = lookupName(getBeanClass(), descriptor);
+      if (this.name == null)
+      {
+         throw new NotEntityException("Unable to establish name of entity " + getBeanClass());
+      }
+      
+      if (descriptor != null)
+      {
+         // Set any methods and fields we need metadata for from the XML
+         // descriptor. These take priority over annotations
+         
+         this.preRemoveMethod = getEntityCallbackMethod(getBeanClass(), descriptor.getPreRemoveMethodName());
+         this.prePersistMethod = getEntityCallbackMethod(getBeanClass(), descriptor.getPrePersistMethodName());
+         this.preUpdateMethod = getEntityCallbackMethod(getBeanClass(), descriptor.getPreUpdateMethodName());
+         this.postLoadMethod = getEntityCallbackMethod(getBeanClass(), descriptor.getPostLoadMethodName());
+         
+         this.identifierField = descriptor.getIdentifierFieldName() != null ? Reflections.getField(getBeanClass(), descriptor.getIdentifierFieldName()) : null;
+         this.identifierGetter = descriptor.getIdentifierPropertyName() != null ? Reflections.getGetterMethod(getBeanClass(), descriptor.getIdentifierPropertyName()) : null;
+         
+         this.versionField = descriptor.getVersionFieldName() != null ? Reflections.getField(getBeanClass(), descriptor.getVersionFieldName()) : null;
+         this.versionGetter = descriptor.getVersionPropertyName() != null ? Reflections.getGetterMethod(getBeanClass(), descriptor.getVersionPropertyName()) : null;
+      }
+      
+      if (descriptor == null || !descriptor.isMetaDataComplete())
+      {
+         for ( Class<?> clazz=getBeanClass(); clazz!=Object.class; clazz = clazz.getSuperclass() )
+         {
+
+            for ( Method method: clazz.getDeclaredMethods() )
+            {
+               //TODO: does the spec allow multiple lifecycle method
+               //      in the entity class heirarchy?
+               if (this.preRemoveMethod == null && method.isAnnotationPresent(PreRemove.class))
+               {
+                  this.preRemoveMethod = method;
+               }
+               if (this.prePersistMethod == null && method.isAnnotationPresent(PrePersist.class) )
+               {
+                  this.prePersistMethod = method;
+               }
+               if (preUpdateMethod == null && method.isAnnotationPresent(PreUpdate.class) )
+               {
+                  preUpdateMethod = method;
+               }
+               if (postLoadMethod == null && method.isAnnotationPresent(PostLoad.class) )
+               {
+                  postLoadMethod = method;
+               }
+               if (identifierField == null && identifierGetter == null && method.isAnnotationPresent(Id.class) || method.isAnnotationPresent(EmbeddedId.class))
+               {
+                  identifierGetter = method;
+               }
+               if (versionField == null && versionGetter == null && method.isAnnotationPresent(Version.class) )
+               {
+                  versionGetter = method;
+               }
+            }
+            
+            if ( ( identifierGetter == null && identifierField == null ) || ( versionField == null && versionGetter == null ) )
+            {
+               for ( Field field: clazz.getDeclaredFields() )
+               {
+                  if ( identifierGetter == null && identifierField == null && (field.isAnnotationPresent(Id.class) || field.isAnnotationPresent(EmbeddedId.class)))
+                  {
+                     identifierField = field;
+                  }
+                  if ( versionGetter == null && versionField == null && field.isAnnotationPresent(Version.class) )
+                  {
+                     versionField = field;
+                  }
+               }
+            }
+         }
+      }
+      
+      setAccessible(this.preRemoveMethod);
+      setAccessible(this.prePersistMethod);
+      setAccessible(this.preUpdateMethod);
+      setAccessible(this.postLoadMethod);
+      setAccessible(this.identifierField);
+      setAccessible(this.identifierGetter);
+      setAccessible(this.versionField);
+      setAccessible(this.versionGetter);
+   }
+   
+   private void setAccessible(AccessibleObject accessibleObject)
+   {
+      if (accessibleObject != null)
+      {
+         accessibleObject.setAccessible(true);
+      }
+   }
+
+   private static String lookupName(Class<?> beanClass, EjbEntityDescriptor descriptor)
+   {
+      if (descriptor != null && descriptor.getEjbName() != null)
+      {
+         // XML overrides annotations
+         return descriptor.getEjbName();
+      }
+      else if ( (descriptor == null || !descriptor.isMetaDataComplete()) && beanClass.isAnnotationPresent(javax.persistence.Entity.class) && !"".equals(beanClass.getAnnotation(javax.persistence.Entity.class).name()))
+      {
+         // Is a name specified?
+         return beanClass.getAnnotation(javax.persistence.Entity.class).name();
+      }
+      else if (descriptor != null || beanClass.isAnnotationPresent(javax.persistence.Entity.class))
+      {
+         // Use the default name if either a descriptor is specified or the
+         // annotation is present
+         return beanClass.getName();
+      }
+      else
+      {
+         return null;
+      }
+   }
+
+   private static Method getEntityCallbackMethod(Class beanClass, String callbackMethodName)
+   {
+      try
+      {
+         if (callbackMethodName != null)
+         {
+            return Reflections.getMethod(beanClass, callbackMethodName);
+         }
+         else
+         {
+            return null;
+         }
+      }
+      catch (IllegalArgumentException e)
+      {
+         throw new IllegalArgumentException("Unable to find Entity callback method specified in orm.xml", e);
+      }
+   }
+   
+   public static class NotEntityException extends IllegalArgumentException 
+   {
+      
+      public NotEntityException(String string)
+      {
+         super(string);
+      }
+      
+   }
+
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerFactory.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerFactory.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerFactory.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,109 @@
+//$Id: EntityManagerFactory.java 6280 2007-09-27 15:29:56Z pmuir $
+package org.jboss.seam.persistence;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.annotation.PreDestroy;
+import javax.context.ApplicationScoped;
+import javax.inject.Initializer;
+import javax.inject.Produces;
+import javax.persistence.Persistence;
+
+import org.hibernate.cfg.Environment;
+import org.jboss.seam.persistence.util.Naming;
+
+/**
+ * A Seam component that bootstraps an EntityManagerFactory,
+ * for use of JPA outside of Java EE 5 / Embedded JBoss.
+ * 
+ * @author Gavin King
+ */
+ at ApplicationScoped
+public class EntityManagerFactory
+{
+   private javax.persistence.EntityManagerFactory entityManagerFactory;
+
+   private String persistenceUnitName;
+   private Map<String, String> persistenceUnitProperties;
+   
+   @Produces
+   public javax.persistence.EntityManagerFactory getEntityManagerFactory()
+   {
+      return entityManagerFactory;
+   }
+   
+   @Initializer
+   public void startup(Component component) throws Exception
+   {
+      if (persistenceUnitName==null)
+      {
+         persistenceUnitName = component.getName();
+      }
+      entityManagerFactory = createEntityManagerFactory();
+   }
+
+   @PreDestroy
+   public void shutdown()
+   {
+      if (entityManagerFactory!=null)
+      {
+         entityManagerFactory.close();
+      }
+   }
+   
+   protected javax.persistence.EntityManagerFactory createEntityManagerFactory()
+   {
+      Map properties = new HashMap();
+      Hashtable<String, String> jndiProperties = Naming.getInitialContextProperties();
+      if ( jndiProperties!=null )
+      {
+         // Prefix regular JNDI properties for Hibernate
+         for (Map.Entry<String, String> entry : jndiProperties.entrySet())
+         {
+            properties.put( Environment.JNDI_PREFIX + "." + entry.getKey(), entry.getValue() );
+         }
+      }
+      if (persistenceUnitProperties!=null)
+      {
+         properties.putAll(persistenceUnitProperties);
+      }
+
+      if ( properties.isEmpty() )
+      {
+         return Persistence.createEntityManagerFactory(persistenceUnitName);
+      }
+      else
+      {
+         return Persistence.createEntityManagerFactory(persistenceUnitName, properties);
+      }
+   }
+   
+   /**
+    * The persistence unit name
+    */
+   public String getPersistenceUnitName()
+   {
+      return persistenceUnitName;
+   }
+
+   public void setPersistenceUnitName(String persistenceUnitName)
+   {
+      this.persistenceUnitName = persistenceUnitName;
+   }
+
+   /**
+    * Properties to pass to Persistence.createEntityManagerFactory()
+    */
+   public Map<String, String> getPersistenceUnitProperties()
+   {
+      return persistenceUnitProperties;
+   }
+
+   public void setPersistenceUnitProperties(Map<String, String> persistenceUnitProperties)
+   {
+      this.persistenceUnitProperties = persistenceUnitProperties;
+   }
+
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerProxy.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerProxy.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerProxy.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,155 @@
+package org.jboss.seam.persistence;
+
+import java.io.Serializable;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.persistence.FlushModeType;
+import javax.persistence.LockModeType;
+import javax.persistence.Query;
+
+import org.jboss.seam.security.permission.PermissionManager;
+
+/**
+ * Proxies the EntityManager, and implements EL interpolation
+ * in JPA-QL
+ * 
+ * @author Gavin King
+ *
+ */
+public class EntityManagerProxy implements EntityManager, Serializable
+{
+   private EntityManager delegate;
+
+   public EntityManagerProxy(EntityManager entityManager)
+   {
+      delegate = entityManager;
+   }
+
+   public void clear()
+   {
+      delegate.clear();
+   }
+
+   public void close()
+   {
+      delegate.close();
+   }
+
+   public boolean contains(Object entity)
+   {
+      return delegate.contains(entity);
+   }
+
+   public Query createNamedQuery(String name)
+   {
+      return delegate.createNamedQuery(name);
+   }
+
+   public Query createNativeQuery(String sql, Class clazz)
+   {
+      return delegate.createNativeQuery(sql, clazz);
+   }
+
+   public Query createNativeQuery(String sql, String lang)
+   {
+      return delegate.createNativeQuery(sql, lang);
+   }
+
+   public Query createNativeQuery(String sql)
+   {
+      return delegate.createNativeQuery(sql);
+   }
+
+   public Query createQuery(String ejbql)
+   {
+      if ( ejbql.indexOf('#')>0 )
+      {
+         QueryParser qp = new QueryParser(ejbql);
+         Query query = delegate.createQuery( qp.getEjbql() );
+         for (int i=0; i<qp.getParameterValueBindings().size(); i++)
+         {
+            query.setParameter( 
+                     QueryParser.getParameterName(i), 
+                     qp.getParameterValueBindings().get(i).getValue() 
+                  );
+         }
+         return query;
+      }
+      else
+      {
+         return delegate.createQuery(ejbql);
+      }
+   }
+
+   public <T> T find(Class<T> clazz, Object id)
+   {
+      return delegate.find(clazz, id);
+   }
+
+   public void flush()
+   {
+      delegate.flush();
+   }
+
+   public Object getDelegate()
+   {
+      return PersistenceProvider.instance().proxyDelegate( delegate.getDelegate() );
+   }
+
+   public FlushModeType getFlushMode()
+   {
+      return delegate.getFlushMode();
+   }
+
+   public <T> T getReference(Class<T> clazz, Object id)
+   {
+      return delegate.getReference(clazz, id);
+   }
+
+   public EntityTransaction getTransaction()
+   {
+      return delegate.getTransaction();
+   }
+
+   public boolean isOpen()
+   {
+      return delegate.isOpen();
+   }
+
+   public void joinTransaction()
+   {
+      delegate.joinTransaction();
+   }
+
+   public void lock(Object entity, LockModeType lm)
+   {
+      delegate.lock(entity, lm);
+   }
+
+   public <T> T merge(T entity)
+   {
+      return delegate.merge(entity);
+   }
+
+   public void persist(Object entity)
+   {
+      delegate.persist(entity);
+   }
+
+   public void refresh(Object entity)
+   {
+      delegate.refresh(entity);
+   }
+
+   public void remove(Object entity)
+   {
+      delegate.remove(entity);
+      PermissionManager.instance().clearPermissions(entity);
+   }
+
+   public void setFlushMode(FlushModeType fm)
+   {
+      delegate.setFlushMode(fm);
+   }
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerProxyInterceptor.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerProxyInterceptor.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/EntityManagerProxyInterceptor.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,67 @@
+package org.jboss.seam.persistence;
+
+import static org.jboss.seam.ComponentType.STATEFUL_SESSION_BEAN;
+import static org.jboss.seam.ComponentType.STATELESS_SESSION_BEAN;
+
+import javax.persistence.EntityManager;
+
+import org.jboss.seam.Component.BijectedAttribute;
+import org.jboss.seam.annotations.intercept.AroundInvoke;
+import org.jboss.seam.annotations.intercept.Interceptor;
+import org.jboss.seam.annotations.intercept.PostActivate;
+import org.jboss.seam.annotations.intercept.PostConstruct;
+import org.jboss.seam.intercept.AbstractInterceptor;
+import org.jboss.seam.intercept.InvocationContext;
+
+/**
+ * Proxy the EntityManager if injected using @PersistenceContext
+ * 
+ * @author Pete Muir
+ */
+
+ at Interceptor(stateless=true)
+public class EntityManagerProxyInterceptor extends AbstractInterceptor
+{
+
+   @AroundInvoke
+   public Object aroundInvoke(InvocationContext ic) throws Exception
+   {
+      return ic.proceed();
+   }
+   
+   @PostActivate
+   public void postActivate(InvocationContext invocation) throws Exception
+   {
+      //just in case the container does some special handling of PC serialization
+      proxyPersistenceContexts(invocation.getTarget());
+      invocation.proceed();
+   }
+   
+   @PostConstruct
+   public void postConstruct(InvocationContext invocation) throws Exception
+   {
+      proxyPersistenceContexts(invocation.getTarget());
+      invocation.proceed();
+   }
+   
+   
+   private void proxyPersistenceContexts(Object bean)
+   {
+      //wrap any @PersistenceContext attributes in our proxy
+      for ( BijectedAttribute ba: getComponent().getPersistenceContextAttributes() )
+      {
+         Object object = ba.get(bean);
+         if ( ! ( object instanceof EntityManagerProxy ) && object instanceof EntityManager )
+         {
+            PersistenceProvider provider = PersistenceProvider.instance();
+            ba.set( bean, provider.proxyEntityManager( (EntityManager) object ) );
+         }
+      }
+   }
+   
+   public boolean isInterceptorEnabled()
+   {
+      return getComponent().getType()==STATEFUL_SESSION_BEAN || getComponent().getType()==STATELESS_SESSION_BEAN;
+   }
+   
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Filter.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Filter.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Filter.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,100 @@
+package org.jboss.seam.persistence;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.seam.Component;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.Create;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.core.Expressions.ValueExpression;
+
+/**
+ * Support for declarative application of
+ * Hibernate filters to persistence contexts.
+ * 
+ * @see org.hibernate.Filter
+ * @see ManagedHibernateSession
+ * @see ManagedPersistenceContext
+ * @author Gavin King
+ */
+ at BypassInterceptors
+ at Scope(ScopeType.APPLICATION)
+public class Filter implements Serializable
+{
+   private String name;
+   // default to no parameters
+   private Map<String, ValueExpression> parameters = new HashMap<String, ValueExpression>();
+   private ValueExpression enabled;
+   
+   @Create
+   public void create(Component component)
+   {
+      //default the filter name to the component name
+      if (name==null)
+      {
+         name = component.getName();
+      }
+   }
+   
+   /**
+    * The filter parameters.
+    * 
+    * @see org.hibernate.Filter#setParameter(String, Object)
+    */
+   public Map<String, ValueExpression> getParameters()
+   {
+      return parameters;
+   }
+   public void setParameters(Map<String, ValueExpression> parameters)
+   {
+      this.parameters = parameters;
+   }
+   
+   /**
+    * The Hibernate filter name.
+    * 
+    * @see org.hibernate.Session#enableFilter(String)
+    */
+   public String getName()
+   {
+      return name;
+   }
+   
+   public void setName(String name)
+   {
+      this.name = name;
+   }
+   
+   public boolean isFilterEnabled()
+   {
+      ValueExpression enabledValueBinding = getEnabled();
+      if (enabledValueBinding==null)
+      {
+         return true;
+      }
+      else
+      {
+         Boolean enabled = (Boolean) enabledValueBinding.getValue();
+         return enabled!=null && enabled;
+      }
+   }
+
+   @Override
+   public String toString()
+   {
+      return "Filter(" + name + ")";
+   }
+
+   public ValueExpression getEnabled()
+   {
+      return enabled;
+   }
+
+   public void setEnabled(ValueExpression enabled)
+   {
+      this.enabled = enabled;
+   }
+}
\ No newline at end of file

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/FullTextEntityManagerProxy.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/FullTextEntityManagerProxy.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/FullTextEntityManagerProxy.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,50 @@
+//$Id: FullTextEntityManagerProxy.java 9696 2008-12-02 12:05:50Z shane.bryzak at jboss.com $
+package org.jboss.seam.persistence;
+
+import java.io.Serializable;
+
+import org.apache.lucene.search.Query;
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.jpa.FullTextEntityManager;
+import org.hibernate.search.jpa.FullTextQuery;
+
+/**
+ * Wrap a FullTextEntityManager
+ *
+ * @author Emmanuel Bernard
+ */
+public class FullTextEntityManagerProxy extends EntityManagerProxy implements FullTextEntityManager
+{
+   private FullTextEntityManager fullTextEntityManager;
+
+   public FullTextEntityManagerProxy(FullTextEntityManager entityManager)
+   {
+      super(entityManager);
+      this.fullTextEntityManager = entityManager;
+   }
+
+   public FullTextQuery createFullTextQuery(Query query, Class... classes)
+   {
+      return fullTextEntityManager.createFullTextQuery(query, classes);
+   }
+
+   public void index(Object object)
+   {
+      fullTextEntityManager.index(object);
+   }
+
+   public SearchFactory getSearchFactory()
+   {
+      return fullTextEntityManager.getSearchFactory();
+   }
+
+   public void purge(Class aClass, Serializable serializable)
+   {
+      fullTextEntityManager.purge(aClass, serializable);
+   }
+
+   public void purgeAll(Class aClass)
+   {
+      fullTextEntityManager.purgeAll(aClass);
+   }
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/FullTextHibernateSessionProxy.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/FullTextHibernateSessionProxy.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/FullTextHibernateSessionProxy.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,168 @@
+package org.jboss.seam.persistence;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.search.FullTextQuery;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.SearchFactory;
+import org.hibernate.type.Type;
+
+/**
+ * Wraps a Hibernate Search session
+ * 
+ * @author Gavin King
+ *
+ */
+ at SuppressWarnings("deprecation")
+public class FullTextHibernateSessionProxy extends HibernateSessionProxy implements FullTextSession
+{
+   private FullTextSession fullTextSession;
+   
+   public FullTextHibernateSessionProxy(FullTextSession fullTextSession)
+   {
+      super(fullTextSession);
+      this.fullTextSession = fullTextSession;
+   }
+
+   public void index(Object arg0)
+   {
+      fullTextSession.index(arg0);
+   }
+
+   public FullTextQuery createFullTextQuery(org.apache.lucene.search.Query arg0, Class... arg1)
+   {
+      return fullTextSession.createFullTextQuery(arg0, arg1);
+   }
+
+   public Query createSQLQuery(String arg0, String arg1, Class arg2)
+   {
+      return fullTextSession.createSQLQuery(arg0, arg1, arg2);
+   }
+
+   public Query createSQLQuery(String arg0, String[] arg1, Class[] arg2)
+   {
+      return fullTextSession.createSQLQuery(arg0, arg1, arg2);
+   }
+
+   public int delete(String arg0, Object arg1, Type arg2) throws HibernateException
+   {
+      return fullTextSession.delete(arg0, arg1, arg2);
+   }
+
+   public int delete(String arg0, Object[] arg1, Type[] arg2) throws HibernateException
+   {
+      return fullTextSession.delete(arg0, arg1, arg2);
+   }
+
+   public int delete(String arg0) throws HibernateException
+   {
+      return fullTextSession.delete(arg0);
+   }
+
+   public Collection filter(Object arg0, String arg1, Object arg2, Type arg3) throws HibernateException
+   {
+      return fullTextSession.filter(arg0, arg1, arg2, arg3);
+   }
+
+   public Collection filter(Object arg0, String arg1, Object[] arg2, Type[] arg3) throws HibernateException
+   {
+      return fullTextSession.filter(arg0, arg1, arg2, arg3);
+   }
+
+   public Collection filter(Object arg0, String arg1) throws HibernateException
+   {
+      return fullTextSession.filter(arg0, arg1);
+   }
+
+   public List find(String arg0, Object arg1, Type arg2) throws HibernateException
+   {
+      return fullTextSession.find(arg0, arg1, arg2);
+   }
+
+   public List find(String arg0, Object[] arg1, Type[] arg2) throws HibernateException
+   {
+      return fullTextSession.find(arg0, arg1, arg2);
+   }
+
+   public List find(String arg0) throws HibernateException
+   {
+      return fullTextSession.find(arg0);
+   }
+
+
+   public SearchFactory getSearchFactory()
+   {
+      return fullTextSession.getSearchFactory();
+   }
+
+   public void purge(Class aClass, Serializable serializable)
+   {
+      fullTextSession.purge(aClass, serializable);
+   }
+
+   public void purgeAll(Class aClass)
+   {
+      fullTextSession.purgeAll(aClass);
+   }
+
+   public Iterator iterate(String arg0, Object arg1, Type arg2) throws HibernateException
+   {
+      return fullTextSession.iterate(arg0, arg1, arg2);
+   }
+
+   public Iterator iterate(String arg0, Object[] arg1, Type[] arg2) throws HibernateException
+   {
+      return fullTextSession.iterate(arg0, arg1, arg2);
+   }
+
+   public Iterator iterate(String arg0) throws HibernateException
+   {
+      return fullTextSession.iterate(arg0);
+   }
+
+   public void save(Object arg0, Serializable arg1) throws HibernateException
+   {
+      fullTextSession.save(arg0, arg1);
+   }
+
+   public void save(String arg0, Object arg1, Serializable arg2) throws HibernateException
+   {
+      fullTextSession.save(arg0, arg1, arg2);
+   }
+
+   public Object saveOrUpdateCopy(Object arg0, Serializable arg1) throws HibernateException
+   {
+      return fullTextSession.saveOrUpdateCopy(arg0, arg1);
+   }
+
+   public Object saveOrUpdateCopy(Object arg0) throws HibernateException
+   {
+      return fullTextSession.saveOrUpdateCopy(arg0);
+   }
+
+   public Object saveOrUpdateCopy(String arg0, Object arg1, Serializable arg2) throws HibernateException
+   {
+      return fullTextSession.saveOrUpdateCopy(arg0, arg1, arg2);
+   }
+
+   public Object saveOrUpdateCopy(String arg0, Object arg1) throws HibernateException
+   {
+      return fullTextSession.saveOrUpdateCopy(arg0, arg1);
+   }
+
+   public void update(Object arg0, Serializable arg1) throws HibernateException
+   {
+      fullTextSession.update(arg0, arg1);
+   }
+
+   public void update(String arg0, Object arg1, Serializable arg2) throws HibernateException
+   {
+      fullTextSession.update(arg0, arg1, arg2);
+   }
+   
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernatePersistenceProvider.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernatePersistenceProvider.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernatePersistenceProvider.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,393 @@
+package org.jboss.seam.persistence;
+import static org.jboss.seam.annotations.Install.FRAMEWORK;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.transaction.Synchronization;
+
+import org.hibernate.EntityMode;
+import org.hibernate.FlushMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.StaleStateException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.type.VersionType;
+import org.jboss.seam.Component;
+import org.jboss.seam.Entity;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.Entity.NotEntityException;
+import org.jboss.seam.annotations.FlushModeType;
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.core.Expressions.ValueExpression;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.log.Logging;
+/**
+ * Support for non-standardized features of Hibernate, when
+ * used as the JPA persistence provider.
+ * 
+ * @author Gavin King
+ * @author Pete Muir
+ *
+ */
+ at Name("org.jboss.seam.persistence.persistenceProvider")
+ at Scope(ScopeType.STATELESS)
+ at BypassInterceptors
+ at Install(precedence=FRAMEWORK, classDependencies={"org.hibernate.Session", "javax.persistence.EntityManager"})
+public class HibernatePersistenceProvider extends PersistenceProvider
+{
+   
+   private static Log log = Logging.getLog(HibernatePersistenceProvider.class);
+   private static Constructor FULL_TEXT_SESSION_PROXY_CONSTRUCTOR;
+   private static Method FULL_TEXT_SESSION_CONSTRUCTOR;
+   private static Constructor FULL_TEXT_ENTITYMANAGER_PROXY_CONSTRUCTOR;
+   private static Method FULL_TEXT_ENTITYMANAGER_CONSTRUCTOR;
+   static
+   {
+      try
+      {
+         String version = null;
+         try {
+            Class searchVersionClass = Class.forName("org.hibernate.search.Version");
+            Field versionField = searchVersionClass.getDeclaredField("VERSION");
+            version = (String) versionField.get(null);
+         }
+         catch (Exception e)
+         {
+            log.debug("no Hibernate Search, sorry :-(", e);
+         }
+         if (version != null) {
+            Class searchClass = Class.forName("org.hibernate.search.Search");
+            FULL_TEXT_SESSION_CONSTRUCTOR = searchClass.getDeclaredMethod("createFullTextSession", Session.class);
+            Class fullTextSessionProxyClass = Class.forName("org.jboss.seam.persistence.FullTextHibernateSessionProxy");
+            Class fullTextSessionClass = Class.forName("org.hibernate.search.FullTextSession");
+            FULL_TEXT_SESSION_PROXY_CONSTRUCTOR = fullTextSessionProxyClass.getDeclaredConstructor(fullTextSessionClass);
+            Class jpaSearchClass = Class.forName("org.hibernate.search.jpa.Search");
+            FULL_TEXT_ENTITYMANAGER_CONSTRUCTOR = jpaSearchClass.getDeclaredMethod("createFullTextEntityManager", EntityManager.class);
+            Class fullTextEntityManagerProxyClass = Class.forName("org.jboss.seam.persistence.FullTextEntityManagerProxy");
+            Class fullTextEntityManagerClass = Class.forName("org.hibernate.search.jpa.FullTextEntityManager");
+            FULL_TEXT_ENTITYMANAGER_PROXY_CONSTRUCTOR = fullTextEntityManagerProxyClass.getDeclaredConstructor(fullTextEntityManagerClass);
+            log.debug("Hibernate Search is available :-)");
+         }
+      }
+      catch (Exception e)
+      {
+         log.debug("no Hibernate Search, sorry :-(", e);
+      }
+   }
+
+   @Override
+   public void init()
+   {
+      super.init();
+      featureSet.add(Feature.WILDCARD_AS_COUNT_QUERY_SUBJECT);
+   }
+   
+   /**
+    * Wrap the Hibernate Session in a proxy that supports HQL
+    * EL interpolation and implements FullTextSession if Hibernate
+    * Search is available in the classpath.
+    */
+   static Session proxySession(Session session)
+   {
+      if (FULL_TEXT_SESSION_PROXY_CONSTRUCTOR==null)
+      {
+         return new HibernateSessionProxy(session);
+      }
+      else
+      {
+         try {
+            return (Session) FULL_TEXT_SESSION_PROXY_CONSTRUCTOR.newInstance( FULL_TEXT_SESSION_CONSTRUCTOR.invoke(null, session) );
+         }
+         catch(Exception e) {
+            log.warn("Unable to wrap into a FullTextSessionProxy, regular SessionProxy returned", e);
+            return new HibernateSessionProxy(session);
+         }
+      }
+   }
+   
+   /**
+    * Wrap the delegate Hibernate Session in a proxy that supports HQL
+    * EL interpolation and implements FullTextSession if Hibernate
+    * Search is available in the classpath.
+    */
+   @Override
+   public Object proxyDelegate(Object delegate)
+   {
+      try
+      {
+         return proxySession( (Session) delegate );
+      }
+      catch (NotHibernateException nhe)
+      {
+         return super.proxyDelegate(delegate);
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("could not proxy delegate", e);
+      }
+   }
+   
+   @Override
+   public void setFlushModeManual(EntityManager entityManager)
+   {
+       try
+       {
+          getSession(entityManager).setFlushMode(FlushMode.MANUAL);
+       }
+       catch (NotHibernateException nhe)
+       {
+          super.setFlushModeManual(entityManager);
+       }
+   }
+   
+   @Override
+   public void setRenderFlushMode()
+   {
+      PersistenceContexts.instance().changeFlushMode(FlushModeType.MANUAL, true);
+   }
+
+   @Override
+   public boolean isDirty(EntityManager entityManager)
+   {
+       try
+       {
+          return getSession(entityManager).isDirty();
+       }
+       catch (NotHibernateException nhe)
+       {
+          return super.isDirty(entityManager);
+       }
+   }
+   
+   @Override
+   public Object getId(Object bean, EntityManager entityManager) 
+   {
+       try
+       {
+          return getSession(entityManager).getIdentifier(bean);
+       }
+       catch (NotHibernateException nhe)
+       {
+          return super.getId(bean, entityManager);
+       }
+       catch (TransientObjectException e) 
+       {
+          if (bean instanceof HibernateProxy)
+          {
+             return super.getId(((HibernateProxy) bean).getHibernateLazyInitializer().getImplementation(), entityManager);
+          }
+          else
+          {
+             return super.getId(bean, entityManager);
+          }
+       }
+   }
+   
+   @Override
+   public Object getVersion(Object bean, EntityManager entityManager) 
+   {
+       try
+       {
+          return getVersion( bean, getSession(entityManager) );
+       }
+       catch (NotHibernateException nhe)
+       {
+          return super.getVersion(bean, entityManager);
+       }
+   }
+   
+   @Override
+   public void checkVersion(Object bean, EntityManager entityManager, Object oldVersion, Object version)
+   {
+       try
+       {
+          checkVersion(bean, getSession(entityManager), oldVersion, version);
+       }
+       catch (NotHibernateException nhe)
+       {
+          super.checkVersion(bean, entityManager, oldVersion, version);
+       }
+   }
+   
+   @Override
+   public void enableFilter(Filter f, EntityManager entityManager)
+   {
+      try
+      {
+         org.hibernate.Filter filter = getSession(entityManager).enableFilter( f.getName() );
+         for ( Map.Entry<String, ValueExpression> me: f.getParameters().entrySet() )
+         {
+		   Object filterValue = me.getValue().getValue();
+		   if ( filterValue instanceof Collection ) {
+		      filter.setParameterList(me.getKey(), (Collection) filterValue);
+		   } else {
+			  filter.setParameter(me.getKey(), filterValue);
+		   }
+         }
+         filter.validate();
+      }
+      catch (NotHibernateException nhe)
+      {
+         super.enableFilter(f, entityManager);
+      }
+
+   }
+   
+   @Override
+   public boolean registerSynchronization(Synchronization sync, EntityManager entityManager)
+   {
+      try
+      {
+         //TODO: just make sure that a Hibernate JPA EntityTransaction
+         //      delegates to the Hibernate Session transaction
+         getSession(entityManager).getTransaction().registerSynchronization(sync);
+         return true;
+      }
+      catch (NotHibernateException nhe)
+      {
+         return super.registerSynchronization(sync, entityManager);
+      }
+
+   }
+
+   @Override
+   public String getName(Object bean, EntityManager entityManager) throws IllegalArgumentException
+   {
+      try 
+      {
+         return getSession(entityManager).getEntityName(bean);
+      } 
+      catch (NotHibernateException nhe)
+      {
+         return super.getName(bean, entityManager);
+      }
+      catch (TransientObjectException e) 
+      {
+         return super.getName(bean, entityManager);
+      }
+   }
+   
+   @Override
+   public EntityManager proxyEntityManager(EntityManager entityManager)
+   {
+      if (FULL_TEXT_ENTITYMANAGER_PROXY_CONSTRUCTOR==null)
+      {
+         return super.proxyEntityManager(entityManager);
+      }
+      else
+      {
+         try
+         {
+            return (EntityManager) FULL_TEXT_ENTITYMANAGER_PROXY_CONSTRUCTOR.newInstance(
+               FULL_TEXT_ENTITYMANAGER_CONSTRUCTOR.invoke(null, super.proxyEntityManager( entityManager) )
+               //TODO is double wrapping the right choice? ie to wrap the session?
+         );
+       }
+         catch (Exception e)
+         {
+            //throw new RuntimeException("could not proxy FullTextEntityManager", e);
+            return super.proxyEntityManager(entityManager);
+         }
+      }
+   }
+   
+   public static void checkVersion(Object value, Session session, Object oldVersion, Object version)
+   {
+      ClassMetadata classMetadata = getClassMetadata(value, session);
+      VersionType versionType = (VersionType) classMetadata.getPropertyTypes()[ classMetadata.getVersionProperty() ];
+      if ( !versionType.isEqual(oldVersion, version) )
+      {
+         throw new StaleStateException("current database version number does not match passivated version number");
+      }
+   }
+   
+   public static Object getVersion(Object value, Session session)
+   {
+      ClassMetadata classMetadata = getClassMetadata(value, session);
+      return classMetadata!=null && classMetadata.isVersioned() ? 
+               classMetadata.getVersion(value, EntityMode.POJO) : null;
+   }
+   
+   private static ClassMetadata getClassMetadata(Object value, Session session)
+   {
+      Class entityClass = getEntityClass(value);
+      ClassMetadata classMetadata = null;
+      if (entityClass!=null)
+      {
+         classMetadata = session.getSessionFactory().getClassMetadata(entityClass);
+         if (classMetadata==null)
+         {
+            throw new IllegalArgumentException( 
+                     "Could not find ClassMetadata object for entity class: " + 
+                     entityClass.getName() 
+                  );
+         }
+      }
+      return classMetadata;
+   }
+   
+   /**
+    * Returns the class of the specified Hibernate entity
+    */
+   @Override
+   public Class getBeanClass(Object bean)
+   {
+      return getEntityClass(bean);
+   }
+   
+   public static Class getEntityClass(Object bean)
+   {
+      Class clazz = null;
+      try
+      {
+         clazz = Entity.forBean(bean).getBeanClass();
+      }
+      catch (NotEntityException e) {
+         // It's ok, try some other methods
+      }
+      
+      if (clazz == null)
+      {
+         clazz = Hibernate.getClass(bean);
+      }
+      
+      return clazz;
+   }
+   
+   private Session getSession(EntityManager entityManager)
+   {
+      Object delegate = entityManager.getDelegate();
+      if ( delegate instanceof Session )
+      {
+         return (Session) delegate;
+      }
+      else
+      {
+         throw new NotHibernateException();
+      }
+   }
+   
+   /**
+    * Occurs when Hibernate is in the classpath, but this particular
+    * EntityManager is not from Hibernate
+    * 
+    * @author Gavin King
+    *
+    */
+   static class NotHibernateException extends IllegalArgumentException {}
+   
+   public static HibernatePersistenceProvider instance()
+   {
+       return (HibernatePersistenceProvider) Component.getInstance(HibernatePersistenceProvider.class, ScopeType.STATELESS);
+   }
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionFactory.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionFactory.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionFactory.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,248 @@
+//$Id: HibernateSessionFactory.java 6143 2007-09-07 00:59:34Z gavin $
+package org.jboss.seam.persistence;
+
+import java.io.File;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.AnnotationConfiguration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.NamingStrategy;
+import org.hibernate.util.ReflectHelper;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.Create;
+import org.jboss.seam.annotations.Destroy;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Startup;
+import org.jboss.seam.annotations.Unwrap;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.security.HibernateSecurityInterceptor;
+import org.jboss.seam.util.Naming;
+
+/**
+ * A Seam component that bootstraps a Hibernate SessionFactory
+ * 
+ * <p>
+ * Loads Hibernate configuration options by checking:
+ * <li>hibernate.properties in root of the classpath
+ * <li>hibernate.cfg.xml in root of the classpath
+ * <li>cfgResourceName as location of a cfg.xml file
+ * <li>factory-supplied cfgProperties options
+ * <p>
+ * Note that this factory only supports cfg.xml files <b>or</b> programmatic
+ * <tt>cfgProperties</tt> supplied to the factory. Any
+ * <tt>hibernate.properties</tt> are always loaded from the classpath.
+ * <p>
+ * Mapping metadata can be supplied via
+ * <li>mappingClasses: equivalent to &lt;mapping class="..."/>
+ * <li>mappingFiles: equivalent to &lt;mapping file="..."/>
+ * <li>mappingJars: equivalent to &lt;mapping jar="..."/>
+ * <li>mappingPackages: equivalent to &lt;mapping package="..."/>
+ * <li>mappingResources: equivalent to &lt;mapping resource="..."/>
+ * <p>
+ * or via cfg.xml files.
+ * <p>
+ * The <tt>jndiProperties</tt> are convenience, the factory will automatically
+ * prefix regular JNDI properties for use as Hibernate configuration properties.
+ * 
+ * @author Gavin King
+ * @author Christian Bauer
+ */
+ at Scope(ScopeType.APPLICATION)
+ at BypassInterceptors
+ at Startup
+public class HibernateSessionFactory
+{
+   private SessionFactory sessionFactory;
+
+   private String cfgResourceName;
+   private Map<String, String> cfgProperties;
+   private List<String> mappingClasses;
+   private List<String> mappingFiles;
+   private List<String> mappingJars;
+   private List<String> mappingPackages;
+   private List<String> mappingResources;
+   private NamingStrategy namingStrategy;
+   
+   @Unwrap
+   public SessionFactory getSessionFactory() throws Exception
+   {
+      return sessionFactory;
+   }
+   
+   @Create
+   public void startup() throws Exception
+   {
+      sessionFactory = createSessionFactory();
+   }
+   
+   @Destroy
+   public void shutdown()
+   {
+      if (sessionFactory!=null)
+      {
+         sessionFactory.close();
+      }
+   }
+
+   protected SessionFactory createSessionFactory() throws ClassNotFoundException
+   {
+      AnnotationConfiguration configuration = new AnnotationConfiguration();
+      
+      // setup non-default naming strategy
+      if (namingStrategy != null)
+      {
+         configuration.setNamingStrategy(namingStrategy);
+      }
+      
+      // Programmatic configuration
+      if (cfgProperties != null)
+      {
+         Properties props = new Properties();
+         props.putAll(cfgProperties);
+         configuration.setProperties(props);
+      }
+      Hashtable<String, String> jndiProperties = Naming.getInitialContextProperties();
+      if ( jndiProperties!=null )
+      {
+         // Prefix regular JNDI properties for Hibernate
+         for (Map.Entry<String, String> entry : jndiProperties.entrySet())
+         {
+            configuration.setProperty( Environment.JNDI_PREFIX + "." + entry.getKey(), entry.getValue() );
+         }
+      }
+      // hibernate.cfg.xml configuration
+      if (cfgProperties==null && cfgResourceName==null)
+      {
+         configuration.configure();
+      } 
+      else if (cfgProperties==null && cfgResourceName!=null)
+      {
+         configuration.configure(cfgResourceName);
+      }
+      // Mapping metadata
+      if (mappingClasses!=null)
+      {
+         for (String className: mappingClasses) 
+         {
+            configuration.addAnnotatedClass(ReflectHelper.classForName(className));
+         }
+      }
+      if (mappingFiles!=null)
+      {
+         for (String fileName: mappingFiles) 
+         {
+            configuration.addFile(fileName);
+         }
+      }
+      if (mappingJars!=null)
+      {
+         for (String jarName: mappingJars) 
+         {
+            configuration.addJar(new File(jarName));
+         }
+      }
+      if (mappingPackages!= null)
+      {
+         for (String packageName: mappingPackages) 
+         {
+            configuration.addPackage(packageName);
+         }
+      }
+      if (mappingResources!= null)
+      {
+         for (String resourceName : mappingResources) 
+         {
+            configuration.addResource(resourceName);
+         }
+      }
+      
+      configuration.setInterceptor(new HibernateSecurityInterceptor(configuration.getInterceptor()));
+      
+      return configuration.buildSessionFactory();
+   }
+   
+   public String getCfgResourceName()
+   {
+      return cfgResourceName;
+   }
+   
+   public void setCfgResourceName(String cfgFileName)
+   {
+      this.cfgResourceName = cfgFileName;
+   }
+   
+   public NamingStrategy getNamingStrategy()
+   {
+      return namingStrategy;
+   }
+   
+   public void setNamingStrategy(NamingStrategy namingStrategy)
+   {
+      this.namingStrategy = namingStrategy;
+   }
+   
+   public Map<String, String> getCfgProperties()
+   {
+      return cfgProperties;
+   }
+   
+   public void setCfgProperties(Map<String, String> cfgProperties)
+   {
+      this.cfgProperties = cfgProperties;
+   }
+   
+   public List<String> getMappingClasses()
+   {
+      return mappingClasses;
+   }
+   
+   public void setMappingClasses(List<String> mappingClasses)
+   {
+      this.mappingClasses = mappingClasses;
+   }
+   
+   public List<String> getMappingFiles()
+   {
+      return mappingFiles;
+   }
+   
+   public void setMappingFiles(List<String> mappingFiles)
+   {
+      this.mappingFiles = mappingFiles;
+   }
+   
+   public List<String> getMappingJars()
+   {
+      return mappingJars;
+   }
+   
+   public void setMappingJars(List<String> mappingJars)
+   {
+      this.mappingJars = mappingJars;
+   }
+   
+   public List<String> getMappingPackages()
+   {
+      return mappingPackages;
+   }
+   
+   public void setMappingPackages(List<String> mappingPackages)
+   {
+      this.mappingPackages = mappingPackages;
+   }
+   
+   public List<String> getMappingResources()
+   {
+      return mappingResources;
+   }
+   
+   public void setMappingResources(List<String> mappingResources)
+   {
+      this.mappingResources = mappingResources;
+   }
+   
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionProxy.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionProxy.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionProxy.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,669 @@
+package org.jboss.seam.persistence;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.CacheMode;
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.Filter;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.ReplicationMode;
+import org.hibernate.SQLQuery;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.ActionQueue;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.event.EventListeners;
+import org.hibernate.event.EventSource;
+import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.jdbc.Batcher;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.loader.custom.CustomQuery;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.stat.SessionStatistics;
+import org.hibernate.type.Type;
+
+/**
+ * Proxies the Session, and implements EL interpolation
+ * in HQL. Needs to implement SessionImplementor because
+ * DetachedCriteria casts the Session to SessionImplementor.
+ * 
+ * @author Gavin King
+ * @author Emmanuel Bernard
+ * FIXME: EventSource should not really be there, remove once HSearch is fixed
+ *
+ */
+public class HibernateSessionProxy implements Session, SessionImplementor, EventSource
+{
+   private Session delegate;
+
+   /**
+    * Don't use that constructor directly, use HibernatePersistenceProvider.proxySession()
+    */
+   public HibernateSessionProxy(Session session)
+   {
+      delegate = session;
+   }
+
+   public Transaction beginTransaction() throws HibernateException
+   {
+      return delegate.beginTransaction();
+   }
+
+   public void cancelQuery() throws HibernateException
+   {
+      delegate.cancelQuery();
+   }
+
+   public void clear()
+   {
+      delegate.clear();
+   }
+
+   public Connection close() throws HibernateException
+   {
+      return delegate.close();
+   }
+
+   @SuppressWarnings("deprecation")
+   public Connection connection() throws HibernateException
+   {
+      return delegate.connection();
+   }
+
+   public boolean contains(Object arg0)
+   {
+      return delegate.contains(arg0);
+   }
+
+   public Criteria createCriteria(Class arg0, String arg1)
+   {
+      return delegate.createCriteria(arg0, arg1);
+   }
+
+   public Criteria createCriteria(Class arg0)
+   {
+      return delegate.createCriteria(arg0);
+   }
+
+   public Criteria createCriteria(String arg0, String arg1)
+   {
+      return delegate.createCriteria(arg0, arg1);
+   }
+
+   public Criteria createCriteria(String arg0)
+   {
+      return delegate.createCriteria(arg0);
+   }
+
+   public Query createFilter(Object arg0, String arg1) throws HibernateException
+   {
+      return delegate.createFilter(arg0, arg1);
+   }
+
+   public Query createQuery(String hql) throws HibernateException
+   {
+      if ( hql.indexOf('#')>0 )
+      {
+         QueryParser qp = new QueryParser(hql);
+         Query query = delegate.createQuery( qp.getEjbql() );
+         for (int i=0; i<qp.getParameterValueBindings().size(); i++)
+         {
+            query.setParameter( 
+                     QueryParser.getParameterName(i), 
+                     qp.getParameterValueBindings().get(i).getValue() 
+                  );
+         }
+         return query;
+      }
+      else
+      {
+         return delegate.createQuery(hql);
+      }
+   }
+
+   public SQLQuery createSQLQuery(String arg0) throws HibernateException
+   {
+      return delegate.createSQLQuery(arg0);
+   }
+
+   public void delete(Object arg0) throws HibernateException
+   {
+      delegate.delete(arg0);
+   }
+
+   public void delete(String arg0, Object arg1) throws HibernateException
+   {
+      delegate.delete(arg0, arg1);
+   }
+
+   public void disableFilter(String arg0)
+   {
+      delegate.disableFilter(arg0);
+   }
+
+   public Connection disconnect() throws HibernateException
+   {
+      return delegate.disconnect();
+   }
+
+   public Filter enableFilter(String arg0)
+   {
+      return delegate.enableFilter(arg0);
+   }
+
+   public void evict(Object arg0) throws HibernateException
+   {
+      delegate.evict(arg0);
+   }
+
+   public void flush() throws HibernateException
+   {
+      delegate.flush();
+   }
+
+   public Object get(Class arg0, Serializable arg1, LockMode arg2) throws HibernateException
+   {
+      return delegate.get(arg0, arg1, arg2);
+   }
+
+   public Object get(Class arg0, Serializable arg1) throws HibernateException
+   {
+      return delegate.get(arg0, arg1);
+   }
+
+   public Object get(String arg0, Serializable arg1, LockMode arg2) throws HibernateException
+   {
+      return delegate.get(arg0, arg1, arg2);
+   }
+
+   public Object get(String arg0, Serializable arg1) throws HibernateException
+   {
+      return delegate.get(arg0, arg1);
+   }
+
+   public CacheMode getCacheMode()
+   {
+      return delegate.getCacheMode();
+   }
+
+   public LockMode getCurrentLockMode(Object arg0) throws HibernateException
+   {
+      return delegate.getCurrentLockMode(arg0);
+   }
+
+   public Filter getEnabledFilter(String arg0)
+   {
+      return delegate.getEnabledFilter(arg0);
+   }
+
+   public EntityMode getEntityMode()
+   {
+      return delegate.getEntityMode();
+   }
+
+   public String getEntityName(Object arg0) throws HibernateException
+   {
+      return delegate.getEntityName(arg0);
+   }
+
+   public FlushMode getFlushMode()
+   {
+      return delegate.getFlushMode();
+   }
+
+   public Serializable getIdentifier(Object arg0) throws HibernateException
+   {
+      return delegate.getIdentifier(arg0);
+   }
+
+   public Query getNamedQuery(String arg0) throws HibernateException
+   {
+      return delegate.getNamedQuery(arg0);
+   }
+
+   public Session getSession(EntityMode arg0)
+   {
+      return delegate.getSession(arg0);
+   }
+
+   public SessionFactory getSessionFactory()
+   {
+      return delegate.getSessionFactory();
+   }
+
+   public SessionStatistics getStatistics()
+   {
+      return delegate.getStatistics();
+   }
+
+   public Transaction getTransaction()
+   {
+      return delegate.getTransaction();
+   }
+
+   public boolean isConnected()
+   {
+      return delegate.isConnected();
+   }
+
+   public boolean isDirty() throws HibernateException
+   {
+      return delegate.isDirty();
+   }
+
+   public boolean isOpen()
+   {
+      return delegate.isOpen();
+   }
+
+   public Object load(Class arg0, Serializable arg1, LockMode arg2) throws HibernateException
+   {
+      return delegate.load(arg0, arg1, arg2);
+   }
+
+   public Object load(Class arg0, Serializable arg1) throws HibernateException
+   {
+      return delegate.load(arg0, arg1);
+   }
+
+   public void load(Object arg0, Serializable arg1) throws HibernateException
+   {
+      delegate.load(arg0, arg1);
+   }
+
+   public Object load(String arg0, Serializable arg1, LockMode arg2) throws HibernateException
+   {
+      return delegate.load(arg0, arg1, arg2);
+   }
+
+   public Object load(String arg0, Serializable arg1) throws HibernateException
+   {
+      return delegate.load(arg0, arg1);
+   }
+
+   public void lock(Object arg0, LockMode arg1) throws HibernateException
+   {
+      delegate.lock(arg0, arg1);
+   }
+
+   public void lock(String arg0, Object arg1, LockMode arg2) throws HibernateException
+   {
+      delegate.lock(arg0, arg1, arg2);
+   }
+
+   public Object merge(Object arg0) throws HibernateException
+   {
+      return delegate.merge(arg0);
+   }
+
+   public Object merge(String arg0, Object arg1) throws HibernateException
+   {
+      return delegate.merge(arg0, arg1);
+   }
+
+   public void persist(Object arg0) throws HibernateException
+   {
+      delegate.persist(arg0);
+   }
+
+   public void persist(String arg0, Object arg1) throws HibernateException
+   {
+      delegate.persist(arg0, arg1);
+   }
+
+   public void reconnect() throws HibernateException
+   {
+      throw new UnsupportedOperationException("deprecated");
+   }
+
+   public void reconnect(Connection arg0) throws HibernateException
+   {
+      delegate.reconnect(arg0);
+   }
+
+   public void refresh(Object arg0, LockMode arg1) throws HibernateException
+   {
+      delegate.refresh(arg0, arg1);
+   }
+
+   public void refresh(Object arg0) throws HibernateException
+   {
+      delegate.refresh(arg0);
+   }
+
+   public void replicate(Object arg0, ReplicationMode arg1) throws HibernateException
+   {
+      delegate.replicate(arg0, arg1);
+   }
+
+   public void replicate(String arg0, Object arg1, ReplicationMode arg2) throws HibernateException
+   {
+      delegate.replicate(arg0, arg1, arg2);
+   }
+
+   public Serializable save(Object arg0) throws HibernateException
+   {
+      return delegate.save(arg0);
+   }
+
+   public Serializable save(String arg0, Object arg1) throws HibernateException
+   {
+      return delegate.save(arg0, arg1);
+   }
+
+   public void saveOrUpdate(Object arg0) throws HibernateException
+   {
+      delegate.saveOrUpdate(arg0);
+   }
+
+   public void saveOrUpdate(String arg0, Object arg1) throws HibernateException
+   {
+      delegate.saveOrUpdate(arg0, arg1);
+   }
+
+   public void setCacheMode(CacheMode arg0)
+   {
+      delegate.setCacheMode(arg0);
+   }
+
+   public void setFlushMode(FlushMode arg0)
+   {
+      delegate.setFlushMode(arg0);
+   }
+
+   public void setReadOnly(Object arg0, boolean arg1)
+   {
+      delegate.setReadOnly(arg0, arg1);
+   }
+
+   public void update(Object arg0) throws HibernateException
+   {
+      delegate.update(arg0);
+   }
+
+   public void update(String arg0, Object arg1) throws HibernateException
+   {
+      delegate.update(arg0, arg1);
+   }
+   
+   private SessionImplementor getDelegateSessionImplementor()
+   {
+      return (SessionImplementor) delegate;
+   }
+
+   private EventSource getDelegateEventSource()
+   {
+      return (EventSource) delegate;
+   }
+
+   public void afterScrollOperation()
+   {
+      getDelegateSessionImplementor().afterScrollOperation();
+   }
+
+   public void afterTransactionCompletion(boolean arg0, Transaction arg1)
+   {
+      getDelegateSessionImplementor().afterTransactionCompletion(arg0, arg1);
+   }
+
+   public void beforeTransactionCompletion(Transaction arg0)
+   {
+      getDelegateSessionImplementor().beforeTransactionCompletion(arg0);
+   }
+
+   public String bestGuessEntityName(Object arg0)
+   {
+      return getDelegateSessionImplementor().bestGuessEntityName(arg0);
+   }
+
+   public int executeNativeUpdate(NativeSQLQuerySpecification arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().executeNativeUpdate(arg0, arg1);
+   }
+
+   public int executeUpdate(String arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().executeUpdate(arg0, arg1);
+   }
+
+   public Batcher getBatcher()
+   {
+      return getDelegateSessionImplementor().getBatcher();
+   }
+
+   public Serializable getContextEntityIdentifier(Object arg0)
+   {
+      return getDelegateSessionImplementor().getContextEntityIdentifier(arg0);
+   }
+
+   public int getDontFlushFromFind()
+   {
+      return getDelegateSessionImplementor().getDontFlushFromFind();
+   }
+
+   public Map getEnabledFilters()
+   {
+      return getDelegateSessionImplementor().getEnabledFilters();
+   }
+
+   public EntityPersister getEntityPersister(String arg0, Object arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().getEntityPersister(arg0, arg1);
+   }
+
+   public Object getEntityUsingInterceptor(EntityKey arg0) throws HibernateException
+   {
+      return getDelegateSessionImplementor().getEntityUsingInterceptor(arg0);
+   }
+
+   public SessionFactoryImplementor getFactory()
+   {
+      return getDelegateSessionImplementor().getFactory();
+   }
+
+   public String getFetchProfile()
+   {
+      return getDelegateSessionImplementor().getFetchProfile();
+   }
+
+   public Type getFilterParameterType(String arg0)
+   {
+      return getDelegateSessionImplementor().getFilterParameterType(arg0);
+   }
+
+   public Object getFilterParameterValue(String arg0)
+   {
+      return getDelegateSessionImplementor().getFilterParameterValue(arg0);
+   }
+
+   public Interceptor getInterceptor()
+   {
+      return getDelegateSessionImplementor().getInterceptor();
+   }
+
+   public JDBCContext getJDBCContext()
+   {
+      return getDelegateSessionImplementor().getJDBCContext();
+   }
+
+   public EventListeners getListeners()
+   {
+      return getDelegateSessionImplementor().getListeners();
+   }
+
+   public Query getNamedSQLQuery(String arg0)
+   {
+      return getDelegateSessionImplementor().getNamedSQLQuery(arg0);
+   }
+
+   public PersistenceContext getPersistenceContext()
+   {
+      return getDelegateSessionImplementor().getPersistenceContext();
+   }
+
+   public long getTimestamp()
+   {
+      return getDelegateSessionImplementor().getTimestamp();
+   }
+
+   public String guessEntityName(Object arg0) throws HibernateException
+   {
+      return getDelegateSessionImplementor().guessEntityName(arg0);
+   }
+
+   public Object immediateLoad(String arg0, Serializable arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().immediateLoad(arg0, arg1);
+   }
+
+   public void initializeCollection(PersistentCollection arg0, boolean arg1) throws HibernateException
+   {
+      getDelegateSessionImplementor().initializeCollection(arg0, arg1);
+   }
+
+   public Object instantiate(String arg0, Serializable arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().instantiate(arg0, arg1);
+   }
+
+   public Object internalLoad(String arg0, Serializable arg1, boolean arg2, boolean arg3) throws HibernateException
+   {
+      return getDelegateSessionImplementor().internalLoad(arg0, arg1, arg2, arg3);
+   }
+
+   public boolean isClosed()
+   {
+      return getDelegateSessionImplementor().isClosed();
+   }
+
+   public boolean isEventSource()
+   {
+      return getDelegateSessionImplementor().isEventSource();
+   }
+
+   public boolean isTransactionInProgress()
+   {
+      return getDelegateSessionImplementor().isTransactionInProgress();
+   }
+
+   public Iterator iterate(String arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().iterate(arg0, arg1);
+   }
+
+   public Iterator iterateFilter(Object arg0, String arg1, QueryParameters arg2) throws HibernateException
+   {
+      return getDelegateSessionImplementor().iterateFilter(arg0, arg1, arg2);
+   }
+
+   public List list(CriteriaImpl arg0)
+   {
+      return getDelegateSessionImplementor().list(arg0);
+   }
+
+   public List list(NativeSQLQuerySpecification arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().list(arg0, arg1);
+   }
+
+   public List list(String arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().list(arg0, arg1);
+   }
+
+   public List listCustomQuery(CustomQuery arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().listCustomQuery(arg0, arg1);
+   }
+
+   public List listFilter(Object arg0, String arg1, QueryParameters arg2) throws HibernateException
+   {
+      return getDelegateSessionImplementor().listFilter(arg0, arg1, arg2);
+   }
+
+   public ScrollableResults scroll(CriteriaImpl arg0, ScrollMode arg1)
+   {
+      return getDelegateSessionImplementor().scroll(arg0, arg1);
+   }
+
+   public ScrollableResults scroll(NativeSQLQuerySpecification arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().scroll(arg0, arg1);
+   }
+
+   public ScrollableResults scroll(String arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().scroll(arg0, arg1);
+   }
+
+   public ScrollableResults scrollCustomQuery(CustomQuery arg0, QueryParameters arg1) throws HibernateException
+   {
+      return getDelegateSessionImplementor().scrollCustomQuery(arg0, arg1);
+   }
+
+   public void setAutoClear(boolean arg0)
+   {
+      getDelegateSessionImplementor().setAutoClear(arg0);
+   }
+
+   public void setFetchProfile(String arg0)
+   {
+      getDelegateSessionImplementor().setFetchProfile(arg0);
+   }
+
+	public ActionQueue getActionQueue() {
+		return getDelegateEventSource().getActionQueue();
+	}
+
+	public Object instantiate(EntityPersister entityPersister, Serializable serializable) throws HibernateException {
+		return getDelegateEventSource().instantiate( entityPersister, serializable );
+	}
+
+	public void forceFlush(EntityEntry entityEntry) throws HibernateException {
+		getDelegateEventSource().forceFlush( entityEntry );
+	}
+
+	public void merge(String s, Object o, Map map) throws HibernateException {
+		getDelegateEventSource().merge( s, o, map );
+	}
+
+	public void persist(String s, Object o, Map map) throws HibernateException {
+		getDelegateEventSource().persist( s, o, map );
+	}
+
+	public void persistOnFlush(String s, Object o, Map map) {
+		getDelegateEventSource().persistOnFlush( s, o, map );
+	}
+
+	public void refresh(Object o, Map map) throws HibernateException {
+		getDelegateEventSource().refresh( o, map );
+	}
+
+	public void saveOrUpdateCopy(String s, Object o, Map map) throws HibernateException {
+		getDelegateEventSource().saveOrUpdateCopy( s, o , map );
+	}
+
+	public void delete(String s, Object o, boolean b, Set set) {
+		getDelegateEventSource().delete( s, o, b, set );
+	}
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionProxyInterceptor.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionProxyInterceptor.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/HibernateSessionProxyInterceptor.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,67 @@
+package org.jboss.seam.persistence;
+
+import static org.jboss.seam.ComponentType.STATEFUL_SESSION_BEAN;
+import static org.jboss.seam.ComponentType.STATELESS_SESSION_BEAN;
+
+import org.hibernate.Session;
+import org.jboss.seam.Component.BijectedAttribute;
+import org.jboss.seam.annotations.intercept.AroundInvoke;
+import org.jboss.seam.annotations.intercept.Interceptor;
+import org.jboss.seam.annotations.intercept.PostActivate;
+import org.jboss.seam.annotations.intercept.PostConstruct;
+import org.jboss.seam.intercept.AbstractInterceptor;
+import org.jboss.seam.intercept.InvocationContext;
+import org.jboss.seam.util.Reflections;
+
+/**
+ * Proxy the Hibernate Session if injected using @PersistenceContext
+ * 
+ * @author Pete Muir
+ *
+ */
+
+ at Interceptor(stateless=true)
+public class HibernateSessionProxyInterceptor extends AbstractInterceptor
+{
+
+   @AroundInvoke
+   public Object aroundInvoke(InvocationContext ic) throws Exception
+   {
+      return ic.proceed();
+   }
+   
+   @PostActivate
+   public void postActivate(InvocationContext invocation) throws Exception
+   {
+      //just in case the container does some special handling of PC serialization
+      proxyPersistenceContexts(invocation.getTarget());
+      invocation.proceed();
+   }
+   
+   @PostConstruct
+   public void postConstruct(InvocationContext invocation) throws Exception
+   {
+      proxyPersistenceContexts(invocation.getTarget());
+      invocation.proceed();
+   }
+   
+   
+   private void proxyPersistenceContexts(Object bean)
+   {
+      //wrap any @PersistenceContext attributes in our proxy
+      for ( BijectedAttribute ba: getComponent().getPersistenceContextAttributes() )
+      {
+         Object object = ba.get(bean);
+         if ( ! ( object instanceof HibernateSessionProxy) && object instanceof Session)
+         {
+            ba.set( bean, HibernatePersistenceProvider.proxySession( (Session) object ) );
+         }
+      }
+   }
+   
+   public boolean isInterceptorEnabled()
+   {
+      return (getComponent().getType()==STATEFUL_SESSION_BEAN || getComponent().getType()==STATELESS_SESSION_BEAN) && Reflections.isClassAvailable("org.hibernate.Session");
+   }
+
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedEntityInterceptor.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedEntityInterceptor.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedEntityInterceptor.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,80 @@
+package org.jboss.seam.persistence;
+
+import static org.jboss.seam.ScopeType.CONVERSATION;
+
+import org.jboss.seam.annotations.intercept.AroundInvoke;
+import org.jboss.seam.annotations.intercept.Interceptor;
+import org.jboss.seam.core.BijectionInterceptor;
+import org.jboss.seam.intercept.AbstractInterceptor;
+import org.jboss.seam.intercept.InvocationContext;
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.transaction.Transaction;
+
+/**
+ * Swizzles entity references around each invocation, maintaining referential
+ * integrity even across passivation of the stateful bean or Seam-managed
+ * extended persistence context, and allowing for more efficient replication.
+ * 
+ * @author Gavin King
+ * @author Pete Muir
+ * 
+ */
+ at Interceptor(around = BijectionInterceptor.class)
+public class ManagedEntityInterceptor extends AbstractInterceptor
+{
+
+   private static LogProvider log = Logging.getLogProvider(ManagedEntityInterceptor.class);
+   
+   private static ManagedEntityWrapper managedEntityWrapper = new ManagedEntityWrapper();
+
+   private boolean reentrant;
+   
+   @AroundInvoke
+   public Object aroundInvoke(InvocationContext ctx) throws Exception
+   {
+      if (reentrant)
+      {
+         return ctx.proceed();
+      }
+      else
+      {
+         reentrant = true;
+         log.trace("Attempting to activate " + getComponent().getName() + " component");
+         managedEntityWrapper.deserialize(ctx.getTarget(), getComponent());
+         log.debug("Activated " + getComponent().getName() + " component");
+         try
+         {
+            return ctx.proceed();
+         }
+         finally
+         {
+            if (!isTransactionRolledBackOrMarkedRollback())
+            {
+               log.trace("Attempting to passivate " + getComponent().getName() + " component");
+               managedEntityWrapper.wrap(ctx.getTarget(), getComponent());
+               reentrant = false;
+               log.debug("Passivated " + getComponent().getName() + " component");
+            }
+         }
+      }
+   }
+
+   public boolean isInterceptorEnabled()
+   {
+      return getComponent().getScope() == CONVERSATION;
+   }
+
+   private static boolean isTransactionRolledBackOrMarkedRollback()
+   {
+      try
+      {
+         return Transaction.instance().isRolledBackOrMarkedRollback();
+      }
+      catch (Exception e)
+      {
+         return false;
+      }
+   }
+
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedEntityWrapper.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedEntityWrapper.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedEntityWrapper.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,253 @@
+package org.jboss.seam.persistence;
+
+import static org.jboss.seam.util.JSF.DATA_MODEL;
+import static org.jboss.seam.util.JSF.getWrappedData;
+import static org.jboss.seam.util.JSF.setWrappedData;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.jboss.seam.Component;
+import org.jboss.seam.Seam;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.core.Manager;
+import org.jboss.webbeans.log.LogProvider;
+import org.jboss.webbeans.log.Logging;
+import org.jboss.seam.util.Reflections;
+
+/**
+ * @author Gavin King
+ * @author Pete Muir
+ * @author Norman Richards
+ * @author Dan Allen
+ */
+public class ManagedEntityWrapper
+{
+
+   private static LogProvider log = Logging.getLogProvider(ManagedEntityWrapper.class);
+   
+   public void wrap(Object target, Component component) throws Exception
+   {
+      if ( !touchedContextsExist() )
+      {
+         log.trace("No touched persistence contexts. Therefore, there are no entities in this conversation whose identities need to be preserved.");
+         return;
+      }
+      
+      String oldCid = switchToConversationContextOfComponent(component);
+      Class beanClass = target.getClass();
+      for (; beanClass!=Object.class; beanClass=beanClass.getSuperclass())
+      {
+         log.trace("Examining fields on " + beanClass);
+         for ( Field field: beanClass.getDeclaredFields() )
+         {
+            if ( !ignore(field) )
+            {
+               Object value = getFieldValue(target, field);
+               if (value!=null)
+               {
+                  Object dataModel = null;
+                  if ( DATA_MODEL.isInstance(value) )
+                  {
+                     dataModel = value;
+                     value = getWrappedData(dataModel);
+                  }
+                  if ( containsReferenceToEntityInstance(value) )
+                  {
+                     log.trace("Attempting to save wrapper for " + field + " (" + value + ")");
+                     saveWrapper(target, component, field, dataModel, value);
+                  }
+                  else
+                  {
+                     log.trace("Clearing wrapper for " + field + " (" + value + ") as it isn't a entity reference");
+                     clearWrapper(component, field);
+                  }
+               }
+               else
+               {
+                  log.trace("Clearing wrapper for " + field + " as it is null");
+                  clearWrapper(component, field);
+               }
+            }
+            else
+            {
+               log.trace("Ignoring field " + field + " as it is static, transient or annotated with @In");
+            }
+         }
+      }
+      restorePreviousConversationContextIfNecessary(oldCid);
+   }
+
+   public void deserialize(Object controllerBean, Component component) throws Exception
+   {
+      if ( !touchedContextsExist() )
+      {
+         log.trace("No touched persistence contexts. Therefore, there are no entities in this conversation whose identities need to be restored.");
+         return;
+      }
+      
+      Class beanClass = controllerBean.getClass();
+      for (; beanClass!=Object.class; beanClass=beanClass.getSuperclass())
+      {
+         log.trace("Examining fields on " + beanClass);
+         for ( Field field: beanClass.getDeclaredFields() )
+         {
+            if ( !ignore(field) )
+            {
+               Object value = getFieldValue(controllerBean, field);
+               Object dataModel = null;
+               if (value!=null && DATA_MODEL.isInstance(value) )
+               {
+                  dataModel = value;
+               }
+               log.trace("Attempting to restore wrapper for " + field + " (" + value + ")");
+               //TODO: be more selective
+               getFromWrapper(controllerBean, component, field, dataModel);
+            }
+            else
+            {
+               log.trace("Ignoring field " + field + " as it is static, transient or annotated with @In");
+            }
+         }
+      }
+   }
+
+   private boolean containsReferenceToEntityInstance(Object value)
+   {
+      if (value == null)
+      {
+         return false;
+      }
+      else if (value instanceof Collection)
+      {
+         // Do a lazy man's generic check by scanning the collection until an entity is found (nested objects not considered).
+         for (Iterator iter = ((Collection) value).iterator(); iter.hasNext();)
+         {
+            Object v = iter.next();
+            if (v != null && Seam.getEntityClass(v.getClass()) != null)
+            {
+               return true;
+            }
+         }
+         return false;
+      }
+      else if (value instanceof Map)
+      {
+         // Do a lazy man's generic check by scanning the collection until an entity is found (nested objects not considered).
+         for (Iterator iter = ((Map) value).entrySet().iterator(); iter.hasNext();)
+         {
+            Entry e = (Entry) iter.next();
+            if ((e.getKey() != null && Seam.getEntityClass(e.getKey().getClass()) != null) ||
+                  (e.getValue() != null && Seam.getEntityClass(e.getValue().getClass()) != null))
+            {
+               return true;
+            }
+         }
+         return false;
+      }
+      else if (Seam.getEntityClass(value.getClass()) != null)
+      {
+         return true;
+      }
+      
+      return false;
+   }
+
+   private Object getFieldValue(Object bean, Field field) throws Exception
+   {
+      if ( !field.isAccessible() ) field.setAccessible(true);
+      Object value = Reflections.get(field, bean);
+      return value;
+   }
+
+   private boolean ignore(Field field)
+   {
+      return Modifier.isTransient( field.getModifiers() ) || 
+            Modifier.isStatic( field.getModifiers() )
+            || field.isAnnotationPresent(In.class);
+   }
+
+   private boolean touchedContextsExist()
+   {
+       PersistenceContexts touchedContexts = PersistenceContexts.instance();
+       return touchedContexts!=null && touchedContexts.getTouchedContexts().size()>0;
+   }
+
+   private String getFieldId(Component component, Field field)
+   {
+      return component.getName() + '.' + field.getName();
+   }
+
+   private void saveWrapper(Object bean, Component component, Field field, Object dataModel, Object value) throws Exception
+   {
+      Contexts.getConversationContext().set( getFieldId(component, field), value );
+      if (dataModel==null)
+      {
+         Reflections.set(field, bean, null);
+      }
+      else
+      {
+         // JBSEAM-1814, JBPAPP-1616 Clearing the wrapped data is simply unnecessary. Either we leave it alone, or we set the field to null.
+         //setWrappedData(dataModel, null);
+      }
+   }
+
+   private void clearWrapper(Component component, Field field) throws Exception
+   {
+      Contexts.getConversationContext().remove( getFieldId(component, field) );
+   }
+
+   private void getFromWrapper(Object bean, Component component, Field field, Object dataModel) throws Exception
+   {
+      Object value =Contexts.getConversationContext().get( getFieldId(component, field) );
+      if (value!=null)
+      {
+         if (dataModel==null)
+         {
+            Reflections.set(field, bean, value);
+         }
+         else
+         {
+            setWrappedData(dataModel, value);
+         }
+      }
+   }
+   
+   /**
+    * Changes the thread's current conversation context to the one that holds a reference to this
+    * component. This is necessary if a nested conversation is making a call to a component in
+    * a parent conversation.
+    */
+   private String switchToConversationContextOfComponent(Component component)
+   {
+      Manager manager = Manager.instance();
+      if (manager.isNestedConversation())
+      {
+         String currentCid = manager.getCurrentConversationId();
+         String residentCid = manager.getCurrentConversationEntry().findPositionInConversationStack(component);
+         if (!currentCid.equals(residentCid))
+         {
+            Contexts.getConversationContext().flush();
+            Manager.instance().switchConversation(residentCid, false);
+            return currentCid;
+         }
+      }
+      
+      return null;
+   }
+   
+   private void restorePreviousConversationContextIfNecessary(String oldCid)
+   {
+      if (oldCid != null)
+      {
+         Contexts.getConversationContext().flush();
+         Manager.instance().switchConversation(oldCid, false);
+      }
+   }
+   
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedHibernateSession.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedHibernateSession.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedHibernateSession.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,322 @@
+//$Id: ManagedHibernateSession.java 9081 2008-09-22 03:02:30Z dan.j.allen $
+package org.jboss.seam.persistence;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PreDestroy;
+import javax.context.ConversationScoped;
+import javax.inject.Current;
+import javax.inject.Initializer;
+import javax.inject.Produces;
+import javax.naming.NamingException;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionEvent;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+
+import org.hibernate.FlushMode;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.jboss.seam.core.Mutable;
+import org.jboss.seam.core.Expressions.ValueExpression;
+import org.jboss.webbeans.log.LogProvider;
+import org.jboss.webbeans.log.Logging;
+import org.jboss.seam.persistence.annotations.FlushModeType;
+import org.jboss.seam.transaction.Transaction;
+import org.jboss.seam.transaction.UserTransaction;
+import org.jboss.seam.util.Naming;
+
+/**
+ * A Seam component that manages a conversation-scoped extended
+ * persistence context that can be shared by arbitrary other
+ * components.
+ * 
+ * @author Gavin King
+ */
+ at ConversationScoped
+public class ManagedHibernateSession 
+   implements Serializable, HttpSessionActivationListener, Mutable, PersistenceContextManager, Synchronization
+{
+   
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 3130309555079841107L;
+
+   private static final LogProvider log = Logging.getLogProvider(ManagedHibernateSession.class);
+   
+   private Session session;
+   private String sessionFactoryJndiName;
+   private String componentName;
+   private ValueExpression<SessionFactory> sessionFactory;
+   private List<Filter> filters = new ArrayList<Filter>(0);
+   
+   private transient boolean synchronizationRegistered;
+   private transient boolean destroyed;
+   
+   @Current PersistenceContexts persistenceContexts;
+   
+   public boolean clearDirty()
+   {
+      return true;
+   }
+
+   @Initializer
+   public void create(Component component)
+   {
+      this.componentName = component.getName();
+      if (sessionFactoryJndiName==null)
+      {
+         sessionFactoryJndiName = "java:/" + componentName;
+      }
+            
+      persistenceContexts.touch(componentName);
+   }
+
+   private void initSession() throws Exception
+   {
+      session = getSessionFactoryFromJndiOrValueBinding().openSession();
+      setSessionFlushMode( persistenceContexts.getFlushMode() );
+      session = HibernatePersistenceProvider.proxySession(session);
+      
+      for (Filter f: filters)
+      {
+         if ( f.isFilterEnabled() )
+         {
+            enableFilter(f);
+         }
+      }
+
+      if ( log.isDebugEnabled() )
+      {
+         log.debug("created seam managed session for session factory: "+ sessionFactoryJndiName);
+      }
+   }
+
+   private void enableFilter(Filter f)
+   {
+      org.hibernate.Filter filter = session.enableFilter( f.getName() );
+      for ( Map.Entry<String, ValueExpression> me: f.getParameters().entrySet() )
+      {
+	     Object filterValue = me.getValue().getValue();
+		 if ( filterValue instanceof Collection ) {
+		    filter.setParameterList(me.getKey(), (Collection) filterValue);
+		 } else {
+			filter.setParameter(me.getKey(), filterValue);
+		}
+      }
+      filter.validate();
+   }
+   
+   @Produces public Session getSession() throws Exception
+   {
+      if (session==null) initSession();
+      
+      if ( !synchronizationRegistered && !Lifecycle.isDestroying() )
+      {
+         joinTransaction();
+      }
+      
+      return session;
+   }
+
+   private void joinTransaction() throws SystemException
+   {
+      UserTransaction transaction = Transaction.instance();
+      if ( transaction.isActive() )
+      {
+         session.isOpen();
+         try
+         {
+            transaction.registerSynchronization(this);
+         }
+         catch (Exception e)
+         {
+            session.getTransaction().registerSynchronization(this);
+         }
+         synchronizationRegistered = true;
+      }
+   }
+   
+   //we can't use @PrePassivate because it is intercept NEVER
+   public void sessionWillPassivate(HttpSessionEvent event)
+   {
+      if (synchronizationRegistered)
+      {
+         throw new IllegalStateException("cannot passivate persistence context with active transaction");
+      }
+      if ( session!=null && session.isOpen() && !session.isDirty() )
+      {
+         session.close();
+         session = null;
+      }
+   }
+   
+   //we can't use @PostActivate because it is intercept NEVER
+   public void sessionDidActivate(HttpSessionEvent event) {}
+   
+   @PreDestroy
+   public void destroy()
+   {
+      destroyed = true;
+      if ( !synchronizationRegistered )
+      {
+         //in requests that come through SeamPhaseListener,
+         //there can be multiple transactions per request,
+         //but they are all completed by the time contexts
+         //are destroyed
+         //so wait until the end of the request to close
+         //the session
+         //on the other hand, if we are still waiting for
+         //the transaction to commit, leave it open
+         close();
+      }
+      persistenceContexts.untouch(componentName);
+   }
+
+   public void afterCompletion(int status)
+   {
+      synchronizationRegistered = false;
+      //if ( !Contexts.isConversationContextActive() )
+      if (destroyed)
+      {
+         //in calls to MDBs and remote calls to SBs, the 
+         //transaction doesn't commit until after contexts
+         //are destroyed, so wait until the transaction
+         //completes before closing the session
+         //on the other hand, if we still have an active
+         //conversation context, leave it open
+         close();
+      }
+   }
+   
+   public void beforeCompletion() {}
+   
+   private void close()
+   {
+      boolean transactionActive = false;
+      try
+      {
+         transactionActive = Transaction.instance().isActive();
+      }
+      catch (SystemException se)
+      {
+         log.debug("could not get transaction status while destroying persistence context");
+      }
+      
+      if ( transactionActive )
+      {
+         throw new IllegalStateException("attempting to destroy the persistence context while an active transaction exists (try installing <transaction:ejb-transaction/>)");
+      }
+      
+      if ( log.isDebugEnabled() )
+      {
+         log.debug("destroying seam managed session for session factory: " + sessionFactoryJndiName);
+      }
+      if (session!=null && session.isOpen())
+      {
+         session.close();
+      }
+   }
+   
+   private SessionFactory getSessionFactoryFromJndiOrValueBinding()
+   {
+      SessionFactory result = null;
+      //first try to find it via the value binding
+      if (sessionFactory!=null)
+      {
+         result = sessionFactory.getValue();
+      }
+      //if its not there, try JNDI
+      if (result==null)
+      {
+         try
+         {
+            result = (SessionFactory) Naming.getInitialContext().lookup(sessionFactoryJndiName);
+         }
+         catch (NamingException ne)
+         {
+            throw new IllegalArgumentException("SessionFactory not found in JNDI: " + sessionFactoryJndiName, ne);
+         }
+      }
+      return result;
+   }
+   
+   public String getComponentName() {
+      return componentName;
+   }
+   
+   public void changeFlushMode(FlushModeType flushMode)
+   {
+      if (session!=null && session.isOpen())
+      {
+         setSessionFlushMode(flushMode);
+      }
+   }
+
+   protected void setSessionFlushMode(FlushModeType flushMode)
+   {
+      switch (flushMode)
+      {
+         case AUTO:
+            session.setFlushMode(FlushMode.AUTO);
+            break;
+         case MANUAL:
+            session.setFlushMode(FlushMode.MANUAL);
+            break;
+         case COMMIT:
+            session.setFlushMode(FlushMode.COMMIT);
+            break;
+      }
+   }
+   
+   /**
+    * The JNDI name of the Hibernate SessionFactory, if it is
+    * to be obtained from JNDI
+    */
+   public String getSessionFactoryJndiName()
+   {
+      return sessionFactoryJndiName;
+   }
+
+   public void setSessionFactoryJndiName(String sessionFactoryName)
+   {
+      this.sessionFactoryJndiName = sessionFactoryName;
+   }
+
+   /**
+    * A value binding expression that returns a SessionFactory,
+    * if it is to be obtained as a Seam component reference
+    */
+   public void setSessionFactory(ValueExpression<SessionFactory> sessionFactory)
+   {
+      this.sessionFactory = sessionFactory;
+   }
+
+   public ValueExpression<SessionFactory> getSessionFactory()
+   {
+      return sessionFactory;
+   }
+
+   /**
+    * Hibernate filters to enable automatically
+    */
+   public List<Filter> getFilters()
+   {
+      return filters;
+   }
+
+   public void setFilters(List<Filter> filters)
+   {
+      this.filters = filters;
+   }
+
+   @Override
+   public String toString()
+   {
+      return "ManagedHibernateSession(" + sessionFactoryJndiName + ")";
+   }
+
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedPersistenceContext.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedPersistenceContext.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/ManagedPersistenceContext.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,323 @@
+//$Id: ManagedPersistenceContext.java 9721 2008-12-04 08:29:25Z dan.j.allen $
+package org.jboss.seam.persistence;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.PreDestroy;
+import javax.context.ConversationScoped;
+import javax.inject.Current;
+import javax.inject.Initializer;
+import javax.inject.Produces;
+import javax.naming.NamingException;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionEvent;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+
+import org.jboss.seam.contexts.Lifecycle;
+import org.jboss.seam.core.Mutable;
+import org.jboss.seam.core.Expressions.ValueExpression;
+import org.jboss.webbeans.log.LogProvider;
+import org.jboss.webbeans.log.Logging;
+import org.jboss.seam.persistence.annotations.FlushModeType;
+import org.jboss.seam.transaction.Transaction;
+import org.jboss.seam.transaction.UserTransaction;
+import org.jboss.seam.util.Naming;
+
+/**
+ * A Seam component that manages a conversation-scoped extended
+ * persistence context that can be shared by arbitrary other
+ * components.
+ * 
+ * @author Gavin King
+ */
+ at ConversationScoped
+public class ManagedPersistenceContext 
+   implements Serializable, HttpSessionActivationListener, Mutable, PersistenceContextManager, Synchronization
+{
+   private static final long serialVersionUID = -4972387440275848126L;
+   private static final LogProvider log = Logging.getLogProvider(ManagedPersistenceContext.class);
+   
+   private transient EntityManager entityManager;
+   private String persistenceUnitJndiName;
+   private String componentName;
+   private ValueExpression<EntityManagerFactory> entityManagerFactory;
+   private List<Filter> filters = new ArrayList<Filter>(0);
+   
+   private transient boolean synchronizationRegistered;
+   private transient boolean destroyed;
+   
+   @Current PersistenceContexts persistenceContexts;
+   @Current PersistenceProvider persistenceProvider;
+  
+   public boolean clearDirty()
+   {
+      return true;
+   }
+   
+   @Initializer
+   public void create(Component component)
+   {
+      this.componentName = component.getName();
+      if (persistenceUnitJndiName==null)
+      {
+         persistenceUnitJndiName = "java:/" + componentName;
+      }
+      
+      persistenceContexts.touch(componentName);      
+   }
+   
+   private void initEntityManager()
+   {
+      entityManager = getEntityManagerFactoryFromJndiOrValueBinding().createEntityManager();
+      entityManager = persistenceProvider.proxyEntityManager(entityManager);
+      setEntityManagerFlushMode( persistenceContexts.getFlushMode() );
+
+      for (Filter f: filters)
+      {
+         if ( f.isFilterEnabled() )
+         {
+            persistenceProvider.enableFilter(f, entityManager);
+         }
+      }
+
+      if ( log.isDebugEnabled() )
+      {
+         if (entityManagerFactory==null)
+         {
+            log.debug("created seam managed persistence context for persistence unit: "+ persistenceUnitJndiName);
+         }
+         else 
+         {
+            log.debug("created seam managed persistence context from EntityManagerFactory");
+         }
+      }
+   }
+   
+   @Produces
+   public EntityManager getEntityManager() throws NamingException, SystemException
+   {
+      if (entityManager==null) initEntityManager();
+      
+      if ( !synchronizationRegistered && !Lifecycle.isDestroying() )
+      {
+         joinTransaction();
+      }
+      
+      return entityManager;
+   }
+
+   private void joinTransaction() throws SystemException
+   {
+      UserTransaction transaction = Transaction.instance();
+      if ( transaction.isActive() )
+      {
+         transaction.enlist(entityManager);
+         try
+         {
+            transaction.registerSynchronization(this);
+            synchronizationRegistered = true;
+         }
+         catch (Exception e)
+         {
+            synchronizationRegistered = persistenceProvider.registerSynchronization(this, entityManager);
+         }
+      }
+   }
+   
+   /**
+    * If a transaction is active, fail the passivation. The field holding the
+    * managed EntityManager is marked as transient so that it is not serialized
+    * (it can't be). The transient keyword was choosen because we don't want to
+    * forcefully close and nullify the EntityManager on every request because
+    * then we have to keep hitting the database to load the entities back into
+    * the persistence context. The only downside is that we cannot clean up
+    * on the old node before the session hops, but it turns out not to matter.
+    * 
+    * Note that we must use the method on the
+    * {@link HttpSessionActivationListener} interface rather than
+    * <code>@PrePassivate</code> since interceptors are disabled on this component.
+    */
+   public void sessionWillPassivate(HttpSessionEvent event)
+   {
+      if (synchronizationRegistered)
+      {
+         throw new IllegalStateException("cannot passivate persistence context with active transaction");
+      }
+   }
+   
+   /**
+    * Note that we must use the method on the {@link HttpSessionActivationListener}
+    * interface rather than @PostActivate since interceptors are disabled
+    * on this component.
+    */
+   public void sessionDidActivate(HttpSessionEvent event) {}
+   
+   @PreDestroy
+   public void destroy()
+   {
+      destroyed = true;
+      if ( !synchronizationRegistered )
+      {
+         //in requests that come through SeamPhaseListener,
+         //there can be multiple transactions per request,
+         //but they are all completed by the time contexts
+         //are destroyed
+         //so wait until the end of the request to close
+         //the session
+         //on the other hand, if we are still waiting for
+         //the transaction to commit, leave it open
+         close();
+      }
+      persistenceContexts.untouch(componentName);
+   }
+
+   public void afterCompletion(int status)
+   {
+      synchronizationRegistered = false;
+      //if ( !Contexts.isConversationContextActive() )
+      if (destroyed)
+      {
+         //in calls to MDBs and remote calls to SBs, the 
+         //transaction doesn't commit until after contexts
+         //are destroyed, so wait until the transaction
+         //completes before closing the session
+         //on the other hand, if we still have an active
+         //conversation context, leave it open
+         close();
+      }
+   }
+   
+   public void beforeCompletion() {}
+   
+   private void close()
+   {
+      boolean transactionActive = false;
+      try
+      {
+         transactionActive = Transaction.instance().isActive();
+      }
+      catch (SystemException se)
+      {
+         log.debug("could not get transaction status while destroying persistence context");
+      }
+      
+      if ( transactionActive )
+      {
+         throw new IllegalStateException("attempting to destroy the persistence context while an active transaction exists (try installing <transaction:ejb-transaction/>)");
+      }
+      
+      if ( log.isDebugEnabled() )
+      {
+         log.debug("destroying seam managed persistence context for persistence unit: " + persistenceUnitJndiName);
+      }
+      
+      if (entityManager!=null && entityManager.isOpen())
+      {
+         entityManager.close();
+      }
+   }
+   
+   public EntityManagerFactory getEntityManagerFactoryFromJndiOrValueBinding()
+   {
+      EntityManagerFactory result = null;
+      //first try to find it via the value binding
+      if (entityManagerFactory!=null)
+      {
+         result = entityManagerFactory.getValue();
+      }
+      //if its not there, try JNDI
+      if (result==null)
+      {
+         try
+         {
+            result = (EntityManagerFactory) Naming.getInitialContext().lookup(persistenceUnitJndiName);
+         }
+         catch (NamingException ne)
+         {
+            throw new IllegalArgumentException("EntityManagerFactory not found in JNDI : " + persistenceUnitJndiName, ne);
+         }
+      }
+      return result;
+   }
+   
+   /**
+    * A value binding expression that returns an EntityManagerFactory,
+    * for use of JPA outside of Java EE 5 / Embeddable EJB3.
+    */
+   public ValueExpression<EntityManagerFactory> getEntityManagerFactory()
+   {
+      return entityManagerFactory;
+   }
+   
+   public void setEntityManagerFactory(ValueExpression<EntityManagerFactory> entityManagerFactory)
+   {
+      this.entityManagerFactory = entityManagerFactory;
+   }
+   
+   /**
+    * The JNDI name of the EntityManagerFactory, for 
+    * use of JPA in Java EE 5 / Embeddable EJB3.
+    */
+   public String getPersistenceUnitJndiName()
+   {
+      return persistenceUnitJndiName;
+   }
+   
+   public void setPersistenceUnitJndiName(String persistenceUnitName)
+   {
+      this.persistenceUnitJndiName = persistenceUnitName;
+   }
+   
+   public String getComponentName() 
+   {
+      return componentName;
+   }
+   
+   /**
+    * Hibernate filters to enable automatically
+    */
+   public List<Filter> getFilters()
+   {
+      return filters;
+   }
+   
+   public void setFilters(List<Filter> filters)
+   {
+      this.filters = filters;
+   }
+   
+   public void changeFlushMode(FlushModeType flushMode)
+   {
+      if (entityManager!=null && entityManager.isOpen())
+      {
+         setEntityManagerFlushMode(flushMode);
+      }
+   }
+   
+   protected void setEntityManagerFlushMode(FlushModeType flushMode)
+   {
+      switch (flushMode)
+      {
+         case AUTO:
+            entityManager.setFlushMode(javax.persistence.FlushModeType.AUTO);
+            break;
+         case COMMIT:
+            entityManager.setFlushMode(javax.persistence.FlushModeType.COMMIT);
+            break;
+         case MANUAL:
+            persistenceProvider.setFlushModeManual(entityManager);
+            break;
+      }
+   }
+   
+   @Override
+   public String toString()
+   {
+      return "ManagedPersistenceContext(" + persistenceUnitJndiName + ")";
+   }
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Model.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Model.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/Model.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,50 @@
+package org.jboss.seam.persistence;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base class of metamodels. For a class which
+ * is neither an entity nor a Seam component,
+ * the concrete type of the metamodel object
+ * will be Model. For components or entities
+ * it is a subclass of Model.
+ * 
+ * @author Gavin King
+ *
+ */
+public class Model
+{
+   protected static Map<String,Model> modelCache = new HashMap<String,Model>();
+   
+   private Class<?> beanClass;
+
+   public Model(Class<?> beanClass)
+   {
+      this.beanClass = beanClass;
+   }
+   
+   public final Class<?> getBeanClass()
+   {
+      return beanClass;
+   }
+
+   public static Model forClass(Class clazz)
+   {      
+      String name = getModelName(clazz);
+      Model model = modelCache.get(name);
+      if ( model==null )
+      {
+         model = clazz.isAnnotationPresent(javax.persistence.Entity.class) ? 
+                  new Entity(clazz) : new Model(clazz);
+         modelCache.put(name, model);
+      }
+      return model;
+   }
+
+   static String getModelName(Class clazz)
+   {
+      return clazz.getName() + ".model";
+   }
+
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceContextManager.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceContextManager.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceContextManager.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,15 @@
+package org.jboss.seam.persistence;
+
+import org.jboss.seam.persistence.annotations.FlushModeType;
+
+/**
+ * Support for changing flushmodes for an existing
+ * persistence context.
+ * 
+ * @author Gavin King
+ *
+ */
+public interface PersistenceContextManager
+{
+   public void changeFlushMode(FlushModeType flushMode);
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceContexts.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceContexts.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceContexts.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,128 @@
+package org.jboss.seam.persistence;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.context.Conversation;
+import javax.context.ConversationScoped;
+import javax.inject.Current;
+import javax.inject.Initializer;
+
+import org.jboss.seam.persistence.annotations.FlushModeType;
+import org.jboss.webbeans.log.LogProvider;
+import org.jboss.webbeans.log.Logging;
+
+/**
+ * Maintains the set of persistence contexts that have been touched in a
+ * conversation. Also controls the flush mode used by the persistence contexts
+ * during the render phase.
+ * 
+ * @author Gavin King
+ */
+ at ConversationScoped
+public class PersistenceContexts extends AbstractMutable implements Serializable
+{
+   private static final long serialVersionUID = -4897350516435283182L;
+   private static final LogProvider log = Logging.getLogProvider(PersistenceContexts.class);
+   private Set<String> set = new HashSet<String>();
+   private FlushModeType flushMode;
+   // the real flush mode is a backup of the flush mode when doing a temporary switch (such as during render)
+   private FlushModeType realFlushMode;
+   
+   @Current PersistenceProvider persistenceProvider;
+   
+   @Initializer
+   public void create()
+   {      
+      FlushModeType defaultFlushMode = Manager.instance().getDefaultFlushMode(); 
+      if (defaultFlushMode != null)
+      {
+         flushMode = defaultFlushMode;
+      }
+      else
+      {
+         flushMode = FlushModeType.AUTO;
+      }
+   }
+   
+   public FlushModeType getFlushMode()
+   {
+      return flushMode;
+   }
+   
+   public Set<String> getTouchedContexts()
+   {
+      return Collections.unmodifiableSet(set);
+   }
+   
+   public void touch(String context)
+   {
+      if ( set.add(context) ) setDirty();
+   }
+   
+   public void untouch(String context)
+   {
+      if ( set.remove(context) ) setDirty();
+   }
+   
+   public void changeFlushMode(FlushModeType flushMode)
+   {
+      changeFlushMode(flushMode, false);   
+   }
+
+   public void changeFlushMode(FlushModeType flushMode, boolean temporary)
+   {
+      if (temporary) {
+         realFlushMode = this.flushMode;
+      }
+      this.flushMode = flushMode;
+      changeFlushModes();
+   }
+
+   /**
+    * Restore the previous flush mode if the current flush mode is marked 
+    * as temporary.
+    */
+   public void restoreFlushMode() {
+      if (realFlushMode != null && realFlushMode != flushMode) {
+         flushMode = realFlushMode;
+         realFlushMode = null;
+         changeFlushModes();
+      }
+   }
+
+   private void changeFlushModes()
+   {
+      for (String name: set)
+      {
+         PersistenceContextManager pcm = (PersistenceContextManager) Contexts.getConversationContext().get(name);
+         if (pcm!=null)
+         {
+            try
+            {
+               pcm.changeFlushMode(flushMode);
+            }
+            catch (UnsupportedOperationException uoe)
+            {
+               // we won't be nasty and throw and exception, but we'll log a warning to the developer
+               log.warn(uoe.getMessage());
+            }
+         }
+      }
+   }
+   
+   public void beforeRender()
+   {
+      // some JPA providers may not support MANUAL flushing
+      // defer the decision to the provider manager component
+      persistenceProvider.setRenderFlushMode();
+   }
+   
+   public void afterRender()
+   {
+      restoreFlushMode();
+   }
+   
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceProvider.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceProvider.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/PersistenceProvider.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,210 @@
+package org.jboss.seam.persistence;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Named;
+import javax.annotation.PostConstruct;
+import javax.persistence.EntityManager;
+import javax.persistence.OptimisticLockException;
+import javax.transaction.Synchronization;
+
+/**
+ * Abstraction layer for persistence providers (JPA implementations).
+ * This class provides a working base implementation that can be
+ * optimized for performance and non-standardized features by extending
+ * and overriding the methods. 
+ * 
+ * The methods on this class are a great todo list for the next rev
+ * of the JPA spec ;-)
+ * 
+ * @author Gavin King
+ * @author Pete Muir
+ *
+ */
+ at Named
+public class PersistenceProvider
+{
+   public enum Feature {
+      /**
+       * Identifies whether this JPA provider supports using a wildcard as the subject of a count query.
+       *
+       * <p>Here's a count query that uses a wildcard as the subject.</p>
+       * <pre>select count(*) from Vehicle v</pre>
+       * <p>Per the JPA 1.0 spec, using a wildcard as a subject of a count query is not permitted. Instead,
+       * the subject must be the entity or the alias, as in this count query:</p>
+       * <pre>select count(v) from Vehicle v</pre>
+       * <p>Hibernate supports the wildcard syntax as an vendor extension. Furthermore, Hibernate produces
+       * an invalid SQL query when using the compliant subject if the entity has a composite primary key.
+       * Therefore, we prefer to use the wildcard syntax if it is supported.</p>
+       */
+      WILDCARD_AS_COUNT_QUERY_SUBJECT
+   }
+
+   protected Set<Feature> featureSet = new HashSet<Feature>();
+
+   @PostConstruct // @Create method not called on stateless components
+   public void init() {}
+   
+   /**
+    * Indicate whether this JPA provider supports the feature defined by the provided Feature enum value.
+    */
+   public boolean supportsFeature(Feature feature) {
+       return featureSet.contains(feature);
+   }
+
+   /**
+    *  Set the flush mode to manual-only flushing. Called when
+    *  an atomic persistence context is required.
+    */
+   public void setFlushModeManual(EntityManager entityManager)
+   {
+      throw new UnsupportedOperationException("Use of FlushMode.MANUAL requires Hibernate as the persistence provider. Please use Hibernate, a custom persistenceProvider, or remove the MANUAL flush mode setting.");
+   }
+
+   /**
+    * <p>
+    * Set the FlushMode the persistence contexts should use during rendering by
+    * calling {@link PersistenceContexts#changeFlushMode(FlushModeType, true)}. The
+    * actual changing of the flush mode is handled by the
+    * {@link PersistenceContexts} instance. The boolean argument should be true
+    * to indicate that this is a temporary change and that the old flush mode
+    * should be restored after render.
+    * </p>
+    * <p>
+    * Ideally, this should be MANUAL since changes should never flush to the
+    * database while in render response and the cost of a dirty check can be
+    * avoided. However, since the MANUAL mode is not officially part of the JPA
+    * specification, the default implementation will perform no operation.
+    * </p>
+    */
+   public void setRenderFlushMode() {
+      // no-op in default implementation
+   }
+   
+   /**
+    * Does the persistence context have unflushed changes? If
+    * it does not, persistence context replication can be
+    * optimized.
+    * 
+    * @return true to indicate that there are unflushed changes
+    */
+   public boolean isDirty(EntityManager entityManager)
+   {
+      return true; //best we can do!
+   }
+   
+   /**
+    * Get the value of the entity identifier attribute.
+    * 
+    * @param bean a managed entity instance
+    */
+   public Object getId(Object bean, EntityManager entityManager)
+   {
+      return Entity.forBean( bean ).getIdentifier(bean);
+   }
+   
+   /**
+    * Get the name of the entity
+    * 
+    * @param bean
+    * @param entityManager
+    * 
+    * @throws IllegalArgumentException if the passed object is not an entity
+    */
+   public String getName(Object bean, EntityManager entityManager) throws IllegalArgumentException
+   {
+      return Entity.forBean( bean ).getName();
+   }
+   
+   /**
+    * Get the value of the entity version attribute.
+    * 
+    * @param bean a managed entity instance
+    */
+   public Object getVersion(Object bean, EntityManager entityManager)
+   {
+      return Entity.forBean( bean ).getVersion(bean);
+   }
+   
+   public void checkVersion(Object bean, EntityManager entityManager, Object oldVersion, Object version)
+   {
+      boolean equal;
+      if (oldVersion instanceof Date)
+      {
+         equal = ( (Date) oldVersion ).getTime() == ( (Date) version ).getTime();
+      }
+      else
+      {
+         equal = oldVersion.equals(version);
+      }
+      if ( !equal )
+      {
+         throw new OptimisticLockException("Current database version number does not match passivated version number");
+      }
+   }
+   /**
+    * Enable a Filter. This is here just especially for Hibernate,
+    * since we well know that other products don't have such cool
+    * features. 
+    */
+   public void enableFilter(Filter filter, EntityManager entityManager)
+   {
+      throw new UnsupportedOperationException("Use of filters requires Hibernate as the persistence provider. Please use Hibernate or remove the filters configuration.");
+   }
+   
+   /**
+    * Register a Synchronization with the current transaction.
+    */
+   public boolean registerSynchronization(Synchronization sync, EntityManager entityManager)
+   {
+      return false; //best we can do!
+   }
+
+   /**
+    * Wrap the delegate before returning it to the application
+    */
+   public Object proxyDelegate(Object delegate)
+   {
+      return delegate;
+   }
+   /**
+    * Wrap the entityManager before returning it to the application
+    */
+   public EntityManager proxyEntityManager(EntityManager entityManager) {
+      return new EntityManagerProxy(entityManager);
+   }
+   
+   /**
+    * Returns the class of an entity bean instance
+    * 
+    * @param bean The entity bean instance
+    * @return The class of the entity bean
+    */
+   public Class getBeanClass(Object bean)
+   {
+      return Entity.forBean(bean).getBeanClass();
+   }
+   
+   public Method getPostLoadMethod(Object bean, EntityManager entityManager)
+   {
+      return Entity.forBean(bean).getPostLoadMethod();
+   }
+   
+   public Method getPrePersistMethod(Object bean, EntityManager entityManager)
+   {
+      return Entity.forBean(bean).getPrePersistMethod();
+   }
+   
+   public Method getPreUpdateMethod(Object bean, EntityManager entityManager)
+   {
+      return Entity.forBean(bean).getPreUpdateMethod();
+   }
+   
+   public Method getPreRemoveMethod(Object bean, EntityManager entityManager)
+   {
+      return Entity.forBean(bean).getPreRemoveMethod();
+   }      
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/QueryParser.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/QueryParser.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/QueryParser.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,58 @@
+package org.jboss.seam.persistence;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.jboss.seam.el.Expressions.ValueExpression;
+
+import org.jboss.seam.el.Expressions;
+
+public class QueryParser
+{
+   private List<ValueExpression> parameterValueBindings = new ArrayList<ValueExpression>();
+   private StringBuilder ejbqlBuilder;
+   
+   public static String getParameterName(int loc)
+   {
+      return "el" + (loc+1);
+   }
+   
+   public String getEjbql()
+   {
+      return ejbqlBuilder.toString();
+   }
+   
+   public List<ValueExpression> getParameterValueBindings()
+   {
+      return parameterValueBindings;
+   }
+   
+   public QueryParser(String ejbql)
+   {
+      this(ejbql, 0);
+   }
+   
+   public QueryParser(String ejbql, int startingParameterNumber)
+   {
+       StringTokenizer tokens = new StringTokenizer(ejbql, "#}", true);
+       ejbqlBuilder = new StringBuilder(ejbql.length());
+       while (tokens.hasMoreTokens()) {
+           String token = tokens.nextToken();
+           if ("#".equals(token) && tokens.hasMoreTokens()) {
+               String expressionToken = tokens.nextToken();
+
+               if (!expressionToken.startsWith("{") || !tokens.hasMoreTokens()) {
+                   ejbqlBuilder.append(token).append(expressionToken);
+               } else {
+                   String expression = token + expressionToken + tokens.nextToken();
+                   ejbqlBuilder.append(':').append( getParameterName( startingParameterNumber + parameterValueBindings.size() ) );
+                   parameterValueBindings.add( Expressions.instance().createValueExpression(expression) );
+               }    
+           } else {
+               ejbqlBuilder.append(token);
+           }
+       }
+   }
+   
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/annotations/FlushModeType.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/annotations/FlushModeType.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/annotations/FlushModeType.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,42 @@
+package org.jboss.seam.persistence.annotations;
+
+/**
+ * A full set of flush modes, including MANUAL,
+ * which is a glaring missing feature of the JPA
+ * spec.
+ * 
+ * @author Gavin King
+ *
+ */
+public enum FlushModeType
+{
+   
+   /**
+    * Flushing never occurs automatically, all changes are queued 
+    * until the application calls flush() explicitly.
+    */
+   MANUAL,
+
+   /**
+    * Flushing occurs automatically at commit time and when necessary
+    * before query executions.
+    */
+   AUTO,
+   
+   /**
+    * Flushing occurs automatically at transaction commit time.
+    */
+   COMMIT;
+   
+   /**
+    * Does this flush mode keep unflushed changes past a 
+    * transaction commit?
+    * 
+    * @return false for all flush modes except for MANUAL
+    */
+   public boolean dirtyBetweenTransactions() 
+   { 
+      return this==MANUAL;
+   }
+    
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Naming.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Naming.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Naming.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,75 @@
+package org.jboss.seam.persistence.util;
+
+import java.util.Hashtable;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.jboss.webbeans.log.LogProvider;
+import org.jboss.webbeans.log.Logging;
+
+public final class Naming
+{
+   private static final LogProvider log = Logging.getLogProvider(Naming.class);
+   
+   private static Hashtable initialContextProperties;
+
+   private static InitialContext initialContext;
+
+   public static InitialContext getInitialContext(
+         Hashtable<String, String> props) throws NamingException
+   {
+      if (props == null)
+      {
+         throw new IllegalStateException(
+               "JNDI properties not initialized, Seam was not started correctly");
+      }
+
+      if (log.isDebugEnabled())
+      {
+         log.debug("JNDI InitialContext properties:" + props);
+      }
+
+      try
+      {
+         return props.size() == 0 ? new InitialContext() : new InitialContext(
+               props);
+      } catch (NamingException e)
+      {
+         log.debug("Could not obtain initial context", e);
+         throw e;
+      }
+
+   }
+
+   public static InitialContext getInitialContext() throws NamingException
+   {
+      if (initialContext == null)
+         initInitialContext();
+
+      return initialContext;
+   }
+
+   private static synchronized void initInitialContext() throws NamingException
+   {
+      if (initialContext == null)
+      {
+         initialContext = getInitialContext(initialContextProperties);
+      }
+   }
+
+   private Naming() {
+   }
+
+   public static void setInitialContextProperties(
+         Hashtable initialContextProperties)
+   {
+      Naming.initialContextProperties = initialContextProperties;
+      initialContext = null;
+   }
+
+   public static Hashtable getInitialContextProperties()
+   {
+      return initialContextProperties;
+   }
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Reflections.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Reflections.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Reflections.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,401 @@
+package org.jboss.seam.persistence.util;
+
+import java.beans.Introspector;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Reflections
+{
+   
+   public static Object invoke(Method method, Object target, Object... args) throws Exception
+   {
+      try
+      {
+         return method.invoke( target, args );
+      }
+      catch (IllegalArgumentException iae)
+      {
+         String message = "Could not invoke method by reflection: " + toString(method);
+         if (args!=null && args.length>0) 
+         {
+            message += " with parameters: (" + Strings.toClassNameString(", ", args) + ')';
+         }
+         message += " on: " + target.getClass().getName();
+         throw new IllegalArgumentException(message, iae);
+      }
+      catch (InvocationTargetException ite)
+      {
+         if ( ite.getCause() instanceof Exception )
+         {
+            throw (Exception) ite.getCause();
+         }
+         else
+         {
+            throw ite;
+         }
+      }
+   }
+   
+   public static Object get(Field field, Object target) throws Exception
+   {
+      boolean accessible = field.isAccessible();
+      try
+      {
+         field.setAccessible(true);
+         return field.get(target);
+      }
+      catch (IllegalArgumentException iae)
+      {
+         String message = "Could not get field value by reflection: " + toString(field) + 
+            " on: " + target.getClass().getName();
+         throw new IllegalArgumentException(message, iae);
+      }
+      finally
+      {
+         field.setAccessible(accessible);
+      }
+   }
+   
+   public static void set(Field field, Object target, Object value) throws Exception
+   {
+      try
+      {
+         field.set(target, value);
+      }
+      catch (IllegalArgumentException iae)
+      {
+         // target may be null if field is static so use field.getDeclaringClass() instead
+         String message = "Could not set field value by reflection: " + toString(field) +
+            " on: " + field.getDeclaringClass().getName();
+         if (value==null)
+         {
+            message += " with null value";
+         }
+         else
+         {
+            message += " with value: " + value.getClass();
+         }
+         throw new IllegalArgumentException(message, iae);
+      }
+   }
+   
+   public static Object getAndWrap(Field field, Object target)
+   {
+      boolean accessible = field.isAccessible();
+      try
+      {
+         field.setAccessible(true);
+         return get(field, target);
+      }
+      catch (Exception e)
+      {
+         if (e instanceof RuntimeException)
+         {
+            throw (RuntimeException) e;
+         }
+         else
+         {
+            throw new IllegalArgumentException("exception setting: " + field.getName(), e);
+         }
+      }
+      finally
+      {
+         field.setAccessible(accessible);
+      }
+   }
+   
+   public static void setAndWrap(Field field, Object target, Object value)
+   {
+      boolean accessible = field.isAccessible();
+      try
+      {
+         field.setAccessible(true);
+         set(field, target, value);
+      }
+      catch (Exception e)
+      {
+         if (e instanceof RuntimeException)
+         {
+            throw (RuntimeException) e;
+         }
+         else
+         {
+            throw new IllegalArgumentException("exception setting: " + field.getName(), e);
+         }
+      }
+      finally
+      {
+         field.setAccessible(accessible);
+      }
+   }
+   
+   public static Object invokeAndWrap(Method method, Object target, Object... args)
+   {
+      try
+      {
+         return invoke(method, target, args);
+      }
+      catch (Exception e)
+      {
+         if (e instanceof RuntimeException)
+         {
+            throw (RuntimeException) e;
+         }
+         else
+         {
+            throw new RuntimeException("exception invoking: " + method.getName(), e);
+         }
+      }
+   }
+   
+   public static String toString(Method method)
+   {
+      return Strings.unqualify( method.getDeclaringClass().getName() ) + 
+            '.' + 
+            method.getName() + 
+            '(' + 
+            Strings.toString( ", ", method.getParameterTypes() ) + 
+            ')';
+   }
+   
+   public static String toString(Member member)
+   {
+      return Strings.unqualify( member.getDeclaringClass().getName() ) + 
+            '.' + 
+            member.getName();
+   }
+   
+   public static Class classForName(String name) throws ClassNotFoundException
+   {
+      try 
+      {
+         return Thread.currentThread().getContextClassLoader().loadClass(name);
+      }
+      catch (Exception e)
+      {
+         return Class.forName(name);
+      }
+   }
+   
+   /**
+    * Return's true if the class can be loaded using Reflections.classForName()
+    */
+   public static boolean isClassAvailable(String name)
+   {
+      try 
+      {
+         classForName(name);
+      }
+      catch (ClassNotFoundException e) {
+         return false;
+      }
+      return true;
+   }
+
+   public static Class getCollectionElementType(Type collectionType)
+   {
+      if ( !(collectionType instanceof ParameterizedType) )
+      {
+         throw new IllegalArgumentException("collection type not parameterized");
+      }
+      Type[] typeArguments = ( (ParameterizedType) collectionType ).getActualTypeArguments();
+      if (typeArguments.length==0)
+      {
+         throw new IllegalArgumentException("no type arguments for collection type");
+      }
+      Type typeArgument = typeArguments.length==1 ? typeArguments[0] : typeArguments[1]; //handle Maps
+      if (typeArgument instanceof ParameterizedType)
+      {
+         typeArgument = ((ParameterizedType) typeArgument).getRawType();
+      }
+      if ( !(typeArgument instanceof Class) )
+      {
+         throw new IllegalArgumentException("type argument not a class");
+      }
+      return (Class) typeArgument;
+   }
+   
+   public static Class getMapKeyType(Type collectionType)
+   {
+      if ( !(collectionType instanceof ParameterizedType) )
+      {
+         throw new IllegalArgumentException("collection type not parameterized");
+      }
+      Type[] typeArguments = ( (ParameterizedType) collectionType ).getActualTypeArguments();
+      if (typeArguments.length==0)
+      {
+         throw new IllegalArgumentException("no type arguments for collection type");
+      }
+      Type typeArgument = typeArguments[0];
+      if ( !(typeArgument instanceof Class) )
+      {
+         throw new IllegalArgumentException("type argument not a class");
+      }
+      return (Class) typeArgument;
+   }
+   
+   public static Method getSetterMethod(Class clazz, String name)
+   {
+      Method[] methods = clazz.getMethods();
+      for (Method method: methods)
+      {
+         String methodName = method.getName();
+         if ( methodName.startsWith("set") && method.getParameterTypes().length==1 )
+         {
+            if ( Introspector.decapitalize( methodName.substring(3) ).equals(name) )
+            {
+               return method;
+            }
+         }
+      }
+      throw new IllegalArgumentException("no such setter method: " + clazz.getName() + '.' + name);
+   }
+   
+   public static Method getGetterMethod(Class clazz, String name)
+   {
+      Method[] methods = clazz.getMethods();
+      for (Method method: methods)
+      {
+         String methodName = method.getName();
+         if ( method.getParameterTypes().length==0 )
+         {
+            if ( methodName.startsWith("get") )
+            {
+               if ( Introspector.decapitalize( methodName.substring(3) ).equals(name) )
+               {
+                  return method;
+               }
+            }
+            else if ( methodName.startsWith("is") )
+            {
+               if ( Introspector.decapitalize( methodName.substring(2) ).equals(name) )
+               {
+                  return method;
+               }
+            }
+         }
+      }
+      throw new IllegalArgumentException("no such getter method: " + clazz.getName() + '.' + name);
+   }
+   
+   /**
+    * Get all the getter methods annotated with the given annotation. Returns an empty list if
+    * none are found
+    */
+   public static List<Method> getGetterMethods(Class clazz, Class annotation) 
+   {
+      List<Method> methods = new ArrayList<Method>();
+      for (Method method : clazz.getMethods())
+      {
+         if (method.isAnnotationPresent(annotation))
+         {
+            methods.add(method);
+         }
+      }
+      return methods;
+   }
+   
+   public static Field getField(Class clazz, String name)
+   {
+      for ( Class superClass = clazz; superClass!=Object.class; superClass=superClass.getSuperclass() )
+      {
+         try
+         {
+            return superClass.getDeclaredField(name);
+         }
+         catch (NoSuchFieldException nsfe) {}
+      }
+      throw new IllegalArgumentException("no such field: " + clazz.getName() + '.' + name);
+   }
+   
+   /**
+    * Get all the fields which are annotated with the given annotation. Returns an empty list
+    * if none are found
+    */
+   public static List<Field> getFields(Class clazz, Class annotation)
+   {
+      List<Field> fields = new ArrayList<Field>();
+      for (Class superClass = clazz; superClass!=Object.class; superClass=superClass.getSuperclass())
+      {
+         for (Field field : superClass.getDeclaredFields())
+         {
+            if (field.isAnnotationPresent(annotation))
+            {
+               fields.add(field);
+            }
+         }
+      }
+      return fields;
+   }
+
+   public static Method getMethod(Annotation annotation, String name)
+   {
+      try
+      {
+         return annotation.annotationType().getMethod(name);
+      }
+      catch (NoSuchMethodException nsme)
+      {
+         return null;
+      }
+   }
+   
+   public static Method getMethod(Class clazz, String name)
+   {
+      for ( Class superClass = clazz; superClass!=Object.class; superClass=superClass.getSuperclass() )
+      {
+         try
+         {
+            return superClass.getDeclaredMethod(name);
+         }
+         catch (NoSuchMethodException nsme) {}
+      }
+      throw new IllegalArgumentException("no such method: " + clazz.getName() + '.' + name);
+   }
+   
+   /**
+    * Check to see if clazz is an instance of name
+    */
+   public static boolean isInstanceOf(Class clazz, String name)
+   {
+      if (name == null)
+      {
+         throw new IllegalArgumentException("name cannot be null");
+      }
+      for (Class c = clazz; c != Object.class; c = c.getSuperclass())
+      {
+         if (instanceOf(c, name))
+         {
+            return true;
+         }
+      }
+      return false;
+   }
+   
+   private static boolean instanceOf(Class clazz, String name)
+   {  
+      if (name.equals(clazz.getName()))
+      {
+         return true;
+      }
+      else
+      {
+         boolean found = false;
+         Class[] interfaces = clazz.getInterfaces();
+         for (int i = 0; i < interfaces.length && !found; i++)
+         {
+            found = instanceOf(interfaces[i], name);
+         }
+         return found;
+      }
+      
+   }
+
+}

Added: modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Strings.java
===================================================================
--- modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Strings.java	                        (rev 0)
+++ modules/trunk/persistence/src/main/java/org/jboss/seam/persistence/util/Strings.java	2009-04-27 12:48:53 UTC (rev 10650)
@@ -0,0 +1,147 @@
+package org.jboss.seam.persistence.util;
+
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.StringTokenizer;
+
+/**
+ * @author <a href="mailto:theute at jboss.org">Thomas Heute</a>
+ */
+public class Strings
+{
+   
+   public static String unqualify(String name)
+   {
+      return unqualify(name, '.');
+   }
+   
+   public static String unqualify(String name, char sep)
+   {
+      return name.substring( name.lastIndexOf(sep)+1, name.length() );
+   }
+   
+   public static boolean isEmpty(String string)
+   {
+      int len;
+      if (string == null || (len = string.length()) == 0)
+      {
+         return true;
+      }
+      
+      for (int i = 0; i < len; i++)
+      {
+         if ((Character.isWhitespace(string.charAt(i)) == false))
+         {
+            return false;
+         }
+      }
+      return true;
+   }
+   
+   public static String nullIfEmpty(String string)
+   {
+      return isEmpty(string) ? null : string;
+   }
+   
+   public static String emptyIfNull(String string)
+   {
+      return string == null ? "" : string;
+   }
+
+   public static String toString(Object component)
+   {
+      try {
+         PropertyDescriptor[] props = Introspector.getBeanInfo( component.getClass() )
+               .getPropertyDescriptors();
+         StringBuilder builder = new StringBuilder();
+         for (PropertyDescriptor descriptor : props)
+         {
+            builder.append( descriptor.getName() )
+               .append('=')
+               .append( descriptor.getReadMethod().invoke(component) )
+               .append("; ");
+         }
+         return builder.toString();
+      }
+      catch (Exception e) {
+         return "";
+      }
+   }
+
+   public static String[] split(String strings, String delims)
+   {
+      if (strings==null)
+      {
+         return new String[0];
+      }
+      else
+      {      
+         StringTokenizer tokens = new StringTokenizer(strings, delims);
+         String[] result = new String[ tokens.countTokens() ];
+         int i=0;
+         while ( tokens.hasMoreTokens() )
+         {
+            result[i++] = tokens.nextToken();
+         }
+         return result;
+      }
+   }
+   
+   public static String toString(Object... objects)
+   {
+      return toString(" ", objects);
+   }
+   
+   public static String toString(String sep, Object... objects)
+   {
+      if (objects.length==0) return "";
+      StringBuilder builder = new StringBuilder();
+      for (Object object: objects)
+      {
+         builder.append(sep).append(object);
+      }
+      return builder.substring(sep.length());
+   }
+   
+   public static String toClassNameString(String sep, Object... objects)
+   {
+      if (objects.length==0) return "";
+      StringBuilder builder = new StringBuilder();
+      for (Object object: objects)
+      {
+         builder.append(sep);
+         if (object==null)
+         {
+            builder.append("null");
+         }
+         else
+         {
+            builder.append( object.getClass().getName() );
+         }
+      }
+      return builder.substring(sep.length());
+   }
+   
+   public static String toString(String sep, Class... classes)
+   {
+      if (classes.length==0) return "";
+      StringBuilder builder = new StringBuilder();
+      for (Class clazz: classes)
+      {
+         builder.append(sep).append( clazz.getName() );
+      }
+      return builder.substring(sep.length());
+   }
+   
+   public static String toString(InputStream in) throws IOException {
+      final StringBuilder out = new StringBuilder();
+      final byte[] b = new byte[4096];
+      for ( int n; (n = in.read(b)) != -1; ) 
+      {
+         out.append(new String(b, 0, n));
+      }
+      return out.toString();
+  }
+}




More information about the seam-commits mailing list