4 Replies Latest reply on Jan 5, 2007 11:07 AM by norman.richards

    No concurrent calls on stateful bean!

    toni

      Hi,

      if I send many simultaneous HTTP requests to my seam application, which carry out a particular action, then I get an exception.

      To reproduce this I have create a short test application, which you find below.

      The test application exists of a JSF Table, which displays a set of entities. Each entity bean can be deleted by clicking on a "delete" link.

      If I delete the entity beans sequentially, then everything works correctly. However, if I very quickly click on many links, then sometimes I get the following exception:

      Caused by: javax.ejb.ConcurrentAccessException: no concurrent calls on stateful bean 'jboss.j2ee:service=EJB3,name=TemplateAction' (EJB3 4.3.13) at org.jboss.ejb3.stateful.StatefulInstanceInterceptor.invoke(StatefulInstanceInterceptor.java:73)

      I read of course, that this happens for reentrant calls on beans (A->B->A) or when one starts a thread inside a EJB, but that's not what I'm doing.

      I already upgraded to SEAM 1.1, because I thought might/could be a bug. But I guess it's my lack of understanding.

      To reproduce the error, I have create the following example, so that people here can reproduce the error. Just copy and paste the source code it into an existing project and call "/test.seam".

      It takes only 3-4 minutes. I appreciate any comments and help.

      Toni


      Test.java
      -------------
      @Entity
      @Name("test")

      public class Test
      {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      long id;

      String testText;

      public long getId()
      {
      return id;
      }

      public void setId(long id)
      {
      this.id = id;
      }

      public String getTestText()
      {
      return testText;
      }

      public void setTestText(String testText)
      {
      this.testText = testText;
      }
      }


      BasicTest.java
      ---------------------
      @Local
      public interface BasicTest
      {
      public String list();

      public String create();

      public String select();

      public String save();

      public String delete();

      public void destroy();

      public DataModel getDataModel();
      }


      TestAction.java
      ------------------------
      @Name("testAction")
      @Stateful

      public class TestAction implements BasicTest
      {
      @In(create = true)
      EntityManager entityManager;

      @In(create = true)
      @Out
      @Valid
      Test test;

      @Out(required = false)
      DataModel dataModel;

      @Begin(join = true)
      public DataModel getDataModel()
      {
      if (dataModel == null)
      dataModel = new ListDataModel();
      dataModel.setWrappedData(entityManager.createQuery("from Test").getResultList());
      return dataModel;
      }

      @Begin(join = true)
      public String list()
      {
      return "tests";
      }

      public String create()
      {
      test = new Test();
      return "test";
      }

      public String select()
      {
      test = (Test) dataModel.getRowData();
      return "test";
      }

      @End
      public String save()
      {
      entityManager.merge(test);
      return "tests";
      }

      @End
      public String delete()
      {
      entityManager.remove(dataModel.getRowData());
      return "tests";
      }

      @Destroy
      @Remove
      public void destroy()
      {
      }
      }


      ----------- test.jsp --------------

      <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
      <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
      <%@ taglib uri="http://jboss.com/products/seam/taglib" prefix="s" %>

      <f:view>

      <h:form>
      <s:validateAll>

      <h:panelGrid columns="1">

      <h:outputText value="Type"/>
      <h:inputText size="20" value="#{test.testText}"/>

      </h:panelGrid>

      <h:messages/>
      <h:commandButton type="submit" value="Save" action="#{testAction.save}"/>

      </s:validateAll>
      </h:form>

      </f:view>




      ----------- tests.jsp ----------------------

      <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
      <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
      <%@ taglib uri="http://jboss.com/products/seam/taglib" prefix="s" %>

      <f:view>


      <h:form>
      <h:outputText value="No Test Entities available" rendered="#{testAction.dataModel.rowCount == 0}"/>

      <h:dataTable value="#{testAction.dataModel}" var="t" rendered="#{testAction.dataModel.rowCount > 0}">

      <h:column>
      <f:facet name="header">
      <h:outputText value="Type"/>
      </f:facet>
      <h:outputText value="#{t.testText}"/>
      </h:column>

      <h:column>
      <f:facet name="header">
      <h:outputText value="Action"/>
      </f:facet>
      <h:commandLink value="delete" action="#{testAction.delete}"/>
      </h:column>

      </h:dataTable>

      </h:form>

      <h:outputLink value="test.seam">
      <f:param name="conversationId" value="#{conversation.id}"/>
      <h:outputText value="Create Another Test Entity"/>
      </h:outputLink>

      </f:view>





      -------------- add to faces config ---------------------

      <navigation-rule>
      <navigation-case>
      <from-outcome>tests</from-outcome>
      <to-view-id>/tests.jsp</to-view-id>
      </navigation-case>
      <navigation-case>
      <from-outcome>test</from-outcome>
      <to-view-id>/test.jsp</to-view-id>
      </navigation-case>
      </navigation-rule>

        • 1. Re: No concurrent calls on stateful bean!

          I think Seam normally serializes requests within a conversation, but if you are accessing a stateful component in the session then you can run into problems like this. You'll need to add @Synchronized to your session bean to get Seam to enforce serialized access to a component across different conversations.

          • 2. Re: No concurrent calls on stateful bean!
            toni

            Thanks for the reply!

            I looked into the documentation and found the following: "Specifies that a stateful component has multiple concurrent clients, and so access to the component must be synchronized. This annotation is not required session scoped components, which are synchronized by default."

            However, I have the feeling that also requests to conversational scoped statefull components get serialized, because I can sent several requests simultaneously, but not too many, otherwise I run into an an exception after some time passes.

            Is there a default timeout, which we can set in the components.xml for serialized requests to a component? And shouldn't the default be to serialize all requests to a component?

            How should be handle simultaneous requests to statefull components? I mean users often click many times onto a link or a button, because they get impatient. How does seam deal with those kind of user requests?

            • 3. Re: No concurrent calls on stateful bean!
              pdpantages

              Hello, I ran into something like this a while ago (when using Seam 1.0.1) and ended up using

              import org.jboss.annotation.ejb.SerializedConcurrentAccess;
              ....
              @SerializedConcurrentAccess
              

              This fixed my exceptions and I have had no probs so far after adding it.

              Is this equivalent to @Syncrhonized? , or should I switch to the latter? I have had no trouble (that I know of) in Seam 1.0.1; I just upgraded to 1.1.0. & all still seems OK.....

              Thanks, PdP

              • 4. Re: No concurrent calls on stateful bean!

                I have never used @SerializedConcurrentAccess, but I'm sure it is not the exactly the same. (practically speaking, it may be roughly equivalent though - but I haven't looked into it)


                You are actually right, seam does serialize access to stateful session beans. I didn't realize we did that. I went ahead and tried the test and the only way I could get anything to happen was by adding a Thread.sleep() to the delete action. And then, I got the expected result:

                The conversation ended, timed out or was processing another request


                I wonder what is happening in your case to make it not work...