【问题标题】:<f:actionListener> causes a "transaction not active" exception on a stateful EJB/CDI bean while <f:setPropertyActionListener> does not<f:actionListener> 在有状态 EJB/CDI bean 上导致“事务不活动”异常,而 <f:setPropertyActionListener> 不会
【发布时间】:2012-09-27 15:59:50
【问题描述】:

我有一个 Seam 3 视图范围的有状态 EJ bean,它在 JSF 页面中使用(您可以跳过第一次阅读的代码):

@Named
@ViewScoped
@Stateful
@TransactionAttribute( TransactionAttributeType.SUPPORTS )
@LocalBean
public class CollisionManager implements ActionListener, Serializable
{
    ...

    @Inject
    private CollisionService          collisionService;

    // selected collisions (must be an array, call 911-PRIMEFACES)
    private Collision[]               selectedCollisionsArray;
    private List<Collision>           selectedCollisions;


    public Collision getSelectedCollision()
    {
        if ( this.selectedCollisionsArray == null || this.selectedCollisionsArray.length == 0 )
        {
            return null;
        }

        return this.selectedCollisionsArray[0];
    }

    public void setSelectedCollision( Collision selectedCollision )
    {
        this.selectedCollisionsArray = new Collision[1];
        this.selectedCollisionsArray[0] = selectedCollision;
    }

    /**
     * Called by <f:actionListener>
     *
     * This throws an exception:
     * java.lang.IllegalStateException: Transaction is not active in the current thread.
     *    ...
     */
    @Override
    public void processAction( ActionEvent event )
    {
        this.log.infov( "Calling single-selection CLASS NAME action listener for {0}", this.selectedCollisionsArray[0] );

        reloadSelectedCollisions();
    }

    /**
     * Called by <f:setPropertyActionListener>
     *
     * This throws no exception.
     */
    public void setReloadCollision( @SuppressWarnings( "unused" ) Collision selectedCollision )
    {
        this.log.infov( "Calling single-selection PROPERTY action listener for {0}", this.selectedCollisionsArray[0] );

        reloadSelectedCollisions();
    }

    public void reloadSelectedCollisions()
    {
        this.selectedCollisions = ...; // reload list for at least one selected collision
    }

    public void setSelectedStateChangeCollision( Collision selectedStateChangeCollision )
    {
        this.selectedStateChangeCollision = selectedStateChangeCollision;
    }
}

我们有一个项目列表(碰撞),其中多个项目可以通过复选框选择。每个项目都有一个专用按钮来编辑单个碰撞。

这是顶部的多选 JSF 命令按钮:

<p:commandButton icon="ui-icon ui-icon-flag"
                 value="Change state"
                 disabled="#{empty collisionManager.selectedCollisionsArray}"
                 actionListener="#{collisionManager.reloadSelectedCollisions}"
                 process="@this"
                 update=":state-change-dialog"
                 oncomplete="stateChangeDialog.show();">
<f:setPropertyActionListener target="#{collisionManager.selectedStateChangeCollision}" value="#{collisionManager.selectedCollisions[0]}" />
</p:commandButton>

此按钮没有问题

由于用户在调用对话框之前选择了项目,因此在按下此按钮(显示编辑对话框)时,多选元素是已知的。

对于列中的单选按钮,我首先使用了以下非常相似的命令按钮:

<p:commandButton icon="ui-icon ui-icon-flag"
                 actionListener="#{collisionManager.reloadSelectedCollisions}"
                 process="@this"
                 update=":state-change-dialog :collision-form:list"
                 oncomplete="stateChangeDialog.show();">
    <f:setPropertyActionListener target="#{collisionManager.selectedCollision}" value="#{cln}" />
    <f:setPropertyActionListener target="#{collisionManager.selectedStateChangeCollision}" value="#{cln}" />
</p:commandButton>

这当然会失败,因为在通过&lt;f:setPropertyActionListener 设置当前实体之前,单击按钮将首先调用collisionManager.reloadSelectedCollisions()

然后我删除了命令按钮actionListener 属性并尝试使用“类名”动作侦听器来解决排序问题:

<p:commandButton icon="ui-icon ui-icon-flag"
                 process="@this"
                 update=":state-change-dialog :collision-form:list"
                 oncomplete="stateChangeDialog.show();">
    <f:setPropertyActionListener target="#{collisionManager.selectedCollision}" value="#{cln}" />
    <f:actionListener type="de.company.project.CollisionManager" />
    <f:setPropertyActionListener target="#{collisionManager.selectedStateChangeCollision}" value="#{cln}" />
</p:commandButton>

为了使processAction( ActionEvent event ) 方法起作用,我必须将接口添加到CollisionManager bean 并使用@LocalBean 对其进行注释。您可以看到上面发布的最终课程。

但是,这会引发异常:

java.lang.IllegalStateException: Transaction is not active in the current thread.
    at com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate.validateTransactionManager(JavaEETransactionManagerJTSDelegate.java:447)
    at com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate.setRollbackOnlyDistributedTransaction(JavaEETransactionManagerJTSDelegate.java:344)
    at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.setRollbackOnly(JavaEETransactionManagerSimplified.java:971)
    at com.sun.enterprise.transaction.UserTransactionImpl.setRollbackOnly(UserTransactionImpl.java:257)
    at org.jboss.seam.transaction.UTTransaction.setRollbackOnly(UTTransaction.java:82)
    at org.jboss.seam.transaction.DefaultSeamTransaction.setRollbackOnly(DefaultSeamTransaction.java:104)
    at org.jboss.seam.faces.transaction.UnexpectedExceptionObserver.processEvent(UnexpectedExceptionObserver.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:267)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:52)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:137)
    at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:263)
    at org.jboss.weld.introspector.jlr.WeldMethodImpl.invokeOnInstance(WeldMethodImpl.java:170)
    at org.jboss.weld.introspector.ForwardingWeldMethod.invokeOnInstance(ForwardingWeldMethod.java:51)
    at org.jboss.weld.injection.MethodInjectionPoint.invokeOnInstanceWithSpecialValue(MethodInjectionPoint.java:154)
    at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:245)
    at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:233)
    at org.jboss.weld.event.ObserverMethodImpl.notify(ObserverMethodImpl.java:213)
    at org.jboss.weld.manager.BeanManagerImpl.notifyObservers(BeanManagerImpl.java:590)
    at org.jboss.weld.manager.BeanManagerImpl.fireEvent(BeanManagerImpl.java:580)
    at org.jboss.weld.manager.BeanManagerImpl.fireEvent(BeanManagerImpl.java:575)
    at org.jboss.seam.faces.event.SystemEventBridge.processEvent(SystemEventBridge.java:69)
    at org.jboss.seam.faces.event.DelegatingSystemEventListener.processEvent(DelegatingSystemEventListener.java:51)
    at javax.faces.event.SystemEvent.processListener(SystemEvent.java:106)
    at com.sun.faces.application.ApplicationImpl.processListeners(ApplicationImpl.java:2168)
    at com.sun.faces.application.ApplicationImpl.invokeListenersFor(ApplicationImpl.java:2144)
    at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:302)
    at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:246)
    at javax.faces.application.ApplicationWrapper.publishEvent(ApplicationWrapper.java:670)
    at javax.faces.application.ApplicationWrapper.publishEvent(ApplicationWrapper.java:670)
    at org.jboss.seam.faces.environment.SeamApplicationWrapper$Proxy$_$$_WeldClientProxy.publishEvent(SeamApplicationWrapper$Proxy$_$$_WeldClientProxy.java)
    at com.sun.faces.lifecycle.Phase.queueException(Phase.java:160)
    at com.sun.faces.lifecycle.Phase.queueException(Phase.java:149)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:104)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
    at de.company.project.filter.encoding.EncodingFilter.doFilter(EncodingFilter.java:62)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
    at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:126)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
    at org.jboss.solder.servlet.exception.CatchExceptionFilter.doFilter(CatchExceptionFilter.java:65)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
    at org.jboss.solder.servlet.event.ServletEventBridgeFilter.doFilter(ServletEventBridgeFilter.java:74)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
    at de.company.project.filter.security.LoginFilter.doFilter(LoginFilter.java:96)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
    at java.lang.Thread.run(Thread.java:722)

作为补丁,我尝试了&lt;f:setPropertyActionListener&gt;,传递了当前列表实体:

<p:commandButton icon="ui-icon ui-icon-flag"
                 process="@this"
                 update=":state-change-dialog :collision-form:list"
                 oncomplete="stateChangeDialog.show();">
    <f:setPropertyActionListener target="#{collisionManager.selectedCollision}" value="#{cln}" />
    <f:setPropertyActionListener target="#{collisionManager.reloadCollision}" value="#{cln}" />
    <f:setPropertyActionListener target="#{collisionManager.selectedStateChangeCollision}" value="#{cln}" />
</p:commandButton>

这个OTOH不会抛出异常。这真的让我想知道......

为什么这与&lt;f:setPropertyActionListener&gt;(调用方法setReloadCollision( Collision selectedCollision ))一起工作,但它因&lt;f:actionListener /&gt;(调用方法processAction( ActionEvent event ))而失败?

请注意,我在两种侦听器方法的所有变体中都尝试了 @TransactionAttribute( TransactionAttributeType.REQUIRED )@TransactionAttribute( TransactionAttributeType.REQUIRES_NEW ),但均未成功。

非常感谢您的帮助。

附录:

  1. 谁应该知道这个?
  2. “普通”Java EE 开发人员应该知道多少?
  3. 这些信息隐藏在哪里?

谢谢

【问题讨论】:

  • 如果您使用action 而不是actionListener 进行业务操作,那么您的第一次尝试应该会成功。动作监听器是按照它们在组件上声明的顺序执行的(并不完全是为了执行业务动作,因为它们根本不传播异常,因此最终用户没有任何形式的反馈)。
  • 你是指第一个发布的?
  • 问题是,我想显示一个对话框并选择显示的 RELOADED 列表中的第一个元素。目前的方法是在所有按钮按下结束时使用另一个属性操作侦听器。我会更新问题。我认为我没有使用正确的方法来处理 bean 中的实体。建议表示赞赏。
  • 重新加载的列表是选中(选中)的元素/行。
  • 只是想知道,什么要求让您认为您需要@Named @ViewScoped @Stateful @LocalBean?为什么不只是一个@Named @ViewScoped bean?此外,使用action,您可以直接传入cln,而无需&lt;f:setPropertyActionListener&gt;

标签: java jsf jakarta-ee ejb cdi


【解决方案1】:

我认为还有另一个异常导致了这个问题,但不知何故它被吞没了。查看堆栈跟踪,我会尝试在

处设置断点

org.jboss.seam.faces.transaction.UnexpectedExceptionObserver.processEvent(UnexpectedExceptionObserver.java:48)

看看那里发生了什么。在调用 UnexpectedExceptionObserver.processEvent 之后,Seam 将事务设置为 rollbackOnly,就像在异常之后一样,例如NullPoInterException。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多