You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by gp...@apache.org on 2012/07/29 17:12:14 UTC

[6/6] git commit: DELTASPIKE-219 BeanManagedUserTransactionPersistenceStrategy (first draft)

DELTASPIKE-219 BeanManagedUserTransactionPersistenceStrategy (first draft)


Project: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/commit/c627f70e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/tree/c627f70e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/diff/c627f70e

Branch: refs/heads/master
Commit: c627f70eb73dff46c77b82b1562f3ea941bcb54c
Parents: 5a204ee
Author: gpetracek <gp...@apache.org>
Authored: Fri Jul 27 10:27:32 2012 +0200
Committer: gpetracek <gp...@apache.org>
Committed: Sun Jul 29 13:55:20 2012 +0200

----------------------------------------------------------------------
 .../deltaspike/core/util/ExceptionUtils.java       |    3 +-
 deltaspike/modules/jpa/impl/pom.xml                |    5 +
 ...nManagedUserTransactionPersistenceStrategy.java |  243 +++++++++++++++
 .../ResourceLocalPersistenceStrategy.java          |   25 +-
 4 files changed, 265 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/c627f70e/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ExceptionUtils.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ExceptionUtils.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ExceptionUtils.java
index 9746dfd..40e18d5 100644
--- a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ExceptionUtils.java
+++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ExceptionUtils.java
@@ -30,10 +30,11 @@ public abstract class ExceptionUtils
         // prevent instantiation
     }
 
-    public static void throwAsRuntimeException(Throwable throwable)
+    public static RuntimeException throwAsRuntimeException(Throwable throwable)
     {
         //Attention: helper which allows to use a trick to throw a cached checked exception without a wrapping exception
         new ExceptionHelper<RuntimeException>().throwException(throwable);
+        return null; //not needed due to the helper trick, but it's easier for using it
     }
 
     public static void changeAndThrowException(Throwable throwable, String customMessage)

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/c627f70e/deltaspike/modules/jpa/impl/pom.xml
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/pom.xml b/deltaspike/modules/jpa/impl/pom.xml
index 7852840..2457f14 100644
--- a/deltaspike/modules/jpa/impl/pom.xml
+++ b/deltaspike/modules/jpa/impl/pom.xml
@@ -46,6 +46,11 @@
             <groupId>org.apache.deltaspike.modules</groupId>
             <artifactId>deltaspike-jpa-module-api</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jta_1.1_spec</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/c627f70e/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/BeanManagedUserTransactionPersistenceStrategy.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/BeanManagedUserTransactionPersistenceStrategy.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/BeanManagedUserTransactionPersistenceStrategy.java
new file mode 100644
index 0000000..19c1fe1
--- /dev/null
+++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/BeanManagedUserTransactionPersistenceStrategy.java
@@ -0,0 +1,243 @@
+/*
+ * 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.projectstage.ProjectStage;
+import org.apache.deltaspike.core.impl.util.JndiUtils;
+import org.apache.deltaspike.core.util.ExceptionUtils;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.inject.Alternative;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+import java.util.logging.Logger;
+
+/**
+ * <p>{@link org.apache.deltaspike.jpa.spi.PersistenceStrategy} for using JTA (bean-managed-)transactions
+ * (including XA transactions with a XA DataSource).
+ * The basic features are identical to the {@link ResourceLocalPersistenceStrategy} (for
+ * persistent-unit-transaction-type 'RESOURCE_LOCAL' only).
+ * Also different transaction-types for different persistence-units are supported.</p>
+ *
+ * <p>It's possible to extend this class, if {@link org.apache.deltaspike.core.api.exclude.annotation.Exclude}
+ * needs to be used e.g. in case of a different dev- and production-environment
+ * (in combination with different {@link javax.persistence.EntityManagerFactory}s).</p>
+ */
+@Dependent
+@Alternative
+@SuppressWarnings("UnusedDeclaration")
+//TODO move to a separated ds-jta module and use @Specializes -> no additional config is needed
+public class BeanManagedUserTransactionPersistenceStrategy extends ResourceLocalPersistenceStrategy
+{
+    private static final long serialVersionUID = -2432802805095533499L;
+
+    private static final Logger LOGGER =
+        Logger.getLogger(BeanManagedUserTransactionPersistenceStrategy.class.getName());
+
+    private static final String USER_TRANSACTION_JNDI_NAME = "java:comp/UserTransaction";
+
+    @Inject
+    private ProjectStage projectStage;
+
+    @Override
+    protected EntityTransaction getTransaction(EntityManager entityManager)
+    {
+        return new UserTransactionAdapter(entityManager);
+    }
+
+    /**
+     * Needed because the {@link EntityManager} was created outside of the {@link UserTransaction}.
+     * Can't be in {@link BeanManagedUserTransactionPersistenceStrategy.UserTransactionAdapter#begin()}
+     * because {@link ResourceLocalPersistenceStrategy} needs to do
+     * <pre>
+     * if (!transaction.isActive())
+     * {
+     *     transaction.begin();
+     * }
+     * </pre>
+     * for the {@link EntityTransaction} of every {@link EntityManager}
+     * and {@link BeanManagedUserTransactionPersistenceStrategy.UserTransactionAdapter#isActive()}
+     * can only use the status information of the {@link UserTransaction} and therefore
+     * {@link BeanManagedUserTransactionPersistenceStrategy.UserTransactionAdapter#begin()}
+     * will only executed once, but {@link javax.persistence.EntityManager#joinTransaction()}
+     * needs to be called for every {@link EntityManager}
+     *
+     * @param entityManager current entity-manager
+     */
+
+    /*
+        Hint: the alternative would be a ThreadLocal to store additional information which would require
+        a callback in ResourceLocalPersistenceStrategy for the cleanup of the ThreadLocal.
+        -> #prepareEntityManager is more solid.
+     */
+    @Override
+    protected void prepareEntityManager(EntityManager entityManager)
+    {
+        entityManager.joinTransaction();
+    }
+
+    private class UserTransactionAdapter implements EntityTransaction
+    {
+        private final UserTransaction userTransaction;
+        private final EntityManager entityManager;
+
+        public UserTransactionAdapter(EntityManager entityManager)
+        {
+            this.userTransaction = JndiUtils.lookup(USER_TRANSACTION_JNDI_NAME, UserTransaction.class);
+            this.entityManager = entityManager;
+        }
+
+        /**
+         * Only delegate to the {@link UserTransaction} if the state of the
+         * {@link UserTransaction} is {@link Status#STATUS_NO_TRANSACTION}
+         * (= the status before and after a started transaction).
+         */
+        @Override
+        public void begin()
+        {
+            try
+            {
+                //2nd check (already done by #isActive triggered by ResourceLocalPersistenceStrategy directly before)
+                //currently to filter STATUS_UNKNOWN - see to-do -> TODO re-visit it
+                if (this.userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION)
+                {
+                    this.userTransaction.begin();
+                }
+            }
+            catch (Exception e)
+            {
+                throw ExceptionUtils.throwAsRuntimeException(e);
+            }
+        }
+
+        /**
+         * Only delegate to the {@link UserTransaction} if the state of the
+         * {@link UserTransaction} is one of
+         * <ul>
+         *     <li>{@link Status#STATUS_ACTIVE}</li>
+         *     <li>{@link Status#STATUS_PREPARING}</li>
+         *     <li>{@link Status#STATUS_PREPARED}</li>
+         * </ul>
+         */
+        @Override
+        public void commit()
+        {
+            try
+            {
+                if (isTransactionReadyToCommit())
+                {
+                    this.userTransaction.commit();
+                }
+            }
+            catch (Exception e)
+            {
+                throw ExceptionUtils.throwAsRuntimeException(e);
+            }
+        }
+
+        /**
+         * Only delegate to the {@link UserTransaction} if the state of the
+         * {@link UserTransaction} is one of
+         * <ul>
+         *     <li>{@link Status#STATUS_ACTIVE}</li>
+         *     <li>{@link Status#STATUS_PREPARING}</li>
+         *     <li>{@link Status#STATUS_PREPARED}</li>
+         *     <li>{@link Status#STATUS_MARKED_ROLLBACK}</li>
+         *     <li>{@link Status#STATUS_COMMITTING}</li>
+         * </ul>
+         */
+        @Override
+        public void rollback()
+        {
+            try
+            {
+                if (isTransactionAllowedToRollback())
+                {
+                    this.userTransaction.rollback();
+                }
+            }
+            catch (SystemException e)
+            {
+                throw ExceptionUtils.throwAsRuntimeException(e);
+            }
+        }
+
+        @Override
+        public void setRollbackOnly()
+        {
+            try
+            {
+                this.userTransaction.setRollbackOnly();
+            }
+            catch (SystemException e)
+            {
+                throw ExceptionUtils.throwAsRuntimeException(e);
+            }
+        }
+
+        @Override
+        public boolean getRollbackOnly()
+        {
+            try
+            {
+                return this.userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK;
+            }
+            catch (SystemException e)
+            {
+                throw ExceptionUtils.throwAsRuntimeException(e);
+            }
+        }
+
+        /**
+         * @return true if the transaction has been started and not ended
+         */
+        @Override
+        public boolean isActive()
+        {
+            //we can't use the status of the overall
+            try
+            {
+                return this.userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION &&
+                        this.userTransaction.getStatus() != Status.STATUS_UNKNOWN; //TODO re-visit it
+            }
+            catch (SystemException e)
+            {
+                throw ExceptionUtils.throwAsRuntimeException(e);
+            }
+        }
+
+        private boolean isTransactionAllowedToRollback() throws SystemException
+        {
+            return isTransactionReadyToCommit() ||
+                    this.userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
+                    this.userTransaction.getStatus() == Status.STATUS_COMMITTING;
+        }
+
+        private boolean isTransactionReadyToCommit() throws SystemException
+        {
+            return this.userTransaction.getStatus() == Status.STATUS_ACTIVE ||
+                    this.userTransaction.getStatus() == Status.STATUS_PREPARING ||
+                    this.userTransaction.getStatus() == Status.STATUS_PREPARED;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/c627f70e/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalPersistenceStrategy.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalPersistenceStrategy.java b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalPersistenceStrategy.java
index dbeb66b..10a34e8 100644
--- a/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalPersistenceStrategy.java
+++ b/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalPersistenceStrategy.java
@@ -95,27 +95,27 @@ public class ResourceLocalPersistenceStrategy implements PersistenceStrategy
         @SuppressWarnings("UnusedDeclaration")
         int transactionLayer = transactionBeanStorage.incrementRefCounter();
 
-        for (Class<? extends Annotation> emQualifier : emQualifiers)
-        {
-            EntityManager entityManager = resolveEntityManagerForQualifier(emQualifier);
-
-            transactionBeanStorage.storeUsedEntityManager(emQualifier, entityManager);
-
-            ems.add(entityManager);
-        }
-
         Exception firstException = null;
 
         try
         {
-            for (EntityManager entityManager : ems)
+            for (Class<? extends Annotation> emQualifier : emQualifiers)
             {
+                EntityManager entityManager = resolveEntityManagerForQualifier(emQualifier);
+
+                transactionBeanStorage.storeUsedEntityManager(emQualifier, entityManager);
+
+                ems.add(entityManager);
+
                 EntityTransaction transaction = getTransaction(entityManager);
 
                 if (!transaction.isActive())
                 {
                     transaction.begin();
                 }
+
+                //don't move it before EntityTransaction#begin() and invoke it in any case
+                prepareEntityManager(entityManager);
             }
 
             return invocationContext.proceed();
@@ -248,6 +248,11 @@ public class ResourceLocalPersistenceStrategy implements PersistenceStrategy
         return entityManager.getTransaction();
     }
 
+    protected void prepareEntityManager(EntityManager entityManager)
+    {
+        //override if needed
+    }
+
     private EntityManager resolveEntityManagerForQualifier(Class<? extends Annotation> emQualifier)
     {
         Bean<EntityManager> entityManagerBean = resolveEntityManagerBean(emQualifier);