You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rm...@apache.org on 2017/06/12 14:29:52 UTC

tomee git commit: TOMEE-2057 patch from Svetlin Zarev, EJB @BeforeCompletion / JPA conflict

Repository: tomee
Updated Branches:
  refs/heads/master fbe42a2fd -> 7033e2bc5


TOMEE-2057 patch from Svetlin Zarev, EJB @BeforeCompletion / JPA conflict


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/7033e2bc
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/7033e2bc
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/7033e2bc

Branch: refs/heads/master
Commit: 7033e2bc554b70e829855f18ccee0f9c29cad3b2
Parents: fbe42a2
Author: rmannibucau <rm...@apache.org>
Authored: Mon Jun 12 16:29:20 2017 +0200
Committer: rmannibucau <rm...@apache.org>
Committed: Mon Jun 12 16:29:47 2017 +0200

----------------------------------------------------------------------
 .../SessionSynchronizationCallbackServlet.java  |  89 ++++++++++++++++
 .../ejb/SessionSynchronizationCallbackTest.java | 103 +++++++++++++++++++
 .../tests/persistence/ejb/StatefulBean.java     |  77 ++++++++++++++
 .../tests/persistence/ejb/TestEntity.java       |  43 ++++++++
 .../session_synchronization/persistence.xml     |  38 +++++++
 container/openejb-jpa-integration/pom.xml       |   5 +
 .../eclipselink/OpenEJBServerPlatform.java      |  10 ++
 7 files changed, 365 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/7033e2bc/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/SessionSynchronizationCallbackServlet.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/SessionSynchronizationCallbackServlet.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/SessionSynchronizationCallbackServlet.java
new file mode 100644
index 0000000..0a56e28
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/SessionSynchronizationCallbackServlet.java
@@ -0,0 +1,89 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.openejb.arquillian.tests.persistence.ejb;
+
+import org.apache.openejb.arquillian.tests.Runner;
+
+import javax.annotation.Resource;
+import javax.ejb.EJB;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.transaction.UserTransaction;
+import java.io.IOException;
+
+
+public class SessionSynchronizationCallbackServlet extends HttpServlet {
+
+    @Resource
+    UserTransaction ut;
+
+    @PersistenceContext
+    EntityManager em;
+
+    @EJB
+    StatefulBean statefulBean;
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        Runner.run(req, resp, this);
+    }
+
+    public void testStatefulBeanSessionSynchronizationCallback() throws ServletException {
+        cleanUpInOwnTransaction();
+        statefulBean.doPersist();
+        verifyExecutionResult();
+    }
+
+    private void cleanUpInOwnTransaction() {
+        try {
+            ut.begin();
+            deleteIfExists(TestEntity.class, StatefulBean.ENTITY_ID_AFTER_BEGIN);
+            deleteIfExists(TestEntity.class, StatefulBean.ENTITY_ID_BEFORE_COMPLETION);
+            deleteIfExists(TestEntity.class, StatefulBean.ENTITY_ID_BUSINESS_METHOD);
+            ut.commit();
+        } catch (Exception ex) {
+            throw new IllegalStateException("Failed to clean up before the test: " + ex.getMessage(), ex);
+        }
+    }
+
+    private void deleteIfExists(Class entityClass, int id) {
+        final Object entity = em.find(entityClass, Integer.valueOf(id));
+        if (null != entity) {
+            em.remove(entity);
+        }
+    }
+
+    private void verifyExecutionResult() {
+        verifyEntityExists(TestEntity.class, StatefulBean.ENTITY_ID_AFTER_BEGIN);
+        verifyEntityExists(TestEntity.class, StatefulBean.ENTITY_ID_BEFORE_COMPLETION);
+        verifyEntityExists(TestEntity.class, StatefulBean.ENTITY_ID_BUSINESS_METHOD);
+    }
+
+    private void verifyEntityExists(Class entityClass, int id) {
+        final Object entity = em.find(entityClass, Integer.valueOf(id));
+        if (null == entity) {
+            throw new IllegalStateException("Expecting entity of type=" + entityClass
+                    + " with id=" + id + " instead of NULL"
+            );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/7033e2bc/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/SessionSynchronizationCallbackTest.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/SessionSynchronizationCallbackTest.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/SessionSynchronizationCallbackTest.java
new file mode 100644
index 0000000..f64173a
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/SessionSynchronizationCallbackTest.java
@@ -0,0 +1,103 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.openejb.arquillian.tests.persistence.ejb;
+
+import org.apache.openejb.arquillian.tests.Runner;
+import org.apache.ziplock.JarLocation;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.descriptor.api.Descriptors;
+import org.jboss.shrinkwrap.descriptor.api.webapp30.WebAppDescriptor;
+import org.jboss.shrinkwrap.descriptor.api.webcommon30.WebAppVersionType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This test verifies that the ORM's transaction synchronizations
+ * are executed in the correct order for the @AfterBegin and @BeforeCompletion
+ * synchronization callbacks.
+ * <p>
+ * See TOMEE-2057
+ */
+@RunWith(Arquillian.class)
+public class SessionSynchronizationCallbackTest {
+    private static final String TEST_NAME = SessionSynchronizationCallbackTest.class.getSimpleName();
+    private static final String SERVLET_NAME = "SessionSynchronizationCallbackServlet";
+    private static final String PERSISTENCE_XML_SOURCE = "org/apache/openejb/arquillian/tests/persistence/session_synchronization/persistence.xml";
+    private static final String PERSISTENCE_XML_DESTINATION = "META-INF/persistence.xml";
+
+    @ArquillianResource
+    private URL url;
+
+    @Deployment(testable = false)
+    public static WebArchive createDeployment() {
+        WebAppDescriptor descriptor = Descriptors.create(WebAppDescriptor.class)
+                .version(WebAppVersionType._3_0)
+                .createServlet()
+                .servletName(SERVLET_NAME)
+                .servletClass(SessionSynchronizationCallbackServlet.class.getName()).up()
+                .createServletMapping()
+                .servletName(SERVLET_NAME)
+                .urlPattern("/" + TEST_NAME).up();
+
+        WebArchive archive = ShrinkWrap.create(WebArchive.class, TEST_NAME + ".war")
+                .addClass(SessionSynchronizationCallbackServlet.class)
+                .addClass(StatefulBean.class)
+                .addClass(TestEntity.class)
+                .addClass(Runner.class)
+                .addAsLibraries(JarLocation.jarLocation(Test.class))
+                .addAsResource(new ClassLoaderAsset(PERSISTENCE_XML_SOURCE), PERSISTENCE_XML_DESTINATION)
+                .setWebXML(new StringAsset(descriptor.exportAsString()));
+
+        return archive;
+    }
+
+    @Test
+    public void testSessionSynchronizationCallback() throws IOException {
+        validateTest("testStatefulBeanSessionSynchronizationCallback=true");
+    }
+
+    private void validateTest(String expectedOutput) throws IOException {
+        try (InputStream is = new URL(url.toExternalForm() + TEST_NAME).openStream()) {
+            final ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+            int bytesRead;
+            byte[] buffer = new byte[8192];
+            while ((bytesRead = is.read(buffer)) > -1) {
+                os.write(buffer, 0, bytesRead);
+            }
+
+            final String output = new String(os.toByteArray(), "UTF-8");
+            assertNotNull("Response shouldn't be null", output);
+            assertTrue("Output should contain: " + expectedOutput + "\nActual output:\n" + output, output.contains(expectedOutput));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/7033e2bc/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/StatefulBean.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/StatefulBean.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/StatefulBean.java
new file mode 100644
index 0000000..4af7c06
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/StatefulBean.java
@@ -0,0 +1,77 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.openejb.arquillian.tests.persistence.ejb;
+
+import javax.ejb.AfterBegin;
+import javax.ejb.BeforeCompletion;
+import javax.ejb.Stateful;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.io.Serializable;
+
+@Stateful
+public class StatefulBean implements Serializable {
+    public static final int ENTITY_ID_AFTER_BEGIN = 1234;
+    public static final int ENTITY_ID_BEFORE_COMPLETION = 5678;
+    public static final int ENTITY_ID_BUSINESS_METHOD = 9876;
+
+    @PersistenceContext
+    EntityManager em;
+
+    @AfterBegin
+    public void afterBegin() {
+        testPersist(ENTITY_ID_AFTER_BEGIN);
+    }
+
+    @BeforeCompletion
+    public void beforeCompletion() {
+        testPersist(ENTITY_ID_BEFORE_COMPLETION);
+        requireThatTestEntityExists(ENTITY_ID_AFTER_BEGIN);
+    }
+
+    public void doPersist() {
+        testPersist(ENTITY_ID_BUSINESS_METHOD);
+        requireThatTestEntityExists(ENTITY_ID_AFTER_BEGIN);
+    }
+
+    private void testPersist(int entityId) {
+        requireThatTestEntityDoesNotExist(entityId);
+        persistEntity(entityId);
+        requireThatTestEntityExists(entityId);
+    }
+
+    private void requireThatTestEntityDoesNotExist(int id) {
+        final TestEntity testEntity = em.find(TestEntity.class, Integer.valueOf(id));
+        if (null != testEntity) {
+            throw new IllegalStateException("The DB must not contain test entity with id=" + id);
+        }
+    }
+
+    private void requireThatTestEntityExists(int id) {
+        final TestEntity testEntity = em.find(TestEntity.class, Integer.valueOf(id));
+        if (null == testEntity) {
+            throw new IllegalStateException("The DB must contain test entity with id=" + id);
+        }
+    }
+
+    private void persistEntity(int id) {
+        final TestEntity testEntity = new TestEntity();
+        testEntity.setId(id);
+        em.persist(testEntity);
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/7033e2bc/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/TestEntity.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/TestEntity.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/TestEntity.java
new file mode 100644
index 0000000..8c1d419
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/java/org/apache/openejb/arquillian/tests/persistence/ejb/TestEntity.java
@@ -0,0 +1,43 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.openejb.arquillian.tests.persistence.ejb;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class TestEntity {
+
+    @Id
+    private int id;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    @Override
+    public String toString() {
+        return "TestEntity{" +
+                "id=" + id +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/7033e2bc/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/resources/org/apache/openejb/arquillian/tests/persistence/session_synchronization/persistence.xml
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/resources/org/apache/openejb/arquillian/tests/persistence/session_synchronization/persistence.xml b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/resources/org/apache/openejb/arquillian/tests/persistence/session_synchronization/persistence.xml
new file mode 100644
index 0000000..0395aea
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-webprofile-tests/src/test/resources/org/apache/openejb/arquillian/tests/persistence/session_synchronization/persistence.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+-->
+<persistence version="2.0"
+             xmlns="http://java.sun.com/xml/ns/persistence"
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
+                       http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
+
+    <persistence-unit name="pu-session-synchronization-callback-test">
+        <jta-data-source>My DataSource</jta-data-source>
+        <non-jta-data-source>My Unmanaged DataSource</non-jta-data-source>
+        <class>org.apache.openejb.arquillian.tests.persistence.ejb.TestEntity</class>
+
+        <properties>
+            <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
+            <property name="eclipselink.target-database"
+                      value="org.eclipse.persistence.platform.database.HSQLPlatform"/>
+            <property name="eclipselink.ddl-generation" value="create-tables"/>
+            <property name="eclipselink.ddl-generation.output-mode" value="database"/>
+        </properties>
+    </persistence-unit>
+</persistence>

http://git-wip-us.apache.org/repos/asf/tomee/blob/7033e2bc/container/openejb-jpa-integration/pom.xml
----------------------------------------------------------------------
diff --git a/container/openejb-jpa-integration/pom.xml b/container/openejb-jpa-integration/pom.xml
index 76d27d2..97e6380 100644
--- a/container/openejb-jpa-integration/pom.xml
+++ b/container/openejb-jpa-integration/pom.xml
@@ -52,6 +52,11 @@
       <artifactId>eclipselink</artifactId>
       <optional>true</optional>
     </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.components</groupId>
+      <artifactId>geronimo-transaction</artifactId>
+      <scope>provided</scope>
+    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/tomee/blob/7033e2bc/container/openejb-jpa-integration/src/main/java/org/apache/openejb/jpa/integration/eclipselink/OpenEJBServerPlatform.java
----------------------------------------------------------------------
diff --git a/container/openejb-jpa-integration/src/main/java/org/apache/openejb/jpa/integration/eclipselink/OpenEJBServerPlatform.java b/container/openejb-jpa-integration/src/main/java/org/apache/openejb/jpa/integration/eclipselink/OpenEJBServerPlatform.java
index eabbae2..ea074c6 100644
--- a/container/openejb-jpa-integration/src/main/java/org/apache/openejb/jpa/integration/eclipselink/OpenEJBServerPlatform.java
+++ b/container/openejb-jpa-integration/src/main/java/org/apache/openejb/jpa/integration/eclipselink/OpenEJBServerPlatform.java
@@ -16,11 +16,14 @@
  */
 package org.apache.openejb.jpa.integration.eclipselink;
 
+import org.apache.geronimo.transaction.manager.TransactionImpl;
 import org.eclipse.persistence.platform.server.JMXServerPlatformBase;
 import org.eclipse.persistence.sessions.DatabaseSession;
+import org.eclipse.persistence.transaction.AbstractSynchronizationListener;
 import org.eclipse.persistence.transaction.JTATransactionController;
 
 import javax.management.MBeanServer;
+import javax.transaction.Synchronization;
 import javax.transaction.TransactionManager;
 
 public class OpenEJBServerPlatform extends JMXServerPlatformBase {
@@ -52,5 +55,12 @@ public class OpenEJBServerPlatform extends JMXServerPlatformBase {
                 OpenEJBJTATransactionController.class.getClassLoader().loadClass("org.apache.openejb.OpenEJB")
                     .getDeclaredMethod("getTransactionManager").invoke(null));
         }
+
+        @Override
+        protected void registerSynchronization_impl(AbstractSynchronizationListener listener, Object txn) throws Exception {
+            final TransactionImpl transaction = (TransactionImpl) txn;
+            final Synchronization synchronization = (Synchronization) listener;
+            transaction.registerInterposedSynchronization(synchronization);
+        }
     }
 }