EXTCDI-265 implementation validation for @TransactionScoped
git-svn-id: https://svn.apache.org/repos/asf/myfaces/extensions/cdi/trunk@1240870 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/jee-modules/jpa-module/api/src/main/java/org/apache/myfaces/extensions/cdi/jpa/api/Transactional.java b/jee-modules/jpa-module/api/src/main/java/org/apache/myfaces/extensions/cdi/jpa/api/Transactional.java
index f146dbe..458ce13 100644
--- a/jee-modules/jpa-module/api/src/main/java/org/apache/myfaces/extensions/cdi/jpa/api/Transactional.java
+++ b/jee-modules/jpa-module/api/src/main/java/org/apache/myfaces/extensions/cdi/jpa/api/Transactional.java
@@ -34,6 +34,8 @@
* The optional qualifier can be used to specify different entity managers.
*/
+//TODO instead of using #qualifier it should be possible to use a custom qualifier annotated with @TransactionQualifier
+
@InterceptorBinding
@Documented
@Inherited
diff --git a/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java b/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java
index b8920c4..6bebb37 100644
--- a/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java
+++ b/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java
@@ -136,23 +136,33 @@
for (Map.Entry<String, EntityManager> emsEntry: emsEntries.entrySet())
{
EntityManager em = emsEntry.getValue();
- transaction = em.getTransaction();
- if (transaction != null && transaction.isActive())
+
+ try
{
- try
+ transaction = em.getTransaction();
+
+ if (transaction != null && transaction.isActive())
{
- transaction.rollback();
- }
- catch (Exception eRollback)
- {
- if(LOGGER.isLoggable(Level.SEVERE))
+ try
{
- LOGGER.log(Level.SEVERE,
- "Got additional Exception while subsequently " +
- "rolling back other SQL transactions", eRollback);
+ transaction.rollback();
+ }
+ catch (Exception eRollback)
+ {
+ if(LOGGER.isLoggable(Level.SEVERE))
+ {
+ LOGGER.log(Level.SEVERE,
+ "Got additional Exception while subsequently " +
+ "rolling back other SQL transactions", eRollback);
+ }
}
}
}
+ catch (IllegalStateException e2)
+ {
+ //just happens if the setup is wrong -> we can't do a proper cleanup
+ //but we have to continue to cleanup the scope
+ }
}
// drop all EntityManagers from the ThreadLocal
@@ -234,6 +244,12 @@
TransactionBeanStorage.getStorage().endAllTransactionScopes();
TransactionBeanStorage.resetStorage();
}
+ else
+ {
+ //just happens if the wrong qualifier is used in an application
+ TransactionBeanStorage.getStorage().endAllTransactionScopes();
+ TransactionBeanStorage.resetStorage();
+ }
}
else
{
diff --git a/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanEntry.java b/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanEntry.java
index 7da453e..837601e 100644
--- a/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanEntry.java
+++ b/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanEntry.java
@@ -20,6 +20,10 @@
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.Set;
/**
* Holds the information we need store to manage
@@ -34,6 +38,7 @@
public TransactionBeanEntry(Contextual<T> bean, T contextualInstance, CreationalContext<T> creationalContext)
{
this.bean = bean;
+
this.contextualInstance = contextualInstance;
this.creationalContext = creationalContext;
}
@@ -52,4 +57,13 @@
{
return creationalContext;
}
+
+ public Set<Annotation> getQualifiers()
+ {
+ if(bean instanceof Bean)
+ {
+ return ((Bean<?>)bean).getQualifiers();
+ }
+ return Collections.emptySet();
+ }
}
diff --git a/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java b/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java
index 3372f82..2997982 100644
--- a/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java
+++ b/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionBeanStorage.java
@@ -184,6 +184,10 @@
return oldTransactionContextKey;
}
+ public String getActiveTransactionKey()
+ {
+ return activeTransactionKey;
+ }
/**
* This will destroy all stored transaction contexts.
diff --git a/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java b/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java
index 3240164..f6f4fbc 100644
--- a/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java
+++ b/jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/context/TransactionContext.java
@@ -48,6 +48,7 @@
TransactionBeanEntry transactionBeanEntry = transactionBeanEntryMap.get(component);
if (transactionBeanEntry != null)
{
+ checkTransactionBeanEntry(transactionBeanEntry);
return (T) transactionBeanEntry.getContextualInstance();
}
@@ -72,6 +73,7 @@
TransactionBeanEntry transactionBeanEntry = transactionBeanEntryMap.get(component);
if (transactionBeanEntry != null)
{
+ checkTransactionBeanEntry(transactionBeanEntry);
return (T) transactionBeanEntry.getContextualInstance();
}
@@ -80,9 +82,43 @@
transactionBeanEntry = new TransactionBeanEntry(component, instance, creationalContext);
transactionBeanEntryMap.put(component, transactionBeanEntry);
+ checkTransactionBeanEntry(transactionBeanEntry);
return instance;
}
+ private void checkTransactionBeanEntry(TransactionBeanEntry<?> transactionBeanEntry)
+ {
+ String activeTransactionKey = TransactionBeanStorage.getStorage().getActiveTransactionKey();
+
+ for(Annotation qualifier : transactionBeanEntry.getQualifiers())
+ {
+ if(qualifier.annotationType().getName().endsWith(activeTransactionKey))
+ {
+ return;
+ }
+ }
+
+ throw new IllegalStateException("Transaction qualifier of the intercepted bean or method and " +
+ "the injected entity-manager has to be the same. Active transaction qualifier: " +
+ activeTransactionKey + " qualifier/s of the entity-manager: " +
+ extractQualifiers(transactionBeanEntry));
+ }
+
+ private String extractQualifiers(TransactionBeanEntry<?> transactionBeanEntry)
+ {
+ StringBuilder result = new StringBuilder();
+ for(Annotation annotation : transactionBeanEntry.getQualifiers())
+ {
+ if(result.length() != 0)
+ {
+ result.append(";");
+ }
+
+ result.append(annotation.annotationType().getName());
+ }
+ return result.toString();
+ }
+
private Map<Contextual, TransactionBeanEntry> getTransactionBeanEntryMap()
{
TransactionBeanStorage transactionBeanStorage = TransactionBeanStorage.getStorage();
diff --git a/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TestTransactionHelper.java b/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TestTransactionHelper.java
new file mode 100644
index 0000000..3fa9224
--- /dev/null
+++ b/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TestTransactionHelper.java
@@ -0,0 +1,46 @@
+/*
+ * 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.test;
+
+import org.apache.myfaces.extensions.cdi.core.api.provider.BeanManagerProvider;
+import org.apache.myfaces.extensions.cdi.jpa.api.Transactional;
+
+import javax.enterprise.context.ApplicationScoped;
+import java.util.concurrent.Callable;
+
+/**
+ * see {@link org.apache.myfaces.extensions.cdi.jpa.api.TransactionHelper}
+ */
+@ApplicationScoped
+public class TestTransactionHelper
+{
+ public static TestTransactionHelper getInstance()
+ {
+ return BeanManagerProvider.getInstance().getContextualReference(TestTransactionHelper.class);
+ }
+
+ /**
+ * see {@link org.apache.myfaces.extensions.cdi.jpa.api.TransactionHelper}
+ */
+ @Transactional(qualifier = TransactionScopeAware.class)
+ public <T> T executeTransactional(Callable<T> callable) throws Exception
+ {
+ return callable.call();
+ }
+}
diff --git a/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java b/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java
index 0bf1e3a..e480c45 100644
--- a/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java
+++ b/jee-modules/jpa-module/impl/src/test/java/org/apache/myfaces/extensions/cdi/jpa/test/TransactionInterceptorTest.java
@@ -20,6 +20,7 @@
import org.apache.myfaces.extensions.cdi.core.api.provider.BeanManagerProvider;
+import org.apache.myfaces.extensions.cdi.core.impl.util.DefaultLiteral;
import org.apache.myfaces.extensions.cdi.core.test.util.ContainerTestBase;
import org.apache.myfaces.extensions.cdi.jpa.api.TransactionHelper;
import org.apache.myfaces.extensions.cdi.jpa.impl.transaction.context.TransactionBeanStorage;
@@ -91,7 +92,7 @@
// this was expected, all is fine!
}
- Integer retVal = TransactionHelper.getInstance().executeTransactional( new Callable<Integer>() {
+ Integer retVal = TestTransactionHelper.getInstance().executeTransactional( new Callable<Integer>() {
public Integer call() throws Exception
{
@@ -116,6 +117,49 @@
Assert.assertNull(TransactionBeanStorage.getStorage());
}
+ @Test
+ public void testTransactionHelperWithOtherQualifierThanCurrentTransaction() throws Exception
+ {
+ try
+ {
+ TransactionHelper.getInstance().executeTransactional( new Callable<Integer>() {
+
+ public Integer call() throws Exception
+ {
+ resolveEntityManager();
+
+ return Integer.valueOf(3);
+ }
+ });
+ }
+ catch (IllegalStateException e)
+ {
+ //expected: TransactionHelper has different transaction qualifier than the injected/resolved entity-manager
+ }
+
+ Integer retVal = TransactionHelper.getInstance().executeTransactional( new Callable<Integer>() {
+
+ public Integer call() throws Exception
+ {
+ resolveDefaultEntityManager();
+
+ return Integer.valueOf(3);
+ }
+ });
+ Assert.assertEquals(retVal, Integer.valueOf(3));
+
+ Assert.assertNull(TransactionBeanStorage.getStorage());
+ }
+
+ private void resolveDefaultEntityManager()
+ {
+ EntityManager em = BeanManagerProvider.getInstance().
+ getContextualReference(EntityManager.class, new DefaultLiteral());
+ Assert.assertNotNull(em);
+ EntityTransaction et = em.getTransaction();
+ Assert.assertNotNull(et);
+ }
+
private void resolveEntityManager()
{
EntityManager em = BeanManagerProvider.getInstance().