0

I have a Seam 3 view-scoped, stateful EJ bean, which is used from JSF pages (you can skip the code on the first read):

@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;
    }
}

We have a list of items (collisions) in which multiple items can be selected via checkbox. There's a dedicated button for each item to edit a single collision.

enter image description here

Here's the multi-selection JSF command button at the top:

<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>

There are no problems with this button.

The multi-select elements are known at the point this button is pressed (edit dialog shown) due to the user having selected the items before invoking the dialog.

For single-selection buttons in the column, I first used the following, very similar command button:

<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>

This of course fails, because a click on the button would first call collisionManager.reloadSelectedCollisions() before the current entity was set via the <f:setPropertyActionListener.

I then removed the command button actionListener attribute and tried a "class name" action listener to solve the ordering problem:

<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>

For the processAction( ActionEvent event ) method to work, I had to add the interface to the CollisionManager bean and annotate it with @LocalBean. You can see the final class as posted above.

However, this throws an exception:

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)

As a patch, I tried an <f:setPropertyActionListener>, passing the current list entity:

<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>

This OTOH does not throw an exception. This really makes me wonder...

Q:

Why is this working with a <f:setPropertyActionListener> (calling method setReloadCollision( Collision selectedCollision )), but it fails with <f:actionListener /> (calling method processAction( ActionEvent event ))?

Note, that I tried @TransactionAttribute( TransactionAttributeType.REQUIRED ) and @TransactionAttribute( TransactionAttributeType.REQUIRES_NEW ) in all variants on both of the listener methods without success.

Help is highly appreciated.

Addendum:

  1. Who is supposed to know this?
  2. How are "average" Java EE developers supposed to know?
  3. Where is such information hidden?

Thanks

Kawu
  • 13,647
  • 34
  • 123
  • 195
  • Your first attempt should have worked if you used `action` instead of `actionListener` for the business action. Action listeners are namely executed in the order they're declared on the component (and not exactly intented for executing business actions as they don't propagate exceptions at all and thus leaving the enduser without any form of feedback). – BalusC Sep 27 '12 at 16:04
  • You mean the very first one posted? – Kawu Sep 27 '12 at 16:41
  • Problem is, I want to display a dialog and select the first element in the RELOADED list shown. Currently the approach was to use another property action listener following at the end of all button presses. I'll update the question. I think I am not using the right way to process my entities in the beans. Advice appreciated. – Kawu Sep 27 '12 at 16:46
  • The reloaded list are the selected (checked) elements/lines. – Kawu Sep 27 '12 at 16:55
  • Just wondering, what requirements let you to think you needed a `@Named @ViewScoped @Stateful @LocalBean`? Why not just a `@Named @ViewScoped` bean? Also, with an `action` you can pass-in `cln` directly without needing a ``. – Arjan Tijms Sep 27 '12 at 19:06
  • We're still in prototyping phase, so it's not all final. I agree, a view-scoped CDI bean might be enough. I have a hard time understanding when to use `actionListener` and `action`, evene after reading http://stackoverflow.com/questions/3909267/differences-between-action-and-actionlistener . I seem to have minor talent grasping the difference, that is when to use which one(s), aside from the technical consequences (exceptions swallowed, no navigation, ...) – Kawu Sep 27 '12 at 20:20

1 Answers1

0

I think that there is another exception that is causing this problem, but somehow it gets swallowed. Looking at the stacktrace i would try to set a breakpoint at

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

and see what is going on there. After calling UnexpectedExceptionObserver.processEvent Seam is setting transaction to rollbackOnly just like it would after exception e.g. NullPoInterException.

kaos
  • 1,598
  • 11
  • 15