Java EE 8:异步观察器中没有事务(CDI 2.0)
Java EE 8: No transaction in asynchronous observer (CDI 2.0)
我有点被一个问题困住了,我想这个问题很常见。
我 运行 Wildfly 12 启用了 Java EE 8 配置文件(包括 CDI 2.0)并且我正在使用 JTA。
问题是什么:我有一个 @ApplicationScoped
bean 触发异步事件。另一个 @ApplicationScoped
bean 通过 @ObservesAsync
观察这个事件。在观察者中,我想处理一些传播的元数据并与数据库交互。为此,我有一个用 @Transactional
注释的存储库 class。设置如下所示:
一个简单的示例如下所示:
@ApplicationScoped
public class ClassA {
@Inject
private Event<ApplicationStarted> applicationStartedEvent;
public void initialize(@Observes @Initialized(ApplicationScoped.class) Object init) {
applicationStartedEvent.fireAsync(new ApplicationStarted());
}
}
@ApplicationScoped
public class ClassB {
@Inject
private Repository repository;
public void onApplicationStarted(@ObservesAsync ApplicationStarted applicationStarted) {
repository.getSomethingFromDatabase();
}
}
@Transactional
@Dependent
public class Repository {
@PersistenceContext
private EntityManager entityManager;
public List<Object> getSomethingFromDatabase() {
return entityManager.createQuery("from User").getResultList();
}
}
很遗憾,我无法使该代码片段正常工作。
时有一个奇怪的异常我不明白
repository.getSomethingFromDatabase();
被执行。
17:17:53,611 ERROR [stderr] (Weld Thread Pool -- 3) java.lang.NullPointerException
17:17:53,611 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
17:17:53,611 ERROR [stderr] (Weld Thread Pool -- 3) at org.wildfly.common.context.ContextManager.getPrivileged(ContextManager.java:271)
17:17:53,611 ERROR [stderr] (Weld Thread Pool -- 3) at org.wildfly.transaction.client.LocalTransactionContext.getCurrent(LocalTransactionContext.java:115)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at org.wildfly.transaction.client.ContextTransactionManager.begin(ContextTransactionManager.java:62)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at org.wildfly.transaction.client.ContextTransactionManager.begin(ContextTransactionManager.java:54)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:116)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:53)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:79)
17:17:53,613 ERROR [stderr] (Weld Thread Pool -- 3) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:47)
17:17:53,613 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
17:17:53,613 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at java.lang.reflect.Method.invoke(Method.java:498)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeAroundInvoke(InterceptorMethodHandler.java:84)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeInterception(InterceptorMethodHandler.java:72)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.invoke(InterceptorMethodHandler.java:56)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:79)
17:17:53,615 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:68)
17:17:53,615 ERROR [stderr] (Weld Thread Pool -- 3) at com.auroraspotter.kp.Repo$Proxy$_$$_WeldSubclass.getSomethingFromDatabase(Unknown Source)
17:17:53,615 ERROR [stderr] (Weld Thread Pool -- 3) at com.auroraspotter.kp.KpIndexUpdateCronControl.onApplicationStarted(KpIndexUpdateCronControl.java:48)
17:17:53,615 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
17:17:53,616 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
17:17:53,616 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
17:17:53,616 ERROR [stderr] (Weld Thread Pool -- 3) at java.lang.reflect.Method.invoke(Method.java:498)
17:17:53,616 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:95)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:85)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.injection.MethodInvocationStrategy$DefaultMethodInvocationStrategy.invoke(MethodInvocationStrategy.java:109)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:330)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:308)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverMethodImpl.notify(ObserverMethodImpl.java:286)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at javax.enterprise.inject.spi.ObserverMethod.notify(ObserverMethod.java:124)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.util.Observers.notify(Observers.java:170)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverNotifier.notifyAsyncObserver(ObserverNotifier.java:413)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverNotifier.lambda$notifyAsyncObservers(ObserverNotifier.java:356)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverNotifier.lambda$null(ObserverNotifier.java:438)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.security.spi.SecurityServices.lambda$getSecurityContextAssociator[=15=](SecurityServices.java:80)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverNotifier.lambda$createSupplier(ObserverNotifier.java:435)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1590)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
17:17:53,619 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
17:17:53,619 ERROR [stderr] (Weld Thread Pool -- 3) at java.lang.Thread.run(Thread.java:748)
17:17:53,619 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.threads.JBossThread.run(JBossThread.java:485)
如果我 运行 使用同步事件的相同代码,一切正常。
那我错过了什么?
我终于找到了解决办法。
由于观察者是异步的,因此没有容器管理的事务可用(这非常有意义,因为事务不能跨越多个线程)。所以我需要自己开始交易。
不幸的是,这也不起作用。经过几个小时的调试,我终于找到了根本原因。出于某种原因,Wildfly 的 ContextManager
抛出了一个 NPE,因为观察者 class (ClassB) 中没有可用的 class 加载器。幸运的是,模块 wildfly-common
已经有可用的错误修复。所以我刚从版本 1.3.0.Final 升级到 1.3.1.Final。
最终代码如下所示:
@ApplicationScoped
public class Observer {
@Inject
private Repository repository;
public void onApplicationStarted(@ObservesAsync ApplicationStarted applicationStarted) throws Exception {
UserTransaction userTransaction = (UserTransaction) new InitialContext().lookup("java:jboss/UserTransaction");
userTransaction.begin();
try {
repository.getSomethingFromDatabase();
// or repository.persistSomething();
} catch (Exception e) {
e.printStackTrace();
}
userTransaction.commit();
}
}
我有点被一个问题困住了,我想这个问题很常见。
我 运行 Wildfly 12 启用了 Java EE 8 配置文件(包括 CDI 2.0)并且我正在使用 JTA。
问题是什么:我有一个 @ApplicationScoped
bean 触发异步事件。另一个 @ApplicationScoped
bean 通过 @ObservesAsync
观察这个事件。在观察者中,我想处理一些传播的元数据并与数据库交互。为此,我有一个用 @Transactional
注释的存储库 class。设置如下所示:
一个简单的示例如下所示:
@ApplicationScoped
public class ClassA {
@Inject
private Event<ApplicationStarted> applicationStartedEvent;
public void initialize(@Observes @Initialized(ApplicationScoped.class) Object init) {
applicationStartedEvent.fireAsync(new ApplicationStarted());
}
}
@ApplicationScoped
public class ClassB {
@Inject
private Repository repository;
public void onApplicationStarted(@ObservesAsync ApplicationStarted applicationStarted) {
repository.getSomethingFromDatabase();
}
}
@Transactional
@Dependent
public class Repository {
@PersistenceContext
private EntityManager entityManager;
public List<Object> getSomethingFromDatabase() {
return entityManager.createQuery("from User").getResultList();
}
}
很遗憾,我无法使该代码片段正常工作。
时有一个奇怪的异常我不明白repository.getSomethingFromDatabase();
被执行。
17:17:53,611 ERROR [stderr] (Weld Thread Pool -- 3) java.lang.NullPointerException
17:17:53,611 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
17:17:53,611 ERROR [stderr] (Weld Thread Pool -- 3) at org.wildfly.common.context.ContextManager.getPrivileged(ContextManager.java:271)
17:17:53,611 ERROR [stderr] (Weld Thread Pool -- 3) at org.wildfly.transaction.client.LocalTransactionContext.getCurrent(LocalTransactionContext.java:115)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at org.wildfly.transaction.client.ContextTransactionManager.begin(ContextTransactionManager.java:62)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at org.wildfly.transaction.client.ContextTransactionManager.begin(ContextTransactionManager.java:54)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:116)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:53)
17:17:53,612 ERROR [stderr] (Weld Thread Pool -- 3) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:79)
17:17:53,613 ERROR [stderr] (Weld Thread Pool -- 3) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:47)
17:17:53,613 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
17:17:53,613 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at java.lang.reflect.Method.invoke(Method.java:498)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeAroundInvoke(InterceptorMethodHandler.java:84)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeInterception(InterceptorMethodHandler.java:72)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.invoke(InterceptorMethodHandler.java:56)
17:17:53,614 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:79)
17:17:53,615 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:68)
17:17:53,615 ERROR [stderr] (Weld Thread Pool -- 3) at com.auroraspotter.kp.Repo$Proxy$_$$_WeldSubclass.getSomethingFromDatabase(Unknown Source)
17:17:53,615 ERROR [stderr] (Weld Thread Pool -- 3) at com.auroraspotter.kp.KpIndexUpdateCronControl.onApplicationStarted(KpIndexUpdateCronControl.java:48)
17:17:53,615 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
17:17:53,616 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
17:17:53,616 ERROR [stderr] (Weld Thread Pool -- 3) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
17:17:53,616 ERROR [stderr] (Weld Thread Pool -- 3) at java.lang.reflect.Method.invoke(Method.java:498)
17:17:53,616 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:95)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:85)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.injection.MethodInvocationStrategy$DefaultMethodInvocationStrategy.invoke(MethodInvocationStrategy.java:109)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:330)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:308)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverMethodImpl.notify(ObserverMethodImpl.java:286)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at javax.enterprise.inject.spi.ObserverMethod.notify(ObserverMethod.java:124)
17:17:53,617 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.util.Observers.notify(Observers.java:170)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverNotifier.notifyAsyncObserver(ObserverNotifier.java:413)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverNotifier.lambda$notifyAsyncObservers(ObserverNotifier.java:356)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverNotifier.lambda$null(ObserverNotifier.java:438)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.security.spi.SecurityServices.lambda$getSecurityContextAssociator[=15=](SecurityServices.java:80)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.weld.event.ObserverNotifier.lambda$createSupplier(ObserverNotifier.java:435)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1590)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java)
17:17:53,618 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
17:17:53,619 ERROR [stderr] (Weld Thread Pool -- 3) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
17:17:53,619 ERROR [stderr] (Weld Thread Pool -- 3) at java.lang.Thread.run(Thread.java:748)
17:17:53,619 ERROR [stderr] (Weld Thread Pool -- 3) at org.jboss.threads.JBossThread.run(JBossThread.java:485)
如果我 运行 使用同步事件的相同代码,一切正常。
那我错过了什么?
我终于找到了解决办法。
由于观察者是异步的,因此没有容器管理的事务可用(这非常有意义,因为事务不能跨越多个线程)。所以我需要自己开始交易。
不幸的是,这也不起作用。经过几个小时的调试,我终于找到了根本原因。出于某种原因,Wildfly 的 ContextManager
抛出了一个 NPE,因为观察者 class (ClassB) 中没有可用的 class 加载器。幸运的是,模块 wildfly-common
已经有可用的错误修复。所以我刚从版本 1.3.0.Final 升级到 1.3.1.Final。
最终代码如下所示:
@ApplicationScoped
public class Observer {
@Inject
private Repository repository;
public void onApplicationStarted(@ObservesAsync ApplicationStarted applicationStarted) throws Exception {
UserTransaction userTransaction = (UserTransaction) new InitialContext().lookup("java:jboss/UserTransaction");
userTransaction.begin();
try {
repository.getSomethingFromDatabase();
// or repository.persistSomething();
} catch (Exception e) {
e.printStackTrace();
}
userTransaction.commit();
}
}