11 Replies Latest reply on Nov 10, 2010 7:39 AM by rapa

    jBPM 4 - Deploy only if ProcessDefinition changed or new

    rapa

      Hi,

       

      I successfully integrated jBPM 4.4 with Seam 2.2, running on JBoss 4.2.

       

      In my overridden Jbpm class I deploy all process definitions, which are got from the components.xml

       

       
         <bpm:jbpm>
              <bpm:process-definitions>
                  <value>bpm/processdefinitions/process_1.jpdl.xml</value>
                  <value>bpm/processdefinitions/process_2.jpdl.xml</value>
                  <value>bpm/processdefinitions/process_3.jpdl.xml</value>        </bpm:process-definitions>
          </bpm:jbpm>
      
      

       

          <bpm:jbpm>
              <bpm:process-definitions>
                  <value>bpm/processdefinitions/partner_registration.jpdl.xml</value>
              </bpm:process-definitions>
          </bpm:jbpm>

       

      The process definitions file paths (resource names) are automatically injected in the String[] processDefinitions array (see Jbpm class)

       

      By having all file paths it is easy to deploy  the process definitions through the repository service, but I would like to avoid to deploy them each time

      I restart my application server.

       

      What I would like to do is to deploy my process definitions only if the xml file is new or has been modified since it's last deployment. The idea is simple: I just compare the last deployed process definition's xml content with the one of the current process definition.

       

      There is a method in the repository service that allows to get the deployed process definition's xml content

       

      repositoryService.getResourceAsStream(deploymentId, resourceName);

       

      I see that the jbpm4_lob table is the one that contains all the required data (resource_names, deployment_ids, xml files contents), so there must be

      a way to retrieve what I need.

       

      The problem is that I looked into the jbpm 4.4 api and I don't see any way to retrieve the highest deploymentId for a given resource name. The only way is to use the process definition key or name, but I would have to map these manually somewhere to their resource name or parse the xml files, which would be very nasty and I absolutely want to avoid that.

       

      Here is the code of my Jbpm class.

       

      @Name("jbpm")
      @Scope(ScopeType.APPLICATION)
      @AutoCreate
      @Startup
      @BypassInterceptors
      @Install(precedence = Install.APPLICATION)
      public class Jbpm extends SeamComponent {
           
          @Getter 
          private ProcessEngine processEngine;
          @Getter @Setter
          private String[] processDefinitions;
          
          @Create
          public void init() {
             // ...
      
             deployProcessDefinitions();
      
             // ...
          }
      
         /**
           * Deploys all changed or new process definitions. 
           */
          private void deployProcessDefinitions() {
              
            RepositoryService repositoryService = this.processEngine.getRepositoryService();
            for (String processDefinition : this.processDefinitions) {
      
                 // deploy d only if new or it has changed
                 if(isNewOrChanged(processDefinition)) {
                    NewDeployment d = repositoryService.createDeployment()
                                          .addResourceFromClasspath(processDefinition); 
                    String newDeploymentId = d.deploy();
                 }
      
            }
      
            private String isNewOrChanged(processDefinition) {
      
                // how to get deploymentId???
      
                InputStream is = repositoryService.getResourceAsStream(deploymentId, processDefinition);
                String xml = convertStreamToString(is);
      
                // ...
            }
      
      

       

       

      Anyone has a solution for this?

       

      Thanks, cheers!

        • 1. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
          mwohlf

          hi rapa,

          maybe this works for you:

           

          ProcessDefinitionImpl processDefinition = repositorySession.findLatestProcessDefinitionByName("myProcNameHere");
          String deploymentId = processDefinition.getDeploymentId();

          • 2. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
            rapa

            Hi Michael,

             

            I am afraid that findLatestProcessDefinitionByName() allows you to get the Process Definition by name and not by resource name (the name of the xml file, which is the only information I have at startup).

             

            Even if it might not be useful for my case, how do you get the RepositorySession object in a clean way?

             

            I see in the GetActivityCoordinates they get it like this:

             

            environment.get(Repository.class)

             

            but how to get the Environment?

             

            EnvironmentImpl.getCurrent()

             

            returns null.

             

            I went through all classes containing the word resourceName in the jbpm4.4 source code but I don't see anything useful.

            It doesn't seem to me that I am trying to solve any nasty problem, shouldn't it be something quite standard?

             

            If it was possible to specify the process definition version (like the name or the key) in the process.jpdl.xml file it would have been solved already. Any solution to this?

            • 3. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
              mwohlf

              you probably have to implement a Command (see: http://community.jboss.org/thread/156911 for an example)
              or open/close the Environment (see: http://community.jboss.org/thread/156730 for an example)

              • 4. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
                rapa

                Implementing a Command looks like a solution

                Tomorrow I'll try that and will let you know.

                 

                Thanks!

                • 5. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
                  rapa

                  Thanks Michael, that worked.

                   

                  I have to admit that I would have been happier to use a cleaner solution provided by the api, but well...

                  Anyway, if anyone is interested: here is the solution to deploy a process if it is new or it has changed.

                   

                  Method executed at startup in the Jbpm class:

                   

                  /**
                       * Deploys all changed or new process definitions.
                       */
                      private void deployProcessDefinitionsIfNewOrChanged() {
                          // Maps process definitions XML to resource names for the latest deployment of each process.
                          this.processDefinitionsXMLbyResourceName = this.processEngine.execute(new GetDeploymentXmlMapCmd());
                          String currentDeploymentId = null;
                          for (String processDefinition : this.processDefinitions) {
                             
                              currentDeploymentId = deployIfNewOrChanged(processDefinition);
                              if (currentDeploymentId != null) {
                                  info("Deployed process definition {0} with id {1}", processDefinition, currentDeploymentId);
                              } else {
                                  info("Process definition {0} did not change from last deployment.", processDefinition);
                              }
                              currentDeploymentId = null;
                          }
                      }
                     
                      /**
                       * Deploys a process definition if it is new or it has been modified since the current deployment.
                       * @param resourceName
                       * @return the deployment id of the deployed process definition or null if the no deployment was needed.
                       */
                      private String deployIfNewOrChanged(String resourceName) {
                          RepositoryService repositoryService = this.processEngine.getRepositoryService();
                          // Get the content of the process definition XML file from the resource path
                          DeploymentImpl newDeployment = (DeploymentImpl) repositoryService.createDeployment()
                              .addResourceFromClasspath(resourceName);
                          String newXml = new String(newDeployment.getBytes(resourceName));
                          // Get the currently deployed process definition (gets the latest version)
                          String currentXml = this.processDefinitionsXMLbyResourceName.get(resourceName);
                         
                          if(currentXml != null ? !newXml.equals(currentXml) : true) {
                              return newDeployment.deploy();
                          }
                          return null;
                      }
                     /**
                       * Deploys all changed or new process definitions.
                       */
                      private void deployProcessDefinitionsIfNewOrChanged() {
                  
                          // Maps process definitions XML to resource names for the latest deployment of each process.
                          this.processDefinitionsXMLbyResourceName = this.processEngine.execute(new GetDeploymentXmlMapCmd());
                  
                          String currentDeploymentId = null;
                          for (String processDefinition : this.processDefinitions) {
                  
                              currentDeploymentId = deployIfNewOrChanged(processDefinition);
                              if (currentDeploymentId != null) {
                                  info("Deployed process definition {0} with id {1}", processDefinition, currentDeploymentId);
                              } else {
                                  info("Process definition {0} did not change from last deployment.", processDefinition);
                              }
                              currentDeploymentId = null;
                          }
                      }
                  
                      /**
                       * Deploys a process definition if it is new or it has been modified since the current deployment.
                       * @param resourceName
                       * @return the deployment id of the deployed process definition or null if the no deployment was needed.
                       */
                      private String deployIfNewOrChanged(String resourceName) {
                  
                          RepositoryService repositoryService = this.processEngine.getRepositoryService();
                  
                          // Get the content of the process definition XML file from the resource path
                          DeploymentImpl newDeployment = (DeploymentImpl) repositoryService.createDeployment()
                              .addResourceFromClasspath(resourceName);
                          String newXml = new String(newDeployment.getBytes(resourceName));
                  
                          // Get the currently deployed process definition (gets the latest version)
                          String currentXml = this.processDefinitionsXMLbyResourceName.get(resourceName);
                  
                          if(currentXml != null ? !newXml.equals(currentXml) : true) {
                              return newDeployment.deploy();
                          }
                  
                          return null;
                      }
                  

                   

                  GetDeploymentXmlMapCmd class

                   

                  public class GetDeploymentXmlMapCmd implements Command<Map<String, String>> {
                  
                      private static final long serialVersionUID = 1L;
                  
                      @Override
                      @SuppressWarnings("unchecked")
                      public Map<String, String> execute(Environment environment) throws Exception {
                          DbSessionImpl dbSessionImpl = environment.get(DbSessionImpl.class);
                          Session session = dbSessionImpl.getSession();
                  
                          // Gets the the process definition key property for the latest deployment of each process
                          List<DeploymentProperty> dps = session
                              .createQuery("select d from DeploymentProperty d where d.key=:key1 and d.deployment.dbid in " +
                                        "(select max(di.deployment.dbid) from DeploymentProperty di where di.key=:key2 " +
                                        "group by di.stringValue)")
                              .setParameter("key1", "pdkey")
                              .setParameter("key2", "pdkey")
                              .list();
                          
                          // Creates the map
                          Map<String, String> processDefinitionsXMLbyResourceName = new HashMap<String, String>();
                          DeploymentImpl deployment;
                          String resourceName, xml;
                          for(DeploymentProperty dp : dps) {
                              deployment = dp.getDeployment();
                              if(deployment.getResourceNames().size() > 0) {
                                  // all deployments always contain 1 resource name
                                  resourceName = deployment.getResourceNames().iterator().next();
                                  xml = new String(deployment.getBytes(resourceName));
                                  processDefinitionsXMLbyResourceName.put(resourceName, xml);
                              }
                          }
                          return processDefinitionsXMLbyResourceName;
                      }
                      
                  }

                   

                  Cheers!

                  Andrea

                  1 of 1 people found this helpful
                  • 6. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
                    mwohlf

                    thanks for sharing your solution

                    • 7. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
                      rapa

                      If anyone has a cleaner solution, be welcome to correct me.

                       

                      In fact I don't understand the reason of some mappings of the jbpm4 tables. For instance, why does the DeploymentImpl class contain a protected Map<String, Lob> resources, where the key set are the resource names? Shouldn't there be only ONE resource name (and respective Lob) per deployment?

                      That is why I have this nasty

                      resourceName = deployment.getResourceNames().iterator().next();

                      in my code...

                       

                      Instead of a simple deployment.getResourceName();

                       

                      Cheers.

                      Andrea

                      • 8. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
                        mwohlf

                        You can have more than one Lob for each DeploymentImpl, the image of the process definition created by the eclipse designer plugin  for example can be deployed too, and would end up next to the process definition in this Map.

                        • 9. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
                          rapa

                          Auch

                           

                          so if I get you right the map could contain something like this:

                           

                          {

                          process.jpdl.xml :: byte[] xmlContent

                          process.jpdl.jpg :: byte[] jpgContent

                          }

                           

                          and the following code is not ok anymore..

                          resourceName = deployment.getResourceNames().iterator().next();

                           

                          And I guess I should check if the resource name extension is "xml"?

                           

                                          for(String currentResName : deployment.getResourceNames()) {
                                              if(currentResName.endsWith(".xml")) {
                                                  resourceName = currentResName;
                                                  break;
                                              }
                                          }
                          for(String currentResName : deployment.getResourceNames()) {
                              if(currentResName.endsWith(".xml")) {
                                 resourceName = currentResName;
                                 break;
                              }
                          }
                          

                          • 10. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
                            mwohlf

                            if you want to be on the safe side, take a look at the code in org.jbpm.jpdl.internal.repository.JpdlDeployer there is even a check for ".jpdl.xml" in order to update the right process definition

                            • 11. Re: jBPM 4 - Deploy only if ProcessDefinition changed or new
                              rapa

                              Ok I see... I'm still thinking that it's a hacky solution, but it's a solution.

                              Thanks for your help!