6 Replies Latest reply on Apr 7, 2009 7:26 AM by Raoul Batista

    Unable to save ProcessInstance after adding RuntimeAction

    Glen Whitbeck Newbie

      It looks like saving a ProcessInstance containing "RuntimeAction" objects is causing a problem because the expected objects are "Action" objects.

      This does not throw an exception:

      jbpmContext = jbpmConfiguration.createJbpmContext();
      try {
       processInstance = jbpmContext.loadProcessInstanceForUpdate(processInstanceId);
      } finally {
       jbpmContext.close();
      }
      


      However, this does throw an exception:

      jbpmContext = jbpmConfiguration.createJbpmContext();
      try {
       processInstance = jbpmContext.loadProcessInstanceForUpdate(processInstanceId);
      
       RuntimeAction ra = new RuntimeAction(node, Event.EVENTTYPE_NODE_ENTER, action);
       processInstance.addRuntimeAction(ra);
      
      } finally {
       jbpmContext.close();
      }
      


      The action being added is serializable and the runtime actions are properly executed when not using a persistable process instance (or when they are added after the context is closed).

      Here is the exception that gets thrown when trying to close the 'persistence' service:

      Caused by: org.hibernate.HibernateException: instance not of expected entity type: org.jbpm.graph.def.Action
       at org.hibernate.persister.entity.AbstractEntityPersister.getSubclassEntityPersister(AbstractEntityPersister.java:3301)
       at org.hibernate.impl.SessionImpl.getEntityPersister(SessionImpl.java:1330)
       at org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:180)
       at org.hibernate.engine.ForeignKeys$Nullifier.isNullifiable(ForeignKeys.java:137)
       at org.hibernate.engine.ForeignKeys$Nullifier.nullifyTransientReferences(ForeignKeys.java:69)
       at org.hibernate.engine.ForeignKeys$Nullifier.nullifyTransientReferences(ForeignKeys.java:47)
       at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:263)
       at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:167)
       at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:101)
       at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
       at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
       at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:98)
       at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
       at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:531)
       at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:523)
       at org.hibernate.engine.CascadingAction$1.cascade(CascadingAction.java:134)
       at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:213)
       at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:157)
       at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
       at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:290)
       at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:185)
       at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:160)
       at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
       at org.hibernate.engine.Cascade.cascade(Cascade.java:248)
       at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:130)
       at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:121)
       at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
       at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
       at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1009)
       at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:356)
       at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
       at org.jbpm.persistence.db.DbPersistenceService.close(DbPersistenceService.java:146)
       ... 19 more
      
      




        • 1. Re: Unable to save ProcessInstance after adding RuntimeActio
          Glen Whitbeck Newbie

          Here is the result of this issue: Although I had extended the Action class in my Java code, I had not declared it in my Hibernate mapping files as a subclass to Action. Even though I had no intention of persisting this class outside of jBPM, I still had to create the subclass mapping for Hibernate's information.

          That's the short story. If you want more, read on.

          First of all, I tried another way of injecting actions into a process at runtime by doing the following:

          Node node = processDefinition.getNode(nodeName);
          node.setAction(action);
          


          The reason for trying this second way was that I was now injecting an "Action" class, rather than injecting a "RuntimeAction" class. Since the Hibernate exception message that I was receiving was "instance not of expected entity type: org.jbpm.graph.def.Action," I was hoping that injecting an org.jbpm.graph.def.Action class (Rather than a RuntimeAction) would solve the problem.

          However, I still received the same exception. Thus, the problem was not the fact that I was using RuntimeAction instead of Action. The message was telling me that my Action subclass wasn't of type Action, either.

          Since I knew my class was in fact extending Action (and that it was being accepted as a valid parameter to methods like node.setAction(...)), it seemed that the disconnect must lie within the Hibernate world. I suppose you could jump straight to the Hibernate mappings at this point, but sometimes you can't help but digging a little deeper.

          After looking at some Hibernate code, I found that my error was occuring because the following method (inside the AbstractEntityPersister class - which had been listed at the top of the exception stack trace) was returning null:
          private String getSubclassEntityName(Class clazz) {
           return (String) entityNameBySubclass.get(clazz);
          }
          

          Then, I found the following code fragment that is responsible for populating this entityNameBySubclass Map:
          iter = persistentClass.getSubclassIterator();
           while ( iter.hasNext() ) {
           PersistentClass pc = (PersistentClass) iter.next();
           entityNameBySubclass.put( pc.getMappedClass(), pc.getEntityName() );
           }
          

          Thanks to this clear coding, it was pretty apparent that Hibernate was expecting the subclass to be mapped to the superclass. From that point, it was just a matter of following the Hibernate mapping syntax for declaring a class to be a subclass of another class.

          Here was the solution:
          First, create a mapping file for your subclass (even if you don't intend to persist it outside of jBPM). The file should have code like the following:
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE hibernate-mapping PUBLIC
           "-//Hibernate/Hibernate Mapping DTD//EN"
           "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
          
          <hibernate-mapping>
          
           <subclass name="my.package.name.MySubclassName" extends="org.jbpm.graph.def.Action">
           </subclass>
          
          </hibernate-mapping>
          

          This file needs to be placed where jBPM/Hibernate will be able to see it.

          Then, you need to tell Hibernate to use this mapping file. Add the following line (using whatever you named the mapping file) inside your hibernate.cfg.xml file:
          <mapping resource="MySubclassName.hbm.xml"/>
          



          • 2. Re: Unable to save ProcessInstance after adding RuntimeActio
            Glen Whitbeck Newbie

            Whoops! I didn't include the discriminator from the subclass mapping file.

            Try this:

            <?xml version="1.0" encoding="UTF-8"?>
            <!DOCTYPE hibernate-mapping PUBLIC
             "-//Hibernate/Hibernate Mapping DTD//EN"
             "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
            
            <hibernate-mapping>
            
             <subclass name="my.package.name.MySubclassName" extends="org.jbpm.graph.def.Action" discriminator-value="D">
             </subclass>
            
            </hibernate-mapping>
            


            • 3. Re: Unable to save ProcessInstance after adding RuntimeActio
              Koen Aers Master

              Congrats for finding the solution yourself. And thanks for sharing it with the community... :-)

              Regards,
              Koen

              • 4. Re: Unable to save ProcessInstance after adding RuntimeActio
                Yuriy Lazarev Newbie

                Here is the exception that gets thrown when trying to save processInstance with RunrimeActions:

                org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: org.jbpm.graph.def.Action
                 at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219)
                 at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397)
                 at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:242)
                 at org.hibernate.type.TypeFactory.findDirty(TypeFactory.java:597)
                 at org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:3123)
                 at org.hibernate.event.def.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:479)
                 at org.hibernate.event.def.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:204)
                 at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:127)
                 at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)
                 at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
                 at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
                 at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
                 at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
                 at org.hibernate.transaction.CacheSynchronization.beforeCompletion(CacheSynchronization.java:59)
                 at com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:114)
                 at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:247)
                 at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:86)
                 at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:177)
                 at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1389)
                 ...


                I've solved this by adding cascade="all" attribute into RuntimeAction.hbm.xml

                <many-to-one name="action" cascade="all"column="ACTION_"
                foreign-key="FK_RTACTN_ACTION"
                index="IDX_RTACTN_ACTION" />


                Is it a bug in JBPM?

                • 5. Re: Unable to save ProcessInstance after adding RuntimeActio
                  Ronald van Kuijk Master

                  Maybe a stupid question, but since it is a runtime action, why not remove it before persisting?

                  • 6. Re: Unable to save ProcessInstance after adding RuntimeActio
                    Raoul Batista Newbie

                    To me this is a bug...
                    As far as I understand, adding a Runtime action means the action will be executed by the process instance to which it is added (and this is a great feature!).
                    The name Runtime comes from the fact it is used by a process instance unlike a process definition but it does not necessarily mean that the action should be executed in the same transaction as the one used to add it (to answer to the previous question: Why not to remove it before persisting...).

                    Anyway thanks for the fix and I think it will be great to see it fixed in future release unless I misunderstand the use of Runtime action (then please let me know...).

                    Arnaud.