You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by st...@apache.org on 2011/09/18 01:16:41 UTC

svn commit: r1172145 - in /myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src: main/java/org/apache/myfaces/extensions/cdi/jpa/impl/ main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/ main/java/org/apache/myfaces/extensions/cdi...

Author: struberg
Date: Sat Sep 17 23:16:40 2011
New Revision: 1172145

URL: http://svn.apache.org/viewvc?rev=1172145&view=rev
Log:
EXTCDI-223 Provide @TransactionScoped Context for @Transactional

This includes a completely new InterceptorStrategy for our @Transactional handling

Added:
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/LegacyTransactionalInterceptorStrategy.java
      - copied, changed from r1172042, myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/DefaultTransactionalInterceptorStrategy.java
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptor.java
      - copied, changed from r1172042, myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/TransactionalInterceptor.java
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java   (with props)
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/services/
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
Removed:
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/DefaultTransactionalInterceptorStrategy.java
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/TransactionalInterceptor.java
Modified:
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanBag.java
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContextExtension.java
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/beans.xml
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java
    myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionScopedEntityManagerProducer.java

Copied: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/LegacyTransactionalInterceptorStrategy.java (from r1172042, myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/DefaultTransactionalInterceptorStrategy.java)
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/LegacyTransactionalInterceptorStrategy.java?p2=myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/LegacyTransactionalInterceptorStrategy.java&p1=myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/DefaultTransactionalInterceptorStrategy.java&r1=1172042&r2=1172145&rev=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/DefaultTransactionalInterceptorStrategy.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/LegacyTransactionalInterceptorStrategy.java Sat Sep 17 23:16:40 2011
@@ -22,7 +22,9 @@ import org.apache.myfaces.extensions.cdi
 import org.apache.myfaces.extensions.cdi.jpa.api.Transactional;
 import org.apache.myfaces.extensions.cdi.core.impl.util.AnyLiteral;
 import org.apache.myfaces.extensions.cdi.core.api.util.ClassUtils;
+import org.apache.myfaces.extensions.cdi.jpa.impl.transaction.TransactionalInterceptor;
 
+import javax.enterprise.inject.Alternative;
 import javax.inject.Inject;
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.inject.spi.Bean;
@@ -45,7 +47,11 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 
 /**
- * <p>Default implementation of our pluggable PersistenceStrategy.
+ * <p><b>Attention!</b> although this impl is called 'Default...' it is <b>not</b>
+ * used anymore! If you still like to use it, then enable it as Alternative in
+ * your bean.xml!</p>
+ *
+ * <p>Old implementation of our pluggable PersistenceStrategy.
  * It supports nested Transactions with the MANDATORY behaviour.</p>
  *
  * <p>The outermost &#064;Transactional interceptor for the given
@@ -60,9 +66,11 @@ import java.lang.reflect.Field;
  *
  * <p>If you like to implement your own PersistenceStrategy, then use the
  * standard CDI &#064;Alternative mechanism.</p>
+ *
  */
 @Dependent
-public class DefaultTransactionalInterceptorStrategy implements PersistenceStrategy
+@Alternative
+public class LegacyTransactionalInterceptorStrategy implements PersistenceStrategy
 {
     private static final long serialVersionUID = -1432802805095533499L;
 
@@ -73,7 +81,7 @@ public class DefaultTransactionalInterce
 
     private static transient ThreadLocal<AtomicInteger> refCount = new ThreadLocal<AtomicInteger>();
 
-    private static final Logger LOGGER = Logger.getLogger(DefaultTransactionalInterceptorStrategy.class.getName());
+    private static final Logger LOGGER = Logger.getLogger(LegacyTransactionalInterceptorStrategy.class.getName());
 
     /** key=qualifier name, value= EntityManager */
     private static transient ThreadLocal<HashMap<String, EntityManager>> entityManagerMap =

Copied: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptor.java (from r1172042, myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/TransactionalInterceptor.java)
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptor.java?p2=myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptor.java&p1=myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/TransactionalInterceptor.java&r1=1172042&r2=1172145&rev=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/TransactionalInterceptor.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptor.java Sat Sep 17 23:16:40 2011
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.myfaces.extensions.cdi.jpa.impl;
+package org.apache.myfaces.extensions.cdi.jpa.impl.transaction;
 
 import org.apache.myfaces.extensions.cdi.jpa.api.Transactional;
 import org.apache.myfaces.extensions.cdi.jpa.impl.spi.PersistenceStrategy;

Added: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java?rev=1172145&view=auto
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java (added)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java Sat Sep 17 23:16:40 2011
@@ -0,0 +1,392 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.extensions.cdi.jpa.impl.transaction;
+
+import org.apache.myfaces.extensions.cdi.core.impl.util.AnyLiteral;
+import org.apache.myfaces.extensions.cdi.jpa.api.Transactional;
+import org.apache.myfaces.extensions.cdi.jpa.impl.spi.PersistenceStrategy;
+import org.apache.myfaces.extensions.cdi.jpa.impl.transaction.context.TransactionBeanStorage;
+
+import javax.enterprise.inject.Default;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.interceptor.InvocationContext;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>Default implementation of our pluggable PersistenceStrategy.
+ * It supports nested Transactions with the MANDATORY behaviour.</p>
+ *
+ * <p>The outermost &#064;Transactional interceptor for the given
+ * {@link javax.inject.Qualifier} will open an {@link javax.persistence.EntityTransaction}
+ * and the outermost &#064;Transactional interceptor for <b>all</b>
+ * EntityManagers will flush and subsequently close all open transactions.</p>
+ *
+ * <p>If an Exception occurs in flushing the EntityManagers or any other Exception
+ * gets thrown inside the intercepted method chain and <i>not</i> gets catched
+ * until the outermost &#064;Transactional interceptor gets reached, then all
+ * open transactions will get rollbacked.</p>
+ *
+ * <p>If you like to implement your own PersistenceStrategy, then use the
+ * standard CDI &#064;Alternative mechanism.</p>
+ */
+public class TransactionalInterceptorStrategy implements PersistenceStrategy, Serializable
+{
+    private static final long serialVersionUID = -1432802805095533499L;
+
+
+    private @Inject BeanManager beanManager;
+
+    private @Inject TransactionBeanStorage transactionBeanStorage;
+
+    private static final Logger LOGGER = Logger.getLogger(TransactionalInterceptorStrategy.class.getName());
+
+
+    /** key=qualifier name, value= reference counter */
+    private static transient ThreadLocal<HashMap<String, AtomicInteger>> refCounterMaps =
+            new ThreadLocal<HashMap<String, AtomicInteger>>();
+
+    /** key=qualifier name, value= EntityManager */
+    private static transient ThreadLocal<HashMap<String, EntityManager>> ems =
+            new ThreadLocal<HashMap<String, EntityManager>>();
+
+
+    public Object execute(InvocationContext invocationContext) throws Exception
+    {
+        Transactional transactionalAnnotation = extractTransactionalAnnotation(invocationContext);
+
+        Class<? extends Annotation> qualifierClass = getTransactionQualifier(transactionalAnnotation);
+        String qualifierKey = qualifierClass.getName();
+
+        // the 'layer' of the transactional invocation, aka the refCounter for the current qualifier
+        int transactionLayer = incrementRefCounter(qualifierKey);
+        if (transactionLayer == 0)
+        {
+            // 0 indicates that a new Context needs to get started
+            transactionBeanStorage.startTransactionScope(qualifierKey);
+        }
+        else
+        {
+            transactionBeanStorage.activateTransactionScope(qualifierKey);
+        }
+
+        Bean<EntityManager> entityManagerBean = resolveEntityManagerBean(qualifierClass);
+
+        EntityManager entityManager = (EntityManager) beanManager.getReference(entityManagerBean, EntityManager.class,
+                                                                beanManager.createCreationalContext(entityManagerBean));
+
+        if (ems.get() == null)
+        {
+            ems.set(new HashMap<String, EntityManager>());
+        }
+        ems.get().put(qualifierKey, entityManager);
+
+
+        EntityTransaction transaction = entityManager.getTransaction();
+
+        // used to store any exception we get from the services
+        Exception firstException = null;
+
+        try
+        {
+            if(!transaction.isActive())
+            {
+                transaction.begin();
+            }
+
+            return invocationContext.proceed();
+        }
+        catch(Exception e)
+        {
+            firstException = e;
+
+            // we only cleanup and rollback all open transactions in the outermost interceptor!
+            // this way, we allow inner functions to catch and handle exceptions properly.
+            if (isOutermostInterceptor())
+            {
+                HashMap<String, EntityManager> emsEntries = ems.get();
+                for (Map.Entry<String, EntityManager> emsEntry: emsEntries.entrySet())
+                {
+                    EntityManager em = emsEntry.getValue();
+                    transaction = em.getTransaction();
+                    if (transaction != null && transaction.isActive())
+                    {
+                        try
+                        {
+                            transaction.rollback();
+                        }
+                        catch (Exception eRollback)
+                        {
+                            LOGGER.log(Level.SEVERE,
+                                       "Got additional Exception while subsequently " +
+                                       "rolling back other SQL transactions", eRollback);
+                        }
+                        finally
+                        {
+                            // close the TransactionContext
+                            transactionBeanStorage.endTransactionScope(emsEntry.getKey());
+                        }
+                    }
+                }
+
+                // drop all EntityManagers from the ThreadLocal
+                ems.remove();
+            }
+
+            // give any extensions a chance to supply a better error message
+            e = prepareException(e);
+
+            // rethrow the exception
+            throw e;
+
+        }
+        finally
+        {
+            // will get set if we got an Exception while committing
+            // in this case, we rollback all later transactions too.
+            boolean commitFailed = false;
+
+            // commit all open transactions in the outermost interceptor!
+            // this is a 'JTA for poor men' only, and will not guaranty
+            // commit stability over various databases!
+            if (isOutermostInterceptor())
+            {
+
+                // only commit all transactions if we didn't rollback
+                // them already
+                if (firstException == null)
+                {
+                    // but first try to flush all the transactions and write the updates to the database
+                    for (EntityManager em: ems.get().values())
+                    {
+                        transaction = em.getTransaction();
+                        if(transaction != null && transaction.isActive())
+                        {
+                            try
+                            {
+                                if (!commitFailed)
+                                {
+                                    em.flush();
+                                }
+                            }
+                            catch (Exception e)
+                            {
+                                firstException = e;
+                                commitFailed = true;
+                                break;
+                            }
+                        }
+                    }
+
+                    // and now either commit or rollback all transactions
+                    for (Map.Entry<String, EntityManager> emEntry: ems.get().entrySet())
+                    {
+                        EntityManager em = emEntry.getValue();
+                        transaction = em.getTransaction();
+                        if(transaction != null && transaction.isActive())
+                        {
+                            try
+                            {
+                                if (!commitFailed)
+                                {
+                                    transaction.commit();
+                                }
+                                else
+                                {
+                                    transaction.rollback();
+                                }
+                            }
+                            catch (Exception e)
+                            {
+                                firstException = e;
+                                commitFailed = true;
+                            }
+                            finally
+                            {
+                                // close the TransactionContext
+                                transactionBeanStorage.endTransactionScope(emEntry.getKey());
+                            }
+
+                        }
+                    }
+                }
+
+            }
+
+            if (! isOutermostInterceptor())
+            {
+                transactionBeanStorage.deactivateTransactionScope(qualifierKey);
+            }
+            decrementRefCounter(qualifierKey);
+
+            if (commitFailed)
+            {
+                throw firstException;
+            }
+        }
+
+    }
+
+    /**
+     * This method might get overridden in subclasses to supply better error messages.
+     * This is useful if e.g. a JPA provider only provides a stubborn Exception for
+     * their ConstraintValidationExceptions.
+     * @param e
+     * @return
+     */
+    protected Exception prepareException(Exception e)
+    {
+        return e;
+    }
+
+    /**
+     * @return <code>true</code> if we are the outermost interceptor over all qualifiers.
+     */
+    private boolean isOutermostInterceptor()
+    {
+        HashMap<String, AtomicInteger> refCounterMap = refCounterMaps.get();
+        return refCounterMap == null ||
+               (refCounterMap.size() == 1 && refCounterMap.values().iterator().next().get() == 1);
+    }
+
+    /**
+     * Increment the ref counter for the given classifier and return the
+     * old value
+     * @param qualifierKey name of the qualifier used for the DB
+     * @return the previous value of the refCounter
+     */
+    private int incrementRefCounter(String qualifierKey)
+    {
+        HashMap<String, AtomicInteger> refCounterMap = refCounterMaps.get();
+
+        if (refCounterMap == null)
+        {
+            refCounterMap = new HashMap<String, AtomicInteger>();
+            refCounterMaps.set(refCounterMap);
+        }
+
+        AtomicInteger refCounter = refCounterMap.get(qualifierKey);
+
+        if (refCounter == null)
+        {
+            refCounter = new AtomicInteger(0);
+            refCounterMap.put(qualifierKey, refCounter);
+        }
+
+        return refCounter.incrementAndGet() - 1;
+    }
+
+    /**
+     * Decrement the reference counter for the given classifier and
+     * return the layer. Also cleans up the {@link #refCounterMaps}.
+     *
+     * @param qualifierKey
+     * @return the layer number. 0 represents the outermost interceptor for the qualifier
+     */
+    private int decrementRefCounter(String qualifierKey)
+    {
+        HashMap<String, AtomicInteger> refCounterMap = refCounterMaps.get();
+        if (refCounterMap == null)
+        {
+            return 0;
+        }
+
+        AtomicInteger refCounter = refCounterMap.get(qualifierKey);
+
+        if (refCounter == null)
+        {
+            return 0;
+        }
+
+        int layer = refCounter.decrementAndGet();
+
+        if (layer == 0)
+        {
+            refCounterMap.remove(qualifierKey);
+        }
+
+        if (refCounterMap.size() == 0)
+        {
+            refCounterMaps.set(null);
+            refCounterMaps.remove();
+        }
+
+        return layer;
+    }
+
+
+    protected Transactional extractTransactionalAnnotation(InvocationContext context)
+    {
+        Transactional transactionalAnnotation = context.getMethod().getAnnotation(Transactional.class);
+
+        if (transactionalAnnotation == null)
+        {
+            transactionalAnnotation = context.getTarget().getClass().getAnnotation(Transactional.class);
+        }
+        return transactionalAnnotation;
+    }
+
+    protected Class<? extends Annotation> getTransactionQualifier(Transactional transactionalAnnotation)
+    {
+        Class<? extends Annotation> qualifierClass = Default.class;
+        if (transactionalAnnotation != null)
+        {
+            qualifierClass = transactionalAnnotation.qualifier();
+        }
+        return qualifierClass;
+    }
+
+    protected Bean<EntityManager> resolveEntityManagerBean(Class<? extends Annotation> qualifierClass)
+    {
+        Set<Bean<?>> entityManagerBeans = beanManager.getBeans(EntityManager.class, new AnyLiteral());
+        if (entityManagerBeans == null)
+        {
+            entityManagerBeans = new HashSet<Bean<?>>();
+        }
+        Bean<EntityManager> entityManagerBean = null;
+
+        it:
+        for (Bean<?> currentEntityManagerBean : entityManagerBeans)
+        {
+            Set<Annotation> foundQualifierAnnotations = currentEntityManagerBean.getQualifiers();
+
+            for (Annotation currentQualifierAnnotation : foundQualifierAnnotations)
+            {
+                if (currentQualifierAnnotation.annotationType().equals(qualifierClass))
+                {
+                    entityManagerBean = (Bean<EntityManager>) currentEntityManagerBean;
+                    break it;
+                }
+            }
+        }
+        return entityManagerBean;
+    }
+
+}

Propchange: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanBag.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanBag.java?rev=1172145&r1=1172144&r2=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanBag.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanBag.java Sat Sep 17 23:16:40 2011
@@ -18,8 +18,8 @@
  */
 package org.apache.myfaces.extensions.cdi.jpa.impl.transaction.context;
 
+import javax.enterprise.context.spi.Contextual;
 import javax.enterprise.context.spi.CreationalContext;
-import javax.enterprise.inject.spi.Bean;
 
 /**
  * Holds the information we need store to manage
@@ -27,18 +27,18 @@ import javax.enterprise.inject.spi.Bean;
  */
 public class TransactionBeanBag<T>
 {
-    private Bean<T> bean;
+    private Contextual<T> bean;
     private T contextualInstance;
     private CreationalContext<T> creationalContext;
 
-    public TransactionBeanBag(Bean<T> bean, T contextualInstance, CreationalContext<T> creationalContext)
+    public TransactionBeanBag(Contextual<T> bean, T contextualInstance, CreationalContext<T> creationalContext)
     {
         this.bean = bean;
         this.contextualInstance = contextualInstance;
         this.creationalContext = creationalContext;
     }
 
-    public Bean<T> getBean()
+    public Contextual<T> getBean()
     {
         return bean;
     }

Modified: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java?rev=1172145&r1=1172144&r2=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java Sat Sep 17 23:16:40 2011
@@ -19,24 +19,44 @@
 package org.apache.myfaces.extensions.cdi.jpa.impl.transaction.context;
 
 import javax.annotation.PreDestroy;
+import javax.enterprise.context.ContextNotActiveException;
 import javax.enterprise.context.RequestScoped;
-import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.context.spi.Contextual;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Stack;
 
 /**
- * This bean stores information about
+ * <p>This bean stores information about
  * &#064;{@link org.apache.myfaces.extensions.cdi.jpa.api.TransactionScoped}
- * contextual instances, their {@link javax.enterprise.context.spi.CreationalContext} etc
+ * contextual instances, their {@link javax.enterprise.context.spi.CreationalContext} etc.</p>
+ *
+ * <p>We use a RequestScoped bean because this way we don't need to take
+ * care about cleaning up any ThreadLocals ourselfs. This also makes sure that
+ * we subsequently destroy any left over TransactionScoped beans (which should not happen,
+ * but who knows). We also don't need to do any fancy synchronization stuff since
+ * we are sure that we are always in the same Thread.</p>
  */
 @RequestScoped
 public class TransactionBeanStorage
 {
 
-    private Map<String, Map<Bean, TransactionBeanBag>> storedBeans =
-            new HashMap<String, Map<Bean, TransactionBeanBag>>();
+    /**
+     * This is the actual bean storage.
+     * The structure is:
+     * <ol>
+     *     <li>transactioKey identifies the 'database qualifier'</li>
+     *     <li>transactionKey -> Stack: we need the Stack because of REQUIRES_NEW, etc</li>
+     *     <li>top Element in the Stack -> Context beans for the transactionKey</li>
+     * </ol>
+     *
+     */
+    private Map<String, Stack<Map<Contextual, TransactionBeanBag>>> storedBeans =
+            new HashMap<String, Stack<Map<Contextual, TransactionBeanBag>>>();
+
+    private Map<Contextual, TransactionBeanBag> activeBeans;
 
-    private Map<Bean, TransactionBeanBag> activeBeanBag;
+    private Stack<String> transactionKeys = new Stack<String>();
 
     /**
      * Start the TransactionScope with the given qualifier
@@ -44,7 +64,17 @@ public class TransactionBeanStorage
      */
     public void startTransactionScope(String transactionKey)
     {
+        Stack<Map<Contextual, TransactionBeanBag>> transStack = storedBeans.get(transactionKey);
 
+        if (transStack == null)
+        {
+            transStack = new Stack<Map<Contextual, TransactionBeanBag>>();
+            storedBeans.put(transactionKey, transStack);
+        }
+
+        activeBeans = new HashMap<Contextual, TransactionBeanBag>();
+        transStack.push(activeBeans);
+        transactionKeys.push(transactionKey);
     }
 
     /**
@@ -55,24 +85,93 @@ public class TransactionBeanStorage
      */
     public void endTransactionScope(String transactionKey)
     {
-        //X TODO
+        String expectedTransactionKey = transactionKeys.pop();
+        if (!transactionKey.equals(expectedTransactionKey))
+        {
+            throw new ContextNotActiveException("Error at deactivating TransactionScope with key " + transactionKey
+                                                + " expected: " + expectedTransactionKey);
+        }
+
+        destroyBeans(activeBeans);
+        activeBeans = null;
+
+        // drop the context from the storage
+        Stack<Map<Contextual, TransactionBeanBag>> transStack = storedBeans.get(transactionKey);
+        transStack.pop();
+
+        if (transStack.size() == 0)
+        {
+            storedBeans.remove(transactionKey);
+        }
+
+        if (!transactionKeys.isEmpty())
+        {
+            String oldTransactionKey = transactionKeys.peek();
+            Stack<Map<Contextual, TransactionBeanBag>> transactionStack = storedBeans.get(oldTransactionKey);
+            if (transactionStack != null)
+            {
+                activeBeans = transactionStack.peek();
+            }
+        }
     }
 
     /**
      * Activate the TransactionScope with the given qualifier.
      * This is needed if a subsequently invoked &#064;Transactional
      * method will switch to another persistence unit.
+     * This method shall not be invoked when the transaction just got started
+     * with {@link #startTransactionScope(String)}.
      *
      * @param transactionKey
      */
     public void activateTransactionScope(String transactionKey)
     {
-        //X TODO
+        Stack<Map<Contextual, TransactionBeanBag>> transStack = storedBeans.get(transactionKey);
+        if (transStack == null)
+        {
+            throw new ContextNotActiveException("Cannot activate TransactionScope with key " + transactionKey);
+        }
+
+        activeBeans =  transStack.peek();
+        transactionKeys.push(transactionKey);
     }
 
-    public Map<Bean, TransactionBeanBag> getActiveBeanBags()
+    /**
+     * Deactivate the TransactionScope with the given qualifier.
+     * This is needed if a subsequently invoked &#064;Transactional
+     * method will switch to another persistence unit.
+     * This method shall not be invoked when the transaction gets ended
+     * with {@link #endTransactionScope(String)}.
+     *
+     * @param transactionKey
+     */
+    public void deactivateTransactionScope(String transactionKey)
     {
-        return activeBeanBag;
+        String expectedTransactionKey = transactionKeys.pop();
+        if (!transactionKey.equals(expectedTransactionKey))
+        {
+            throw new ContextNotActiveException("Error at deactivating TransactionScope with key " + transactionKey
+                                                + " expected: " + expectedTransactionKey);
+        }
+
+
+        if (!transactionKeys.isEmpty())
+        {
+            String oldTransactionKey = transactionKeys.peek();
+            Stack<Map<Contextual, TransactionBeanBag>> transactionStack = storedBeans.get(oldTransactionKey);
+            if (transactionStack != null)
+            {
+                activeBeans = transactionStack.peek();
+            }
+        }
+    }
+
+    /**
+     * @return the Map which represents the currently active Context content.
+     */
+    public Map<Contextual, TransactionBeanBag> getActiveBeans()
+    {
+        return activeBeans;
     }
 
     /**
@@ -80,9 +179,23 @@ public class TransactionBeanStorage
      * stored in the context.
      */
     @PreDestroy
-    protected void requestEnded()
+    public void requestEnded()
     {
         //X TODO
     }
+
+    /**
+     * Properly destroy all the given beans.
+     * @param activeBeans
+     */
+    private void destroyBeans(Map<Contextual, TransactionBeanBag> activeBeans)
+    {
+        for (TransactionBeanBag beanBag : activeBeans.values())
+        {
+            beanBag.getBean().destroy(beanBag.getContextualInstance(), beanBag.getCreationalContext());
+        }
+    }
+
+
 }
 

Modified: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java?rev=1172145&r1=1172144&r2=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java Sat Sep 17 23:16:40 2011
@@ -26,6 +26,7 @@ import javax.enterprise.context.spi.Cont
 import javax.enterprise.context.spi.Contextual;
 import javax.enterprise.context.spi.CreationalContext;
 import java.lang.annotation.Annotation;
+import java.util.Map;
 
 /**
  * CDI Context for managing &#064;{@link TransactionScoped} contextual instances.
@@ -44,25 +45,55 @@ public class TransactionContext implemen
 
     public <T> T get(Contextual<T> component)
     {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+        Map<Contextual, TransactionBeanBag> beanBags = beanStorage.getActiveBeans();
+
+        if (beanBags == null)
+        {
+            throw new ContextNotActiveException();
+        }
+
+        TransactionBeanBag beanBag = beanBags.get(component);
+        if (beanBag != null)
+        {
+            return (T) beanBag.getContextualInstance();
+        }
+
+        return null;
     }
 
-    public Class<? extends Annotation> getScope()
+    public <T> T get(Contextual<T> component, CreationalContext<T> creationalContext)
     {
-        return TransactionScoped.class;
+        Map<Contextual, TransactionBeanBag> beanBags = beanStorage.getActiveBeans();
+
+        if (beanBags == null)
+        {
+            throw new ContextNotActiveException();
+        }
+
+        TransactionBeanBag beanBag = beanBags.get(component);
+        if (beanBag != null)
+        {
+            return (T) beanBag.getContextualInstance();
+        }
+
+        // if it doesn't yet exist, we need to create it now!
+        T instance = component.create(creationalContext);
+        beanBag = new TransactionBeanBag(component, instance, creationalContext);
+        beanBags.put(component, beanBag);
+
+        return instance;
     }
 
-    public <T> T get(Contextual<T> component, CreationalContext<T> creationalContext)
+    public Class<? extends Annotation> getScope()
     {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+        return TransactionScoped.class;
     }
 
     public boolean isActive()
     {
         try
         {
-            //X TODO beanStorage....
-            return true;
+            return beanStorage.getActiveBeans() != null;
         }
         catch(ContextNotActiveException cnae)
         {

Modified: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContextExtension.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContextExtension.java?rev=1172145&r1=1172144&r2=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContextExtension.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContextExtension.java Sat Sep 17 23:16:40 2011
@@ -25,12 +25,13 @@ import javax.enterprise.event.Observes;
 import javax.enterprise.inject.spi.AfterBeanDiscovery;
 import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.Extension;
 
 /**
  * CDI Extension which registers and manages the {@link TransactionContext}.
  *
  */
-public class TransactionContextExtension implements Deactivatable
+public class TransactionContextExtension implements Extension, Deactivatable
 {
     /**
      * Register the TransactionContext as a CDI Context

Modified: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/beans.xml
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/beans.xml?rev=1172145&r1=1172144&r2=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/beans.xml (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/beans.xml Sat Sep 17 23:16:40 2011
@@ -22,6 +22,6 @@
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
 
     <interceptors>
-        <class>org.apache.myfaces.extensions.cdi.jpa.impl.TransactionalInterceptor</class>
+        <class>org.apache.myfaces.extensions.cdi.jpa.impl.transaction.TransactionalInterceptor</class>
     </interceptors>
 </beans>

Added: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension?rev=1172145&view=auto
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension (added)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension Sat Sep 17 23:16:40 2011
@@ -0,0 +1,21 @@
+#####################################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#####################################################################################
+
+# myfaces-codi support for @TransactionScoped
+org.apache.myfaces.extensions.cdi.jpa.impl.transaction.context.TransactionContextExtension

Modified: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java?rev=1172145&r1=1172144&r2=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java Sat Sep 17 23:16:40 2011
@@ -49,7 +49,7 @@ public class TransactionInterceptorTest 
         Assert.assertNotNull(trans);
     }
 
-    @Test(enabled = false)
+    @Test
     public void testTransactionScopedTransactionInterceptor()
     {
         TransactionScopedEmTestServiceImpl testService = getBeanInstance(TransactionScopedEmTestServiceImpl.class);
@@ -61,6 +61,7 @@ public class TransactionInterceptorTest 
         try
         {
             EntityManager em = getBeanInstance(EntityManager.class, new AnnotationLiteral<TransactionScopeAware>(){});
+            em.getTransaction();
             Assert.fail("ContextNotActiveException expected!");
         }
         catch(ContextNotActiveException cnae)

Modified: myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionScopedEntityManagerProducer.java
URL: http://svn.apache.org/viewvc/myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionScopedEntityManagerProducer.java?rev=1172145&r1=1172144&r2=1172145&view=diff
==============================================================================
--- myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionScopedEntityManagerProducer.java (original)
+++ myfaces/extensions/cdi/trunk/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionScopedEntityManagerProducer.java Sat Sep 17 23:16:40 2011
@@ -20,6 +20,7 @@ package org.apache.myfaces.extensions.cd
 
 import org.apache.myfaces.extensions.cdi.jpa.api.TransactionScoped;
 
+import javax.enterprise.context.Dependent;
 import javax.enterprise.context.RequestScoped;
 import javax.enterprise.inject.Disposes;
 import javax.enterprise.inject.Produces;
@@ -29,6 +30,7 @@ import javax.persistence.PersistenceCont
 /**
  * producer for a simple &#064;Default EntityManager
  */
+@Dependent
 public class TransactionScopedEntityManagerProducer {
 
     private @PersistenceContext(unitName = "test") EntityManager entityManager;