Sunday, March 15, 2015

Penggunaan Bounded dan Unbounded TaskFlow

ADF Taskflow menyediakan pendekatan modular dalam pengembangan dan pengaturan flow dalam aplikasi. Daripada membangun 1 buah page JSF raksasa yang berisi seluruh fungsionalitas web kita, akan lebih baik apabila kita memecah-mecahkan page tersebut menjadi sekumpulan taskflow yang reusable. Hal ini memudahkan kita untuk membagi tugas dalam proses development dan juga me-reuse modul-module yang telah kita pecah menjadi task flow. 

Dalam implementasinya terdapat 2 macam task flow, yaitu Bounded Task Flow dan Unbounded Task Flow. Saya akan menjelaskan perbedaan Bounded dan Unbounded Task Flow dan kapan kedua task flow ini dapat dipakai. Task Flow terdiri dari komponen-komponen seperti Activity dan Control Flow, yang kemudian dirangkai menjadi Navigational Graph sebuah aplikasi. 


1 aplikasi besar biasanya terdiri dari 1 Unbounded Task Flow dan banyak Bounded Task Flow. Bounded task flow merepresentasikan module-module atau task spesifik (misal: registrasi, log in, cek saldo dsb), sedangkan unbounded task flow menggambarkan alur keseluruhan aplikasi (Misal: aplikasi bank dapat mengakses task flow Log In, kemudian dapat dinavigasikan ke halaman Registrasi atau Cek Saldo). 


Secara garis besar, Task Flow memiliki keuntungan berikut:
  • Aplikasi dapat dipecah menjadi flow modular yang dapat saling memanggil
  • Task flow dapat di-reuse dalam aplikasi yang sama ataupun di-export dan lalu digunakan pada aplikasi lain
  • Shared memory scope, memungkinkan pertukaran data antar task flow. Misal: variable yang didefinisikan sebagai Page Flow Scope.


Unbounded task flow digunakan untuk kebutuhan sebagai berikut:      
  • Menggunakan fitur ADF Controller yang tidak ada pada Bounded Task Flow seeprti Bookmarking.
  • Task flow tidak akan dipanggil oleh task flow lain.
  • Mempunyai berbagai macam Point of Entry/ dapat diakses dari halaman mana saja dari web page kita.

Sedangkan Bounded task flow biasanya digunakan dalam kasus berikut:
  • Module hanya memiliki 1 entry point. Misal: proses ijin cuti hanya dimulai dari pengisian data karyawan. Ini dapat dijadikan bounded task flow dengan default activity adalah pengisian data karyawan.
  • Dapat menerima input parameter. Kita dapat mendefinisikan Input parameter untuk setiap bounded task flow.
  • Memiliki return value. Kita juga dapat mendefinisikan Output parameter untuk bounded task flow.
  • Module akan direuse dalam module lain. Misal: module Request Cuti dan Request Kas Bon sama-sama memerlukan Entry Data Karyawan, maka Entry Data Karyawan dapat dijadikan Bounded Task Flow yang akan di-consume oleh 2 aplikasi lain.


Ketika mengerjakan aplikasi dalam ADF, langkah pertama adalah menentukan pembagian fungsionalitas dan module. Kita harus menentukan mana saja yang dapat dimasukkan sebagai Bounded Task Flow lalu kemudian merangkai alurnya navigasi dalam Unbounded Task Flow. Setelah kita menentukan berdasarkan kriteria diatas, proses development dapat dilakukan masing-masing developer berdasarkan Bounded Task Flow yang ada, lalu merangkainya ke dalam Unbounded Task Flow ketika proses developmentnya selesai.

Sample aplikasi akan saya contohkan dalam blog berikutnya. Semoga bermanfaat!











SSL Offloading with HAProxy


Secure Socket Layer (SSL) merupakan sebuah standardisasi komunikasi yang aman antara sebuah client dengan sebuah server dengan menggunakan enkripsi asimetris (pasangan public/private key). Tanpa SSL, komunikasi client dengan server biasa dilakukan melalui plain text. Sehingga pertukaran data yang sensitif seperti informasi kartu kredit, identitas, dan data sensitif lainnya dapat ditangkap oleh pihak ketiga yang tidak bertanggung jawab [1].

HTTP Secure (HTTPS) merupakan protokol Web HTTP yang telah dilengkapi dengan fitur SSL. Adalah suatu kewajiban bagi service provider untuk menggunakan SSL jika service yang akan digunakan dikonsumsi oleh khalayak umum. Untuk itulah kita patut mencurigai website e-banking atau media sosial yang tidak memiliki awalan https pada URL lengkapnya, karena artinya segala bentuk pertukaran data yang terjadi pada website tersebut dilakukan tanpa enkripsi apapun. Tentunya kita tidak ingin kan password kita yang sangat rahasia dikirim ke server dalam bentuk plain text? :)

Artikel kali ini saya tidak akan membahas secara lebih mendalam mengenai apa itu SSL serta teknologi dan sejarahnya. Namun saya ingin berbagi mengenai bagaimana cara mengkonfigurasi SSL pada aplikasi sehingga pertukaran data lebih aman. Konfigurasi SSL dapat dilakukan langsung di tier aplikasi backend (Tomcat, WebLogic, Glassfish, dsb.) atau dilakukan di tier load balancer/proxy (HAProxy, Nginx, Apache HTTP) sebagai SSL offloader. Artikel ini akan membahas bagaimana saya mengkonfigurasi SSL pada HAProxy.

Setidaknya ada 3 keuntungan yang dapat diperoleh dengan mengkonfigurasi SSL terminator pada HAProxy:
  1. Konfigurasi yang lebih mudah dan lugas.
  2. Terpusat pada load balancer, sehingga konfigurasi tidak perlu dilakukan kembali jika ada penambahan backend server.
  3. Resource pada backend server tidak perlu digunakan untuk proses mengenkripsi/mendekripsi request/response, sehingga dapat lebih fokus menjalankan beban aplikasi.

Sebagai gambaran, saya akan menggunakan topologi yang dibahas pada artikel saya sebelumnya [2]. Pada topologi tersebut, URL yang diakses oleh public adalah https://api.example.co.id. Dengan asumsi kedua backend Tomcat server tersebut berada di lingkungan yang secure (firewall, intranet, dsb.), HAProxy meneruskan request tersebut dengan menggunakan protokol HTTP ke kedua Tomcat tersebut. Pada topologi ini jelas terlihat bahwa komunikasi berbasis SSL dihentikan (terminate) pada node HAProxy.

Langkah-langkah yang perlu dilakukan adalah [4]:
  1. Membuat private key
  2. Membuat Certificate Signing Request (CSR)
  3. Membeli SSL certificate berdasarkan CSR 
  4. Instalasi SSL certificate di HAProxy
Detil dari setiap langkah tersebut akan dijabarkan pada bagian berikutnya.


1. Membuat Private Key


Keseluruhan langkah-langkah ini membutuhkan command openssl. Sehingga kita harus memastikan OpenSSL package terinstalasi di server. Private key dibuat dengan cara menggunakan command berikut ini:
okky@hap01:~$ sudo mkdir /etc/ssl/example.co.id
okky@hap01:~$ sudo openssl genrsa -out "/etc/ssl/example.co.id/example_co_id.key" 2048

2. Membuat Certificate Signing Request (CSR)


CSR diperlukan sebagai input file bagi Certificate Authority (CA) untuk menandatangani SSL certificate kita. CSR ini berisi informasi terkait website domain spesifik yang ingin kita buat koneksi SSL-nya. Dalam artikel ini domain yang akan dibuat SSL certificate-nya adalah domain example.co.id.
okky@hap01:~$ sudo openssl req -new -key "/etc/ssl/example.co.id/example_co_id.key" -out "/etc/ssl/example.co.id/example_co_id.csr"
Country Name (2 letter code) [AU]:ID
State or Province Name (full name) [Some-State]: DKI Jakarta
Locality Name (eg, city) []: Jakarta
Organization Name (eg, company) [Internet Widgets Pty Ltd]: Nostratech Example
Organizational Unit Name (eg, section) []: IT Department
Common Name (e.g. server FQDN or YOUR name) []: *.example.co.id
Email Address []: it-admin@nostratech.com

Please enter the following 'extra' attributes
 to be sent with your certificate request
 A challenge password []: S4n64tR4h4s14
 An optional company name []: Nostratech Example

3. Membeli SSL Certificate Berdasarkan CSR


Setelah CSR telah dibuat, proses selanjutnya adalah pembelian SSL certificate ke CA yang diinginkan, misalnya AlphaSSL [3]. Kemudian CA memberikan file SSL certificate yang akan saya rename menjadi example_co_id.crt. HAProxy bisa membaca private key dan certificate sekaligus dalam 1 file .pem, dengan cara menggabungkan kedua file tersebut dengan command cat.
okky@hap01:~$ sudo su
root@hap01:/home/okky$ sudo cat /etc/ssl/example.co.id/example_co_id.crt /etc/ssl/example.co.id/example_co_id.key > /etc/ssl/example.co.id/example_co_id.pem
root@hap01:/home/okky$ exit

Alternatif lainnya jika kita belum bisa membeli SSL certificate untuk kebutuhan development misalnya, adalah dengan menggunakan self-signed certificate.
okky@hap01:~$ sudo openssl x509 -req -days 365 -in "/etc/ssl/example.co.id/example_co_id.csr" -signkey "/etc/ssl/example.co.id/example_co_id.key" -out "/etc/ssl/example.co.id/example_co_id.crt"

4. Instalasi SSL/Self-Signed Certificate di HAProxy


Ubah konfigurasi HAProxy pada bagian frontend dari Tomcat menjadi seperti ini. Jika pada langkah sebelumnya yang dilakukan adalah membeli SSL certificate dari CA, maka lampirkan juga file CAroot.crt yang merupakan intermediary CA certificate.
frontend tomcat
    bind *:80
    bind *:443 ssl crt /etc/ssl/example_co_id.pem ca-file /etc/ssl/CAroot.crt
    mode http
    option httplog
    option http-server-close
    redirect scheme https if !{ ssl_fc }
    default_backend tomcat-cluster

backend tomcat-cluster
    mode http
    option httplog
    option httpchk HEAD /gojek HTTP/1.1\r\nHost:localhost
    compression algo gzip
    compression offload
    compression type text/html text/plain text/css text/js application/json
    option forwardfor
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    cookie SERVERID insert indirect nocache
    balance roundrobin
    server tom01 tom01:8080 check cookie tom01
    server tom02 tom02:8080 check cookie tom02


Restart service HAProxy dan selamat mencoba! :)

Hibernate - Optimisasi query

Pada blog kali ini saya akan memberikan tips untuk meningkatkan performance Hibernate aplikasi.

Skenario :
Kita mempunyai tabel dengan Department(Master) dan Employee(Detail).
Pada aplikasi akan menampilkan semua data pada Master dan Detail. Jika aplikasi berjalan sangat lamban mungkin hal ini terjadi dikarenakan Hibernate mengeksekusi query yang tidak diharapkan. Hal ini terjadi dikarenakan relasi one to many atau one to one antar master dan detail. Pertama - tama kita lakukan konfigurasi hibernate.cfg.xml dengan menambahkan :

<property name="hibernate.show_sql" value="true" />

Jika di set true SQL statements akan ditampilkan di console.

Entity class Department :
 

package com.byteslounge.spring.tx.model;
import java.util.Set;
/**
 * Created by nostra on 1/14/15.
 */
import javax.persistence.*;

@Entity
public class Department
{
    @Id
    @GeneratedValue( strategy=GenerationType.AUTO )
    private int id;
    @OneToMany(mappedBy = "eid",fetch = FetchType.EAGER)
    private Set<Employee> employeeCollection;
    private String name;

    public int getId()
    {
        return id;
    }

    public void setId(int id)
    {
        this.id = id;
    }

    public String getName( )
    {
        return name;
    }

    public void setName( String deptName )
    {
        this.name = deptName;
    }

    public Set getEmployeeCollection() {
        return employeeCollection;
    }

    public void setEmployeeCollection(Set employeeCollection) {
        this.employeeCollection = employeeCollection;
    }
}
Entity Class Employee:
 
package com.byteslounge.spring.tx.model;

import javax.persistence.*;

/**
 * Created by nostra on 1/14/15.
 */
@Entity
public class Employee
{
    @Id
    @GeneratedValue( strategy= GenerationType.AUTO )
    private int eid;
    private String ename;
    private double salary;
    private String deg;
    @ManyToOne
    private Department department;

    public Employee(int eid,
                    String ename, double salary, String deg)
    {
        super( );
        this.eid = eid;
        this.ename = ename;
        this.salary = salary;
        this.deg = deg;
    }

    public Employee( )
    {
        super();
    }

    public int getEid( )
    {
        return eid;
    }
    public void setEid(int eid)
    {
        this.eid = eid;
    }

    public String getEname( )
    {
        return ename;
    }
    public void setEname(String ename)
    {
        this.ename = ename;
    }

    public double getSalary( )
    {
        return salary;
    }
    public void setSalary(double salary)
    {
        this.salary = salary;
    }

    public String getDeg( )
    {
        return deg;
    }
    public void setDeg(String deg)
    {
        this.deg = deg;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
}
Java Code :
 
      List <department> listDeprt= sess.createQuery("from department").list();
             for(Department dept : listDeprt){
            for(Employee emp : dept.getEmployeeCollection()){
                //do Something
            }
        }

Hibernate akan mengeksekusi 1+ N (jumlah row di Department) select statement. 1 Untuk mendapatkan data Department dan N untuk Employee. Jika ada 20 data pada tabel Master maka hibernate akan mengeksekusi 1+20 select statement.

Dan untuk solusi ini adalah:

Pertama dengan menggunakan @BatchSize
@OneToMany(mappedBy = "eid",fetch = FetchType.EAGER)
@BatchSize(size = 10) 
private Set<Employee> employeeCollection;
Jika terdapat 20 data pada tabel master maka select statement yang dihasilkan adalah 3 statement, Keterangan:

  • Select statement untuk mendapatkan semua data Department(Master).
  • Select statement untuk 10 data pertama.
  • Select statement untuk 10 data selanjutnya.

Kedua dengan menggunakan @Fetch(FetchMode.JOIN)
  @OneToMany(mappedBy = "eid")
  @Fetch(FetchMode.JOIN)
  private Set employeeCollection;
Maka hibernate akan mengeksekusi satu query (join column) .


Sekian blog dari saya, semoga bermanfaat :)

Synchronizing Multiple VIP Failover with Keepalived


Tepat setahun yang lalu, saya sempat menuangkan langkah-langkah bagaimana mengkonfigurasi 2 node HAProxy cluster secara active/passive dengan menggunakan Keepalived [1]. Konfigurasi tersebut dapat digunakan ketika HAProxy berada di belakang DMZ. Namun bagaimana jika HAProxy dan Keepalived berada di DMZ dan harus me-manage 2 VIP address? Misalnya pada kasus 1 public VIP address sebagai frontend IP address yang diakses oleh client dan 1 private VIP address yang diakses oleh aplikasi backend untuk komunikasi dengan database. Dalam konfigurasi ini, Keepalived harus mengatur failover kedua VIP secara bersamaan, dengan asumsi ketika salah satu VIP (public atau private) fail di node master, maka kedua VIP akan pindah ke node backup.

Gambar berikut ini menunjukkan topologi HAProxy sebagai load balancer di sisi public network.
HAProxy/Keepalived with Public VIP

Sedangkan gambar berikut menunjukkan topologi HAProxy ketika berperan sebagai load balancer di sisi private network.
HAProxy/Keepalived with Private VIP

Keepalived memiliki subblock configuration untuk kebutuhan ini, yaitu dengan menggunakan VRRP instance synchronization group subblock vrrp_sync_group [2][3].

Berikut adalah konfigurasi Keepalived yang digunakan di node master:
okky@hap01:~$ cat /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
 script "killall -0 haproxy"
 interval 2
 weight 2
}

vrrp_sync_group VSG {
 group {
  VI_PUBLIC
  VI_PRIVATE
 }
}

vrrp_instance VI_PUBLIC {
 interface eth0
 state MASTER
 virtual_router_id 51
 priority 101
 virtual_ipaddress {
  100.100.100.100/24 brd 100.100.100.255 dev eth0
 }
 track_script {
  chk_haproxy
 }
}

vrrp_instance VI_PRIVATE {
 interface eth1
 state MASTER
 virtual_router_id 52
 priority 101
 virtual_ipaddress {
  192.168.1.100/24 brd 192.168.1.255 dev eth1
 }
 track_script {
  chk_haproxy
  }
}

Sedangkan konfigurasi di node backup adalah sebagai berikut:
okky@hap02:~$ cat /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
 script "killall -0 haproxy"
 interval 2
 weight 2
}

vrrp_sync_group VSG {
 group {
  VI_PUBLIC
  VI_PRIVATE
 }
}

vrrp_instance VI_PUBLIC {
 interface eth0
 state BACKUP
 virtual_router_id 51
 priority 50
 virtual_ipaddress {
  100.100.100.100/24 brd 100.100.100.255 dev eth0
 }
 track_script {
  chk_haproxy
 }
}

vrrp_instance VI_PRIVATE {
 interface eth1
 state BACKUP
 virtual_router_id 52
 priority 50
 virtual_ipaddress {
  192.168.1.100/24 brd 192.168.1.255 dev eth1
 }
 track_script {
  chk_haproxy
 }
}

Restart service Keepalived di kedua node (master dan backup) setelah melakukan perubahan konfigurasi Keepalived tersebut. Output dari command ip addr show pada node master adalah seperti berikut:
okky@hap01:~$ ip addr show
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: venet0:  mtu 1500 qdisc noop state DOWN
    link/void
3: eth0:  mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 00:18:51:47:76:40 brd ff:ff:ff:ff:ff:ff
    inet 100.100.100.101/24 brd 100.100.100.255 scope global eth0
    inet 100.100.100.100/24 brd 100.100.100.255 scope global secondary eth0
    inet6 fe80::218:51ff:fe47:7640/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever
4: eth1:  mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 00:18:51:a2:f0:65 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.101/24 brd 192.168.1.255 scope global eth1
    inet 192.168.1.100/24 brd 192.168.1.255 scope global secondary eth1
    inet6 fe80::218:51ff:fea2:f065/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever

Sedangkan output command tersebut pada node backup adalah:
okky@hap02:~$ ip addr show
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: venet0:  mtu 1500 qdisc noop state DOWN
    link/void
3: eth0:  mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 00:18:51:54:c7:5f brd ff:ff:ff:ff:ff:ff
    inet 100.100.100.102/24 brd 100.100.100.255 scope global eth0
    inet6 fe80::218:51ff:fe54:c75f/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever
4: eth1:  mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 00:18:51:49:a2:f1 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.102/24 brd 192.168.1.255 scope global eth1
    inet6 fe80::218:51ff:fe49:a2f1/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever

Selamat mencoba! :)

[1] Active Passive HAProxy with Keepalived
[2] LVS NAT + Keepalived HOWTO

Redis dan Java dengan framework Spring

Pada kali ini saya ingin menunjukan kolaborasi Redis dan Java dengan framework Spring dengan Maven sebagai dependency management-nya. Merujuk pada website redis.io/clients#java, saya menggunakan Jedis untuk berinteraksi dengan server Redis.

Jedis saya pilih karena sudah tersedia pada Maven public repository dan hampir semua Redis Command sudah didukung pada library ini.

Untuk mulai menggunakan Jedis, tambahkan dependency Jedis pada file pom.xml Maven.
<dependency>
   <groupid>redis.clients</groupid>
   <artifactid>jedis</artifactid>
   <version>2.6.2</version>
</dependency>

Kemudian tambahkan konfigurasi bean untuk object JedisPool pada Spring context.
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
   <property name="testOnBorrow" value="true" />
   <property name="testOnReturn" value="true" />
   <property name="testWhileIdle" value="true" />
   <property name="numTestsPerEvictionRun" value="10" />
   <property name="maxTotal" value="100"/>
</bean>
 
<bean id="redisConnectionPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy">
   <constructor-arg index="0" ref="jedisPoolConfig" />
   <constructor-arg index="1" value="localhost" type="java.lang.String"/>
   <constructor-arg index="2" value="6379" type="int"/>
</bean>

Dan dengan memanfaatkan anotasi @Autowired pada Spring, object JedisPool bisa lansung dipakai untuk mengambil koneksi ke server Redis.
   @Autowired
   @Qualifier(value = "redisConnectionPool")
   JedisPool redisConnectionPool;


Berikut contoh pemanggilan perintah Redis RPUSH dengan menggunakan Jedis.
   public void rpush(String key, String value) {
      Jedis jedis = null;
      try {
         jedis = redisConnectionPool.getResource();    //ambil koneksi dari pool
         jedis.rpush(key, value);                      //RPUSH value ke list key
      } catch (JedisConnectionException jce) {
         jce.printStackTrace());
      } finally {
         if(null != jedis) {
            redisConnectionPool.returnResource(jedis); //kembalikan koneksi ke pool
         }
      }
   }


Sekian blog kali ini, saya senang apabila ada yang ingin berdiskusi lebih lanjut tentang Redis. Silahkan komen pada blog ini.

Semoga bermanfaat. :)