4 Replies Latest reply on Apr 17, 2008 5:23 PM by Ronald van Kuijk

    Using a Seam SLSB in a JBPM action handler?

    Brom bie Newbie

      Hi all,


      My seam project has the jbpm module enabled and I'm wondering how I can get my jbpm action handler to access a Seam component/object.


      For example, in my jpdl, I have a transition which has an action handler pointing to test.transitionHandler class.


      Inside the transitionHandler.class, I have something like this


      Component comp = Seam.componentForName("fruitManager");
      assert comp !=null;
      FruitManager fm= (FruitManager) comp.newInstance();
      fm.updateDescription(1, "Apple");
      



      fruitManager is a name of a SLSB. updateDescription() simply updates the description field of the Fruit bean and persist it.


      When executed, here's the stack trace that shows:


      08:54:28,546 WARN  [loggerI18N] [com.arjuna.ats.internal.jta.transaction.arjunacore.lastResource.disallow] [com.arjuna.ats.internal.jta.transaction.arjunacore.lastResource.disallow] Adding multiple last resources is disallowed. Current resource is org.jboss.resource.connectionmanager.TxConnectionManager$LocalXAResource@17bb117
      08:54:28,562 WARN  [JDBCExceptionReporter] SQL Error: 0, SQLState: null
      08:54:28,562 ERROR [JDBCExceptionReporter] Could not enlist in transaction on entering meta-aware object!; - nested throwable: (javax.transaction.SystemException: java.lang.Throwable: Unabled to enlist resource, see the previous warnings. tx=TransactionImple < ac, BasicAction: -3f57fffd:5e1:47f24bc0:1f6 status: ActionStatus.ABORT_ONLY >); - nested throwable: (org.jboss.resource.JBossResourceException: Could not enlist in transaction on entering meta-aware object!; - nested throwable: (javax.transaction.SystemException: java.lang.Throwable: Unabled to enlist resource, see the previous warnings. tx=TransactionImple < ac, BasicAction: -3f57fffd:5e1:47f24bc0:1f6 status: ActionStatus.ABORT_ONLY >))
      08:54:28,562 INFO  [DefaultLoadEventListener] Error performing load command
      org.hibernate.exception.GenericJDBCException: Cannot open connection
           at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
           at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
           at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
           at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:29)
           at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:426)
           at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:144)
           at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:139)
           at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1547)
           at org.hibernate.loader.Loader.doQuery(Loader.java:673)
           at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
           at org.hibernate.loader.Loader.loadEntity(Loader.java:1860)
           at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
           at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
           at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3044)
           at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
           at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
           at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
           at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:195)
           at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:103)
           at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
           at org.hibernate.impl.SessionImpl.get(SessionImpl.java:815)
           at org.hibernate.impl.SessionImpl.get(SessionImpl.java:808)
           at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:174)
           at org.jboss.ejb3.entity.TransactionScopedEntityManager.find(TransactionScopedEntityManager.java:171)
           at org.jboss.seam.persistence.EntityManagerProxy.find(EntityManagerProxy.java:85)
           at test.FruitManager.updateDescription(ImageTicketManagerBean.java:737)



      Any help is greatly appreciated!


        • 2. Re: Using a Seam SLSB in a JBPM action handler?
          Jesse Sweetland Newbie

          I'm having a similar issue.  What is happening is this:




          1. CMP EJB3 SFSB component changes an entity (on purpose or on accident) and calls EntityManager.merge

          2. SFSB calls SLSB wrapper for JBPM in transaction TX1

          3. SLSB creates JBPM context (transaction TX2)

          4. JBPM ProcessInstance is loaded an signaled in TX2

          5. JBPM ActionHandler is executed as a result of the transition

          6. ActionHandler makes call to EJB3 SLSB to modify same entity affected in step 1

          7. TX2 blocks pending commit of TX1, which is waiting for TX2 to complete (same thread)

          8. TX2 times out, resulting in the error indicated above, causing cascading failure and rollback of the XA transaction



          An example situation is the following:



          1. OrderManager SFSB component calls OrderSession SLSB to set EJB3 Order entity status to NEW

          2. Create and signal order process

          3. Order process updates EJB3 Order entity status to SUBMITTED or VALIDATED via OrderSession



          Before EJB3/Seam I was managing my own transactions manually in the web tier using JTA and I simply committed the first transaction (TX1) before doing anything with the JBPM context (TX2).  Then after the JBPM context stuff was complete I would fire up a new JTA transaction and complete the rest of the request (response rendering et al)


          With Seam managed transactions I'm not sure how to accomplish this.  My SFSB components farm out all of their persistence logic to a SLSB DAO tier that encapsulates business logic and the external API for importing and exporting data.  Currently everything uses CMP and the same injected EntityManager instance.   Unless I make everything BMP (SLSB included), I can't think if a way to commit transactions in the middle of the request.


          The thing I'm looking at now is starting an async thread to make the JBPM context calls.  The persistence operations would initially block pending commit of TX1, but since control would immediately return back to the parent thread TX1 could commit and TX2 would then be released to do its business.  The only problem with that is that there would be a maverick thread running loose and it would be difficult, if not impossible, to get back any meaningful status info to display to the user (such as yes, the task completed successfully, or no, there was an error and the task did not actually complete).


          The only other option would be to set up all EJB3 SLSB access from ActionHandlers as asynchronous actions in the process definition.  The problem with that is the potential for async updates to fail and the processes to get stuck in that async state.


          Any suggestions from the Seam community?


          • 3. Re: Using a Seam SLSB in a JBPM action handler?
            Jesse Sweetland Newbie

            After re-reading the EJB3 core spec I discovered that it is possible to use BMT to get around deadlock.  I have a couple of SFSB components that back the UI in which the process is manipulated, so I made these BMT.  Neither of these two SFSB components access the EJB3 persistence context directly (all persistence is done via CMT SLSB DAOs) so all I had to do was identify the 2 or 3 places in the BMT SFSB where I needed to group operations into transactions and add explicity UserTransaction.begin() and UserTransaction.commit()s.


            I also made the SLSB that wrap JBPM BMT as well.  Since I don't access any of the other CMT SLSBs from these process/task SLSBs, I didn't have to deal with UserTransaction at all.  (The JBPM transactions are handled by JBPM and Hibernate within the JbpmContext.)


            In the BMT SFSB, I either perform the EJB3 persistence context operations in a UserTransaction and commit before calling the BMT process/task SLSB, or I skip the UserTransaction altogether and let the container create an implicit transaction for the life of the CMT invocation.  This ensures that TX1 (EBJ3) commits before TX2 (JBPM) even starts.


            For example:


            @Stateful
            @Name("OrderSubmissionManager")
            @TransactionManagement(TransactionManagementType.BEAN)
            public class OrderSubmissionManagerBean implements OrderSubmissionManager {
                @Resource
                private UserTransaction ut;
                /** BMT SLSB wrapper for JBPM */
                @EJB
                private ProcessSessionLocal processSession;
                /** CMP SLSB for order DAO */
                @EJB
                private OrderSessionLocal orderSession;
                @Logger
                private Log log;
                @In
                private Order order;
                @In
                private FacesMessages facesMessages;
            
                public String submitOrder() {
                    try {
                        // TX1: EJB3 persistence context
                        ut.begin();
                        order = orderSession.getOrderById(order.getId());
                        order.setStatus(OrderStatus.NEW);
                        order.setOrderVersion(1);
                        order = orderSession.updateOrder(order);
                        ut.commit();
            
                        // TX2: JBPM/Hibernate
                        // Create process.  Really no risk of transaction
                        // deadlock here.
                        WorkflowProcess process = processSession.createProcess("Order process");
            
                        process.getMetadata().setOrderId(order.getId());
                        process.getVariables().put("orderNumber", order.getOrderNumber());
            
                        // TX3: JBPM/Hibernate
                        // Signal process and potentially execute action
                        // handlers that make calls to other CMT SLSB
                        // in the EJB3 persistence context
                        process = processSession.startProcess(process);
            
                        // TX4: EJB3 persistence context
                        ut.begin();
                        order = orderSession.getOrderById(order.getId());
                        order.setStatus(OrderStatus.SUBMITTED);
                        order.setSubmittedDate(new Date());
                        order = orderSession.updateOrder(order);
                        ut.commit();
            
                        facesMessages.add("Order submitted successfully");
                        return Outcomes.SUCCESS;
                    } catch(Throwable t) {
                        log.error("Error submitting order");
                        try {
                            ut.rollback();
                        } catch(Throwable t2) {
                            log.warn("Error rolling back transaction", t2);
                        }
                        facesMessages.add("An error occurred while submitting the order.  Please contact an administrator");
                        return Outcomes.FAILURE;
                    }
                }
            }


            • 4. Re: Using a Seam SLSB in a JBPM action handler?
              Ronald van Kuijk Apprentice

              Is there a specific reason to use 2 transactions initially? You can make jBPM use the same transaction as the S(FL)SB. Imo (a very humble one) that would make things a lot simpler wouldn't it?