Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FISH-8309: Persistence Context is NULL on CDI Injected beans into MDB and tck fix #6744

Conversation

breakponchito
Copy link
Contributor

@breakponchito breakponchito commented May 31, 2024

Fix for Persistence Context injection issue on CDI beans and TCK fix for EJB Remote tests

Description

This PR addresses an issue where a PersistenceContext injected into a CDI bean, subsequently injected into a Message-Driven Bean (MDB) or a Stateless EJB, resolves to null. The problem occurs due to the different contexts in which the EntityManager Resource Descriptor instances are added during processing.

Context

The issue arises when the EntityManager Resource Descriptor instance added to the CDI bean's EjbBundleDescriptor is not accessed from the MDB bean's EjbMessageBeanDescriptor. Although EjbBundleDescriptor is the parent descriptor context, it is not the superclass of EjbMessageBeanDescriptor. As a result, when accessing the EntityManager Resource Descriptor from EjbMessageBeanDescriptor, it does not look up in the EjbBundleDescriptor context, leading to a null resolution of the PersistenceContext.

An instance of a PersistenceContext injected into a CDI bean that is then injected into a Message-Driven bean, incorrectly resolves to NULL.

@RequestScoped
@Transactional(Transactional.TxType.SUPPORTS)
public class DemoCdiServiceImpl implements DemoCdiService {

	@PersistenceContext(unitName = "payara-demoapp")
	private EntityManager entityManager;

	@Override
	public String getDemoData() {
		final DemoDataBE be = this.entityManager.find(DemoDataBE.class, 1L); //This will trigger a `NullPointerException`
		if (be == null) {
			return null;
		}
		return be.getDescTest();
	}
}
@MessageDriven(name = "DemoJmsTopicReceiverImpl", activationConfig = { 
		@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/PayaraDemoJmsTopic"), 
		@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "jakarta.jms.Topic"), 
		@ActivationConfigProperty(propertyName = "resourceAdapter", propertyValue = "jmsra") 
})
public class DemoJmsTopicReceiverImpl implements MessageListener {

	@Inject
	DemoCdiService service;

	@Override
	public void onMessage(final Message message) {
		try {
			this.service.saveDemoData("FROM JMS: " + messageString); //But only when called via this injection.
		} catch (final JMSException e) {
			throw new RuntimeException("demo does not handle exceptions", e);
		}
	}
}

The injection of a @PersistenceContext should work in this case, as the injection of CDI beans is fully supported in Message Driven Beans.

Testing

Steps to Reproduce

1 - Download the attached project and build it.
3598-payara-jms-inject-bug-reproducerPayara6.zip

mvn clean install

2 - Start a new Payara Server domain (the default domain works well)

asadmin start-domain domain1

3 - Create a new JMS Topic with the following command:

asadmin create-jms-resource --resType=jakarta.jms.Topic --property=Name=DemoTopic jms/PayaraDemoJmsTopic

4 - Deploy the EAR artefact from the project build in the server’s DAS:

asadmin deploy payara-jms-inject-bug-reproducer-ear/target/payara-jms-inject-bug-reproducer-ear-0.0.1-SNAPSHOT.ear

5 - Fire this sample HTTP request:

curl -H "Content-Type: text/plain" -X POST http://localhost:8080/payara-bug-jms/api/data/createJms -d "This is a sample message"

The request will be completed successfully, but the following error stack trace (and additional details) will be printed out to the server log:

[2024-02-05T15:43:00.689-0500] [Payara 5.57.0] [SEVERE] [] [fmg.lz06.eapps.payarafull.demoapp.ejb.DemoJmsTopicReceiverImpl] [tid: _ThreadID=236 _ThreadName=orb-thread-pool-1 (pool #1): worker-2] [timeMillis: 1707165780689] [levelValue: 1000] [[
  EJB has entitymanager: true]]

[2024-02-05T15:43:00.690-0500] [Payara 5.57.0] [INFO] [] [fmg.lz06.eapps.payarafull.demoapp.business.DemoCdiServiceImpl] [tid: _ThreadID=236 _ThreadName=orb-thread-pool-1 (pool #1): worker-2] [timeMillis: 1707165780690] [levelValue: 800] [[
  Calling demo data method save]]

[2024-02-05T15:43:00.691-0500] [Payara 5.57.0] [SEVERE] [] [fmg.lz06.eapps.payarafull.demoapp.business.DemoCdiServiceImpl] [tid: _ThreadID=236 _ThreadName=orb-thread-pool-1 (pool #1): worker-2] [timeMillis: 1707165780691] [levelValue: 1000] [[
  CDI has entitymanager: false]]

[2024-02-05T15:43:00.692-0500] [Payara 5.57.0] [WARNING] [] [jakarta.resourceadapter.mqjmsra.inbound.message] [tid: _ThreadID=236 _ThreadName=orb-thread-pool-1 (pool #1): worker-2] [timeMillis: 1707165780692] [levelValue: 900] [[
  MQJMSRA_MR2001: run:Caught Exception from onMessage():Redelivering:
jakarta.ejb.EJBException: message-driven bean method public abstract void jakarta.jms.MessageListener.onMessage(jakarta.jms.Message) system exception
	at org.glassfish.ejb.mdb.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1250)
	at org.glassfish.ejb.mdb.MessageBeanListenerImpl.deliverMessage(MessageBeanListenerImpl.java:131)
	at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:171)
	at com.sun.proxy.$Proxy363.onMessage(Unknown Source)
	at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:253)
	at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:108)
	at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$TaskRunner.run(ThreadPoolImpl.java:189)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.NullPointerException
	at fmg.lz06.eapps.payarafull.demoapp.business.DemoCdiServiceImpl.saveDemoData(DemoCdiServiceImpl.java:57)
	at fmg.lz06.eapps.payarafull.demoapp.business.DemoCdiServiceImpl$Proxy$_$$_WeldSubclass.saveDemoData$$super(Unknown Source)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.jboss.weld.interceptor.proxy.TerminalAroundInvokeInvocationContext.proceedInternal(TerminalAroundInvokeInvocationContext.java:51)
	at org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:78)
	at org.glassfish.jersey.ext.cdi1x.transaction.internal.WebAppExceptionInterceptor.intercept(WebAppExceptionInterceptor.java:53)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73)
	at org.jboss.weld.interceptor.proxy.NonTerminalAroundInvokeInvocationContext.proceedInternal(NonTerminalAroundInvokeInvocationContext.java:66)
	at org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:78)
	at org.glassfish.cdi.transaction.TransactionalInterceptorBase.proceed(TransactionalInterceptorBase.java:212)
	at org.glassfish.cdi.transaction.TransactionalInterceptorRequired.transactional(TransactionalInterceptorRequired.java:115)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73)
	at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeAroundInvoke(InterceptorMethodHandler.java:84)
	at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeInterception(InterceptorMethodHandler.java:72)
	at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.invoke(InterceptorMethodHandler.java:56)
	at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:79)
	at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:68)
	at fmg.lz06.eapps.payarafull.demoapp.business.DemoCdiServiceImpl$Proxy$_$$_WeldSubclass.saveDemoData(Unknown Source)
	at fmg.lz06.eapps.payarafull.demoapp.business.DemoCdiServiceImpl$Proxy$_$$_WeldClientProxy.saveDemoData(Unknown Source)
	at fmg.lz06.eapps.payarafull.demoapp.ejb.DemoJmsTopicReceiverImpl.onMessage(DemoJmsTopicReceiverImpl.java:41)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:588)
	at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:408)
	at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4835)
	at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:665)
	at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:834)
	at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:615)
	at org.jboss.weld.module.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:81)
	at org.jboss.weld.module.ejb.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:52)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:888)
	at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:833)
	at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:375)
	at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4807)
	at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4795)
	at org.glassfish.ejb.mdb.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1215)
	... 11 more
]]

Expected Outcome

An instance of a PersistenceContext should not be null when injected on a CDI bean that is injected into an MDB. The reproducer code tests the injection of a persistence context directly in the Message Driven Bean and on the CDI bean when used directly by an HTTP resource.

The same scenario tested on Payara Server 6 fails with the same error.

You can test the reproducer on Payara Server 6 using the same application, but you’ll have to modify the Message Driven Bean configuration to reference a jakarta.jms.Topic instead:

@MessageDriven(name = "DemoJmsTopicReceiverImpl", activationConfig = { //
		@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/PayaraDemoJmsTopic"), //
		@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "jakarta.jms.Topic"), //
		@ActivationConfigProperty(propertyName = "resourceAdapter", propertyValue = "jmsra") //
})

Use this same principle for creating the topic on the server.

Solution

To resolve this issue, the EntityManager Resource Descriptor instance added to the CDI bean's EjbBundleDescriptor context should also be accessible from the MDB bean's EjbMessageBeanDescriptor context. This can be achieved by ensuring that processing is done in a way that EntityManager Resource Descriptors are looked up from child contexts to parent context.

Blockers

Testing

New tests

Testing Performed

Manual testing following steps to reproduce and seeing the following messages on console:

image

and passing EJB TCK from CDI
[INFO] Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 13.974 s - in TestSuite [INFO] [INFO] Results: [INFO] [INFO] Tests run: 9, Failures: 0, Errors: 0, Skipped: 0

and the full execution:
[INFO] [mvn.test] [INFO] Tests run: 1831, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 960.949 s - in TestSuite [INFO] [mvn.test] [INFO] [INFO] [mvn.test] [INFO] Results: [INFO] [mvn.test] [INFO] [INFO] [mvn.test] [INFO] Tests run: 1831, Failures: 0, Errors: 0, Skipped: 0 [INFO] [mvn.test] [INFO] [INFO] [mvn.test] [INFO] [INFO] [mvn.test] [INFO] --- maven-surefire-report-plugin:3.0.0-M5:report-only (generate-test-report) @ weld-payara-runner-tck --- [INFO] [mvn.test] [WARNING] Unable to locate Test Source XRef to link to - DISABLED

Testing Environment

windows 11, Azul JDK 11, Maven 3.9.5
Ubuntu Linux 20.04, Azul JDK 11, Maven 3.8.6

Documentation

Notes for Reviewers

@breakponchito
Copy link
Contributor Author

Jenkins test please

Copy link
Contributor

@jGauravGupta jGauravGupta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@breakponchito breakponchito merged commit 9583406 into payara:master Jun 5, 2024
1 check passed
Pandrex247 pushed a commit to Pandrex247/Payara that referenced this pull request Jun 19, 2024
…ce-context-fix-on-cdi-beans-into-mdb

FISH-8309: Persistence Context is NULL on CDI Injected beans into MDB and tck fix
Pandrex247 pushed a commit to Pandrex247/Payara that referenced this pull request Jun 26, 2024
…ce-context-fix-on-cdi-beans-into-mdb

FISH-8309: Persistence Context is NULL on CDI Injected beans into MDB and tck fix
Pandrex247 pushed a commit to Pandrex247/Payara that referenced this pull request Jul 2, 2024
…ce-context-fix-on-cdi-beans-into-mdb

FISH-8309: Persistence Context is NULL on CDI Injected beans into MDB and tck fix
Pandrex247 pushed a commit to Pandrex247/Payara that referenced this pull request Jul 5, 2024
…ce-context-fix-on-cdi-beans-into-mdb

FISH-8309: Persistence Context is NULL on CDI Injected beans into MDB and tck fix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants