6 Replies Latest reply on Oct 13, 2011 3:30 AM by Chris Melas

    How to Complete a WorkItem using REST API

    analamala Newbie

      Hi,

      I am planning on extending the REST API to "complete" a work item asynchronously. For example, if I add this to "org.jboss.bpm.console.server.ProcessMgmtFacade" or some other (Task, Form, or something else) existing facade implementations, here are my questions.

       

      1. What information do I need to pass as @FormParam or @PathParam, to be able to complete the work item.
      2. How do I use these parameters inside the method signature, to complete the work item.

       

      There may be other approaches, but I found this option the easiest implement, if it can be done. Once I know what parameters are needed and they can be used to complete, I will incorporate this into my design such that the calling client (REST API client) is already aware of what to pass into the REST API call.

       

      Your feedback will be invaluable.

       

      Thank you.

      -Ajay

        • 1. Re: How to Complete a WorkItem using REST API
          analamala Newbie

          Hi,

          Adding to my previous post, the context of my previous question is to be able to accomplish the equivalent of what the following code does.

           

          manager.completeWorkItem(workItem.getId(), null);

           

          The challenge I have is how to re-build the manager object using the REST API.

           

          Experts, please let me know if my approach doesn't work. That way it will help me focus on a different approach.

           

          Thank you.

          -Ajay

          • 2. Re: How to Complete a WorkItem using REST API
            Chris Melas Apprentice

            Hello,

            You can ask the knowledge session to complete a workitem by calling,

            ksession.getWorkItemManager().completeWorkItem(workItemId, null);//or instead of null a map of parameters

             

            So workItemId would have to be passed to your web service and the web service will have to be part of the system that contains the jbpm engine in order to have access to the knowledge session.

             

            Whether this approach is correct or not depends on your problem domain, we can discuss it if you want.

            Generally I believe it is a fine method for delegating the responsibility of managing the workings of the engine to some engine related controller. I actually use such approach as part of a generic, implemenation independent framework.

            • 3. Re: How to Complete a WorkItem using REST API
              analamala Newbie

              Hello Chris,

              Thanks a lot for your time. Let me step back and thank you sincerely for the information that you posted at http://community.jboss.org/thread/166755?start=0&tstart=0. Really helped in many ways.

               

              Coming back to this discussion, I made some progress on how to complete a workitem by extending the REST API. To answer your question, yes, the code that I modified, is running in the context of a jbpm engine. Here are my changes to "gwt-console-server\WEB-INF\classes\org\jboss\bpm\console\server\ProcessMgmtFacade.java"

               

              @POST

                  @Path("definition/{id}/ctr_component_complete")

                  @Produces("application/json")

                  public Response ctrComponentComplete(@PathParam("id") String definitionId,

                          @FormParam("sessionID") String sessionID,

                          @FormParam("workitemID") String workItemID) {

                      try {

                          EntityManagerFactory emf = Persistence

                                  .createEntityManagerFactory("org.jbpm.persistence.jpa");

                          Environment env = KnowledgeBaseFactory.newEnvironment();

                          env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf);

                          env.set(EnvironmentName.TRANSACTION_MANAGER,

                                  TransactionManagerServices.getTransactionManager());

                          UserTransaction ut = (UserTransaction) new InitialContext()

                                  .lookup("java:comp/UserTransaction");

                          env.set(EnvironmentName.TRANSACTION, ut);

                          StatefulKnowledgeSession ksession = JPAKnowledgeService

                                  .loadStatefulKnowledgeSession(

                                          new Integer(sessionID).intValue(),

                                          readKnowledgeBase(), null, env);

                          ksession.getWorkItemManager().completeWorkItem(

                                  new Long(workItemID).longValue(), null);

                         

                          ksession.dispose();

                         

                          return createJsonResponse("SUCCESS");

                      } catch (Throwable t) {

                          throw new WebApplicationException(t, 500);

                      }

                  }

               

               

              private KnowledgeBase readKnowledgeBase() throws Exception {

                      KnowledgeBuilder kbuilder = KnowledgeBuilderFactory

                              .newKnowledgeBuilder();

                      kbuilder.add(ResourceFactory.newClassPathResource("CTRWorkflow.bpmn"),

                              ResourceType.BPMN2);

                      KnowledgeBase kbase = kbuilder.newKnowledgeBase();

                      kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());

                      return kbase;

              }

               

              As you can see, I am creating a new KnowledgeBase and using the parameters, 'sessionID' and 'workitemID' to accomplish this. Everything seems to be working as per the process design. However, in the jbpm-console web application's 'Process Overview' tab, the process instance is still showing up as 'RUNNING', eventhough I can see from the logs that the process has reached the end state. To confirm that the state is not changing, I clicked on the 'Diagram' button to bring up the process image. The image is coming up but not updating with the latest state.

               

              I have a feeling that this issue may be related to creating a new KnowledgeBase rather than using the existing KnowledgeBase that JBPM engine and the jbpm-console web application share. To mitigate this problem, I tried to get a reference to the KnowledgeBase by trying several alternate approaches. But by these alternate approaches, I am not able to even complete the workitem. Here is one of the approaches that I tried.

               

              private KnowledgeBase readKnowledgeBase() throws Exception {

                      KnowledgeBase kbase = null;

                      try {

                          ResourceChangeScannerConfiguration sconf = ResourceFactory.getResourceChangeScannerService().newResourceChangeScannerConfiguration();

                          sconf.setProperty( "drools.resource.scanner.interval", "10" );

                          ResourceFactory.getResourceChangeScannerService().configure( sconf );

                          ResourceFactory.getResourceChangeScannerService().start();

                          ResourceFactory.getResourceChangeNotifierService().start();

                          KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();

                          aconf.setProperty("drools.agent.newInstance", "false");

                          KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent("Guvnor default", aconf);

                          kagent.applyChangeSet(ResourceFactory.newClassPathResource("ChangeSet.xml"));

                          kbase = kagent.getKnowledgeBase();

                          for (org.drools.definition.process.Process process: kbase.getProcesses()) {

                              log.debug("Loading process from Guvnor: " + process.getId());

                          }

                      } catch (Throwable t) {

                          if (t instanceof RuntimeException

                                  && "KnowledgeAgent exception while trying to deserialize".equals(t.getMessage())) {

                              log.error("Could not connect to guvnor");

                              if (t.getCause() != null) {

                                  System.out.println(t.getCause().getMessage());

                              }

                          }

                          log.error("Could not load processes from guvnor: " + t.getMessage());

                          t.printStackTrace();

                      }

                      if (kbase == null) {

                          System.out.println("kbase is NULL");

                          kbase = KnowledgeBaseFactory.newKnowledgeBase();

                      }

                      return kbase;

                  }

               

              Here is my process image. Note that both the domain specific service tasks, 'Extraction' & 'Validation' operate in asynchronous mode, by EXCLUDING the "manager.completeWorkItem(workItem.getId(), null);" call.

               

              CTRworkflow.png

              I really apreciate your time on this and hope this discussion helps few more community members out there.

               

              Thank you.

              -Ajay

              • 4. Re: How to Complete a WorkItem using REST API
                Chris Melas Apprentice

                Hi,

                I'm very glad to have assisted in any way and thank you very much for your kind words.

                 

                If you use jbpm-gwt-console-server then you should use the knowledge session provided by the console-server, as you have noticed.

                 

                At first glance things are not very flexible, but there is a way of extending them in a little dirty approach.

                 

                To understand the context, the jbpm-gwt-console-server provides a REST API by communicating with the jbpm5 API through some integration classes.

                The REST API classes as you have spoted are inside the jbpm-gwt-console-server.war and the integration classes are in org.jbpm.integration.console package of the jbpm5 sources. So in your case inside ProcessMgmtFacade (from the REST API) there is a field processManagement of type org.jboss.bpm.console.server.integration.ProcessManagement, which has another field called delegate of type org.jbpm.integration.console.Delegate. The delegate field initialises all jbpm5 knowledge related objects, such as the ksession (StatefulKnowledgeSession). So this is what you need....

                The problem is that it's private and all accessors are either private or not really what we could use to extend and somehow get hold of delegate and ksession etc.

                 

                So the approaches are,

                1. Edit the other classes , or inherit from them (i.e. the integration classes) and expose the field/methods that would provide what is needed. Then provide the new implementations in the already edited ProcessMgmtFacade .

                2. Create your own implementation/use of all i.e. your own web services, instantiated as in jbpm-gwt-console-server etc so in other words create your own little system/REST API with jbpm-gwt-console-server as a reference implementation.

                3. Use the dirty old reflection in an inappropriate manner .... as i show below  

                 

                So the dirty approach is to use reflection accessing private method/fields

                i.e.

                //inside ProcessMgmtFacade in your web method (this is the idea, haven't tested this code i write it here directly....)

                 

                Field fields[] =this.getClass().getDeclaredFields();

                for(Field field : fields){

                if(field.getName().equals("delegate")){

                field.setAccessible(true);

                field.get(this);//this will return the delegate object, so you can do the same to retrieve the ksession field

                }

                }

                 

                Generally, if there is no other way of doing it properly i.e. via inheritance, or accessing some other object that will eventually give access to ksession , then the author of this code did not intend to give such access and one has to provide his own implementation, possibly by reusing code etc as stated in approaches 1 and 2.

                • 5. Re: How to Complete a WorkItem using REST API
                  analamala Newbie

                  Thank you Chris. Your feedback is again very valuable. I took approach 1 and everything worked perfectly at the end. Here is what I did.

                   

                  • gwt-console-server\WEB-INF\lib\jbpm-gwt-core-5.1.0.Final.jar\org\jbpm\integration\console\CommandDelegate.java. Added the following method.

                  public void completeWorkItem(String workItemId,

                              Map<String,Object> results) {

                          ksession.getWorkItemManager().completeWorkItem(

                                  new Long(workItemId).longValue(), results);

                      }

                   

                  • gwt-console-server\WEB-INF\lib\jbpm-gwt-core-5.1.0.Final.jar\org\jbpm\integration\console\ProcessManagement.java. Added the following two methods

                  public void completeWorkItem(String workItemId) {

                          delegate.completeWorkItem(workItemId, null);       

                      }   

                   

                      @Override

                      public void completeWorkItem(String workItemId, Map<String,Object> results) {

                          delegate.completeWorkItem(workItemId, results);       

                      }  

                   

                  • gwt-console-server\WEB-INF\lib\gwt-console-server-integration-2.1.jar\org\jboss\bpm\console\server\integration\ProcessManagement.java. Added the following method.

                  void completeWorkItem(String workItemId, Map<String,Object> results);

                   

                  • Finally I integrated all the changes into gwt-console-server\WEB-INF\classes\org\jboss\bpm\console\server\ProcessMgmtFacade.java. Added the following REST method.

                  @POST

                      @Path("definition/{id}/ctr_component_complete")

                      @Produces("application/json")

                      public Response ctrComponentComplete(@PathParam("id") String definitionId,

                              @FormParam("workitemID") String workItemID) {

                          try {

                              getProcessManagement().completeWorkItem(workItemID, null);

                              return createJsonResponse("SUCCESS");

                          } catch (Throwable t) {

                              throw new WebApplicationException(t, 500);

                          }

                      }

                   

                   

                  I deployed my changes and was SUCCESSFULLY able to verify that the jbpm-console web application state and the diagram highlighting are updating as expected.

                   

                  Thanks for your help again.

                   

                  -Ajay

                  • 6. Re: How to Complete a WorkItem using REST API
                    Chris Melas Apprentice

                    Great, glad everything worked :-) .

                    Can you please mark the question as answered? Thanks