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

No application context available in a transactional AFTER_* CDI observer in an enterprise bean timer #30602

Open
jonasrutishauser opened this issue Jan 21, 2025 · 1 comment
Labels
in:CDI Needs member attention release bug This bug is present in a released version of Open Liberty

Comments

@jonasrutishauser
Copy link

Describe the bug
In a transaction observer method with for example @Observes(during = TransactionPhase.AFTER_SUCCESS), there is no application context available if the transaction is managed by an enterprise bean timer method.

For example a call to InitialContext.doLookup("java:app/AppName") will fail:

javax.naming.NamingException: CWNEN1000E: A JNDI operation on a java:comp/env name cannot be completed because the current thread is not associated with a Java Enterprise Edition application component. This condition can occur when the JNDI client using the java:comp/env name does not occur on the thread of a server application request. Make sure that a Java EE application does not run JNDI operations on java:comp/env names within static code blocks or in threads created by that application. Such code does not necessarily run on the thread of a server application request and therefore is not supported by JNDI operations on java:comp/env names.
	at com.ibm.ws.injectionengine.osgi.internal.naming.InjectionJavaColonHelper.getInjectionScopeData(InjectionJavaColonHelper.java:161)
	at com.ibm.ws.injectionengine.osgi.internal.naming.InjectionJavaColonHelper.getInjectionBinding(InjectionJavaColonHelper.java:135)
	at com.ibm.ws.injectionengine.osgi.internal.naming.InjectionJavaColonHelper.getObjectInstance(InjectionJavaColonHelper.java:115)
	at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLContext.lookup(JavaURLContext.java:334)
	at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLContext.lookup(JavaURLContext.java:372)
	at org.apache.aries.jndi.DelegateContext.lookup(DelegateContext.java:149)
	at java.naming/javax.naming.InitialContext.lookup(InitialContext.java:409)
	at java.naming/javax.naming.InitialContext.doLookup(InitialContext.java:282)
	at io.github.jonasrutishauser.liberty.timer.context.EventDto.showContext(EventDto.java:11)
	at io.github.jonasrutishauser.liberty.timer.context.Observer.afterCommit(Observer.java:18)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:95)
	at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:85)
	at org.jboss.weld.injection.MethodInvocationStrategy$SimpleMethodInvocationStrategy.invoke(MethodInvocationStrategy.java:168)
	at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:330)
	at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:308)
	at org.jboss.weld.event.ObserverMethodImpl.notify(ObserverMethodImpl.java:286)
	at jakarta.enterprise.inject.spi.ObserverMethod.notify(ObserverMethod.java:125)
	at org.jboss.weld.util.Observers.notify(Observers.java:166)
	at org.jboss.weld.module.jta.DeferredEventNotification$1.execute(DeferredEventNotification.java:76)
	at org.jboss.weld.module.jta.DeferredEventNotification$RunInRequest.run(DeferredEventNotification.java:121)
	at org.jboss.weld.module.jta.DeferredEventNotification.run(DeferredEventNotification.java:82)
	at org.jboss.weld.module.jta.TransactionNotificationSynchronization.afterCompletion(TransactionNotificationSynchronization.java:48)
	at com.ibm.tx.jta.impl.RegisteredSyncs.coreDistributeAfter(RegisteredSyncs.java:302)
	at com.ibm.tx.jta.impl.RegisteredSyncs.distributeAfter(RegisteredSyncs.java:279)
	at com.ibm.tx.jta.embeddable.impl.EmbeddableTransactionImpl.distributeAfter(EmbeddableTransactionImpl.java:330)
	at com.ibm.tx.jta.impl.TransactionImpl.postCompletion(TransactionImpl.java:2530)
	at com.ibm.tx.jta.impl.TransactionImpl.stage1CommitProcessing(TransactionImpl.java:868)
	at com.ibm.tx.jta.impl.TransactionImpl.processCommit(TransactionImpl.java:818)
	at com.ibm.tx.jta.impl.TransactionImpl.commit(TransactionImpl.java:761)
	at com.ibm.tx.jta.impl.TranManagerImpl.commit(TranManagerImpl.java:155)
	at com.ibm.tx.jta.impl.TranManagerSet.commit(TranManagerSet.java:112)
	at com.ibm.ejs.csi.TranStrategy.commit(TranStrategy.java:866)
	at com.ibm.ejs.csi.TranStrategy.postInvoke(TranStrategy.java:188)
	at com.ibm.ejs.csi.TransactionControlImpl.postInvoke(TransactionControlImpl.java:483)
	at com.ibm.ejs.container.EJSContainer.postInvoke(EJSContainer.java:3688)
	at com.ibm.ejs.container.TimedObjectWrapper.invokeCallback(TimedObjectWrapper.java:123)
	at com.ibm.ejs.container.TimerNpRunnable.doWork(TimerNpRunnable.java:232)
	at com.ibm.ejs.container.TimerNpRunnable.run(TimerNpRunnable.java:146)
	at com.ibm.ws.context.service.serializable.ContextualRunnable.run(ContextualRunnable.java:81)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:298)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1583)

Steps to Reproduce
The following simple implementation shows the problem:

  • timer bean:
    @Startup
    @Singleton
    public class TimerBean {
    
        @Inject
        private Event<EventDto> event;
    
        @Schedule(hour = "*", minute = "*", persistent = false)
        public void timer() {
            EventDto dto = new EventDto();
            event.fire(dto);
        }
    }
  • Observer:
    @Dependent
    public class Observer {
        void afterCommit(@Observes(during = TransactionPhase.AFTER_SUCCESS) EventDto dto) {
            try {
                System.out.println("application: " + InitialContext.doLookup("java:app/AppName"));
            } catch (NamingException e) {
                e.printStackTrace();
            }
        }
    }

Expected behavior
I expect that the application context is still available.

Diagnostic information:

  • OpenLiberty Version: 24.0.0.12
  • Affected feature(s): enterpriseBeansLite-4.0
  • Java Version: doesn't matter
  • server.xml configuration (WITHOUT sensitive information like passwords): feature activation is enough
  • If it would be useful, upload the messages.log file found in $WLP_OUTPUT_DIR/messages.log: There isn't much more output than the stacktrace shown above.

Additional context

@jonasrutishauser jonasrutishauser added the release bug This bug is present in a released version of Open Liberty label Jan 21, 2025
@tkburroughs
Copy link
Member

@jonhawkes and @benjamin-confino
Appears the problem here is that afterCompletion is being driven on the EJB Container and the Observer bean in the wrong order.

AfterCompletion on the EJB Timer bean will return the bean to the pool, making it available for use on other threads, so the bean contexts must be removed from the current thread during afterCompletion.

If afterCompletion occurs on the Observer bean after the EJB, then the subject problem occurs; no java: naming contexts on the thread.

If afterCompletion occurs on the Observer bean BEFORE the EJB, then the EJB context would still be on the thread and the naming lookup would work fine.

@jonhawkes Can you verify that the correct way to resolve this would be for CDI to register the Observer bean using the SYNC_TIER_RRS tier when registering for synchronization. I seem to recall a similar issue with connections, and it appears that JCA uses SYNC_TIER_RRS to solve this, as those with SYNC_TIER_RRS run afterCompletion before other normal registered objects

@benjamin-confino Can you verify whether or not the CDI specification requires the AFTER_SUCCESS notification to run in the context of the EJB (or servlet) for which the event was registered? I would suspect yes, since it is registered in the context of the EJB or servlet in which it is running, but I'm not familiar with that aspect of the CDI spec. If required, then CDI would just need to switch to register with SYNC_TIER_RRS (assuming transaction team agrees).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in:CDI Needs member attention release bug This bug is present in a released version of Open Liberty
Projects
None yet
Development

No branches or pull requests

4 participants