วันจันทร์ที่ 16 กันยายน พ.ศ. 2556

Alfresco Workflow with Activiti

Overview

การสร้าง Custom Workflow บน Alfresco นั้นแบ่งออกเป็น 3 ส่วนใหญ่ๆ คือ
  1. Workflow Definition
  2. Alfresco Model
  3. Share UI

Preparation

ก่อนที่เราจะเริ่มสร้าง Workflow กันเราก็ต้องมาเตรียมความพร้อมกันก่อนโดยสิ่งที่เราต้องเตรียมมีดังนี้
  1. Assume Alfresco Installed in your machine
  2. Eclipse (can be STS) with Activiti Designer http://www.activiti.org/userguide/index.html#eclipseDesignerInstallation

Creating Workflow Definition

หลังจากติดตั้ง Tools กันเรียบร้อยแล้ว ขั้นตอนต่อไปเราจะมาสร้าง (Simple) Custom Workflow  กันโดยมี Step การทำงานซัก 1-2 step โดยมีขั้นตอนดังนี้
  1. Start the Eclipse (or STS)
  2. Create Activiti Diagram ใน project ใดก็ได้ (เนื่องจาก Alfresco ใช้งานเฉพาะ xml definition file ไม่ได้ต้องการ Activiti Project ดังนั้นการสร้างแค่ Diagram จึงทำได้ง่ายกว่า) โดยผมตั้งชื่อเป็น sample.bpmn20.xml
  3. วาด Workflow ตามรูปด้านล่าง
  4. Edit the candidate user or group in text editor

    จากรูปจะเห็นได้ว่ามีการกำหนด formKey ลงไปที่ startEvent และ userTask หมายความว่าเมื่อเรา Deploy Definition นี้ลงไปที่ Alfresco แล้ว เราต้องการให้ใช้ form ใดในการแสดงผลนั่นเอง ซึ่งผมกำหนดไว้ว่า startEvent ให้ใช้ form bpm:startTask และ userTask ต่างๆ ให้ใช้ form ของตัวเองที่ชื่อว่า sample:task1 และ sample:task2

    ในส่วนของ acvititi:assignee นั้นผมกำหนดให้ task วิ่งไปยัง user tantai ซึ่งเมื่อมีการ Start Workflow แล้ว Task ก็จะวิ่งไปยัง user tantai ทันที รวมทั้งเมื่อจบ task1 ก็จะวิ่งไปยัง user tantai เช่นกันที่ task2

    ซึ่งต่อไปเราจะมาสร้าง Workflow Model กันบนฝั่ง Alfresco

    Note :  Recommended to edit workflow definition in the text editor (not in Activiti Designer) because Activiti Designer have a lot of bug in current version (My version is 5.12.0)

Creating Alfresco Model

การสร้าง Model บน Alfresco นั้นประกอบไปด้วย 3 ส่วนคือ
  1. Spring Context files
  2. Model files
  3. Message
Note: แต่ในการสร้าง Workflow Model นั้น Message ใน Model ไม่จำเป็นครับ ผมขอตัดออกไปก่อนเพื่อ Focus ในส่วนของ Workflow จริงๆ

แนะนำให้อ่าน Data Dictionary ของ Alfresco ก่อนครับ http://wiki.alfresco.com/wiki/Data_Dictionary_Guide

เนื่องจาก Alfresco ได้เขียน Spring Config ไว้ให้ import ไฟล์ที่ชื่อตามด้วย -context.xml ทุกไฟล์ใน folder TOMCAT_HOME/shared/classes/alfresco/extension/ ไว้ ขั้นตอนแรกให้เราสร้าง Spring Context file ขึ้นมาโดยต้องตั้งชื่อเป็น sample-context.xml และวางไว้ที่ folder extension config bean ดังนี้



<beans>

   <!-- Registration of new models -->
   <bean depends-on="dictionaryBootstrap" id="sample.model" parent="dictionaryModelBootstrap">
      <property name="models">
         <list>
            <value>alfresco/extension/sample-model.xml</value>
         </list>
      </property>
   </bean>
</beans>
จาก code ด้านบนเมื่อ Start Alfresco แล้ว Alfresco จะ load model จากไฟล์ที่ชื่อว่า sample-model.xml ดังนั้นให้เราสร้างไฟล์ sample-model.xml วางไว้ที่เดียวกันและ config bean ดังนี้




<?xml version="1.0" encoding="UTF-8"?>
<model name="sample:sampleModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">

   <!-- Optional meta-data about the model -->
   <description>Sample Model</description>
   <author>tantai@osdev.co.th</author>
   <version>1.0</version>

   <imports>
      <!-- Import Alfresco Dictionary Definitions -->
      <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
      <!-- Import Alfresco Content Domain Model Definitions -->
      <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
      <!-- Import Alfresco BPM Model Definitions -->
      <import uri="http://www.alfresco.org/model/bpm/1.0" prefix="bpm" />
      <!-- Import Alfresco Workflow Model Definitions -->
      <import uri="http://www.alfresco.org/model/workflow/1.0" prefix="wf"/>
   </imports>

   <!-- Introduction of new namespaces defined by this model -->
   <namespaces>
      <namespace uri="http://www.osdev.co.th/sample/workflow" prefix="sample"/>
   </namespaces>

   <types>
      <type name="sample:task1">
         <title>Task1</title>
         <parent>wf:reviewTask</parent>
      </type> 
      <type name="sample:task2">
         <title>Task2</title>
         <parent>wf:reviewTask</parent>
      </type>
   </types> 
</model>

จาก code ด้านบน ผมกำหนด type ขึ้นมา 2 type คือ sample:task1 และ sample:task2  เพื่อให้ Workflow ใช้ type ดังกล่าวในการแสดงผล สังเกตุได้ว่า ผมไม่ได้สร้าง bpm:startTask เนื่องจาก bpm:startTask นั้นมีอยู่แล้วบน Alfresco (สามารถดูได้จากไฟล์ bpmModel.xml) และทั้ง 2 type ที่ผมสร้างขึ้นมาใหม่นั้นใช้ parent เป็น wf:reviewTask ซึ่งมีอยู่แล้วใน Alfresco เช่นกัน โดยเมื่อสร้าง Model เรียบร้อยแล้ว ให้สั่ง Restart Service Alfresco เพื่อให้ Alfresco load model ไปใช้งาน

Deploy Workflow

การ Deploy Workflow บน Alfresco นั้นไม่ยากครับเพราะว่า Alfresco มีหน้า workflow-console เพื่อใช้จัดการพวก Workflow อยู่แล้ว โดยขั้นตอนการ Deploy เราแค่ copy file sample.bpmn20.xml ไปไว้ที่ folder extension จากนั้นไปยัง http://{host}:{port}/alfresco/faces/jsp/admin/workflow-console.jsp เพื่อสั่ง deploy workflow ด้วยคำสั่ง
 
deploy activiti alfresco/extension/sample.bpmn20.xml 
Note: หากเข้า url ดังกล่าวไม่ได้ ให้เช้าไปที่ url http://{host}:{port}/alfresco เพื่อทำการ login ก่อนแล้วจึงเข้าไปยัง url ดังกล่าวอีกครั้ง  

โดยเมื่อสั่ง Deploy แล้ว ที่ Alfresco Share เราจะสามารถสั่ง Start Workflow Sample ที่เราทำได้
 
 แต่เมื่อเลือก Sample Workflow แล้วจะสังเกตุได้ว่า form UI ต่างๆ นั้นจะแสดง properties ทั้งหมดออกมา
ซึ่งตามความเป็นจริงแล้ว เราต้องเลือกที่จะแสดงผล properties อะไรบ้าง ซึ่งจะอธิบายใน Step ต่อไปว่าเราจะกำหนด Share UI กันอย่างไร

Configure Share UI 

 การ Config Share UI นั้นเราสามารถทำได้ที่ไฟล์ share-config-custom.xml ซึ่งอยู่ที่ TOMCAT_HOME/shared/classes/alfresco/web-extension/share-config-custom.xml โดยเราสามารถกำหนดการแสดงผลของแต่ละ form ได้ดังนี้
   
<config evaluator="string-compare" condition="activiti$sample">
   <forms>
      <form>
         <field-visibility>
            <show id="packageItems" />
            <show id="transitions" />
         </field-visibility>
         <appearance>
            <set id="" appearance="title" label-id="workflow.set.general" />
            <set id="items" appearance="title" label-id="workflow.set.items" />
            <set id="response" appearance="title" label-id="workflow.set.response" />

            <field id="packageItems" set="items" />
         </appearance>
      </form>
   </forms>
</config>

<config evaluator="task-type" condition="sample:task1">
   <forms>
      <form>
         <field-visibility>
            <show id="packageItems" />
            <show id="transitions" />
         </field-visibility>
         <appearance>
            <set id="" appearance="title" label-id="workflow.set.general" />
            <set id="items" appearance="title" label-id="workflow.set.items" />
            <set id="response" appearance="title" label-id="workflow.set.response" />

            <field id="packageItems" set="items" />
         </appearance>
      </form>
   </forms>
</config>

<config evaluator="task-type" condition="sample:task2">
   <forms>
      <form>
         <field-visibility>
            <show id="packageItems" />
            <show id="transitions" />
         </field-visibility>
         <appearance>
            <set id="" appearance="title" label-id="workflow.set.general" />
            <set id="items" appearance="title" label-id="workflow.set.items" />
            <set id="response" appearance="title" label-id="workflow.set.response" />

            <field id="packageItems" set="items" />
         </appearance>
      </form>
   </forms>
</config>

จาก config ด้านบนผมกำหนดให้ตอน startEvent, task1, task2 นั้นแสดงผล properties แค่ packageItem เท่านั้นหมายถึงผู้ Start Workflow จะ add file เข้ามาได้เพียงอย่างเดียว
โดยเมื่อ Start Workflow แล้ว Task จะวิ่งไปหา tantai ซึ่งถูกกำหนดไว้ที่ definition แล้ว โดยเมื่อ tantai login เข้ามาในระบบ จะเห็น task ดังนี้
โดยเมื่อคลิกเข้าไปก็จะเห็นหน้าตาดังที่เรา Config ไว้
โดยเมื่อกดปุ่ม Task Done, Task ก็จะวิ่งไปยัง tantai อีกครั้ง (หน้าตาเหมือนกันเพราะ Config ไว้เหมือนกัน)
โดยเมื่อกด Task Done อีกครั้งก็จะจบ Flow

Summary

โดยปกติแล้วการสร้าง Workflow จะมีขั้นตอนพื้นฐานเพียงเท่านี้ แต่หากจะต้องการให้ Workflow ของเราทำงานอื่นๆ ด้วยเช่น ต้องการส่งเมลเตือน, Generate PDF files เราจำเป็นต้องทำเองโดยกำหนด Listener ของแต่ละ userTask เช่น เมื่อจบ task1 ให้สร้าง PDF files และ mail ไปยังบุคคลที่กำหนดไว้ที่ task2 สามารถดูรายละเอียดได้จาก link นี้เลยครับ http://www.activiti.org/userguide/index.html#taskListeners ซึ่งผมแนะนำให้เขียน Listener เป็น ScriptTaskListener พอครับ ไม่จำเป็นต้องเป็น Java Class เนื่องจาก Alfresco ใช้ Rhino เป็น JavaScript Engine ทำให้เราสามารถเรียกใช้งาน Java Class ได้ผ่าน Js ทันที ในส่วนการ Customize form หากต้องการเพิ่ม properties ใดๆ ก็สามารถทำได้ที่ Model จากนั้นก็มา Config การแสดงผลที่ share-config-cuxtom.xml ได้ทันที
Continue Reading...

Blogroll

About