8 Replies Latest reply on Dec 1, 2015 4:11 PM by tydalious

    Passing domain-specific objects to a domain-specific task in jbpm-console

    greg.moulds

      Hi all,

      I've been struggling with this for a few days and haven't had much luck. In jBPM 6 workbench running on jboss wildfly, I'm trying to invoke a custom work item handler and pass in an instance of a domain-specific object.

      Here's my process so far:

       

      1. Define a domain-specific class acme.datamodel.UserBio in an external jar: datamodel.jar.
      2. Upload "datamodel.jar" to jbpm-console via the "Artifact repository"->Upload feature.
      3. Create a new repository in jbpm-console and create a new jbpm project.
      4. Add datamodel.jar as a dependency to my jbpm project via the Project editor.
      5. Create a new process in the jbpm project.
      6. Create a new process variable of type acme.datamodel.UserBio.
      7. Create a new work item definition for my domain-specific task (Greeting.wid) like so:

         

        import org.drools.core.process.core.datatype.impl.type.StringDataType;
        import org.drools.core.process.core.datatype.impl.type.ObjectDataType;
        [
          [
            "name" : "Greeting",
            "parameters" : [
                "UserBio" : new ObjectDataType("acme.datamodel.UserBio")
            ],
            "results" : [
                "Result" : new ObjectDataType("java.util.Map")
            ],
            "displayName" : "Greeting",
            "icon" : "../../../global/defaultservicenodeicon.png"
          ]
        ]
        
      8. Add the domain-specific task (Greeting) to my process workflow.
      9. Map the process variable from step 5 as the input parameter for the Greeting task.
      10. Define my custom work item handler in an external jar (workitems.jar) like so:

         

        package acme.workitem.greeting;
        
        import java.util.Map;
        
        import org.kie.api.runtime.process.WorkItem;
        import org.kie.api.runtime.process.WorkItemManager;
        import org.kie.internal.runtime.StatefulKnowledgeSession;
        
        import acme.datamodel.UserBio;
        
        public class GreetingWorkItemHandler implements WorkItemHandler {
        
                private StatefulKnowledgeSession ksession;
        
            public GreetingWorkItemHandler(StatefulKnowledgeSession ksession) {
                    this.ksession = ksession;
                }
        
            public void executeWorkItem(WorkItem workItem, WorkItemManager workItemManager) {
        
                // Extract required parameters
                UserBio bio = (UserBio) workItem.getParameter("UserBio");
        
                // Do something
        
                // Notify manager that work item has been completed and return results
                Map<String, Object> results = new HashMap<String, Object>();
                workItemManager.completeWorkItem(workItem.getId(), results);
            }
        
            public void abortWorkItem(WorkItem workItem, WorkItemManager workItemManager) {
                // Can't abort a greeting work item handler
            }
        }
        
      11. Copy workitems.jar to the jbpm-installer/dependencies directory.
      12. Edit jbpm-installer/conf/META-INF/CustomWorkItemHandlers.conf and add the following line:

         

        "Greeting" : new acme.workitem.greeting.GreetingWorkItemHandler(ksession)
        
      13. Re-install jbpm demo (i.e. jbpm-console) and restart jboss.
      14. Load jbpm-console in a browser, start an instance of my process and attempt to invoke the domain-specific task.

       

      After all this, when the domain-specific task is loaded and the custom work item handler is invoked, I get the following error in jbpm-console but nothing displayed in my jboss log:

      Unexpected error encountered : java.lang.NoClassDefFoundError:acme/datamodel/UserBio

      Are custom work item handlers not loaded with the same classloader as the process instances that invoke them?

       

      I've tried adding the datamodel.jar into my workitems.jar under the META-INF/lib directory but that didn't seem to change anything.

       

      Do I need to add datamodel.jar as a dependency to the Greeting.wid? How do I define the location of the jar file when it's already been uploaded to jbpm-console? Do I just copy datamodel.jar to the dependencies directory like I did for the workitems.jar and re-install the jbpm-console? Won't this cause potential collisions with the version uploaded to the jbpm-console in step 2?

       

      These are just some of the things I've been wrestling with over the past few days. All the examples I've seen for dealing with custom work item handlers only ever seem to pass in primitives, never more complicated objects.

       

      If anyone can explain where I've gone wrong or point me to better examples I'd very much appreciate it.

        • 1. Re: Passing domain-specific objects to a domain-specific task in jbpm-console
          salaboy21

          Hi Greg,

          I think that you need to add to your datamodel.jar a kmodule.xml file inside the META-INF/ directory. That will make the classes inside it available in the class path..

          All the rest seems to be ok. I think that we are looking into different ways to avoid this limitation.

          • 2. Re: Passing domain-specific objects to a domain-specific task in jbpm-console
            greg.moulds

            Thanks Mauricio.

             

            I checked datamodel.jar and you were correct that there wasn't a kmodule.xml file inside the META-INF directory so I added one (just a basic one - all it contains is the kmodule tag), bumped the version up, rebuilt and uploaded to jbpm-console. I then deleted the dependency on the previous datamodel jar and updated the project to point to the new version. I started a new process instance and invoked the task but without success. This was the output:


            Unexpected error encountered : java.lang.ClassNotFoundException:acme.datamodel.UserBio from [Module "deployment.jbpm-console.war:main" from Service Module Loader]

             

            Slightly different error message but still amounting to the same thing?

             



            • 3. Re: Passing domain-specific objects to a domain-specific task in jbpm-console
              salaboy21

              So the project that contains the business process depends on the datamodel.jar that now contains the kmodule.xml file? hmm I think that you also need to add an empty beans.xml file for the CDI scanning of the jar

              • 4. Re: Passing domain-specific objects to a domain-specific task in jbpm-console
                greg.moulds

                That's right. I created the project that contains the business process inside the console so it had a kmodule.xml file already. As per your suggestion, I added an empty beans.xml to my datamodel project (built in eclipse) under META-INF, bumped the version, uploaded to the console repository and updated my business process project to depend on the new datamodel jar. Rebuilt, deployed and started a new process instance and still got the same error. After this, I added an empty beans.xml to my custom work item handler project (under META-INF) to see if that would help but after redeploying to wildfly that didn't work -- same error. As a last resort, I added the same beans.xml file to the business process project and tried again with similar results.

                 

                Here are the contents of my 2 eclipse projects as they currently stand:

                Greg:switchyard-kepler gregmoulds$ jar tf datamodel/target/datamodel-0.0.4-SNAPSHOT.jar

                META-INF/

                META-INF/MANIFEST.MF

                acme/

                acme/datamodel/

                acme/datamodel/UserBio.class

                META-INF/beans.xml

                META-INF/kmodule.xml

                META-INF/maven/

                META-INF/maven/acme/

                META-INF/maven/acme/datamodel/

                META-INF/maven/acme/datamodel/pom.xml

                META-INF/maven/acme/datamodel/pom.properties

                Greg:switchyard-kepler gregmoulds$ jar tf jbpm-custom-workitems/target/jbpm-custom-workitems-0.0.4-SNAPSHOT.jar

                META-INF/

                META-INF/MANIFEST.MF

                acme/

                acme/workitem/

                acme/workitem/greeting/

                acme/workitem/greeting/GreetingWorkItemHandler.class

                META-INF/beans.xml

                META-INF/kmodule.xml

                META-INF/maven/

                META-INF/maven/acme/

                META-INF/maven/acme/jbpm-custom-workitems/

                META-INF/maven/acme/jbpm-custom-workitems/pom.xml

                META-INF/maven/acme/jbpm-custom-workitems/pom.properties

                • 5. Re: Passing domain-specific objects to a domain-specific task in jbpm-console
                  greg.moulds

                  So I have had some success by copying the datamodel.jar to the dependencies folder (along with the workitems.jar) and re-deploying the jbpm-console. It seems odd to me that I have to deploy the same jar in 2 different places but maybe that's by design...? Anyway, I'm back in business so thanks for the help Mauricio.

                  • 6. Re: Re: Passing domain-specific objects to a domain-specific task in jbpm-console
                    aha001

                    Hi Mauricio,

                     

                    I created a custom REST servive task WorkItemahandler. Following the same step as specified by Greg above. Also from cuyum/jbpm-wih · GitHub

                     

                    Then

                    (1) from kie-workbench console, I create a new project

                    (2) switch to "repository view" and navigate to src/main/resources/META-INF

                    (3) select "New Item/Uploaded File" and upload the "CustomWorkItem.wid" file

                         

                        import org.drools.core.process.core.datatype.impl.type.StringDataType;

                         import org.drools.core.process.core.datatype.impl.type.ObjectDataType;

                         import org.drools.core.process.core.datatype.impl.type.BooleanDataType;

                         [

                                [

                                  "name" : "CustomREST",

                                  "parameters" : [

                                      "Url" : new StringDataType(),

                                      "Method" : new StringDataType(),

                                      "Body" : new StringDataType(),

                                      "ConnectTimeout" : new StringDataType(),

                                      "ReadTimeout" : new StringDataType(),

                                      "Username" : new StringDataType(),

                                      "Password" : new StringDataType(),

                                      "ContentType" : new StringDataType(),

                                      "Authorization" : new StringDataType(),

                                      "IgnoreSSL" : new BooleanDataType()

                                  ],

                                  "results" : [

                                      "Result" : new ObjectDataType(),

                                  ],

                                  "displayName" : "CustomREST",

                                  "icon" : "icons/restlogo.png"

                                ]

                         ]

                     

                    (4) create a new "Business Process", from the process designer in the palette on the left in "Service Tasks" the activity should appear CustomREST, but for some reason I can't see this icon.


                    Can you or anyone please advise what have I missed out? your help will be much appreciated.

                    • 7. Re: Re: Passing domain-specific objects to a domain-specific task in jbpm-console
                      aha001

                      I have resolved this issue. I loaded the CustomWorkItem.wid file to the wrong directory. It should be loaded into src/main/resources instead of src/main/resources.

                      • 8. Re: Passing domain-specific objects to a domain-specific task in jbpm-console
                        tydalious

                        Greg,

                            Any help on a similar issue? Went through the steps you did on yours, but my "workitem.jar" is my jenkinsWorkItemHandler.jar which has a dependency of the jersey-client. I'm just trying to setup a service task that can do basic jenkins actions. The handler also has a basic httpAuthenticationFeature in it (hence this error):

                        Unexpected error encountered : java.lang.ClassNotFoundException:org.glassfish.jersey.client.authentication.HttpAuthenticationFeature from [Module "deployment.jbpm-console.war:main" from Service Module Loader]

                        Currently my jbpm-installer/dependencies folder is such-

                             jenkinsWIH.jar

                         

                        which has a pom.xml of:

                        <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                            <modelVersion>4.0.0</modelVersion>
                            <groupId>com.company.jbpm.workitemhandler</groupId>
                            <artifactId>jenkinsWIH</artifactId>
                            <version>1.0</version>
                            <name>jenkinsWIH</name>
                        
                            <dependencies>
                                <dependency>
                                    <groupId>org.jbpm</groupId>
                                    <artifactId>jbpm-flow</artifactId>
                                    <version>6.0.1.Final</version>
                                    <scope>provided</scope>
                                </dependency>
                                <dependency>
                                    <groupId>org.glassfish.jersey.core</groupId>
                                    <artifactId>jersey-client</artifactId>
                                    <version>2.22.1</version>
                                </dependency>
                                <dependency>
                                    <groupId>javax.ws.rs</groupId>
                                    <artifactId>javax.ws.rs-api</artifactId>
                                    <version>2.0.1</version>
                                </dependency>
                            </dependencies>
                        </project>
                        

                         

                        From what I can figure out, it seems like its just not pulling the org.glassfish jar down. I've tried throwing the jersey-client jar into the dependencies folder and rebuilding jbpm/restarting but that only produced more verification warnings, and a new error of a ClassNotFound thats for the next jar needed, the jersey-common. So all in all, the ClassNotFound errors are against classes I am using, but should be part of the dependencies I've included in my pom.xml. I have tested all the code in a separate java project outside of jbpm and it works fine.

                         

                        Is there anything you figured out since this solution that might help?

                        (Old post I know, but exactly the issue I'm having, hoping there's still someone watching :/)