Showing posts with label Intalio. Show all posts
Showing posts with label Intalio. Show all posts

Sunday, August 21, 2011

Intalio BPMS : replacing the Derby in-memory DB by MySQL

By default, Intalio BPMS Enterprise Edition is configured to run against a Derby in-memory DB. This DB configuration is pretty handy and make Intalio an out of the box tool for deploying and executing business processes. Nevertheless, as soon as you'll be considering integrating Intalio to a production environment, you'll may also consider having Intalio run against a more robust RDBMS for different reasons : crash recovery facilities, backup & restore procedures, and so on. Most of the most popular RDMBS are supported by Intalio BPMS but the Community Edition only supports MySQL. 

This post describes the procedure to follow to have Intalio BPMS run against a MySQL DB.

1° Tool versions

Here are the tools and their respective version the following procedure is intended to :
  • intalio-bpms-6.0.3.022
  • mysql-5.5.15-win32 (Zip archive)
  • Windows Vista 32bits
2° Setting up MySQL and creating a database for Intalio
  • First, unzip the MySQL archive and let's refer to this directory as MYSQL_HOME
  • Open a CMD console and go to the %MYSQL_HOME%/bin directory. Then execute : mysqld --console to startup the DB server
  • By default, the MySQL root user does not have any password associated. You can defined its password by executing : mysqladmin -u root password PASSWORD (PASSWORD's value is up to you, of course)
  • Let's create a DB user for the Intalio server. First, connect to MySQL as user "root" : mysql -u root -p (the -p option will make the console ask you the password). Then execute the following command : mysql > CREATE USER 'intalio'@'localhost' IDENTIFIED BY PASSWORD; (PASSWORD's value corresponds to the password assigned to the user 'intalio' and is up to you ) (don't forget the semicolon to have the MySQL prompt execute your command)
  • Let's create a database for Intalio (I called it "intaliodb" but you can choose another name for yours). For this command too, you'll have to connect to the MySQL server as user "root" : mysql > create database intaliodb; (after having executed this command, you can check the "intaliodb" database does exist by executing this command : show databases;)
  • Once the "intaliodb" has been created, you'll have to grant some permissions to the user "intalio" so that it will be able to create new tables, rows, and so on. Here's the command to execute, once you're connected as user "root" : mysql > grant CREATE,INSERT,DELETE,UPDATE,SELECT on intaliodb.* to intalio@localhost;
3° Configuring Intalio server
  • At %INTALIO_HOME%/databases/MySQL, you'll find a SQL script named BPMS.sql. Executing this script on the "intaliodb" database will create the tables and indexes required by Intalio BPMS. Beware !!! This script contains the "type" keyword that's deprecated since MySQL 4.x. To execute this script on our MySQL 5.x server, we'll have to manually replace the "type" keyword by "engine" (basically, you only need to replace all the "type=innodb" and "type = innodb" in the BPMS.sql script by "engine=innodb"). Then, simply execute the script : (from a CMD console) mysql -u root -p intaliodb < PATH_TO_SCRIPT_FILE
  • The last required step to configure the Intalio server is telling the Intalio server which JDBC driver it shoud use, and which database it should connect to. To achieve this, just replace the content of the %INTALIO_HOME%/conf/resources.properties file by the content of the %INTALIO_HOME%/databases/MySQL/tomcat-5-resources.properties file. Then, you'll need to customize some parameters to your own configuration. If you followed this procedure, here's how you properties file should look like : 


4° Redeploying existing processes 
  • Now that we've replaced the default Derby database server by MySQL, all we need to do is redeploying the existing processes so that their associated info are stored in the MySQL server. To do that, start your Intalio server and then delete all the files with the .deployed extension in the %INTALIO_HOME%/var/deploy directory. The deleted file should be recreated after a few seconds, which is the time the Intalio server needs to hot-redeploy the processes.
5° Test

In order to check that your new Intalio configuration runs smoothly, you could run some of your existing processes (preferably, some with tasks or notifications) and then, check what is actually stored in the database. 

I'm personally using either SqlWave or HeidiSQL which are both great MySQL GUI clients when I need to perform some sort of monitoring work on the database used by my Intalio processes. Feel free to take a look at the tables whose name begins with "tempo_", they hold all the info related to your processes (tasks, notification, task status, assigned user, and so on) : 


    Sunday, May 29, 2011

    Intalio : example on how to manage deadline with tasks

    In this post, I'll define a simple process to illustrate how to check whether a task deadline is reached. Below, you'll find a link to download my process project as well as the detailed process definition step by step.


    Process definition (step by step)
    1. In the Intalio Designer, create a new project and name it "DeadlineManagement"
    2. Create a new "Business Process Diagram" and name it "DeadlineManagement.bpm"
    3. Add a pool to the diagram and name it "User"
    4. Set the "User" pool as non executable
    5. Add a task to the "User" pool and name it "Start process"
    6. Add a second pool and name it "Process tasks" (make sure this one is set as executable, which is the default configuration)
    7. Add a message start event to the "Process tasks" pool
    8. Drag the outgoing arrow from the "Start process" task (from the "User" pool) to the message start event
    9. In the project explorer, add a new directory and name it "messages".
    10. In the "messages" directory, create a new XML schema and name it "ProcessInput.xsd" (this schema defines the structure of the message that executes the process)
    11. In the XML schema, add a complex type and name it "InputMessage". In this complex type, add 3 elements :
      •  a string type element named "initialUser"
      • a string type element named "deadline"
      • a string type element named "reminderUser"
    12. Drag the complex type onto the arrow that links the "Start process" task and the message start event. Then choose the "Set schema type InputMessage as the content of the message"
    13. From the message start event, drag the outgoing arrow to the right and create a new task. Then, name this task "Create user task"
    14. Back in the project explorer view, create a new directory and name it "Forms"
    15. In the "Forms" directory, create a new Ajax form and name it "UserForm"
    16. Drag the form into the "User" pool and choose the "Create and complete" option
    17. From the "Create user task", drag the outgoing arrow to the right and create a new task named "Complete user task"
    18. From the "UserForm-create" node, drag both the outgoing and ingoing arrows to the "Create user task" task
    19. From the "Complete user task" task, drag both the outgoing and ingoing arrows to the "UserForm-complete" node
    20. Set the focus on the "Create user task" task and open the mapper view
    21. Map the "initialUser" from the input message to the "userOwner" task metadata. Then, map the "deadline" from the input message to the "until" task metadata
    22. From the "Complete user task" task, drag the outgoing arrow to the right and create a Gateway with type "Exclusive data-based gateway
    23. From the gateway, drag the outgoing arrow to the right; create a new task and name it "Process task without expiration"
    24. From the gateway, drag another outgoing arrow to the right; create a new task and name it "Clone task with high priority"
    25. Right click on the arrow that binds the gateway and the "Process task without expiration" task. Then choose "Condition type --> default" 
    26. Set the focus on the gateway and open the mapper view : 
      • add an "=" operator
      • Create a static argument with the value "DeadlineReached" and set it as input to the "=" operator
      • Set the "status" element from the "userFormNotifyTaskCompletionRequestMsg.root" node as the second argument of the "=" operator
      • map the result of the "=" operator to the "condition" node
    27. At this point, from the "Clone task with high priority" task, you could manage task expiration as you wish. In this example, I've chosen to recreate the initial task and assign it to another user, with another priority and a deadline = 31/12/2011 22:00  (this illustrates how you can reassign a task to a manager, in case it hasn't been completed by one of its underlings, for instance)
    Running the process

    Once the process is deploy, you can run it from the BPMS console. The input message we've defined through the XML schema will result in the user having to introduce the following info : 

    Now, let's log in the UI-FW console as the "examples/msmith" user : 


    Then, let's wait until 22:20. The task will disappear from the list of the "examples/msmith" user. Once logged in as "examples/ewilliams", we can see that the cloned task has been created : 


    Download the sample project

      Wednesday, April 27, 2011

      Intalio : how to dynamically set task metadata

      In this post, I'll explain how you can dynamically set values to the task metadata. By doing so, you'll be able to have absolute control of your tasks on runtime : dynamically assigning a task to one/several users, one/several user groups, defining a deadline, etc, would be a piece of cake.

      To illustrate my point, let's define a real simple and basic process (its design shouldn't be a problem for you at all but if you're not familiar with the Intalio designer yet, just leave a comment and I'll send the whole sample project to you) :

      1° Sample process


      Two important things here :
      • for this post purpose, I didn't waste my time creating an elaborate AjaxForm for the task. Actually, I've simply created an empty form. So, don't waste yours neither if you're only trying to replicate the process.
      • as you can see on the screenshot, there's a warning mark on the form. The reason is that I did not assign the task to any user or group, given I am going to show how you can dynamically assign a task. For those who absolutely can't bear to have warnings in their project, just go ahead and assign a static user/group, the result will be the same.

      2° Setting value on task metadata

      Task metadata can have a value dynamically set through the mapper : set the focus on the node that is creating the task (in the example process, that is the red surrounded node) and open the Mapper view :


      Then, expand the "createTaskMsg" root element and you'll see all the task metadata to which you can dynamically set a value. In this example, I've assigned the task to 2 users : examples/ewilliams and examples/msmith.

      Note that in my example, though the users are set through the mapper, it is rather static because I used a static value. But this can be done dynamically because the value that is set to the userOwners element can be retrieved from a web service, a DB datasource, a message that is defined with an XSD, ...



      3° Running the example

      Executing this process will yield the following result in the UI-FW console :

      Logged in as "intalio/admin":



      Logged in as "examples/msmith":



      Logged in as "examples/ewilliams":


      By the way, in this example, you've probably noticed how convenient it is to have a column in the task list showing the users to whom a task has been assigned ... This feature could be very useful once you need to implement hierarchies among users and user groups.


      4° Adding an actual dynamic metadata


      Now that we've seen how values can be set to task metadata, let's add an actual dynamic metadata. In the process view, we'll map the String element that comes as input to the process when it is executed to the task deadline element : 



      Now, let's start the same process from the BPMS console and as input, we'll provide the following value : 2011-12-31T11:00:00 (correct format for datetime type is yyyy-MM-ddThh:mm:ss)



      As a result, the "examples/ewilliams" and "examples/msmith" users will have a task that must be completed by 11:00 December 31 2011.





      Monday, April 25, 2011

      Intalio : How to customize the task list in UI-FW console

      In this post, I'll explain how the UI-FW console can be customized, that is, how you can add columns to the task list. Note that the additional columns are intended to display task metadata (such a task's status, owner user, owner group, ...). I've never needed to customize the task list to display information other than task related ones, so far, but it's definitely something I'll try to do later.

      As an example, I'll add a column to display the user to whom a task is assigned.
      Adding a column to the task list is done with the two following steps

      1° Adding the new column to grids.jsp

      The grids.jsp file is located in INTALIO_HOME\webapps\ui-fw\script
      Edit this file and search for "Table for activity tasks" (should be at the line 546). Below this comment, you'll find the definition of the existing columns :

      /*
          Table for activity tasks
          */
       var taskIcons = getToolbarIconsCodes(taskIconSet);
          var t1 = $("#table1").flexigrid($.extend({
            <% if(useToolbar) {%> 
              buttons : taskIcons,
              <%} %>
              params: [
              { name : 'type', value : 'PATask' },
              { name : 'update', value : true }
              ],
              colModel : [
              {
                display: '', 
                name : '_description', 
                width : width*0.44, 
                sortable : true, 
                align: 'left'},
              {
                display: '', 
                name : '_state', 
                width : width*0.035, 
                resize : true, 
                sortable : true, 
                align: 'center'},
              {
                display: '', 
                name : '_creationDate', 
                width : width*0.15, 
                sortable : true, 
                align: 'left'},
              {
                display: '', 
                name : '_deadline', 
                width : width*0.15, 
                sortable : true, 
                align: 'left'},
              {
                display: '', 
                name : '_priority', 
                width : width*0.070, 
                sortable : true, 
                align: 'center'},
              {
                display: '', 
                name : '_attachments', 
                width : width*0.12, 
                sortable : false, 
                align: 'center'}
              ]
          },p));
      

      Then, simply add a new column definition block (between brackets) for a column named _userOwner and watch out for the comma (the different column definition blocks are separated by a comma) :
      /*
          Table for activity tasks
          */
       var taskIcons = getToolbarIconsCodes(taskIconSet);
          var t1 = $("#table1").flexigrid($.extend({
            <% if(useToolbar) {%> 
              buttons : taskIcons,
              <%} %>
              params: [
              { name : 'type', value : 'PATask' },
              { name : 'update', value : true }
              ],
              colModel : [
              {
                display: '', 
                name : '_description', 
                width : width*0.44, 
                sortable : true, 
                align: 'left'},
              {
                display: '', 
                name : '_state', 
                width : width*0.035, 
                resize : true, 
                sortable : true, 
                align: 'center'},
              {
                display: '', 
                name : '_creationDate', 
                width : width*0.15, 
                sortable : true, 
                align: 'left'},
              {
                display: '', 
                name : '_deadline', 
                width : width*0.15, 
                sortable : true, 
                align: 'left'},
              {
                display: '', 
                name : '_priority', 
                width : width*0.070, 
                sortable : true, 
                align: 'center'},
              {
                display: '', 
                name : '_attachments', 
                width : width*0.12, 
                sortable : false, 
                align: 'center'},
              {
                display: '', 
                name : '_userOwner', 
                width : width*0.035,
                sortable : true, 
                align: 'left'}  
              ]
          },p));
      

      Note that the value of the "key" attribute is used to define which label will be displayed in the column header. These labels can be specified in the internationalization files (properties files) located the INTALIO_HOME\webapps\ui-fw\WEB-INF\classes directory.

      2° Editing the updates.jsp file

      The updates.jsp file is located in INTALIO_HOME\webapps\ui-fw\WEB-INF\jsp.
      This file needs to be edited in order to map the task metadata to the columns from the task list. In the updates.jsp file, there is a "choose" tag that contains a "Notification" section, a "PIPATask" section and an "otherwise" section. Simply add a "cell" definition in that last one and you good to go :


      <cell>
           <c:choose>
            <c:when test="${taskHolder.task.userOwners != ''}">
                        ${taskHolder.task.userOwners}
            </c:when>
            <c:otherwise>none</c:otherwise>
            </c:choose>
      </cell>

      To correctly map a task metadata with a column, you'll need to indicate the correct attribute name from the Task.java class (in my case, taking a peek to the source was very helpful to find out what are the attributes name). For instance, if you use taskHolder.task.userOwnersWrong instead of taskHolder.task.userOwners, you'll get exceptions in the Intalio console and logs when loading the task list.
      Here's the result :

       Thanks


      Finally, I just want to thank Mr. Ihab El Alami (process expert at Intalio) who gave me some hints to customize the UI-FW console.

      Sunday, April 24, 2011

      Recipe to build the Intalio Tempo project (Windows environment)


      For those who want or need to build the Intalio Tempo project from its source, I am pretty sure you ended up reading the install instruction provided in the GIT repository from the Tempo project (if not, here's the link to it : https://github.com/intalio/tempo/blob/master/INSTALL.txt).

      When I followed these instructions myself (FYI, I was trying to build the project in a Windows XP environment), I faced lots of problem (gem version problems, missing Ruby environment variables and so on) and at some point, I had the feeling that the instructions of the INSTALL.txt file were mainly UNIX/Linux oriented.

      It took me a few days to figure out how to build the project in my Windows XP and here's the steps to go through : 

      1° Prerequisites

      First and foremost, make sure you've installed JDK 5 and set the JAVA_HOME variable.


      2° Retrieving the Tempo project's source

      The Tempo project source are located in a GIT repository. Personally, I've installed the Windows installer for te 1.7.4 preview version (http://code.google.com/p/msysgit/downloads/detail?name=Git-1.7.4-preview20110204.exe&can=2&q=).

      - Create a directory in C:/ and name it "Tempo-src"
      - Open a GIT bash console and go into the "C:/Tempo-src" directory
      - Execute the following command :
       
      git clone git://github.com/intalio/tempo.git
       

      3° Installing Ruby for Windows

      Download and install Ruby 1.8.7 for Windows (available at : http://www.ruby-lang.org/en/downloads/).



      4° Installing development kit

      - Download and install the development kit for Ruby. This will allow your gems, among other things, to execute C and C++ libraries. This kit is available at http://rubyinstaller.org/downloads/

      Actually, the executable that you've just downloaded is a 7-zip archive. Run it, specify the extraction path to C:\devKit

      (If you specify another path, make sure it does not contain any blank !!! That will prevent you from getting some errors that are far from being well documented)

      - Open a CMD shell, go into the C:\devKit directory and execute the following commands :

                - ruby dk.rb init
           - ruby dk.rb install

      5° Installing buildr gem
      - Open a CMD shell and execute the command : gem install buildr -v 1.3.5 --platform=mswin32
      This command will install the version 1.3.5 of buildr for Windows platform. During the installation process, you may get some error concerning the documentation file installation. Hopefully, these files are not necessary for the following steps.


      6° Installing hpricot gem

      In order to build the Tempo project, you'll also need the hpricot gem. In a CMD shell, simply execute the command gem install hpricot --platform=mswin32


      Patching buildr

      It seems that there are some version compatibility issues between the different tools. These issues are resolved by applying a patch on configuration file. For more information, you can refer to this link : https://fedorahosted.org/candlepin/wiki/PatchingBuildr

      The link above described an automated procedure to apply the patch but it only works for UNIX/Linux platform. Working on Windows, I've simply edited the concerned configuration file manually. To make things simpler, just go into the C:\Ruby187\lib\ruby\gems\1.8\gems\buildr-1.3.3\lib\buildr\java directory. Open the file named version_requirement.rb and replace its content by the following lines :


      # Licensed to the Apache Software Foundation (ASF) under one or more
      # contributor license agreements.  See the NOTICE file distributed with this
      # work for additional information regarding copyright ownership.  The ASF
      # licenses this file to you under the Apache License, Version 2.0 (the
      # "License"); you may not use this file except in compliance with the License.
      # You may obtain a copy of the License at
      #
      #    http://www.apache.org/licenses/LICENSE-2.0
      #
      # Unless required by applicable law or agreed to in writing, software
      # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
      # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
      # License for the specific language governing permissions and limitations under
      # the License.
      
      
      module Buildr
        
        #
        # See ArtifactNamespace#need
        class VersionRequirement
          
          CMP_PROCS = Gem::Requirement::OPS.dup
          CMP_REGEX = if defined?(Gem::Requirement::OP_RE)
        Gem::Requirement::OP_RE
       else
        Gem::Requirement::OPS.keys.map { |k| Regexp.quote k }.join "|"
       end
      
          CMP_CHARS = CMP_PROCS.keys.join
          BOOL_CHARS = '\|\&\!'
          VER_CHARS = '\w\.\-'
      
          class << self
            # is +str+ a version string?
            def version?(str)
              /^\s*[#{VER_CHARS}]+\s*$/ === str
            end
            
            # is +str+ a version requirement?
            def requirement?(str)
              /[#{BOOL_CHARS}#{CMP_CHARS}\(\)]/ === str
            end
            
            # :call-seq:
            #    VersionRequirement.create(" >1 <2 !(1.5) ") -> requirement
            #
            # parse the +str+ requirement 
            def create(str)
              instance_eval normalize(str)
            rescue StandardError => e
              raise "Failed to parse #{str.inspect} due to: #{e}"
            end
      
            private
            def requirement(req)
              unless req =~ /^\s*(#{CMP_REGEX})?\s*([#{VER_CHARS}]+)\s*$/
                raise "Invalid requirement string: #{req}"
              end
              comparator, version = $1, $2
              version = if defined?(Gem::Version.version)
         Gem::Version.new(0).tap { |v| v.version = version }
              else
         Gem::Version.new(version)
              end
      
              VersionRequirement.new(nil, [$1, version])
            end
      
            def negate(vreq)
              vreq.negative = !vreq.negative
              vreq
            end
            
            def normalize(str)
              str = str.strip
              if str[/[^\s\(\)#{BOOL_CHARS + VER_CHARS + CMP_CHARS}]/]
                raise "version string #{str.inspect} contains invalid characters"
              end
              str.gsub!(/\s+(and|\&\&)\s+/, ' & ')
              str.gsub!(/\s+(or|\|\|)\s+/, ' | ')
              str.gsub!(/(^|\s*)not\s+/, ' ! ')
              pattern = /(#{CMP_REGEX})?\s*[#{VER_CHARS}]+/
              left_pattern = /[#{VER_CHARS}\)]$/
              right_pattern = /^(#{pattern}|\()/
              str = str.split.inject([]) do |ary, i|
                ary << '&' if ary.last =~ left_pattern  && i =~ right_pattern
                ary << i
              end
              str = str.join(' ')
              str.gsub!(/!([^=])?/, ' negate \1')
              str.gsub!(pattern) do |expr|
                case expr.strip
                when 'not', 'negate' then 'negate '
                else 'requirement("' + expr + '")'
                end
              end
              str.gsub!(/negate\s+\(/, 'negate(')
              str
            end
          end
      
          def initialize(op, *requirements) #:nodoc:
            @op, @requirements = op, requirements
          end
      
          # Is this object a composed requirement?
          #   VersionRequirement.create('1').composed? -> false
          #   VersionRequirement.create('1 | 2').composed? -> true
          #   VersionRequirement.create('1 & 2').composed? -> true
          def composed?
            requirements.size > 1
          end
      
          # Return the last requirement on this object having an = operator.
          def default
            default = nil
            requirements.reverse.find do |r|
              if Array === r
                if !negative && (r.first.nil? || r.first.include?('='))
                  default = r.last.to_s
                end
              else
                default = r.default
              end
            end
            default
          end
      
          # Test if this requirement can be satisfied by +version+
          def satisfied_by?(version)
            return false unless version
            unless version.kind_of?(Gem::Version)
              raise "Invalid version: #{version.inspect}" unless self.class.version?(version)
              version = Gem::Version.new(0).tap { |v| v.version = version.strip }
            end
            message = op == :| ? :any? : :all?
            result = requirements.send message do |req|
              if Array === req
                cmp, rv = *req
                CMP_PROCS[cmp || '='].call(version, rv)
              else
                req.satisfied_by?(version)
              end
            end
            negative ? !result : result
          end
      
          # Either modify the current requirement (if it's already an or operation)
          # or create a new requirement
          def |(other)
            operation(:|, other)
          end
      
          # Either modify the current requirement (if it's already an and operation)
          # or create a new requirement
          def &(other)
            operation(:&, other)
          end
          
          # return the parsed expression
          def to_s
            str = requirements.map(&:to_s).join(" " + @op.to_s + " ").to_s
            str = "( " + str + " )" if negative || requirements.size > 1
            str = "!" + str if negative
            str
          end
      
          attr_accessor :negative
          protected
          attr_reader :requirements, :op
          def operation(op, other)
            @op ||= op 
            if negative == other.negative && @op == op && other.requirements.size == 1
              @requirements << other.requirements.first
              self
            else
              self.class.new(op, self, other)
            end
          end
        end # VersionRequirement
      end 

      8° Building the Tempo project


      And finally, in a CMD shell, execute the ruby command on the tempo-builder.rb file which is located in the C:\Tempo-src\tempo\rsc\scripts> directory :




      At this point, if you got a message similar to "looking for TOOL_XXX (Axis2, Tomcat,and so on) and all I got was 404!", it is likely that the source of the Tempo project you've checked out are configured to work with previous version of the concerned tool.


      So, you'll need to manually edit following file : C:\Tempo-src\tempo\rsc\scripts\lib\build_support.rb

      To resolve this kind of issue, you could either change the version of change the path to the indicated resource. But my opinion is that it is safer to change the path and keep the same version. That way, you won't create new version incompatibilities.



      9° External links


      This recipe worked for me but if you still encounter errors following it, take a look at the Tempo development
      group page (http://groups.google.com/group/tempo-dev) where you will definitely find some helpful information. Here's another helpful link that will provide you plenty of information about Intalio Tempo : http://intaliotempo.wordpress.com/.

       




      Saturday, January 8, 2011

      Intalio : how to deploy a process as an archive (without designer)

      Introduction

      Though deploying a process from the Intalio Designer is really straightforward (done in 1 click once the project is appropriately configured), one might need to be able to deploy a process without the Designer.

      Step 1 : creating an archive from the process project

      From the Explorer View in the Intalio Designer, click on the project you want to deploy to select it. Then, click on the button with the gear icon to open the process configuration page.


      On the configuration page, check the "Archive the bundle" box : 
      The "Archive the bundle" option specifies that a zip archive on the project will be created. The possible options are :
      • only deploy
      • only archive
      • deploy + archive
      Once you're ready with the configuration, click on the "Archive button" :

      The Designer console will then display the path of the generated archive :


      As you can see, the project is archived in a zip file and it is named accordingly with your project name.

      Step 2 : deploying the archive

       The content of the archive is as such :


      Simply unzip the archive in a directory that has the same name as your project, in the ...\intalio-bpms-6.0.3.022\var\deploy directory.

      Manually, you can simply copy the archive in the ...\intalio-bpms-6.0.3.022\var\deploy directory and unzip it with the option "Extrat to yourProjectName".

      Note that the Intalio server supports hot deployment and consequently, you can unzip the archive with the server running as well as stopped.

      Wednesday, November 17, 2010

      Intalio : sample process (with external WS invokation and mapping WS <-> Matrix Table)

      1 Introduction

      In my company, we've been considering, for a while, integrating a BPMS solution to our in-house application. One of the goals of that integration is to involved more actively business and functional analysts, and even our clients, in the definition of processes within our application.

      The main requirements that must be fulfilled by the BPMS are :
      •  ease of integration with our application
      • commercial use allowed free of charge (clients that would be interested in upgrade or premium version are free to opt for such licenses)
      • 100% BPMN standards compliance
        After some investigation, Intalio Community Edition seemed to be the perfect candidate...
        The next step was then to implement a sample process in order to get to know Intalio and at the same time, confirm it actually fulfills our expectations.

        2 Tools

        The tools I used to implement and test my first process were : 
        • Intalio-Designer-6.0.3.034 (Community edition)
        • Intalio|Server 6.0.3.022 
        3 Process definition

        Here is an overview of my process :



        4 Key steps

        In this section, I'm going to detail the different steps I went through to define my process (note that I'll highlight the steps I consider rather tricky nay complicated) :

        1° Create an non-executable pool and name it "ForHRM-FrontOffice"
        2° In the "ForHRM-FrontOffice" pool, create a task and name it "Trigger Process" : this task within the "ForHRM-FrontOffice" pool represents our application from which the process will be executed.
        3° Create an executable pool and name it "ProcessIntalioLotProcess"
        4° In this pool, create a "Message start" event
        5° Focus on the "Trigger Process" task and drag outgoing message flow icon to the "Message start" event
        6° Still in the same pool, create a new task and name it "Invoke WS to retrieve data"
        7° Focus on the "Message start" event and drag outgoing message flow icon to the last created task
        8° Create a new non-executable pool and name it "ForHRM-WS" : this pool will contain external web services
        9° In the process explorer, go to the "externalWSDLs" directory and drag the "RetrieveFeuillesPaieForPaie" operation into the "ForHRM-WS" pool, and choose the "single task" type


        10° Focus on this last task and drag the ingoing message flow icon to the "Invoke WS to retrieve data" task
        11° Focus on the same task and drag the outgoing message flow incon to the "Invoke WS to retrieve data" task
        12° Get back in the "ProcessIntalioLotProcess" pool, create the "Begin validation", "End validation" and "Invoke WS to update data" tasks
        13° Create a new non-executable pool and name it "Gestionnaires" : this pool represents specific users to whom a validation form will be displayed
        14° Drag the form named "validation.gi" (located in the "forms" directory") into the last created pool and choose the "create and complete" type
        15° Focus on the "validation-create" task and drag the ingoing message flow icon to the "Begin validation" task. Then drag the outgoing message flow icon to the "Begin validation" task
        16° Focus on the "End validation" task and drag the ingoing message flow icon to the "validation-complete" task. Then drang the outgoing message flow icon to the "validation-complete" task
        17° From the process explorer view, drag the "ValidateFeuillesPaieForPaie" operation into the "ForHRM-WS" pool, and choose the "single task" type


        18° Focus on the "validateFeuillesPaieForPaie" task and drag the ingoing message flow icon to the "Invoke WS to update data" task. Then, drag the outgoing message flow icon to the "Invoke WS to update data" task.
        19° From the "messages" directory, expand the "triggerMessage.xsd" file and drag the "tns:RepertoirePaie" element onto the message flow that goes from the "Trigger Process" task to the "Start Message" event. Then choose the "Set schema element ... as the content of the message"



        This step defines the structure of the message that will be passed to the Intalio process when it is executed from our application. Since the "RepertoirePaie" element is composed of 2 String elements, the process will be executed by calling a web service operation that receives 2 string parameters (read further for more details on that).

        20° Focus on the "Invoke WS to retrieve data" step and open the "Mapper View". Then, connect the nodes as shown below


        This step defines which are the values and where they're actually come from, that will be passed as parameters to the web service operation.
        21°  Focus on the "Begin validation" task and open the "Mapper" View. 
        22°  From the "Mapper Palette" view, drag the "doXslTransform" function into the work area of the "Mapper"


        Then, within the "Mapper" view, create a new operator (by clicking on the red button from the "Mapper" View) and define its value as ""/XSL/feuillePaie.xsl".
         Connect this operator to the "bpel:doXslTranform" function that you just drag-and-dropped.
        Now, connect the input and output nodes to the  "bpel:doXslTranform" function as shown below




         This steps indicates how the response from the "retrieveFeuillesPaieForPaie" WS operation must be mapped to the matrix-table (so that the results could be displayed in the form) I've defined in my form which looks like this :




        23° My sample project (that can be downloaded with the link provided further) comes with all the .XSL files completely written. Here's how I've created them.
        First, create an empty directory within the project and name it "XSL".
        Then create an empty file and name it "feuillePaie.xsl" and put this file in the "XSL" directory.
        Go back in the "Mapper View" and focus on the "Begin validation" task.
        Right-click on the "bpel:doXslTransform" function and select "Generate XSL helper...". This will edit your "feuillePaie.xsl" file and create a "feuillePaie.xsl.xsltest" file (that I've deleted afterwards, since I didn't need it)




        Make sure you're working with the Intalio designer at the 6.0.3.034 version !!! I've tried the previous version of the designer and the XSL helper option didn't work...

         The "feuillePaie.xsl" as it has been completed by the XSL helper then looks like this : 


        <?xml version="1.0" encoding="UTF-8"?>
        <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="2.0" xmlns:tns="http://www.example.org/triggerMessage"
            xmlns:IntalioPaieService="http://wsIntalioProcessLot.webServices.web.forhrm.formatech.be/"
            xmlns:this="http://thisdiagram/ProcessIntalioLotProcess"
            xmlns:validation="http://www.intalio.com/gi/forms/validation.gi"
            xmlns:Gestionnaires="http://thisdiagram/Gestionnaires"
            xmlns:attachments="http://www.intalio.com/gi/forms/validation.gi/attachments"
            xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:diag="http://thisdiagram">
            <!--
                XSL Skeleton generated on Fri Nov 19 15:09:50 CET 2010 for
                F/IntalioPrototype/ProcessIntalioLot.bpm
                pool:_lsCI8OzPEd-OxcY5gTnOlw.bpdm activity: Begin Validation Complete
                doXslTransform: bpel:doXslTransform("/XSL/feuillePaie.xsl",
                $intalioPaieServiceRetrieveFeuillesPaieForPaieResponseMsg.parameters)
                Input document as defined in the mapper:
                $intalioPaieServiceRetrieveFeuillesPaieForPaieResponseMsg.parameters
            -->
            <xsl:output />
            <!--No parameters are currently passed to doXslTransform.-->
            <xsl:template match="/IntalioPaieService:retrieveFeuillesPaieForPaieResponse">
                <DonneesLot>
                    <lot-matrix>
                        <empCode>string</empCode>
                        <travCode>string</travCode>
                        <noPaie>string</noPaie>
                        <montantBrut>string</montantBrut>
                        <validated>string</validated>
                    </lot-matrix>
                </DonneesLot>
            </xsl:template>
        </xsl:stylesheet>
         

        From this point, I've just removed the comments and add the "for-each" instruction so that each "occurrence" from the WS response would be map to an occurrence within my matrix table. For each field of an occurrence within my matrix table, I've also defined which "element" of the WS response occurrence should be mapped to it. For instance, in the following excerpt, I'm mapping the "empCode" attribute from a "FeuillePaie" occurrence (the WS operation returns an ArrayList<FeuillePaie>) to the column with the "empCode" fields : 

                            <empCode>
                                <xsl:value-of select="empCode" />
                            </empCode>








        So, after completion of the "FeuillePaie.xsl" file, this looks like this : 



        <?xml version="1.0" encoding="UTF-8"?>
        <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="2.0" xmlns:tns="http://www.example.org/triggerMessage"
            xmlns:IntalioPaieService="http://wsIntalioProcessLot.webServices.web.forhrm.formatech.be/"
            xmlns:this="http://thisdiagram/ProcessIntalioLotProcess"
            xmlns:validation="http://www.intalio.com/gi/forms/validation.gi"
            xmlns:Gestionnaires="http://thisdiagram/Gestionnaires"
            xmlns:attachments="http://www.intalio.com/gi/forms/validation.gi/attachments"
            xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:diag="http://thisdiagram">
            <xsl:output />
            <xsl:template match="/IntalioPaieService:retrieveFeuillesPaieForPaieResponse">
                <DonneesLot>
                    <xsl:for-each select="*">
                        <lot-matrix>
                            <empCode>
                                <xsl:value-of select="empCode" />
                            </empCode>
                            <travCode>
                                <xsl:value-of select="travCode" />
                            </travCode>
                            <noPaie>
                                <xsl:value-of select="paie" />
                            </noPaie>
                            <montantBrut>
                                <xsl:value-of select="brut" />
                            </montantBrut>
                            <validated>
                            </validated>
                        </lot-matrix>
                    </xsl:for-each>
                </DonneesLot>
            </xsl:template>
        </xsl:stylesheet>



        24° Focus on the "Invoke WS to update data" task, go to the "Mapper" view and configure the mapping as shown below. This time, I created an XSL file called "feuillePaieValidee.xsl" and I followed the same logic used at step 23° to complete that. So you should be able to get to the same result by yourself, I guess.
        In this step, what I've done, is mapping the content of my matrix table (that might have been by the "formatech/khy" user - did you see that I've defined a user on the pool that contains the forms ???) to the input parameters of the WS operation named "validateFeuillesPaieForPaie". In my sample, this operation will update the data that have been modified by the user through the Ajax form, into our database.

        5 Conclusion

        This process is quite basic but embedded enough requirements in order to be integrated into an existing application (it actually was a good prototype for us to check that we could integrate Intalio to our in-house application), so feel free to base upon it to create your own prototype and discover Intalio by yourself.

        6 Disclaimer and remarks

        I've never followed any BPM training nor BPMN training, so don't take this sample as 100% correct from a functional process designing point of view.

        For those who would wonder why I did not send back a response to from the end of the process to the "Trigger process" task, here's the reason : once integrated into our application, if the "Trigger process" task were sent back a response message, the execution of the process would have been synchronous. Consequently, the user would have to complete the task within the UI-FW console before the code from our application could resume its execution.

        If you have any question/suggestion, just leave a comment. 


        I will create new posts/topics on Intalio as soon as I'll have time (or be asked at work) to try on other Intalio features, so follow me and stay tuned ;-)

        7 Download link


        I've posted my process in the showcase section of the Intalio community website : 

        http://community.intalio.com/forums/show-cases/basic-process-mapping-ws-with-matrix-table/view.html#25348

        PS : should this link die or my process be deleted from the website, let me know and I'll re-upload it.