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 @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 @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 @Transactional interceptor for the given
+ * {@link javax.inject.Qualifier} will open an {@link javax.persistence.EntityTransaction}
+ * and the outermost @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 @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 @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
* @{@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 @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 @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 @{@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 @Default EntityManager
*/
+@Dependent
public class TransactionScopedEntityManagerProducer {
private @PersistenceContext(unitName = "test") EntityManager entityManager;