This is an automated email from the ASF dual-hosted git repository.
rzo1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomee.git
The following commit(s) were added to refs/heads/main by this push:
new c674d1950a TOMEE-4447 Don't attempt to propagate the transaction for
CDI async events
c674d1950a is described below
commit c674d1950a11401b0b9ba76d209bef383e5adb5b
Author: Jonathan Gallimore <[email protected]>
AuthorDate: Thu Jan 16 14:30:02 2025 +0000
TOMEE-4447 Don't attempt to propagate the transaction for CDI async events
---
.../openejb/cdi/ThreadSingletonServiceImpl.java | 4 +-
.../apache/openejb/cdi/CdiAsyncObserverTest.java | 133 +++++++++++++++++++++
2 files changed, 135 insertions(+), 2 deletions(-)
diff --git
a/container/openejb-core/src/main/java/org/apache/openejb/cdi/ThreadSingletonServiceImpl.java
b/container/openejb-core/src/main/java/org/apache/openejb/cdi/ThreadSingletonServiceImpl.java
index f0bcb88161..1a35fe0afe 100644
---
a/container/openejb-core/src/main/java/org/apache/openejb/cdi/ThreadSingletonServiceImpl.java
+++
b/container/openejb-core/src/main/java/org/apache/openejb/cdi/ThreadSingletonServiceImpl.java
@@ -177,9 +177,9 @@ public class ThreadSingletonServiceImpl implements
ThreadSingletonService {
executor = new ManagedExecutorServiceImpl(
new ExecutorBuilder()
.size(3)
- .threadFactory(new
ManagedThreadFactoryImpl(appContext.getId() + "-cdi-fireasync-", null,
ContextServiceImplFactory.newPropagateEverythingContextService()))
+ .threadFactory(new
ManagedThreadFactoryImpl(appContext.getId() + "-cdi-fireasync-", null,
ContextServiceImplFactory.newDefaultContextService()))
.prefix("CDIAsyncPool")
-
.build(appContext.getOptions()),
ContextServiceImplFactory.newPropagateEverythingContextService());
+
.build(appContext.getOptions()),
ContextServiceImplFactory.newDefaultContextService());
delegate.compareAndSet(null, executor);
} else {
executor = alreadyUpdated;
diff --git
a/container/openejb-core/src/test/java/org/apache/openejb/cdi/CdiAsyncObserverTest.java
b/container/openejb-core/src/test/java/org/apache/openejb/cdi/CdiAsyncObserverTest.java
new file mode 100644
index 0000000000..51b419bb95
--- /dev/null
+++
b/container/openejb-core/src/test/java/org/apache/openejb/cdi/CdiAsyncObserverTest.java
@@ -0,0 +1,133 @@
+/**
+ * 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.openejb.cdi;
+
+import jakarta.ejb.EJB;
+import jakarta.ejb.Lock;
+import jakarta.ejb.LockType;
+import jakarta.ejb.Singleton;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Event;
+import jakarta.enterprise.event.ObservesAsync;
+import jakarta.inject.Inject;
+import jakarta.transaction.TransactionManager;
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.Module;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+@RunWith(ApplicationComposer.class)
+public class CdiAsyncObserverTest {
+ private static final AtomicInteger eventFired = new AtomicInteger(0);
+ private static final AtomicInteger eventReceived = new AtomicInteger(0);
+
+ @EJB
+ private WorkBean workBean;
+
+ @Test
+ public void test() throws Exception {
+ workBean.doWork();
+
+ Assert.assertEquals(1, eventFired.get());
+
+ retry(2, 1000, () -> {
+ Assert.assertEquals(1, eventReceived.get());
+ });
+ }
+
+ @Module
+ @Classes(cdi = true, value = {WorkBean.class, MyEvent.class,
Listener.class})
+ public EjbJar jar() {
+ return new EjbJar("cdi-async-observer");
+ }
+
+
+ @Singleton
+ @Lock(LockType.READ)
+ public static class WorkBean {
+ private static final Logger LOGGER =
Logger.getLogger(WorkBean.class.getName());
+
+ @Inject
+ private Event<MyEvent> event;
+
+ public void doWork() {
+ LOGGER.info("Executing doWork()");
+ event.fireAsync(new MyEvent("Executed doWork()"));
+ eventFired.incrementAndGet();
+ }
+ }
+
+
+ public static class MyEvent {
+ private final String details;
+
+ public MyEvent(final String details) {
+ this.details = details;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+ }
+
+
+ @ApplicationScoped
+ public static class Listener {
+ private static final Logger LOGGER =
Logger.getLogger(Listener.class.getName());
+
+ public void observer(final @ObservesAsync MyEvent event) throws
Exception{
+ final TransactionManager txMgr =
SystemInstance.get().getComponent(TransactionManager.class);
+ Assert.assertNotNull(txMgr);
+ Assert.assertNull(txMgr.getTransaction());
+ LOGGER.info("Received async event: " + event.getDetails());
+ eventReceived.incrementAndGet();
+ }
+ }
+
+ private void retry(final int maxRetries, final int retryDelay, final
NoArgExceptionFunction func) throws Exception {
+ int retry = 0;
+ while (true) {
+ try {
+ func.run();
+ break;
+ } catch (Error | Exception err) {
+ retry++;
+ if (retry >= maxRetries) {
+ throw err;
+ }
+ }
+
+ try {
+ Thread.sleep(retryDelay);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+
+ @FunctionalInterface
+ public interface NoArgExceptionFunction {
+ public abstract void run() throws Exception;
+ }
+}