Memakai Bookmark Di Windows Workflow (WF)


Salah satu fitur bawaan sejak .NET Framework 3 adalah Windows Workflow (WF). Fitur yang satu ini cukup unik. Pertama, tidak seperti temannya yang disingkat WCF dan WPF, Windows Workflow cukup disingkat menjadi WF. Alasannya? Bila disingkat menjadi WWF, maka ia terlihat seperti ‘berhubungan’ dengan organisasi tinju (World Wrestling Federation) atau pencinta binatang (World Wide Fund for Nature). Kedua, teknologi kompetitor .NET seperti Java sama sekali tidak menyediakan workflow engine secara bawaan sehingga dibutuhkan tools dari pihak ketiga seperti yang disediakan Oracle (secara teknis, karena Java sudah dibeli oleh Oracle, maka score-nya seimbangšŸ˜‰. Beberapa orang lebih senang membuat workflow engine sendiri untuk mengurangi ketergantungan pada pihak ketiga. Sebagai contoh nyata, peralihan dari WF 3 ke WF 4 menimbulkan perubahan yang cukup drastis sehingga proses upgrade tidak gampang.

Apa itu workflow? Pada aplikasi sederhana untuk restoran, misalnya, pemesanan dimulai dari waitress, kemudian disajikan oleh koki, lalu dinikmati pelanggan, dan berakhir dengan dibayar oleh pelanggan melalui kasir. Proses yang butuh waktu beberapa menit hingga beberapa jam ini adalah contoh dari workflow. Pada implementasinya, kode program biasanya mencatat nilai statusTerakhir dari sebuah pemesanan, lalu mengubah state tersebut selangkah demi selangkah. Ini adalah kandidat untuk state machine workflow. Contoh ini sangat sederhana sekali sehingga penggunaan workflow engine akan terlalu berlebihan.

Lalu kapan memakai workflow engine? Tentu saja bila aplikasi memiliki workflow yang rumitšŸ™‚ Sebagai contoh, pada aplikasi pengajuan kredit di perbankan, workflow dimulai dengan aplikasi kredit, kemudian dilanjutkan dengan verifikasi, setelah data untuk verifikasi terkumpul, dibutuhkan konfirmasi manual dari credit analyst. Verifikasi terdiri atas lebih dari satu jenis aktifitas, misalnya verifikasi lapangan (seperti mengambil foto bangunan yang dijaminkan), verifikasi riwayat kartu kredit, dan sebagainya. Jumlah langkah verifikasi akan berbeda tergantung pada jenis pelanggan (misalnya corporate atau personal). Sebelum seluruh data verifikasi terkumpul (beberapa verifikasi wajib dan beberapa lagi tidak wajib, tergantung pada jenis pelanggan), eksekusi workflow tidak bisa dilanjutkan. Proses seperti ini tentunya akan lebih mudah dipahami bila dirancang dan dibuat secara visual.

Seperti apa contoh workflow? Untuk menunjukkannya, saya akan mulai dengan membuat sebuah proyek baru di Visual Studio 2010. Saya memilih Visual C#, Workflow, Workflow Console Application seperti yang terlihat pada gambar berikut ini:

Membuat proyek yang memakai workflow

Membuat proyek yang memakai workflow

Saya kemudian membuat sebuah domain class baru bernama Order dengan men-klik kanan nama proyek, memilih Add, Class…. Isi dari class tersebut adalah:

using System;

namespace LatihanBookmark
{
    public class Order
    {
        public String Id { get; set; }

        public String namaPelanggan { get; set; }

        public String menuMakanan { get; set; }

        public String namaKoki { get; set; }

        public String namaKasir { get; set; }

        public DateTime mulaiDimasak { get; set; }

        public DateTime selesaiDimasak { get; set; }

        public override string ToString()
        {
            return "ID: " + Id + "\n" +
                "Nama Pelanggan: " + NamaPelanggan + "\n" +
                "Menu Makanan: " + MenuMakanan + "\n" +
                "Nama Koki: " + NamaKoki + "\n" +
                "Nama Kasir: " + NamaKasir + "\n" +
                "Mulai Dimasak: " + MulaiDimasak.ToString("dd-MM-yyyy hh:mm") + "\n" +
                "Selesai Dimasak: " + SelesaiDimasak.ToString("dd-MM-yyyy hh:mm");
        }
    }
}

Class di atas adalah contoh rancangan yang buruk dan tidak rapi, tapi fokus saya adalah pada workflow sehingga saya bisa mengabaikan masalah tersebut.

Berikutnya, saya men-klik kanan file Workflow1.xaml dan memilih Rename untuk mengubah nama file tersebut menjadi ProsesPemesanan.xaml. Sama seperti pada WPF, rancangan workflow ditulis dalam format XAML (Extensible Application Markup Language). XAML sebenarnya adalah sebuah teknologi untuk membuat object dan mengisi property-nya melalui XML. Walaupun ia lebih dikenal berkat WPF, XAML tidak hanya terbatas dipakai untuk merancang form. Untuk melihat XAML dari sebuah workflow, saya dapat men-klik kanan dan memilih menu View Code. Pada XAML ini, saya perlu mengubah nilai atribut x:Class pada <Activity> di baris paling awal menjadi seperti berikut ini:

<Activity mc:Ignorable="sads sap" x:Class="LatihanBookmark.ProsesPemesanan" 
...

Sekarang, saya kembali melihat visualisinya di workflow designer. Untuk itu, saya men-klik kanan file ProsesPemesanan.xaml dan memilih menu View Designer.

Sebuah workflow dapat memiliki parameter. Sebagai contoh, pada proses pemesanan, saya dapat memakai sebuah instance dari object Order sebagai argumen. Saya segera mendefinisikan parameter dengan men-klik Arguments di bagian bawah designer dan mengisinya seperti yang terlihat pada gambar berikut ini:

Menambah parameter untuk workflow

Menambah parameter untuk workflow

Sebelum memilih Argument Type, saya men-build proyek terlebih dahulu dengan men-klik menu Build, Build Solution. Setelah itu, pada pilihan di Argumen Type, saya memilih Browse For Types.. untuk memilih Order.

Saya kemudian merancang workflow untuk proses pemesanan yang terlihat seperti pada gambar berikut ini:

Contoh rancangan flowchart workflow

Contoh rancangan flowchart workflow

Untuk menjalankan workflow, saya mengubah kode program di Programs.cs menjadi seperti berikut ini:

using System;
using System.Activities;
using System.Collections.Generic;
using System.Threading;

namespace LatihanBookmark
{

    class Program
    {
        static void Main(string[] args)
        {
            Order contohOrder = new Order
            {
                Id = "ORDER1",
                menuMakanan = "Tasty Food",
                namaPelanggan = "A Snake"
            };

            var argumen = new Dictionary<string, object>()
            {
                { "order", contohOrder }
            };


            AutoResetEvent syncEvent = new AutoResetEvent(false);
            WorkflowApplication app = new WorkflowApplication(new ProsesPemesanan(), argumen);
            app.Completed = (e) => syncEvent.Set();
            app.Run();

            // Menunggu hingga workflow selesai dikerjakan
            syncEvent.WaitOne();

            Console.ReadLine();
        }
    }
}

Kode program bawaan memakai WorkflowInvoker.Invoke() untuk menjalankan workflow. Method tersebut akan menunggu hingga workflow selesai dikerjakan baru lanjut ke eksekusi baris berikutnya (bersifat synchronous). Pada kode program di atas, saya membuat instance WorkflowApplication dan memanggil method Run() miliknya. Method tersebut tidak akan menunggu hingga workflow selesai dikerjakan tetapi langsung lanjut ke eksekusi baris berikutnya. Oleh sebab itu, saya memakai AutoResetEvent untuk menunggu hingga eksekusi workflow selesai dikerjakan.

Bila saya menjalankan program di atas, maka workflow akan langsung berjalan hingga tahap terakhir yang men-cetak “Order ORDER1 selesai diproses!”. Ini bukan sesuatu yang diharapkan, bukan?

Saya men-double click sequence Periksa Ketersediaan Bahan dan menambahkan rancangan seperti yang terlihat pada gambar berikut ini:

Contoh rancangan workflow

Contoh rancangan workflow

Pada sequence di atas, agar sederhana, saya hanya memeriksa nama menu dan membatalkan workflow berdasarkan nama menu. Pada kasus nyatanya, saya perlu memeriksa ketersediaan bahan di database (dan sejenisnya).

Berikutnya, saya perlu membuat detail untuk sequence Mulai dimasak. Bagian ini sedikit membingungkan: saya harus menunggu konfirmasi dari koki bahwa order sedang diproses olehnya! Tapi saat ini, workflow saya hanyalah imitasi sebuah method. Workflow yang sesungguhnya adalah proses yang berjalan selama jangka waktu lama yang tidak dapat dicakup oleh sebuah method. Terkadang workflow perlu ditunda (di-suspend) untuk dilanjutkan (di-resume) oleh pihak terkait. Untuk keperluan tersebut, saya dapat memakai bookmark.

Sayang sekali workflow designer tidak menyediakan komponen untuk membuat bookmark sehingga saya perlu membuat custom activity baru dengan nama MulaiDimasak yang isinya seperti berikut ini:

using System;
using System.Activities;

namespace LatihanBookmark
{
    public class MulaiDimasak : NativeActivity
    {
        protected override void Execute(NativeActivityContext context)
        {
            context.CreateBookmark("MulaiDimasak", new BookmarkCallback(OnResume));
        }

        protected override bool CanInduceIdle
        {
            get
            {
                return true;
            }
        }

        public void OnResume(NativeActivityContext context, Bookmark bookmark, object koki)
        {
            WorkflowDataContext dataContext = context.DataContext;
            Order order = (Order) dataContext.GetProperties()
                                  .Find("order", false).GetValue(dataContext);
            order.NamaKoki = (string) koki;
            order.MulaiDimasak = DateTime.Now;                      
            Console.WriteLine("{0} sudah mulai memasak...", koki);
        }
    }
}

Pada NativeActivity di atas, saya membuat bookmark baru dengan memakai context.CreateBookMark(). Pada bookmark tersebut, saya menyertakan sebuah callback yaitu OnResume. Dengan demikian, pada saat seseorang me-resume bookmark tersebut, maka kode program di method OnResume() akan dikerjakan. Method tersebut akan mengisi property NamaKoki dan MulaiDimasuk dari order yang diberikan sebagai argumen pada workflow ini.

Saya kemudian men-build proyek dengan memilih menu Build, Build Solution. Saat saya kembali workflow designer untuk ProsesPemesanan, saya akan menemukan item baru di Toolbox dengan nama MulaiDimasak. Saya akan memakainya untuk menggantikan sequence yang sudah ada sebelumnya seperti yang terlihat pada gambar berikut ini:

Memakai custom activity di workflow designer

Memakai custom activity di workflow designer

Bila saya menjalankan program, maka kali ini tidak akan ada output yang muncul di layar karena program sedang menunggu hingga seseorang me-resume bookmark dengan nama MulaiDimasak. Pada kenyataannya, aplikasi di koki yang perlu me-resume bookmark ini pada saat koki men-klik tombol tertentu. Pada contoh sederhana disini, saya akan me-resume bookmark secara manual pada saat itu juga, dengan mengubah kode program di Program.cs menjadi seperti berikut ini:

using System;
using System.Activities;
using System.Collections.Generic;
using System.Threading;

namespace LatihanBookmark
{

    class Program
    {
        static void Main(string[] args)
        {
            Order contohOrder = new Order
            {
                Id = "ORDER1",
                MenuMakanan = "Tasty Food",
                NamaPelanggan = "A Snake"
            };

            var argumen = new Dictionary<string, object>()
            {
                { "order", contohOrder }
            };


            AutoResetEvent syncEvent = new AutoResetEvent(false);
            WorkflowApplication app = new WorkflowApplication(new ProsesPemesanan(), argumen);
            app.Completed = (e) => syncEvent.Set();
            app.Run();

            app.ResumeBookmark("MulaiDimasak", "Master Chef");

            // Menunggu hingga workflow selesai dikerjakan
            syncEvent.WaitOne();

            Console.WriteLine("Workflow sudah ditutup.\n\nIsi Contoh Order adalah:");
            Console.WriteLine(contohOrder);
            Console.ReadLine();
        }
    }
}

Method ResumeBookmark() akan menunggu hingga ada bookmark dengan nama MulaiDimasak yang dapat di-resume.

Hasil dari kode program di atas akan terlihat seperti berikut ini:

Master Chef sudah mulai memasak...
Order ORDER1 selesai diproses!
Workflow sudah ditutup.

Isi Contoh Order adalah:
ID: ORDER1
Nama Pelanggan: A Snake
Menu Makanan: Tasty Food
Nama Koki: Master Chef
Nama Kasir:
Mulai Dimasak: 18-01-2014 11:00
Selesai Dimasak: 01-01-0001 12:00

Sama seperti pada langkah sebelumnya, saya juga perlu mengubah sequence Selesai Dimasak menjadi custom activity yang memakai bookmark. Hal ini karena ia perlu menunggu hingga koki mengisyaratkan bahwa makanan telah selesai dimasak. Oleh sebab itu, saya membuat class SelesaiDimasak yang isinya seperti berikut ini:

using System;
using System.Activities;

namespace LatihanBookmark
{
    public class SelesaiDimasak : NativeActivity
    {
        protected override void Execute(NativeActivityContext context)
        {
            context.CreateBookmark("SelesaiDimasak", new BookmarkCallback(OnResume));
        }

        protected override bool CanInduceIdle
        {
            get
            {
                return true;
            }
        }

        public void OnResume(NativeActivityContext context, Bookmark bookmark, object koki)
        {
            WorkflowDataContext dataContext = context.DataContext;
            Order order = (Order)dataContext.GetProperties()
                                  .Find("order", false).GetValue(dataContext);
            order.SelesaiDimasak = DateTime.Now;            
            Console.WriteLine("Order sudah mulai dimasak...");
        }
    }
}

Sequence Pelanggan Membayar juga harus membuat bookmark karena ia perlu menunggu hingga pelanggan selesai makan dan membayar di kasir. Untuk itu, saya membuat class PelangganMembayar yang isinya seperti berikut ini:

using System;
using System.Activities;

namespace LatihanBookmark
{
    public class PelangganMembayar : NativeActivity
    {
        protected override void Execute(NativeActivityContext context)
        {
            context.CreateBookmark("PelangganMembayar", new BookmarkCallback(OnResume));
        }

        protected override bool CanInduceIdle
        {
            get
            {
                return true;
            }
        }

        public void OnResume(NativeActivityContext context, Bookmark bookmark, object namaKasir)
        {
            WorkflowDataContext dataContext = context.DataContext;
            Order order = (Order)dataContext.GetProperties()
                                  .Find("order", false).GetValue(dataContext);
            order.NamaKasir = (string) namaKasir;
            Console.WriteLine("Pelanggan sudah melunasi tagihannya...");
        }
    }
}

Seperti biasa, saya men-build ulang proyek. Lalu pada workflow designer, saya memakai custom activity yang sudah saya buat sebelumnya sehingga terlihat seperti pada gambar berikut ini:

Memakai custom activity di workflow designer

Memakai custom activity di workflow designer

Sebagai langkah terakhir, saya perlu mensimulasikan proses resume dari bookmark yang sudah dibuat sebelumnya. Caranya adalah dengan menambahkan kode program berikut ini pada Programs.cs:

...
app.ResumeBookmark("MulaiDimasak", "Master Chef");
app.ResumeBookmark("SelesaiDimasak", "Master Chef");
app.ResumeBookmark("PelangganMembayar", "Cute Girl");
...

Bila saya menjalankan program, saya akan memperoleh hasil seperti berikut ini:

Master Chef sudah mulai memasak...
Order sudah mulai dimasak...
Pelanggan sudah melunasi tagihannya...
Order ORDER1 selesai diproses!
Workflow sudah ditutup.

Isi Contoh Order adalah:
ID: ORDER1
Nama Pelanggan: A Snake
Menu Makanan: Tasty Food
Nama Koki: Master Chef
Nama Kasir: Cute Girl
Mulai Dimasak: 17-01-2014 10:00
Selesai Dimasak: 17-01-2014 10:10

Bila saya menghilangkan salah satu dari ResumeBookmark() di Programs.cs, maka workflow proses pemesanan tidak akan pernah selesai dikerjakan karena ia akan terus menunggu hingga seseorang men-resume bookmark-nya.

Perihal Solid Snake
I'm nothing...

One Response to Memakai Bookmark Di Windows Workflow (WF)

  1. Ping-balik: Memakai Persistence Di Windows Workflow | The Solid Snake

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: