6 Replies Latest reply on Mar 8, 2007 10:45 AM by estaub

    Strange class cast exception when changing node assignment

    ij_dett

      Hello folks,

      I discovered a strange class cast exception when evaluating jBPM and its starter kit. My team and me want to use jBPM in a scenario where the workflow engine is controlling other external processes.

      Due to certain project requirements we need to extend the functionality of class org.jbpm.graph.def.Node by subclassing it. Following the jBPM documentation I applied the necesarry changes to the sources in the starter kit, rebuilded and redeployed the jBPM service archive and the web application.

      Here are the steps I performed:

      Step 1: Changed file jbpm.cfg.xml to reference a different node types mapping file

      <jbpm-configuration>
      
       <!--
       This configuration is used when there is no jbpm.cfg.xml file found in the
       root of the classpath. It is a very basic configuration without persistence
       and message services. Only the authorization service installed.
       You can parse and create processes, but when you try to use one of the
       unavailable services, you'll get an exception.
       -->
      
       <jbpm-context>
       <service name="persistence" factory="org.jbpm.persistence.db.DbPersistenceServiceFactory" />
       <service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />
       <service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
       <service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
       <service name="authentication" factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
       </jbpm-context>
      
       <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
       <string name="resource.hibernate.cfg.xml" value="hibernate.cfg.xml" />
       <string name="resource.business.calendar" value="org/jbpm/calendar/jbpm.business.calendar.properties" />
       <string name="resource.default.modules" value="org/jbpm/graph/def/jbpm.default.modules.properties" />
       <string name="resource.converter" value="org/jbpm/db/hibernate/jbpm.converter.properties" />
       <string name="resource.action.types" value="org/jbpm/graph/action/action.types.xml" />
       <string name="resource.node.types" value="de/webde/geppi/node.types.xml" />
       <string name="resource.parsers" value="org/jbpm/jpdl/par/jbpm.parsers.xml" />
       <string name="resource.varmapping" value="org/jbpm/context/exe/jbpm.varmapping.xml" />
      
       <long name="jbpm.msg.wait.timout" value="5000" singleton="true" />
       <int name="jbpm.byte.block.size" value="1024" singleton="true" />
       <string name="mail.smtp.host" value="localhost" />
       <bean name="jbpm.task.instance.factory" class="org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl" singleton="true" />
       <bean name="jbpm.variable.resolver" class="org.jbpm.jpdl.el.impl.JbpmVariableResolver" singleton="true" />
       <bean name="jbpm.mail.address.resolver" class="org.jbpm.identity.mail.IdentityAddressResolver" singleton="true" />
      
      </jbpm-configuration>
      


      The important line is
      <string name="resource.action.types" value="org/jbpm/graph/action/action.types.xml" />
      


      Step 2: Added the alternative node mapping configuration
      <node-types>
       <node-type element="start-state" class="org.jbpm.graph.node.StartState" />
       <node-type element="end-state" class="org.jbpm.graph.node.EndState" />
       <node-type element="node" class="de.webde.geppi.GeppiNode" />
       <node-type element="state" class="org.jbpm.graph.node.State" />
       <node-type element="task-node" class="org.jbpm.graph.node.TaskNode" />
       <node-type element="fork" class="org.jbpm.graph.node.Fork" />
       <node-type element="join" class="org.jbpm.graph.node.Join" />
       <node-type element="decision" class="org.jbpm.graph.node.Decision" />
       <node-type element="process-state" class="org.jbpm.graph.node.ProcessState" />
       <node-type element="super-state" class="org.jbpm.graph.def.SuperState" />
       <node-type element="merge" class="org.jbpm.graph.node.Merge" />
       <node-type element="milestone-node" class="org.jbpm.graph.node.MilestoneNode" />
       <node-type element="interleave-start" class="org.jbpm.graph.node.InterleaveStart" />
       <node-type element="interleave-end" class="org.jbpm.graph.node.InterleaveEnd" />
       <node-type element="page" class="org.jboss.seam.pageflow.Page" />
       <node-type element="start-page" class="org.jboss.seam.pageflow.Page" />
      </node-types>
      


      The important line switching the implementation from class org.jbpm.graph.def.Node to class de.webde.geppi.GeppiNode (Geppi is the system under development) is
       <node-type element="node" class="de.webde.geppi.GeppiNode" />
      


      Step 3: Implemented class de.webde.geppi.GeppiNode and its corresponding hibernate ORM configuration file.

      Besides some additional console printing the current implementation of class de.webde.geppi.GeppiNode does not do anything different than class org.jbpm.graph.def.Node. The functional extension will be done later.
      /**
       *
       */
      package de.webde.geppi;
      
      import org.jbpm.graph.def.Node;
      import org.jbpm.graph.exe.ExecutionContext;
      
      /**
       *
       */
      public class GeppiNode extends Node {
      
       /**
       *
       */
       private static final long serialVersionUID = -3137521269855001903L;
      
       /* (non-Javadoc)
       * @see org.jbpm.graph.def.Node#execute(org.jbpm.graph.exe.ExecutionContext)
       */
       public void execute(ExecutionContext executionContext) {
       // if there is a custom action associated with this node
       if (action != null) {
       try {
       System.out.println("+++++++++++++ Execute Action +++++++++++++");
       // execute the action
       action.execute(executionContext);
       System.out.println("+++++++++++++ Finished Action +++++++++++++");
      
       } catch (Exception exception) {
       // NOTE that Error's are not caught because that might halt the
       // JVM and mask the original Error.
       // search for an exception handler or throw to the client
       raiseException(exception, executionContext);
       }
      
       } else {
       // let this node handle the token
       // the default behaviour is to leave the node over the default
       // transition.
       leave(executionContext);
       }
       }
      
      }
      

      <?xml version="1.0"?>
      <!DOCTYPE hibernate-mapping PUBLIC
       "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
       "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
      
      <hibernate-mapping default-access="field" >
       <subclass name="de.webde.geppi.GeppiNode"
       discriminator-value="G"
       extends="org.jbpm.graph.def.Node"/>
      </hibernate-mapping>
      


      Step 4: Updated the Hibernate configuration to include a mapping for class de.webde.geppi.GeppiNode
      <?xml version='1.0' encoding='utf-8'?>
      
      <!DOCTYPE hibernate-configuration PUBLIC
       "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
       "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
      
      <hibernate-configuration>
       <session-factory>
      
       <!-- jdbc connection properties -->
       <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
       <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
       <property name="hibernate.connection.url">jdbc:hsqldb:mem:.;sql.enforce_strict_size=true</property>
       <property name="hibernate.connection.username">sa</property>
       <property name="hibernate.connection.password"></property>
      
       <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
      
       <!-- other hibernate properties
       <property name="hibernate.show_sql">true</property>
       <property name="hibernate.format_sql">true</property>
       <property name="hibernate.use_sql_comments">true</property>
       -->
      
       <!-- ############################################ -->
       <!-- # mapping files with external dependencies # -->
       <!-- ############################################ -->
      
       <!-- following mapping file has a dependendy on -->
       <!-- 'bsh-{version}.jar'. -->
       <!-- uncomment this if you don't have bsh on your -->
       <!-- classpath. you won't be able to use the -->
       <!-- script element in process definition files -->
       <mapping resource="org/jbpm/graph/action/Script.hbm.xml"/>
      
       <!-- following mapping files have a dependendy on -->
       <!-- 'jbpm-identity-{version}.jar', mapping files -->
       <!-- of the pluggable jbpm identity component. -->
       <!-- comment out the following 3 lines if you don't-->
       <!-- want to use the default jBPM identity mgmgt -->
       <!-- component -->
       <mapping resource="org/jbpm/identity/User.hbm.xml"/>
       <mapping resource="org/jbpm/identity/Group.hbm.xml"/>
       <mapping resource="org/jbpm/identity/Membership.hbm.xml"/>
      
       <!-- ###################### -->
       <!-- # jbpm mapping files # -->
       <!-- ###################### -->
      
       <!-- hql queries and type defs -->
       <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />
      
       <!-- graph.def mapping files -->
       <mapping resource="org/jbpm/graph/def/ProcessDefinition.hbm.xml"/>
       <mapping resource="org/jbpm/graph/def/Node.hbm.xml"/>
       <mapping resource="org/jbpm/graph/def/Transition.hbm.xml"/>
       <mapping resource="org/jbpm/graph/def/Event.hbm.xml"/>
       <mapping resource="org/jbpm/graph/def/Action.hbm.xml"/>
       <mapping resource="org/jbpm/graph/def/SuperState.hbm.xml"/>
       <mapping resource="org/jbpm/graph/def/ExceptionHandler.hbm.xml"/>
       <mapping resource="org/jbpm/instantiation/Delegation.hbm.xml"/>
      
       <!-- Geppi mapping files -->
       <mapping resource="de/webde/geppi/GeppiNode.hbm.xml"/>
      
      
       <!-- graph.node mapping files -->
       <mapping resource="org/jbpm/graph/node/StartState.hbm.xml"/>
       <mapping resource="org/jbpm/graph/node/EndState.hbm.xml"/>
       <mapping resource="org/jbpm/graph/node/ProcessState.hbm.xml"/>
       <mapping resource="org/jbpm/graph/node/Decision.hbm.xml"/>
       <mapping resource="org/jbpm/graph/node/Fork.hbm.xml"/>
       <mapping resource="org/jbpm/graph/node/Join.hbm.xml"/>
       <mapping resource="org/jbpm/graph/node/State.hbm.xml"/>
       <mapping resource="org/jbpm/graph/node/TaskNode.hbm.xml"/>
      
       <!-- context.def mapping files -->
       <mapping resource="org/jbpm/context/def/ContextDefinition.hbm.xml"/>
       <mapping resource="org/jbpm/context/def/VariableAccess.hbm.xml"/>
      
       <!-- taskmgmt.def mapping files -->
       <mapping resource="org/jbpm/taskmgmt/def/TaskMgmtDefinition.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/def/Swimlane.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/def/Task.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/def/TaskController.hbm.xml"/>
      
       <!-- module.def mapping files -->
       <mapping resource="org/jbpm/module/def/ModuleDefinition.hbm.xml"/>
      
       <!-- bytes mapping files -->
       <mapping resource="org/jbpm/bytes/ByteArray.hbm.xml"/>
      
       <!-- file.def mapping files -->
       <mapping resource="org/jbpm/file/def/FileDefinition.hbm.xml"/>
      
       <!-- scheduler.def mapping files -->
       <mapping resource="org/jbpm/scheduler/def/CreateTimerAction.hbm.xml"/>
       <mapping resource="org/jbpm/scheduler/def/CancelTimerAction.hbm.xml"/>
      
       <!-- graph.exe mapping files -->
       <mapping resource="org/jbpm/graph/exe/Comment.hbm.xml"/>
       <mapping resource="org/jbpm/graph/exe/ProcessInstance.hbm.xml"/>
       <mapping resource="org/jbpm/graph/exe/Token.hbm.xml"/>
       <mapping resource="org/jbpm/graph/exe/RuntimeAction.hbm.xml"/>
      
       <!-- module.exe mapping files -->
       <mapping resource="org/jbpm/module/exe/ModuleInstance.hbm.xml"/>
      
       <!-- context.exe mapping files -->
       <mapping resource="org/jbpm/context/exe/ContextInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/TokenVariableMap.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/VariableInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/variableinstance/ByteArrayInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/variableinstance/DateInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/variableinstance/DoubleInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/variableinstance/HibernateLongInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/variableinstance/HibernateStringInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/variableinstance/LongInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/variableinstance/NullInstance.hbm.xml"/>
       <mapping resource="org/jbpm/context/exe/variableinstance/StringInstance.hbm.xml"/>
      
       <!-- msg.db mapping files -->
       <mapping resource="org/jbpm/msg/Message.hbm.xml"/>
       <mapping resource="org/jbpm/msg/db/TextMessage.hbm.xml"/>
       <mapping resource="org/jbpm/command/ExecuteActionCommand.hbm.xml"/>
       <mapping resource="org/jbpm/command/ExecuteNodeCommand.hbm.xml"/>
       <mapping resource="org/jbpm/command/SignalCommand.hbm.xml"/>
       <mapping resource="org/jbpm/command/TaskInstanceEndCommand.hbm.xml"/>
      
       <!-- taskmgmt.exe mapping files -->
       <mapping resource="org/jbpm/taskmgmt/exe/TaskMgmtInstance.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/exe/TaskInstance.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/exe/PooledActor.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/exe/SwimlaneInstance.hbm.xml"/>
      
       <!-- scheduler.exe mapping files -->
       <mapping resource="org/jbpm/scheduler/exe/Timer.hbm.xml"/>
      
       <!-- logging mapping files -->
       <mapping resource="org/jbpm/logging/log/ProcessLog.hbm.xml"/>
       <mapping resource="org/jbpm/logging/log/MessageLog.hbm.xml"/>
       <mapping resource="org/jbpm/logging/log/CompositeLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/ActionLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/NodeLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/ProcessInstanceCreateLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/ProcessInstanceEndLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/ProcessStateLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/SignalLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/TokenCreateLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/TokenEndLog.hbm.xml"/>
       <mapping resource="org/jbpm/graph/log/TransitionLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/VariableLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/VariableCreateLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/VariableDeleteLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/VariableUpdateLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/variableinstance/ByteArrayUpdateLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/variableinstance/DateUpdateLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/variableinstance/DoubleUpdateLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/variableinstance/HibernateLongUpdateLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/variableinstance/HibernateStringUpdateLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/variableinstance/LongUpdateLog.hbm.xml"/>
       <mapping resource="org/jbpm/context/log/variableinstance/StringUpdateLog.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/log/TaskLog.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/log/TaskCreateLog.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/log/TaskAssignLog.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/log/TaskEndLog.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/log/SwimlaneLog.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml"/>
       <mapping resource="org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml"/>
      
       </session-factory>
      </hibernate-configuration>
      


      The lines adding the additional mapping are
       <!-- Geppi mapping files -->
       <mapping resource="de/webde/geppi/GeppiNode.hbm.xml"/>
      
      


      By the way, node.types.xml, GeppiNode.java and GeppiNode.hbm.xml were added in package de.webde.geppi in directory src/java.jbpm.

      So far so good - Using the starter kits build file I could build and deploy the updated service archive.

      But when starting the starter kit's JBoss I detected a strange error log on its console:
      13:57:26,300 INFO [SchedulerThread] runtime exception while executing timers
      org.hibernate.HibernateException: Could not parse configuration: hibernate.cfg.xml
       at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1494)
       at org.hibernate.cfg.Configuration.configure(Configuration.java:1428)
       at org.jbpm.db.hibernate.HibernateHelper.createConfiguration(HibernateHelper.java:90)
       at org.jbpm.persistence.db.DbPersistenceServiceFactory.getConfiguration(DbPersistenceServiceFactory.java:68)
       at org.jbpm.persistence.db.DbPersistenceServiceFactory.getSessionFactory(DbPersistenceServiceFactory.java:90)
       at org.jbpm.persistence.db.DbPersistenceService.getSessionFactory(DbPersistenceService.java:74)
       at org.jbpm.persistence.db.DbPersistenceService.getSession(DbPersistenceService.java:78)
       at org.jbpm.persistence.db.DbPersistenceService.getSchedulerSession(DbPersistenceService.java:254)
       at org.jbpm.JbpmContext.getSchedulerSession(JbpmContext.java:529)
       at org.jbpm.scheduler.impl.SchedulerThread.executeTimers(SchedulerThread.java:104)
       at org.jbpm.scheduler.impl.SchedulerThread.run(SchedulerThread.java:71)
      Caused by: org.dom4j.DocumentException: org.dom4j.DocumentFactory Nested exception: org.dom4j.DocumentFactory
       at org.dom4j.io.SAXReader.read(SAXReader.java:484)
       at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1484)
       ... 10 more
      


      To me a parsing exception looked odd since file hibernate.cfg.xml does not contain any syntax errors and the added line was correct.

      To really find out what is going on I started a remote debugging session and discovered the following:

      The root cause is a class cast exception I really do not understand:
      ClassCastException: Cannot cast org.dom4j.DocumentFactory (id=169) to org.dom4j.DocumentFactory
      


      It is originated in the static method getInstance() of class org.dom4j.DocumentFactory provided by the Dom4J library
       public static synchronized DocumentFactory getInstance() {
       if (singleton == null) {
       singleton = createSingleton();
       }
       return (DocumentFactory) singleton.instance();
       }
      


      The class cast exception is thrown in the last line of this method. The stack trace looks like this:
      Thread [JbpmCommandExecutor] (Suspended)
       DocumentFactory.getInstance() line: 97
       SAXReader.getDocumentFactory() line: 645
       SAXReader.createContentHandler(XMLReader) line: 969
       SAXReader.read(InputSource) line: 449
       Configuration.doConfigure(InputStream, String) line: 1484
       Configuration.configure(String) line: 1428
       HibernateHelper.createConfiguration(String, String) line: 90
       DbPersistenceServiceFactory.getConfiguration() line: 68
       DbPersistenceServiceFactory.getSessionFactory() line: 90
       DbPersistenceService.getSessionFactory() line: 74
       DbPersistenceService.getSession() line: 78
       DbPersistenceService.getMessagingSession() line: 245
       JbpmContext.getMessagingSession() line: 521
       DbMessageService.<init>() line: 49
       DbMessageServiceFactory.openService() line: 32
       Services.getService(String) line: 136
       Services.getMessageService() line: 172
       CommandExecutorThread.executeCommand() line: 122
       CommandExecutorThread.run() line: 83
      


      Does anybody have an idea what exactly is going on here?
      IMHO I think I got trapped in JBoss' class loader hell, but currently I have no clue how to solve this issue. I know I am guessing, but ...

      Any help and hints are appreciated ... Thanks in advance ...

      Cheers,
      Ingo

        • 1. Re: Strange class cast exception when changing node assignme
          kukeltje

          more than one dom4j on the classpath?

          • 2. Re: Strange class cast exception when changing node assignme
            kukeltje

            Usually there is more than one dom4j on the classpath. But can you elaborate a little on

            Due to certain project requirements we need to extend the functionality of class org.jbpm.graph.def.Node


            There might be other options

            • 3. Re: Re: Strange class cast exception when changing node assi
              ij_dett

              I already checked that. The only dom4j.jar present is located in directory C:\jbpm-starters-kit-3.1.3\jbpm-server\server\jbpm\lib.

              So multiple dom4j.jar files in the class path is not the reason for this strange class cast exception. The question remains - what could be the reason? :-(

              Cheers,
              Ingo

              • 4. Re: Strange class cast exception when changing node assignme

                Ingo,

                Can you get to the point it's breaking with a debugger, or can you insert code there?

                It's hard for me to picture a classloader problem in this scenario, but that may just be my lack of imagination.
                Have you looked at DocumentFactory.instance() to see what it does?
                Is it possible that you have a configuration problem where it really is returning an object of the wrong class?


                public static synchronized DocumentFactory getInstance() {
                 if (singleton == null) {
                 singleton = createSingleton();
                 }
                 return (DocumentFactory) singleton.instance();
                 }


                -Ed Staub

                • 5. Re: Strange class cast exception when changing node assignme
                  ij_dett

                  Hi Ed,

                  as I mentioned before the class cast exception is thrown in the last line of this method. It is thrown in the explicit casting statement - the expression being returned.

                  return (DocumentFactory) singleton.instance();


                  And here comes the funny part:

                  If I inspect
                  singleton.instance()
                  in the debugger, I see and can access a valid instance of org.dom4j.DocumentFactory.

                  If I inspect
                  (DocumentFactory) singleton.instance()
                  the class cast exception is thrown - even in the debugger.

                  I simply do not understand this behavior ...

                  Cheers,
                  Ingo

                  • 6. Re: Strange class cast exception when changing node assignme

                    Ingo,

                    If you're right about it being a classloader problem, then I think this would fail in getInstance:

                    assert(DocumentFactory.class == singleton.instance.getClass());

                    -Ed Staub