You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by st...@apache.org on 2012/06/05 18:15:23 UTC
[1/4] git commit: DELTASPIKE-185 DELTASPIKE-175 @Transactional
interceptor cleanup
Updated Branches:
refs/heads/master 4b81872a0 -> 94327565d
DELTASPIKE-185 DELTASPIKE-175 @Transactional interceptor cleanup
This commit simplifies the whole structure and removes the handling
of @PersistenceContext. We must not touch those as this would leed
to Exceptions on EE servers.
Project: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/commit/94327565
Tree: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/tree/94327565
Diff: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/diff/94327565
Branch: refs/heads/master
Commit: 94327565d2c558185b71473a14786e11dc26c0b6
Parents: 88c4b00
Author: Mark Struberg <st...@apache.org>
Authored: Tue Jun 5 18:05:56 2012 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Tue Jun 5 18:05:56 2012 +0200
----------------------------------------------------------------------
.../deltaspike/jpa/spi/PersistenceStrategy.java | 2 +-
.../deltaspike/jpa/impl/EntityManagerRef.java | 95 ----
.../jpa/impl/EntityManagerRefHolder.java | 40 --
.../jpa/impl/PersistenceContextMetaEntry.java | 47 --
.../deltaspike/jpa/impl/PersistenceHelper.java | 198 --------
.../transaction/InternalTransactionContext.java | 98 ----
.../PersistenceStrategyCleanupTestEvent.java | 23 -
.../transaction/PersistenceStrategyHelper.java | 188 ++++++++
.../TransactionBeanStorageCleanupTestEvent.java | 23 -
.../impl/transaction/TransactionMetaDataEntry.java | 73 ---
.../impl/transaction/TransactionalInterceptor.java | 3 +
.../TransactionalInterceptorStrategy.java | 368 ++++-----------
.../transaction/context/TransactionBeanEntry.java | 13 -
.../context/TransactionBeanStorage.java | 262 +++++------
.../transaction/context/TransactionContext.java | 159 ++-----
.../test/jpa/api/shared/TestEventObserver.java | 54 ---
.../DefaultEntityManagerInjectionTest.java | 10 -
.../DefaultNestedTransactionTest.java | 7 -
...NestedMultiTransactionCatchedExceptionTest.java | 6 -
.../NestedTransactionCatchedExceptionTest.java | 6 -
...tityManagerInjectionUncatchedExceptionTest.java | 6 -
...anagerInjectionUncatchedFlushExceptionTest.java | 6 -
...ultiTransactionUncatchedFlushExceptionTest.java | 6 -
...stedMultiTransactionUncatchedExceptionTest.java | 6 -
.../nested/NestedTransactionWithExceptionTest.java | 6 -
.../auto/MultipleEntityManagerInjectionTest.java | 6 -
.../manual/ManualTransactionTest.java | 7 -
.../nested/NestedMultiTransactionTest.java | 6 -
.../nested/NestedTransactionTest.java | 6 -
.../stereotype/StereotypeTransactionalTest.java | 7 -
.../transactionhelper/TransactionHelperTest.java | 116 +++++
.../TransactionScopedEntityManagerProducer.java | 30 ++
...ransactionScopedEntityManagerInjectionTest.java | 12 -
...aultTransactionScopedNestedTransactionTest.java | 6 -
...ransactionScopedEntityManagerInjectionTest.java | 6 -
.../ManualTransactionScopedTransactionTest.java | 7 -
...estedMultiTransactionScopedTransactionTest.java | 6 -
.../NestedTransactionScopedTransactionTest.java | 6 -
...ereotypeTransactionScopedTransactionalTest.java | 6 -
39 files changed, 583 insertions(+), 1349 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/api/src/main/java/org/apache/deltaspike/jpa/spi/PersistenceStrategy.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/api/src/main/java/org/apache/deltaspike/jpa/spi/PersistenceStrategy.java b/deltaspike/modules/jpa/api/src/main/java/org/apache/deltaspike/jpa/spi/PersistenceStrategy.java
index a5d49e4..49916ab 100644
--- a/deltaspike/modules/jpa/api/src/main/java/org/apache/deltaspike/jpa/spi/PersistenceStrategy.java
+++ b/deltaspike/modules/jpa/api/src/main/java/org/apache/deltaspike/jpa/spi/PersistenceStrategy.java
@@ -21,7 +21,7 @@ package org.apache.deltaspike.jpa.spi;
import org.apache.deltaspike.core.spi.InterceptorStrategy;
/**
- * Marker interface for a pluggable strategy for {@link org.apache.deltaspike.jpa.api.Transactional}
+ * Marker interface for a plugable strategy for {@link org.apache.deltaspike.jpa.api.Transactional}.
*/
public interface PersistenceStrategy extends InterceptorStrategy
{
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/EntityManagerRef.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/EntityManagerRef.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/EntityManagerRef.java
deleted file mode 100644
index 8c0d349..0000000
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/EntityManagerRef.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.deltaspike.jpa.impl;
-
-import javax.persistence.EntityManager;
-import java.io.Serializable;
-import java.lang.reflect.Field;
-
-/**
- * Serializable entry which stores information about an {@link javax.persistence.EntityManager}
- */
-public class EntityManagerRef implements Serializable
-{
- private static final long serialVersionUID = -4273544531446327680L;
-
- private String key;
-
- private final Class sourceClass;
- private final String fieldName;
-
- private transient Field entityManagerField;
-
- EntityManagerRef(Class sourceClass, String fieldName, String key)
- {
- this.sourceClass = sourceClass;
- this.fieldName = fieldName;
- this.key = key;
- }
-
- public String getKey()
- {
- return key;
- }
-
- public Class getSourceClass()
- {
- return sourceClass;
- }
-
- public String getFieldName()
- {
- return fieldName;
- }
-
- //we can't cache the reference,
- //because this instance will be stored in a cache and we have to support dependent scoped entity managers
- public EntityManager getEntityManager(Object target)
- {
- if (this.entityManagerField == null)
- {
- try
- {
- this.entityManagerField = this.sourceClass.getDeclaredField(this.fieldName);
- }
- catch (NoSuchFieldException e)
- {
- //TODO add logging in case of project stage dev.
- return null;
- }
- }
-
- if (!entityManagerField.isAccessible())
- {
- entityManagerField.setAccessible(true);
- }
-
- try
- {
- @SuppressWarnings({ "UnnecessaryLocalVariable" })
- EntityManager entityManager = (EntityManager) entityManagerField.get(target);
- return entityManager;
- }
- catch (IllegalAccessException e)
- {
- //TODO add logging in case of project stage dev.
- return null;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/EntityManagerRefHolder.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/EntityManagerRefHolder.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/EntityManagerRefHolder.java
deleted file mode 100644
index 00dbe34..0000000
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/EntityManagerRefHolder.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.deltaspike.jpa.impl;
-
-import java.util.List;
-
-//TODO refactor it to remove this workaround
-class EntityManagerRefHolder extends EntityManagerRef
-{
- private static final long serialVersionUID = -7002376898682639768L;
-
- private List<EntityManagerRef> entityManagerRefs;
-
- EntityManagerRefHolder(List<EntityManagerRef> entityManagerRefs)
- {
- super(null, null, null);
- this.entityManagerRefs = entityManagerRefs;
- }
-
- List<EntityManagerRef> getEntityManagerRefs()
- {
- return entityManagerRefs;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/PersistenceContextMetaEntry.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/PersistenceContextMetaEntry.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/PersistenceContextMetaEntry.java
deleted file mode 100644
index b2d7170..0000000
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/PersistenceContextMetaEntry.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.deltaspike.jpa.impl;
-
-/**
- * Entry which stores information about a {@link javax.persistence.PersistenceContext}
- */
-class PersistenceContextMetaEntry extends EntityManagerRef
-{
- private static final long serialVersionUID = 637184416580955956L;
-
- private String unitName;
- private boolean extended;
-
- PersistenceContextMetaEntry(Class sourceClass, String fieldName, String unitName, boolean extended)
- {
- super(sourceClass, fieldName, unitName);
- this.unitName = unitName;
- this.extended = extended;
- }
-
- String getUnitName()
- {
- return unitName;
- }
-
- boolean isExtended()
- {
- return extended;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/PersistenceHelper.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/PersistenceHelper.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/PersistenceHelper.java
deleted file mode 100644
index f4561ad..0000000
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/PersistenceHelper.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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.deltaspike.jpa.impl;
-
-import org.apache.deltaspike.core.api.provider.BeanManagerProvider;
-import org.apache.deltaspike.core.util.ClassUtils;
-
-import javax.enterprise.inject.Default;
-import javax.enterprise.inject.Typed;
-import javax.enterprise.inject.spi.BeanManager;
-import javax.persistence.EntityManager;
-import javax.persistence.PersistenceContext;
-import javax.persistence.PersistenceContextType;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Helper which provides util methods for
- * {@link org.apache.deltaspike.jpa.impl.transaction.TransactionalInterceptorStrategy}
- */
-@Typed()
-public class PersistenceHelper
-{
- private static final String NO_FIELD_MARKER = PersistenceHelper.class.getName() + ":DEFAULT_FIELD";
-
- private static transient volatile Map<ClassLoader, Map<String, EntityManagerRef>> persistenceContextMetaEntries =
- new ConcurrentHashMap<ClassLoader, Map<String, EntityManagerRef>>();
-
- private PersistenceHelper()
- {
- //prevent instantiation
- }
-
- /**
- * Analyzes the given instance and returns the found reference to an injected {@link EntityManager}
- * or null otherwise
- *
- * @param target instance to analyze
- * @return the injected entity-manager or null otherwise
- */
- public static List<EntityManagerRef> tryToFindEntityManagerReference(Object target)
- {
- List<EntityManagerRef> entityManagers = tryToFindEntityManagerEntryInTarget(target);
-
- if (entityManagers == null || entityManagers.isEmpty())
- {
- return null;
- }
- return entityManagers;
- }
-
- /*
- * needed for special add-ons - don't change it!
- */
- static List<EntityManagerRef> tryToFindEntityManagerEntryInTarget(Object target)
- {
- Map<String, EntityManagerRef> mapping = persistenceContextMetaEntries.get(getClassLoader());
-
- mapping = initMapping(mapping);
-
- String key = target.getClass().getName();
- EntityManagerRef entityManagerRef = mapping.get(key);
-
- if (entityManagerRef != null && entityManagerRef instanceof PersistenceContextMetaEntry &&
- NO_FIELD_MARKER.equals(entityManagerRef.getFieldName()))
- {
- return null;
- }
-
- if (entityManagerRef == null)
- {
- List<EntityManagerRef> foundEntries = findEntityManagerMetaReferences(target.getClass());
-
- if (foundEntries.isEmpty())
- {
- mapping.put(key, new PersistenceContextMetaEntry(
- Object.class, NO_FIELD_MARKER, Default.class.getName(), false));
- return null;
- }
-
- if (foundEntries.size() == 1)
- {
- entityManagerRef = foundEntries.iterator().next();
- }
- else
- {
- //TODO remove workaround
- entityManagerRef = new EntityManagerRefHolder(foundEntries);
- }
- mapping.put(key, entityManagerRef);
- }
-
- List<EntityManagerRef> result = new ArrayList<EntityManagerRef>();
-
- if (entityManagerRef instanceof EntityManagerRefHolder)
- {
- for (EntityManagerRef currentRef : ((EntityManagerRefHolder) entityManagerRef).getEntityManagerRefs())
- {
- result.add(currentRef);
- }
- }
- else
- {
- result.add(entityManagerRef);
- }
- return result;
- }
-
- private static synchronized Map<String, EntityManagerRef> initMapping(
- Map<String, EntityManagerRef> mapping)
- {
- if (mapping == null)
- {
- mapping = new ConcurrentHashMap<String, EntityManagerRef>();
- persistenceContextMetaEntries.put(getClassLoader(), mapping);
- }
- return mapping;
- }
-
- private static List<EntityManagerRef> findEntityManagerMetaReferences(Class target)
- {
- List<EntityManagerRef> result = new ArrayList<EntityManagerRef>();
-
- BeanManager beanManager = BeanManagerProvider.getInstance().getBeanManager();
-
- //TODO support other injection types
- Class currentParamClass = target;
- PersistenceContext persistenceContext;
- while (currentParamClass != null && !Object.class.getName().equals(currentParamClass.getName()))
- {
- //TODO scan methods to support a manual lookup
-
- for (Field currentField : currentParamClass.getDeclaredFields())
- {
- persistenceContext = currentField.getAnnotation(PersistenceContext.class);
- if (persistenceContext != null)
- {
- result.add(new PersistenceContextMetaEntry(
- currentParamClass,
- currentField.getName(),
- persistenceContext.unitName(),
- PersistenceContextType.EXTENDED.equals(persistenceContext.type())));
- continue;
- }
-
- if (EntityManager.class.isAssignableFrom(currentField.getType()))
- {
- String key = createKey(currentField, beanManager);
- //TODO discuss support of extended entity-managers
- result.add(new EntityManagerRef(currentParamClass, currentField.getName(), key));
- }
- }
- currentParamClass = currentParamClass.getSuperclass();
- }
-
- return result;
- }
-
- private static String createKey(Field entityManagerField, BeanManager beanManager)
- {
- for (Annotation annotation : entityManagerField.getAnnotations())
- {
- if (beanManager.isQualifier(annotation.annotationType()))
- {
- //TODO add values of binding annotation-members
- //for now it's ok because we haven't allowed binding qualifier values here
- return annotation.annotationType().getName();
- }
- }
-
- return Default.class.getName();
- }
-
- private static ClassLoader getClassLoader()
- {
- return ClassUtils.getClassLoader(null);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/InternalTransactionContext.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/InternalTransactionContext.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/InternalTransactionContext.java
deleted file mode 100644
index 3634362..0000000
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/InternalTransactionContext.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.deltaspike.jpa.impl.transaction;
-
-import org.apache.deltaspike.core.api.literal.AnyLiteral;
-
-import javax.enterprise.inject.spi.Bean;
-import javax.enterprise.inject.spi.BeanManager;
-import javax.persistence.EntityManager;
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-class InternalTransactionContext
-{
- private final BeanManager beanManager;
-
- private Map<String, TransactionMetaDataEntry> transactionMetaDataEntries
- = new HashMap<String, TransactionMetaDataEntry>();
-
- InternalTransactionContext(BeanManager beanManager)
- {
- this.beanManager = beanManager;
- }
-
- void addTransactionMetaDataEntry(String key, EntityManager entityManager)
- {
- if (!this.transactionMetaDataEntries.containsKey(key))
- {
- this.transactionMetaDataEntries.put(key, new TransactionMetaDataEntry(key, entityManager));
- }
- }
-
- void addTransactionMetaDataEntry(Class<? extends Annotation> qualifier)
- {
- addTransactionMetaDataEntry(qualifier.getName(), resolveEntityManagerForQualifier(qualifier));
- }
-
- Collection<TransactionMetaDataEntry> getTransactionMetaDataEntries()
- {
- return transactionMetaDataEntries.values();
- }
-
- private EntityManager resolveEntityManagerForQualifier(Class<? extends Annotation> qualifierClass)
- {
- Bean<EntityManager> entityManagerBean = resolveEntityManagerBean(qualifierClass);
-
- if (entityManagerBean == null)
- {
- return null;
- }
-
- return (EntityManager) beanManager.getReference(entityManagerBean, EntityManager.class,
- beanManager.createCreationalContext(entityManagerBean));
- }
-
- protected Bean<EntityManager> resolveEntityManagerBean(Class<? extends Annotation> qualifierClass)
- {
- Set<Bean<?>> entityManagerBeans = beanManager.getBeans(EntityManager.class, new AnyLiteral());
- if (entityManagerBeans == null)
- {
- entityManagerBeans = new HashSet<Bean<?>>();
- }
-
- for (Bean<?> currentEntityManagerBean : entityManagerBeans)
- {
- Set<Annotation> foundQualifierAnnotations = currentEntityManagerBean.getQualifiers();
-
- for (Annotation currentQualifierAnnotation : foundQualifierAnnotations)
- {
- if (currentQualifierAnnotation.annotationType().equals(qualifierClass))
- {
- return (Bean<EntityManager>) currentEntityManagerBean;
- }
- }
- }
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/PersistenceStrategyCleanupTestEvent.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/PersistenceStrategyCleanupTestEvent.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/PersistenceStrategyCleanupTestEvent.java
deleted file mode 100644
index b255199..0000000
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/PersistenceStrategyCleanupTestEvent.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.deltaspike.jpa.impl.transaction;
-
-public class PersistenceStrategyCleanupTestEvent
-{
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/PersistenceStrategyHelper.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/PersistenceStrategyHelper.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/PersistenceStrategyHelper.java
new file mode 100644
index 0000000..8a641bc
--- /dev/null
+++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/PersistenceStrategyHelper.java
@@ -0,0 +1,188 @@
+/*
+ * 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.deltaspike.jpa.impl.transaction;
+
+import org.apache.deltaspike.jpa.api.Transactional;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Default;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.interceptor.InvocationContext;
+import javax.persistence.EntityManager;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Helper which provides utility methods for any
+ * {@link org.apache.deltaspike.jpa.spi.PersistenceStrategy}.
+ */
+@Dependent
+public class PersistenceStrategyHelper implements Serializable
+{
+ @Inject
+ private BeanManager beanManager;
+
+ /**
+ * <p>This method uses the InvocationContext to scan the @Transactional
+ * interceptor for a manually specified Qualifier.</p>
+ *
+ * <p>If none is given (defaults to Any.class) then we scan the intercepted
+ * instance and resolve the Qualifiers of all it's injected EntityManagers.</p>
+ *
+ * <p>Please note that we will only pickup the first Qualifier on the
+ * injected EntityManager. We also do <b>not</b> parse for binding or
+ * &h#064;NonBinding values. A @Qualifier should not have any parameter at all.</p>
+ * @param transactionalAnnotation the @Transactional annotation found on the intercepted class
+ * @param interceptedTargetClass the Class of the intercepted target
+ */
+ public Set<Class<? extends Annotation>> resolveEntityManagerQualifiers(Transactional transactionalAnnotation,
+ Class interceptedTargetClass)
+ {
+ HashSet<Class<? extends Annotation>> emQualifiers;
+
+
+ Class<? extends Annotation>[] qualifierClasses = transactionalAnnotation.qualifier();
+
+ if (qualifierClasses.length == 1 && Any.class.equals(qualifierClasses[0]) )
+ {
+ // this means we have no special EntityManager configured in the interceptor
+ // thus we should scan all the EntityManagers ourselfs from the intercepted class
+ emQualifiers = new HashSet<Class<? extends Annotation>>();
+ collectEntityManagerQualifiersOnClass(emQualifiers, interceptedTargetClass);
+ }
+ else
+ {
+ // take the qualifierKeys from the qualifierClasses
+ emQualifiers = new HashSet<Class<? extends Annotation>>();
+ for (Class<? extends Annotation> qualifier : qualifierClasses)
+ {
+ emQualifiers.add(qualifier);
+ }
+ }
+
+ return emQualifiers;
+ }
+
+ /**
+ * Scan the given class and return all the injected EntityManager fields.
+ * <p>Attention: we do only pick up EntityManagers which use @Inject!</p>
+ */
+ private void collectEntityManagerQualifiersOnClass(Set<Class<? extends Annotation>> emQualifiers,Class target)
+ {
+ // first scan all declared fields
+ Field[] fields = target.getDeclaredFields();
+
+ for (Field field : fields)
+ {
+ if (EntityManager.class.equals(field.getType()))
+ {
+ // also check if this is an injected EM
+ if (field.getAnnotation(Inject.class) != null)
+ {
+ boolean qualifierFound = false;
+ Class<? extends Annotation> qualifier = getFirstQualifierAnnotation(field.getAnnotations());
+ if (qualifier != null)
+ {
+ emQualifiers.add(qualifier);
+ qualifierFound = true;
+ }
+
+ if (!qualifierFound)
+ {
+ // according to the CDI injection rules @Default is assumed
+ emQualifiers.add(Default.class);
+ }
+ }
+ }
+ }
+
+ // finally recurse into the superclasses
+ Class superClass = target.getSuperclass();
+ if (!Object.class.equals(superClass))
+ {
+ collectEntityManagerQualifiersOnClass(emQualifiers, superClass);
+ }
+ }
+
+ /**
+ * Extract the first CDI-Qualifier Annotation from the given annotations array
+ */
+ private Class<? extends Annotation> getFirstQualifierAnnotation(Annotation[] annotations)
+ {
+ for (Annotation ann : annotations)
+ {
+ if (beanManager.isQualifier(ann.annotationType()))
+ {
+ return ann.annotationType();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return the @Transactional annotation from either the method or class
+ * or <code>null</code> if none present.
+ */
+ protected Transactional extractTransactionalAnnotation(InvocationContext context)
+ {
+ // try to detect the interceptor on the method
+ Transactional transactionalAnnotation = extractTransactionalAnnotation(context.getMethod().getAnnotations());
+
+ if (transactionalAnnotation == null)
+ {
+ // and if not found search on the class
+ transactionalAnnotation = extractTransactionalAnnotation(context.getTarget().getClass().getAnnotations());
+ }
+ return transactionalAnnotation;
+ }
+
+ /**
+ * @return a @Transactional annotation extracted from the list of given annotations
+ * or <code>null</code> if none present.
+ */
+ private Transactional extractTransactionalAnnotation(Annotation[] annotations)
+ {
+ for (Annotation annotation : annotations)
+ {
+ if (Transactional.class.equals(annotation.annotationType()))
+ {
+ return (Transactional) annotation;
+ }
+ if (beanManager.isStereotype(annotation.annotationType()))
+ {
+ Transactional transactionalAnnotation =
+ extractTransactionalAnnotation(annotation.annotationType().getAnnotations());
+ if (transactionalAnnotation != null)
+ {
+ return transactionalAnnotation;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionBeanStorageCleanupTestEvent.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionBeanStorageCleanupTestEvent.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionBeanStorageCleanupTestEvent.java
deleted file mode 100644
index 4a50658..0000000
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionBeanStorageCleanupTestEvent.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.deltaspike.jpa.impl.transaction;
-
-public class TransactionBeanStorageCleanupTestEvent
-{
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionMetaDataEntry.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionMetaDataEntry.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionMetaDataEntry.java
deleted file mode 100644
index 997f4bc..0000000
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionMetaDataEntry.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.deltaspike.jpa.impl.transaction;
-
-import javax.persistence.EntityManager;
-
-class TransactionMetaDataEntry
-{
- private final String id;
- private EntityManager entityManager;
-
- private int methodCallDepth = 0;
-
- private int startLevel = -1;
-
- TransactionMetaDataEntry(String id, EntityManager entityManager)
- {
- this.id = id;
- this.entityManager = entityManager;
- }
-
- String getId()
- {
- return id;
- }
-
- void enterNewMethodLevel()
- {
- this.methodCallDepth++;
- }
-
- void leave()
- {
- this.methodCallDepth--;
- }
-
- int getMethodCallDepth()
- {
- return methodCallDepth;
- }
-
- EntityManager getEntityManager()
- {
- return entityManager;
- }
-
- int getStartLevel()
- {
- return startLevel;
- }
-
- @Deprecated
- void markLevel()
- {
- this.startLevel = this.methodCallDepth;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptor.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptor.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptor.java
index 8d57694..a7eec08 100644
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptor.java
+++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptor.java
@@ -29,6 +29,9 @@ import java.io.Serializable;
/**
* Interceptor for wrapping transactional database requests.
+ * This interceptor itself doesn't contain any functionality.
+ * Instead the 'real' work is done inside a pluggable
+ * {@link PersistenceStrategy}.
*/
@Interceptor
@Transactional
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptorStrategy.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptorStrategy.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptorStrategy.java
index 5d8022d..1d07120 100644
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptorStrategy.java
+++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/TransactionalInterceptorStrategy.java
@@ -18,18 +18,13 @@
*/
package org.apache.deltaspike.jpa.impl.transaction;
-import org.apache.deltaspike.core.api.projectstage.TestStage;
-import org.apache.deltaspike.core.util.ProjectStageProducer;
+
+import org.apache.deltaspike.core.api.literal.AnyLiteral;
import org.apache.deltaspike.jpa.api.Transactional;
-import org.apache.deltaspike.jpa.impl.EntityManagerRef;
-import org.apache.deltaspike.jpa.impl.PersistenceHelper;
import org.apache.deltaspike.jpa.impl.transaction.context.TransactionBeanStorage;
import org.apache.deltaspike.jpa.spi.PersistenceStrategy;
-
-import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
-import javax.enterprise.inject.Any;
-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;
@@ -37,130 +32,129 @@ import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * <p>Default implementation of our pluggable PersistenceStrategy.
+ * <p>Default implementation of our plugable PersistenceStrategy.
* It supports nested Transactions with the MANDATORY behaviour.</p>
- * <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/>
+ *
* <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/>
+ *
* <p>If you like to implement your own PersistenceStrategy, then use the
* standard CDI @Alternative mechanism.</p>
*/
@Dependent
public class TransactionalInterceptorStrategy implements PersistenceStrategy
{
- private static final long serialVersionUID = 2433371956913151976L;
+ private static final long serialVersionUID = -1432802805095533499L;
private static final Logger LOGGER = Logger.getLogger(TransactionalInterceptorStrategy.class.getName());
- /**
- * key=qualifier name, value= reference counter
- */
- private static transient ThreadLocal<InternalTransactionContext> transactionContext =
- new ThreadLocal<InternalTransactionContext>();
-
@Inject
private BeanManager beanManager;
- private boolean isTestProjectStage;
+ @Inject
+ private TransactionBeanStorage transactionBeanStorage;
+
+ @Inject
+ private PersistenceStrategyHelper persistenceHelper;
- @PostConstruct
- protected void init()
- {
- this.isTestProjectStage = TestStage.class.isAssignableFrom(
- ProjectStageProducer.getInstance().getProjectStage().getClass());
- }
public Object execute(InvocationContext invocationContext) throws Exception
{
- Transactional transactionalAnnotation = extractTransactionalAnnotation(invocationContext);
+ Transactional transactionalAnnotation = persistenceHelper.extractTransactionalAnnotation(invocationContext);
- //TODO add support for entity managers injected as argument/s
+ // all the configured qualifier keys
+ Set<Class<? extends Annotation>> emQualifiers = persistenceHelper.resolveEntityManagerQualifiers(
+ transactionalAnnotation, invocationContext.getTarget().getClass());
- InternalTransactionContext currentTransactionContext =
- getOrCreateTransactionContext(transactionalAnnotation, invocationContext.getTarget());
+ List<EntityManager> ems = new ArrayList<EntityManager>();
- List<String> transactionKeys = getTransactionKeys(currentTransactionContext);
+ boolean isOutermostInterceptor = transactionBeanStorage.isEmpty();
- if (TransactionBeanStorage.getStorage() == null)
+ if (isOutermostInterceptor)
{
- TransactionBeanStorage.activateNewStorage();
+ // a new Context needs to get started
+ transactionBeanStorage.startTransactionScope();
}
- for (String transactionKey : transactionKeys)
- {
- TransactionBeanStorage.getStorage().startTransactionScope(transactionKey);
- }
+ // the 'layer' of the transactional invocation, aka the refCounter
+ int transactionLayer = transactionBeanStorage.incrementRefCounter();
- List<String> previousTransactionKeys = null;
- if (transactionKeys != null && !transactionKeys.isEmpty())
+ for (Class<? extends Annotation> emQualifier : emQualifiers)
{
- TransactionBeanStorage.getStorage().activateTransactionScope(transactionKeys);
+ EntityManager entityManager = resolveEntityManagerForQualifier(emQualifier);
+
+ transactionBeanStorage.storeUsedEntityManager(emQualifier, entityManager);
+
+ ems.add(entityManager);
}
- beginOrJoinTransactionsAndEnter(currentTransactionContext);
- // used to store any exception we get from the services
Exception firstException = null;
try
{
+ for (EntityManager entityManager : ems)
+ {
+ EntityTransaction transaction = entityManager.getTransaction();
+
+ if (!transaction.isActive())
+ {
+ transaction.begin();
+ }
+ }
+
return invocationContext.proceed();
}
catch (Exception e)
{
firstException = e;
- leave(currentTransactionContext);
// 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(currentTransactionContext))
+ if (isOutermostInterceptor)
{
- for (TransactionMetaDataEntry transactionMetaDataEntry :
- currentTransactionContext.getTransactionMetaDataEntries())
+ HashMap<Class, EntityManager> emsEntries = transactionBeanStorage.getUsedEntityManagers();
+ for (Map.Entry<Class, EntityManager> emsEntry: emsEntries.entrySet())
{
- try
+ EntityManager em = emsEntry.getValue();
+ EntityTransaction transaction = em.getTransaction();
+ if (transaction != null && transaction.isActive())
{
- EntityTransaction transaction = transactionMetaDataEntry.getEntityManager().getTransaction();
-
- if (transaction != null && transaction.isActive())
+ try
{
- try
- {
- transaction.rollback();
- }
- catch (Exception eRollback)
+ transaction.rollback();
+ }
+ catch (Exception eRollback)
+ {
+ if (LOGGER.isLoggable(Level.SEVERE))
{
- if (LOGGER.isLoggable(Level.SEVERE))
- {
- LOGGER.log(Level.SEVERE,
- "Got additional Exception while subsequently " +
- "rolling back other SQL transactions", eRollback);
- }
+ 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
- }
}
- cleanupTransactionBeanStorage();
+ // drop all EntityManagers from the ThreadLocal
+ transactionBeanStorage.cleanUsedEntityManagers();
}
// give any extensions a chance to supply a better error message
@@ -168,6 +162,7 @@ public class TransactionalInterceptorStrategy implements PersistenceStrategy
// rethrow the exception
throw e;
+
}
finally
{
@@ -175,32 +170,28 @@ public class TransactionalInterceptorStrategy implements PersistenceStrategy
// in this case, we rollback all later transactions too.
boolean commitFailed = false;
- // only commit all transactions if we didn't rollback them already
- if (firstException == null)
+ // 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)
{
- leave(currentTransactionContext);
- // 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(currentTransactionContext))
+ // only commit all transactions if we didn't rollback
+ // them already
+ if (firstException == null)
{
- EntityTransaction transaction;
- EntityManager entityManager;
+ HashMap<Class, EntityManager> emsEntries = transactionBeanStorage.getUsedEntityManagers();
// but first try to flush all the transactions and write the updates to the database
- for (TransactionMetaDataEntry transactionMetaDataEntry :
- currentTransactionContext.getTransactionMetaDataEntries())
+ for (EntityManager em: emsEntries.values())
{
- entityManager = transactionMetaDataEntry.getEntityManager();
- transaction = entityManager.getTransaction();
-
+ EntityTransaction transaction = em.getTransaction();
if (transaction != null && transaction.isActive())
{
try
{
if (!commitFailed)
{
- entityManager.flush();
+ em.flush();
}
}
catch (Exception e)
@@ -213,19 +204,16 @@ public class TransactionalInterceptorStrategy implements PersistenceStrategy
}
// and now either commit or rollback all transactions
- for (TransactionMetaDataEntry transactionMetaDataEntry :
- currentTransactionContext.getTransactionMetaDataEntries())
+ for (EntityManager em : emsEntries.values())
{
- entityManager = transactionMetaDataEntry.getEntityManager();
- transaction = entityManager.getTransaction();
-
+ EntityTransaction transaction = em.getTransaction();
if (transaction != null && transaction.isActive())
{
try
{
if (!commitFailed)
{
- transaction.commit(); //shouldn't fail since the transaction was flushed already
+ transaction.commit();
}
else
{
@@ -239,20 +227,14 @@ public class TransactionalInterceptorStrategy implements PersistenceStrategy
}
}
}
- cleanupTransactionBeanStorage();
- }
- else
- {
- // we are NOT the outermost TransactionInterceptor
- // so we have to re-activate the previous transaction
- if (previousTransactionKeys != null && !previousTransactionKeys.isEmpty())
- {
- TransactionBeanStorage.getStorage().activateTransactionScope(previousTransactionKeys);
- }
+
+ // and now we close the open transaction scope
+ transactionBeanStorage.endTransactionScope();
}
+
}
- cleanup(currentTransactionContext);
+ transactionBeanStorage.decrementRefCounter();
if (commitFailed)
{
@@ -262,202 +244,54 @@ public class TransactionalInterceptorStrategy implements PersistenceStrategy
}
}
- private void cleanupTransactionBeanStorage()
- {
- // and now we close all open transaction-scopes and reset the storage
- TransactionBeanStorage.getStorage().endAllTransactionScopes();
- TransactionBeanStorage.resetStorage();
- }
-
- private List<String> getTransactionKeys(InternalTransactionContext currentTransactionContext)
- {
- List<String> transactionKeys = new ArrayList<String>();
-
- for (TransactionMetaDataEntry transactionMetaDataEntry :
- currentTransactionContext.getTransactionMetaDataEntries())
- {
- if (transactionMetaDataEntry.getMethodCallDepth() == 0)
- {
- transactionKeys.add(transactionMetaDataEntry.getId());
- }
- }
- return transactionKeys;
- }
-
- private void removeTransactionContext()
+ private EntityManager resolveEntityManagerForQualifier(Class<? extends Annotation> emQualifier)
{
- if (this.isTestProjectStage)
- {
- this.beanManager.fireEvent(new PersistenceStrategyCleanupTestEvent());
- }
+ Bean<EntityManager> entityManagerBean = resolveEntityManagerBean(emQualifier);
- transactionContext.set(null);
- transactionContext.remove();
- }
-
- private void startTransactionStorage(String transactionKey)
- {
- if (TransactionBeanStorage.getStorage() == null)
+ if (entityManagerBean == null)
{
- TransactionBeanStorage.activateNewStorage();
+ return null;
}
- TransactionBeanStorage.getStorage().startTransactionScope(transactionKey);
- }
-
- private void beginOrJoinTransactionsAndEnter(InternalTransactionContext transactionContext)
- {
- for (TransactionMetaDataEntry transactionMetaDataEntry : transactionContext.getTransactionMetaDataEntries())
- {
- if (transactionMetaDataEntry.getMethodCallDepth() == 0)
- {
- startTransactionStorage(transactionMetaDataEntry.getId());
-
- beginTransaction(transactionMetaDataEntry);
- }
- transactionMetaDataEntry.enterNewMethodLevel();
- }
- }
-
- private void beginTransaction(TransactionMetaDataEntry transactionMetaDataEntry)
- {
- EntityManager entityManager = transactionMetaDataEntry.getEntityManager();
-
- EntityTransaction transaction = entityManager.getTransaction();
-
- if (!transaction.isActive())
- {
- transaction.begin();
- transactionMetaDataEntry.markLevel();
- }
+ return (EntityManager) beanManager.getReference(entityManagerBean, EntityManager.class,
+ beanManager.createCreationalContext(entityManagerBean));
}
/**
* 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 the wrapped or unwrapped Exception
*/
protected Exception prepareException(Exception e)
{
return e;
}
- protected Transactional extractTransactionalAnnotation(InvocationContext context)
- {
- Transactional transactionalAnnotation = context.getMethod().getAnnotation(Transactional.class);
-
- if (transactionalAnnotation == null)
- {
- transactionalAnnotation = context.getTarget().getClass().getAnnotation(Transactional.class);
- }
-
- //check class stereotypes
- if (transactionalAnnotation == null)
- {
- for (Annotation annotation : context.getTarget().getClass().getAnnotations())
- {
- if (this.beanManager.isQualifier(annotation.annotationType()))
- {
- for (Annotation metaAnnotation : annotation.annotationType().getAnnotations())
- {
- if (Transactional.class.isAssignableFrom(metaAnnotation.annotationType()))
- {
- return (Transactional) metaAnnotation;
- }
- }
- }
- }
- }
- return transactionalAnnotation;
- }
- private InternalTransactionContext getOrCreateTransactionContext(Transactional transactionalAnnotation,
- Object target)
+ protected Bean<EntityManager> resolveEntityManagerBean(Class<? extends Annotation> qualifierClass)
{
- InternalTransactionContext currentTransactionContext = transactionContext.get();
-
- if (currentTransactionContext == null)
+ Set<Bean<?>> entityManagerBeans = beanManager.getBeans(EntityManager.class, new AnyLiteral());
+ if (entityManagerBeans == null)
{
- currentTransactionContext = new InternalTransactionContext(beanManager);
- transactionContext.set(currentTransactionContext);
+ entityManagerBeans = new HashSet<Bean<?>>();
}
- if (transactionalAnnotation == null)
- {
- //TODO check if we still need it
- currentTransactionContext.addTransactionMetaDataEntry(Default.class);
- }
- else if (!Any.class.isAssignableFrom(transactionalAnnotation.qualifier()[0]))
+ for (Bean<?> currentEntityManagerBean : entityManagerBeans)
{
- for (Class<? extends Annotation> qualifier : transactionalAnnotation.qualifier())
- {
- currentTransactionContext.addTransactionMetaDataEntry(qualifier);
- }
- }
- else
- {
- findAndAddInjectedEntityManagers(target, currentTransactionContext);
- }
-
- return currentTransactionContext;
- }
-
- private void findAndAddInjectedEntityManagers(Object target, InternalTransactionContext currentTransactionContext)
- {
- List<EntityManagerRef> entityManagerRefList = PersistenceHelper.tryToFindEntityManagerReference(target);
-
- if (entityManagerRefList == null)
- {
- return;
- }
-
- for (EntityManagerRef entityManagerRef : entityManagerRefList)
- {
- currentTransactionContext.addTransactionMetaDataEntry(
- entityManagerRef.getKey(), entityManagerRef.getEntityManager(target));
- }
- }
+ Set<Annotation> foundQualifierAnnotations = currentEntityManagerBean.getQualifiers();
- private void leave(InternalTransactionContext currentTransactionContext)
- {
- for (TransactionMetaDataEntry transactionMetaDataEntry :
- currentTransactionContext.getTransactionMetaDataEntries())
- {
- transactionMetaDataEntry.leave();
- }
- }
-
- private boolean isOutermostInterceptor(InternalTransactionContext currentTransactionContext)
- {
- for (TransactionMetaDataEntry transactionMetaDataEntry :
- currentTransactionContext.getTransactionMetaDataEntries())
- {
- //can be < 0 if a 2nd entity-manager is used for a nested call which
- //should be committed/rolled back before the outermost method returns
- if (transactionMetaDataEntry.getMethodCallDepth() > 0)
+ for (Annotation currentQualifierAnnotation : foundQualifierAnnotations)
{
- return false;
- }
- }
-
- return true;
- }
-
- private void cleanup(InternalTransactionContext currentTransactionContext)
- {
- for (TransactionMetaDataEntry transactionMetaDataEntry :
- currentTransactionContext.getTransactionMetaDataEntries())
- {
- //can be < 0 if a 2nd entity-manager is used for a nested call which
- //should be committed/rolled back before the outermost method returns
- if (transactionMetaDataEntry.getMethodCallDepth() > 0)
- {
- return;
+ if (currentQualifierAnnotation.annotationType().equals(qualifierClass))
+ {
+ return (Bean<EntityManager>) currentEntityManagerBean;
+ }
}
}
-
- removeTransactionContext();
+ return null;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanEntry.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanEntry.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanEntry.java
index 5b13a9c..c96b1c2 100644
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanEntry.java
+++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanEntry.java
@@ -20,10 +20,6 @@ package org.apache.deltaspike.jpa.impl.transaction.context;
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
@@ -57,13 +53,4 @@ public class TransactionBeanEntry<T>
{
return creationalContext;
}
-
- public Set<Annotation> getQualifiers()
- {
- if (bean instanceof Bean)
- {
- return ((Bean<?>) bean).getQualifiers();
- }
- return Collections.emptySet();
- }
}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanStorage.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanStorage.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanStorage.java
index a06cac9..f6a6b49 100644
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanStorage.java
+++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionBeanStorage.java
@@ -18,229 +18,200 @@
*/
package org.apache.deltaspike.jpa.impl.transaction.context;
-import org.apache.deltaspike.core.api.projectstage.TestStage;
-import org.apache.deltaspike.core.api.provider.BeanManagerProvider;
-import org.apache.deltaspike.core.util.ProjectStageProducer;
-import org.apache.deltaspike.jpa.impl.transaction.TransactionBeanStorageCleanupTestEvent;
-
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.spi.Contextual;
-import javax.enterprise.inject.Typed;
-import java.util.ArrayList;
-import java.util.Collections;
+import javax.persistence.EntityManager;
+import java.lang.annotation.Annotation;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
+import java.util.Stack;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * <p>This bean stores information about
+ * <p>This class stores information about
* @{@link org.apache.deltaspike.jpa.api.TransactionScoped}
* contextual instances, their {@link javax.enterprise.context.spi.CreationalContext} etc.</p>
- * <p/>
+ *
* <p>We use a RequestScoped bean because this way we don't need to take
* care about cleaning up any ThreadLocals ourselves. 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>
*/
-@Typed()
+@RequestScoped
public class TransactionBeanStorage
{
private static final Logger LOGGER = Logger.getLogger(TransactionBeanStorage.class.getName());
- private static ThreadLocal<TransactionBeanStorage> currentStorage = new ThreadLocal<TransactionBeanStorage>();
+ private static class TransactionContextInfo
+ {
+ /**
+ * 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<Contextual, TransactionBeanEntry> contextualInstances =
+ new HashMap<Contextual, TransactionBeanEntry>();
+
+ /** key=qualifier name, value= EntityManager */
+ private HashMap<Class, EntityManager> ems = new HashMap<Class, EntityManager>();
+
+ /**
+ * counts the 'depth' of the interceptor invocation.
+ */
+ private AtomicInteger refCounter = new AtomicInteger(0);
+ }
/**
- * 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>
+ * If we hit a layer with REQUIRES_NEW, then create a new TransactionContextInfo
+ * and push the old one on top of this stack.
*/
- private Map<String, Map<Contextual, TransactionBeanEntry>> storedTransactionContexts =
- new HashMap<String, Map<Contextual, TransactionBeanEntry>>();
-
- private List<Map<Contextual, TransactionBeanEntry>> activeTransactionContextList;
-
- private List<String> activeTransactionKeyList = new ArrayList<String>();
-
- private boolean isTestProjectStage;
-
- private TransactionBeanStorage()
- {
- this.isTestProjectStage = TestStage.class.isAssignableFrom(
- ProjectStageProducer.getInstance().getProjectStage().getClass());
- }
+ private Stack<TransactionContextInfo> oldTci = new Stack<TransactionContextInfo>();
/**
- * @return the storage for the current thread if there is one - null otherwise
+ * The TransactionContextInfo which is on top of the stack.
*/
- public static TransactionBeanStorage getStorage()
- {
- return currentStorage.get();
- }
+ private TransactionContextInfo currentTci = null;
/**
- * Creates a new storage for the current thread
+ * Increment the ref counter and return the old value.
+ * Must only be called if the bean storage is not {@link #isEmpty()}.
*
- * @return the storage which was associated with the thread before - null if there was no storage
+ * @return the the previous values of the refCounters. If 0 then we are 'outermost'
*/
- public static TransactionBeanStorage activateNewStorage()
+ public int incrementRefCounter()
{
- TransactionBeanStorage previousStorage = currentStorage.get();
- currentStorage.set(new TransactionBeanStorage());
- return previousStorage;
+ return currentTci.refCounter.incrementAndGet() - 1;
}
/**
- * Removes the current storage
+ * Decrement the reference counter and return the layer.
+ *
+ * @return the layer number. 0 represents the outermost interceptor for the qualifier
*/
- public static void resetStorage()
+ public int decrementRefCounter()
{
- TransactionBeanStorage currentBeanStorage = currentStorage.get();
-
- if (currentBeanStorage != null)
+ if (currentTci == null)
{
- currentBeanStorage.close();
-
- currentStorage.set(null);
- currentStorage.remove();
+ return 0;
}
+
+ return currentTci.refCounter.decrementAndGet();
}
- private void close()
+
+ /**
+ * @return <code>true</code> if we are the outermost interceptor over all qualifiers
+ * and the TransactionBeanStorage is yet empty.
+ */
+ public boolean isEmpty()
{
- if (this.isTestProjectStage)
- {
- BeanManagerProvider.getInstance().getBeanManager().fireEvent(new TransactionBeanStorageCleanupTestEvent());
- }
+ return currentTci == null;
}
/**
- * Start the TransactionScope with the given qualifier
- *
- * @param transactionKey
+ * Start a new TransactionScope
+ * @return the
*/
- public void startTransactionScope(String transactionKey)
+ public void startTransactionScope()
{
- if (LOGGER.isLoggable(Level.FINER))
+ // first store away any previous TransactionContextInfo
+ if (currentTci != null)
{
- LOGGER.finer("starting TransactionScope " + transactionKey);
+ oldTci.push(currentTci);
}
+ currentTci = new TransactionContextInfo();
- Map<Contextual, TransactionBeanEntry> transactionBeanEntryMap = storedTransactionContexts.get(transactionKey);
-
- if (transactionBeanEntryMap == null)
+ if (LOGGER.isLoggable(Level.FINER))
{
- transactionBeanEntryMap = new HashMap<Contextual, TransactionBeanEntry>();
- storedTransactionContexts.put(transactionKey, transactionBeanEntryMap);
+ LOGGER.finer( "starting TransactionScope");
}
}
/**
- * Activate the TransactionScope with the given qualifier.
- * This is needed if a subsequently invoked @Transactional
- * method will switch to another persistence unit.
- * This method must also be invoked when the transaction just got started
- * with {@link #startTransactionScope(String)}.
+ * End the TransactionScope with the given qualifier.
+ * This will subsequently destroy all beans which are stored
+ * in the context.
+ *
+ * This method only gets used if we leave a transaction with REQUIRES_NEW.
+ * In all other cases we use {@link #endAllTransactionScopes()}.
*/
- public void activateTransactionScope(List<String> transactionKeyList)
+ public void endTransactionScope()
{
- //TODO remove it after a review
- /*
- if (transactionKeyList != null && transactionKeyList.isEmpty())
- {
- transactionKeyList.add(Default.class.getName()); //needed for the transaction test helper
- }
- */
-
if (LOGGER.isLoggable(Level.FINER))
{
- if (transactionKeyList != null && LOGGER.isLoggable(Level.FINER))
- {
- for (String transactionKey : transactionKeyList)
- {
- LOGGER.finer("activating TransactionScope " + transactionKey);
- }
- }
+ LOGGER.finer("ending TransactionScope");
}
- //can be null on the topmost stack-layer
- if (transactionKeyList == null && this.activeTransactionKeyList == null)
- {
- return;
- }
+ destroyBeans(currentTci.contextualInstances);
- if (transactionKeyList == null)
+ if (oldTci.size() > 0)
{
- transactionKeyList = this.activeTransactionKeyList;
+ currentTci = oldTci.pop();
}
-
- if (activeTransactionContextList == null)
+ else
{
- activeTransactionContextList = new ArrayList<Map<Contextual, TransactionBeanEntry>>();
+ currentTci = null;
}
+ }
- for (String transactionKey : transactionKeyList)
- {
- Map<Contextual, TransactionBeanEntry> transactionBeanEntryMap =
- this.storedTransactionContexts.get(transactionKey);
-
- if (transactionBeanEntryMap == null)
- {
- throw new IllegalStateException("Cannot activate TransactionScope with key " + transactionKey);
- }
- if (!this.activeTransactionContextList.contains(transactionBeanEntryMap))
- {
- this.activeTransactionContextList.add(transactionBeanEntryMap);
- }
- }
+ public EntityManager storeUsedEntityManager(Class<? extends Annotation> emQualifier, EntityManager entityManager)
+ {
+ return currentTci.ems.put(emQualifier, entityManager);
+ }
- this.activeTransactionKeyList.addAll(transactionKeyList);
+ public HashMap<Class,EntityManager> getUsedEntityManagers()
+ {
+ return currentTci.ems;
}
- public List<String> getActiveTransactionKeyList()
+ public void cleanUsedEntityManagers()
{
- return Collections.unmodifiableList(this.activeTransactionKeyList);
+ currentTci.ems.clear();
}
/**
- * This will destroy all stored transaction contexts.
+ * @return the Map which represents the currently active Context content.
*/
- public void endAllTransactionScopes()
+ public Map<Contextual, TransactionBeanEntry> getActiveTransactionContext()
{
- if (LOGGER.isLoggable(Level.FINER))
- {
- LOGGER.finer("destroying all TransactionScopes");
- }
-
- for (Map<Contextual, TransactionBeanEntry> beans : this.storedTransactionContexts.values())
+ if (currentTci == null)
{
- destroyBeans(beans);
+ return null;
}
- // we also need to clean our active context info
- storedTransactionContexts.clear();
- activeTransactionContextList.clear();
- activeTransactionKeyList.clear();
+ return currentTci.contextualInstances;
}
-
/**
- * @return the Map which represents the currently active Context content.
+ * At the end of the request we will destroy all beans still
+ * stored in the context.
*/
- public List<Map<Contextual, TransactionBeanEntry>> getActiveTransactionContextList()
+ @PreDestroy
+ public void requestEnded()
{
- return Collections.unmodifiableList(this.activeTransactionContextList);
+ endAllTransactionScopes();
+ }
+
+ private void endAllTransactionScopes()
+ {
+ while (!isEmpty())
+ {
+ endTransactionScope();
+ }
}
/**
* Properly destroy all the given beans.
- *
* @param activeBeans
*/
private void destroyBeans(Map<Contextual, TransactionBeanEntry> activeBeans)
@@ -250,24 +221,5 @@ public class TransactionBeanStorage
beanBag.getBean().destroy(beanBag.getContextualInstance(), beanBag.getCreationalContext());
}
}
-
- public void storeTransactionBeanEntry(String transactionKey, TransactionBeanEntry transactionBeanEntry)
- {
- if (!this.activeTransactionKeyList.contains(transactionKey))
- {
- throw new IllegalStateException("Transaction for " + transactionKey + " is not active.");
- }
-
- Map<Contextual, TransactionBeanEntry> storedTransactionContext =
- this.storedTransactionContexts.get(transactionKey);
-
- if (storedTransactionContext == null)
- {
- storedTransactionContext = new HashMap<Contextual, TransactionBeanEntry>();
- this.storedTransactionContexts.put(transactionKey, storedTransactionContext);
- }
-
- storedTransactionContext.put(transactionBeanEntry.getBean(), transactionBeanEntry);
- }
}
http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/94327565/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionContext.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionContext.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionContext.java
index f3f9115..f75e4fe 100644
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionContext.java
+++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context/TransactionContext.java
@@ -19,6 +19,8 @@
package org.apache.deltaspike.jpa.impl.transaction.context;
+
+import org.apache.deltaspike.core.api.provider.BeanProvider;
import org.apache.deltaspike.jpa.api.TransactionScoped;
import org.apache.deltaspike.jpa.api.Transactional;
@@ -26,46 +28,34 @@ import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
-import javax.enterprise.inject.Any;
-import javax.enterprise.inject.Typed;
-import javax.enterprise.inject.spi.Bean;
-import javax.inject.Named;
import java.lang.annotation.Annotation;
-import java.util.HashSet;
-import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
- * CDI Context for managing @{@link TransactionScoped} contextual instances.
+ * CDI Context for managing @{@link org.apache.deltaspike.jpa.api.TransactionScoped} contextual instances.
*/
-@Typed()
public class TransactionContext implements Context
{
- public <T> T get(Contextual<T> component)
- {
- List<Map<Contextual, TransactionBeanEntry>> transactionBeanEntryMaps = getTransactionBeanEntryMaps();
+ // Attention! this is not a normal instance but a PROXY
+ // thus it resolves the correct contextual instance every time
+ // it will lazily initialized at runtime after the container
+ // got started.
+ private TransactionBeanStorage beanStorage;
- if (transactionBeanEntryMaps == null)
- {
- return null;
- }
- TransactionBeanEntry transactionBeanEntry = null;
+ public <T> T get(Contextual<T> component)
+ {
+ Map<Contextual, TransactionBeanEntry> transactionBeanEntryMap = getBeanStorage().getActiveTransactionContext();
- for (Map<Contextual, TransactionBeanEntry> transactionBeanEntryMap : transactionBeanEntryMaps)
+ if (transactionBeanEntryMap == null)
{
- transactionBeanEntry = transactionBeanEntryMap.get(component);
-
- if (transactionBeanEntry != null)
- {
- break;
- }
+ throw new ContextNotActiveException("Not accessed within a transactional method - use @" +
+ Transactional.class.getName());
}
+ TransactionBeanEntry transactionBeanEntry = transactionBeanEntryMap.get(component);
if (transactionBeanEntry != null)
{
- checkTransactionBeanEntry(transactionBeanEntry);
return (T) transactionBeanEntry.getContextualInstance();
}
@@ -74,130 +64,55 @@ public class TransactionContext implements Context
public <T> T get(Contextual<T> component, CreationalContext<T> creationalContext)
{
- if (!(component instanceof Bean))
- {
- throw new IllegalStateException(Contextual.class.getName() + " is not of type " + Bean.class.getName());
- }
+ Map<Contextual, TransactionBeanEntry> transactionBeanEntryMap = getBeanStorage().getActiveTransactionContext();
- Set<Annotation> qualifiers = ((Bean)component).getQualifiers();
- Set<Annotation> transactionKeys = new HashSet<Annotation>();
-
- for (Annotation currentQualifier : qualifiers)
- {
- if (Any.class.isAssignableFrom(currentQualifier.annotationType()))
- {
- continue;
- }
- if (Named.class.isAssignableFrom(currentQualifier.annotationType()))
- {
- continue;
- }
-
- //TODO since we just support a simple qualifier as key, we can exclude all other qualifiers
-
- transactionKeys.add(currentQualifier);
- }
-
- if (transactionKeys.size() != 1)
- {
- throw new IllegalStateException(transactionKeys.size() + " qualifiers found at " + component.toString() +
- " only one is allowed!");
- }
-
- String transactionKey = transactionKeys.iterator().next().annotationType().getName();
-
- if (TransactionBeanStorage.getStorage().getActiveTransactionContextList() == null)
- {
- TransactionBeanStorage.activateNewStorage();
- }
-
- List<Map<Contextual, TransactionBeanEntry>> activeTransactionBeanEntryMaps = getTransactionBeanEntryMaps();
-
- if (activeTransactionBeanEntryMaps == null)
+ if (transactionBeanEntryMap == null)
{
throw new ContextNotActiveException("Not accessed within a transactional method - use @" +
Transactional.class.getName());
}
- TransactionBeanEntry transactionBeanEntry;
- if (!activeTransactionBeanEntryMaps.isEmpty())
+ TransactionBeanEntry transactionBeanEntry = transactionBeanEntryMap.get(component);
+ if (transactionBeanEntry != null)
{
- for (Map<Contextual, TransactionBeanEntry> currentTransactionBeanEntryMap : activeTransactionBeanEntryMaps)
- {
- transactionBeanEntry = currentTransactionBeanEntryMap.get(component);
-
- if (transactionBeanEntry != null)
- {
- checkTransactionBeanEntry(transactionBeanEntry);
- return (T) transactionBeanEntry.getContextualInstance();
- }
- }
+ return (T) transactionBeanEntry.getContextualInstance();
}
// if it doesn't yet exist, we need to create it now!
T instance = component.create(creationalContext);
transactionBeanEntry = new TransactionBeanEntry(component, instance, creationalContext);
-
- checkTransactionBeanEntry(transactionBeanEntry);
-
- TransactionBeanStorage.getStorage().storeTransactionBeanEntry(transactionKey, transactionBeanEntry);
+ transactionBeanEntryMap.put(component, transactionBeanEntry);
return instance;
}
- private void checkTransactionBeanEntry(TransactionBeanEntry<?> transactionBeanEntry)
+ public Class<? extends Annotation> getScope()
{
- List<String> activeTransactionKeys = TransactionBeanStorage.getStorage().getActiveTransactionKeyList();
-
- for (Annotation qualifier : transactionBeanEntry.getQualifiers())
- {
- if (activeTransactionKeys.contains(qualifier.annotationType().getName()))
- {
- 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: " +
- //TODO
- activeTransactionKeys + " qualifier/s of the entity-manager: " +
- extractQualifiers(transactionBeanEntry));
+ return TransactionScoped.class;
}
- private String extractQualifiers(TransactionBeanEntry<?> transactionBeanEntry)
+ public boolean isActive()
{
- StringBuilder result = new StringBuilder();
- for (Annotation annotation : transactionBeanEntry.getQualifiers())
+ try
{
- if (result.length() != 0)
- {
- result.append(";");
- }
-
- result.append(annotation.annotationType().getName());
+ return getBeanStorage().getActiveTransactionContext() != null;
}
- return result.toString();
- }
-
- private List<Map<Contextual, TransactionBeanEntry>> getTransactionBeanEntryMaps()
- {
- TransactionBeanStorage transactionBeanStorage = TransactionBeanStorage.getStorage();
-
- if (transactionBeanStorage != null)
+ catch (ContextNotActiveException e)
{
- transactionBeanStorage.activateTransactionScope(null);
- return transactionBeanStorage.getActiveTransactionContextList();
+ return false;
}
- return null;
}
- public Class<? extends Annotation> getScope()
+ private TransactionBeanStorage getBeanStorage()
{
- return TransactionScoped.class;
+ if (beanStorage == null)
+ {
+ synchronized (this)
+ {
+ beanStorage = BeanProvider.getContextualReference(TransactionBeanStorage.class);
+ }
+ }
+ return beanStorage;
}
- public boolean isActive()
- {
- return TransactionBeanStorage.getStorage() != null;
- }
}