Living life and Make it Better

life, learn, contribute

Endy Muhardin

Software Developer berdomisili di Jabodetabek, berkutat di lingkungan open source, terutama Java dan Linux.

Menggunakan HermesJMS

Kalau kita ingin membuat aplikasi menggunakan database, tentunya hal pertama yang kita lakukan adalah menginstal server database. Selanjutnya, langkah kedua adalah menginstal aplikasi untuk mengelola database. Paling tidak, kita harus bisa membuat tabel, melihat isi tabel, mengisi ataupun menghapus data ke dalam tabel.

Demikian juga kalau kita membangun aplikasi menggunakan JMS (Java Messaging Service). Sebelum membuat Hello JMS, terlebih dulu kita harus menginstal JMS server dan aplikasi yang dapat melihat isi JMS Server tersebut.

Pada artikel kali ini, kita akan menginstal JMS Server ActiveMQ dan aplikasi pengelolaan JMS yang bernama HermesJMS.

ActiveMQ dan HermesJMS dapat diunduh di websitenya masing-masing.

Setelah diunduh, terlebih dulu kita instal ActiveMQ. Caranya sangat gampang, cukup diekstrak saja. Saya meletakkannya di folder /opt

<code>cd /opt
sudo tar xvzf apache-activemq-4.1.1.tar.gz
</code>

Setelah diekstrak, kita bisa langsung jalankan dari folder instalasinya.

<code>cd /opt/apache-activemq-4.1.1
bin/activemq</code>

Kalau sukses, akan muncul log message seperti ini:

<code>INFO  TransportServerThreadSupport   - Listening for connections at: tcp://sweetdreams:61616
INFO  TransportConnector             - Connector openwire Started
INFO  TransportServerThreadSupport   - Listening for connections at: ssl://sweetdreams:61617
INFO  TransportConnector             - Connector ssl Started
INFO  TransportServerThreadSupport   - Listening for connections at: stomp://sweetdreams:61613
INFO  TransportConnector             - Connector stomp Started
INFO  NetworkConnector               - Network Connector default-nc Started
INFO  BrokerService                  - ActiveMQ JMS Message Broker (localhost, ID:sweetdreams-32991-1188983887811-1:0) started
</code>

Server kita sudah siap diakses di port 61616.

Sekarang kita akan menginstal HermesJMS. Inipun instalasinya tidak sulit, cukup jalankan saja installernya.

<code>java -jar hermes-installer-1.12.jar</code>

Nantinya akan muncul layar instalasi. Klik saja Next … Next … seperti biasa sampai selesai.

Setelah Hermes berhasil diinstal, jalankan Hermes. Berikut adalah tampilan awalnya. Hello Hermes

Selanjutnya, kita akan menambahkan koneksi ke server ActiveMQ kita. Buka menu Options - Preferences, kemudian pilih tab Classpath di sisi bawah panel. Belum ada classpath group

Selanjutnya, klik kanan di panel kosong tersebut dan masukkan classpath group baru bernama ActiveMQ. Tambah Classpath Group

Nanti akan muncul entri ActiveMQ dengan tab dibawahnya yang bertulisan Library. Klik kanan tepat pada tulisan Library dan pilih Add Jar. Kita harus menambahkan beberapa jar berikut:

  • apache-activemq-4.1.1.jar

  • backport-util-concurrent-2.1.jar

  • geronimo-j2ee-management_1.0_spec-1.0.jar

Semua file tersebut dapat ditemukan di lokasi instalasi ActiveMQ.

Setelah konfigurasi dilakukan, layar tampilannya akan tampak seperti ini: Classpath ActiveMQ

Tutup layar Preference, dan buka lagi. Entah kenapa kalau tidak ditutup dulu, pilihan ActiveMQ tidak akan keluar.

Di layar Preference tab Session, ketikkan nama Session yang baru, yaitu ActiveMQ. Klik Apply, nantinya Hermes akan menanyakan apakah ini session baru atau bukan. Jawab saja, “Ya, ini adalah session baru”.

Selanjutnya, ganti pilihan Loader menjadi ActiveMQ, dan pilih ActiveMQ Connection Factory pada pilihan Class. Hasilnya akan nampak seperti ini: Session Baru ActiveMQ

Setelah selesai, session ActiveMQ akan muncul di panel sebelah kiri. Konfigurasi Session ActiveMQ

Kita bisa membuat queue baru di session tersebut. Gunakan tombol Create New Queue. Membuat Queue

Pembuatan queue pada layar ini bersifat sementara. Artinya, bila HermesJMS dimatikan, Queuenya akan hilang. Untuk membuat Queue yang permanen, kita dapat mendefinisikannya pada layar konfigurasi session.

Queue yang sudah dibuat dapat dilihat di panel sebelah kiri. Queue Browser

Pada queue yang sudah terbentuk, kita bisa mengirim Text Message. Gunakan menu Messages - Send TextMessages. Kita harus memilih satu text file untuk dikirim.

Setelah pengiriman selesai dilakukan, refresh tampilan sehingga message baru tersebut muncul. Browse Message

Kita dapat mengambil message tersebut dengan menggunakan kode program Java sebagai berikut.

<code>package tutorial.jms;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class HelloQueueReceiver {
	public static void main(String[] args) throws Exception {
		ConnectionFactory factory = new ActiveMQConnectionFactory(
				ActiveMQConnection.DEFAULT_USER,
				ActiveMQConnection.DEFAULT_PASSWORD,
				ActiveMQConnection.DEFAULT_BROKER_URL);
		System.out.println("Connecting to: "+ActiveMQConnection.DEFAULT_BROKER_URL);
		System.out.println("With username : "+ActiveMQConnection.DEFAULT_USER);
		System.out.println("and password : "+ActiveMQConnection.DEFAULT_PASSWORD);
		
		Connection conn = factory.createConnection();
		System.out.println("Connected");
		
		Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
		System.out.println("Session created");
		
		Queue destination = sess.createQueue("coba");
		System.out.println("Queue created");
		
		MessageConsumer consumer = sess.createConsumer(destination);
		System.out.println("Consumer created");
		
		System.out.println("Fetching message");
		conn.start();
		int counter = 1;
		while(true) {			
			Message msg = consumer.receive(1000);
			if (msg == null) break;
			System.out.println("Retrieving message #"+counter++);
			System.out.println("Message fetched");
			if (msg instanceof TextMessage) {
				TextMessage txtmsg = (TextMessage) msg;
				System.out.println("Message ID: "+txtmsg.getJMSMessageID());
				System.out.println("Message Content: "+txtmsg.getText());
			}
		}
		
		
		System.out.println("Closing connections");
		consumer.close();
		sess.close();
		conn.close();
		System.out.println("All closed");
	}
}
</code>

Setelah kode program di atas dijalankan, refresh queue browser di Hermes. Harusnya pengambilan message tersebut akan menghapus message di server.

Kita juga bisa mengirim message menggunakan kode program berikut.

<code>package tutorial.jms;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class HelloQueueSender {
	public static void main(String[] args) throws JMSException {
		ConnectionFactory factory = new ActiveMQConnectionFactory(
				ActiveMQConnection.DEFAULT_USER,
				ActiveMQConnection.DEFAULT_PASSWORD,
				ActiveMQConnection.DEFAULT_BROKER_URL);
		System.out.println("Connecting to: "+ActiveMQConnection.DEFAULT_BROKER_URL);
		System.out.println("With username : "+ActiveMQConnection.DEFAULT_USER);
		System.out.println("and password : "+ActiveMQConnection.DEFAULT_PASSWORD);
		
		Connection conn = factory.createConnection();
		System.out.println("Connected");
		
		Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
		System.out.println("Session created");
		
		Queue destination = sess.createQueue("coba");
		System.out.println("Queue created");
		
		MessageProducer prod = sess.createProducer(destination);
		TextMessage msg = sess.createTextMessage();
		msg.setText("Hello JMS");
		
		prod.send(msg);
		System.out.println("Message sent");
		
		prod.close();
		sess.close();
		conn.close();
	}
}
</code>

Setelah kode program di atas dijalankan, kita dapat merefresh queue browser di Hermes untuk memastikan bahwa kode program kita berhasil mengirim message.

Demikianlah, dengan menggunakan HermesJMS, kita dapat memeriksa secara visual apakah kode program kita dapat mengakses JMS Server dengan benar.


Network Address Translation

Pada artikel kali ini, kita akan membahas penggunaan NAT untuk memungkinkan aplikasi kita yang jalan di laptop bisa diakses orang dari internet. NAT adalah singkatan dari Network Address Translation.

Gunanya supaya kita bisa mempublish aplikasi yang berjalan komputer di jaringan internal. Misalnya kita menjalankan aplikasi web di port 8080 di laptop dengan IP private : 192.168.0.10. Kita ingin aplikasi ini bisa diakses dari orang di internet. Laptop kita terhubung ke router yang memiliki IP public 202.159.11.11.

Normalnya, orang di internet tidak bisa langsung mengakses aplikasi kita melalui dengan alamat http://192.168.0.10:8080 dari internet. Karena alamat IP 192.168.0.10 adalah alamat internal, yang tidak dikenali di luar.

Untuk itu, kita harus mengkonfigurasi gateway internet kita agar bisa meneruskan request dari internet ke laptop kita. Ini bisa dilakukan dengan memasang konfigurasi NAT di gateway internet kita. Berikut cara kerjanya.

Request http dilakukan dari komputer asal ke komputer tujuan, dengan menuliskan alamat pengirim dan alamat penerima. Mirip dengan kalau kita kirim surat. Pesannya ditulis di kertas, masukkan amplop, tulis alamat penerima dan alamat pengirim.

Contoh paketnya kira-kira seperti ini.

Dari  : 202.159.22.22
Untuk : 202.159.11.11
Pesan : halo

Pesan di atas akan bisa sampai ke gateway, karena 202.159.11.11 adalah IP public yang dikenal semua orang di internet.

Bila gateway ingin meneruskan pesan ini ke komputer internal, maka dia akan membungkus paket tersebut dengan amplop baru, alamat tujuannya diganti dengan IP internal, agar bisa mencapai laptop yang alamat IPnya internal. Proses ini dinamakan DNAT (Destination NAT).

Bila alamat tujuan tidak diganti, maka paket tidak akan sampai ke komputer internal.

Di Linux, ini dilakukan dengan perintah iptables. Perintahnya adalah sebagai berikut:

iptables -t nat -A PREROUTING -d 202.159.11.11 -p tcp --dport 8080 -j DNAT --to-destination 192.168.0.10:8080

Setelah diDNAT, paketnya menjadi seperti ini:

Dari  : 202.159.22.22
Untuk : 192.168.0.10
Pesan : halo

Setelah itu, amplop lewat proses routing di gateway. Router akan bisa menyampaikan paket tersebut ke laptop, karena dia kenal 192.168.0.10 itu adalah laptop.

Akhirnya paket sampai di tujuan.

Dengan menggunakan DNAT ini, kita bisa mempublikasikan laptop kita di jaringan internal agar bisa diakses dari luar.

Bagaimana kalau laptop kita ingin mengirim balasan data?

Prosesnya kira-kira sama. Dia masukkan ke amplop, menaruh alamat pengirim menjadi alamat tujuan, dan menaruh alamat laptop menjadi alamat pengirim. Kemudian data tersebut dikirim ke router.

Paketnya kira-kira seperti ini:

Dari  : 192.168.0.10
Untuk : 202.159.22.22
Pesan : halo juga

Laptop kita akan mengalami kebingungan untuk mengirim paket tersebut. Karena dia tidak kenal 202.159.22.22. Dia hanya kenal teman-temannya sesama anggota jaringan 192.168.0.0. Paket balasan ini tidak akan berhasil terkirim.

Untuk itu, gateway perlu melakukan satu perubahan lagi. Sebelum dia mengirim paket tadi ke laptop kita, dia perlu mengganti alamat pengirim menjadi alamat internalnya sendiri (biasanya nomor terkecil, yaitu 192.168.0.1). Jadinya kira-kira seperti ini

Dari  : 192.168.0.1
Untuk : 192.168.0.10
Pesan : halo

Proses ini dinamakan SNAT (Source NAT). Perintahnya adalah sebagai berikut:

iptables -t nat -A POSTROUTING -p tcp --dport 8080 -j SNAT --to-source 192.168.0.1

Setelah diSNAT pesan tersebut bisa dibalas oleh aplikasi di laptop kita seperti ini:

Dari  : 192.168.0.10
Untuk : 192.168.0.1
Pesan : halo juga

Pesan ini akan diterima oleh gateway, dan kemudian akan diteruskan ke pengirim aslinya yaitu 202.159.22.22

Dengan menggunakan SNAT, kita juga bisa mengimplementasikan Internet Connection Sharing, yaitu satu koneksi internet dibagi beramai-ramai.

Ada satu kasus khusus untuk SNAT, namanya Masquerade. Ini digunakan apabila IP public yang digunakan berubah-ubah. Misalnya kalau kita pakai dialup connection.

Kalau kita nekat pakai SNAT, nanti akan repot, karena harus update rule setiap dial ke internet dan mendapatkan IP public yang berbeda.

Solusinya, kita gunakan masquerade. Berikut perintahnya:

iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j MASQUERADE

Dengan menggunakan masquerade, kita tidak perlu menyebutkan --to-source karena alamat IP asal (IP publik gateway kita) berubah-ubah.

Perhatian: Jangan lupa untuk mengaktifkan IP Forwarding di gateway dengan perintah

cat 1 > /proc/sys/net/ipv4/ip_forward

Rangkaian perintah ini akan hilang pada saat reboot. Jadi harus ada usaha tambahan agar konfigurasi ini jadi permanen. Caranya, tergantung masing-masing distro. Biasanya, kita simpan dulu ke file menggunakan perintah iptables-save

iptables-save > /etc/iptables/rules.v4

Kemudian file tersebut kita load menggunakan perintah iptables-restore

iptables-restore < /etc/iptables/rules.v4

Agar berjalan setiap kali booting, panggil perintah iptables-restore dari script rc.local. Lokasi script ini berbeda antar distro, untuk keluarga Debian terletak di folder /etc.

Sayangnya saat ini iptables hanya ada di Linux, dan nampaknya tidak akan ada versi Windowsnya. Karena iptables sangat tightly-coupled dengan kernel linux.

Untuk Windows, kita dapat gunakan fitur Internet Connection Sharing apabila ada. Beberapa versi Windows (misalnya XP Home), tidak punya fitur ini. Jadi solusinya adalah dengan menggunakan aplikasi tambahan seperti misalnya WinGate atau WinRoute.


Konfigurasi mod_jk

Ada kalanya, kita ingin mempublikasikan aplikasi Java kita melalui Apache HTTPD, webserver paling populer dan paling banyak digunakan di dunia.

Beberapa alasan untuk melakukan ini antara lain:

  • Kita ingin menjalankan Tomcat dalam cluster, diakses melalui satu pintu gerbang, yaitu Apache HTTPD

  • Kita tidak ingin membuka port 8080 ke seluruh dunia, cukup buka port 80 dan 443 saja. Sedangkan di port 80 sudah ada aplikasi lain (PHP, Rails, whatever) yang dihosting di atas Apache HTTPD

  • Kita ingin melayani static page dengan Apache HTTPD, dan dynamic page dengan Tomcat

  • Dan masih banyak alasan lain.

Konfigurasi ini saya lakukan di Ubuntu Feisty 7.04. Langkah-langkahnya relatif mirip untuk distro lain, yang kalau disimpulkan terdiri dari:

  1. Install Apache

  2. Install Tomcat

  3. Deploy Aplikasi Java di Tomcat

  4. Install mod_jk

  5. Konfigurasi worker

  6. Konfigurasi virtual host

Baiklah, mari kita mulai saja.

Instalasi Apache

Saya gunakan Apache 2.2 yang disediakan oleh repository Ubuntu. Instalasi sangat mudah: sudo apt-get install apache2

Setelah instalasi selesai pastikan webserver sudah berjalan dengan baik dengan cara mengakses http://localhost

Instalasi Tomcat

Kita bisa menginstal Tomcat melalui apt-get, tetapi saya lebih suka menggunakan cara manual. Unduh dari situsnya, kemudian ekstrak di folder /opt.

cd /opt
sudo tar xvzf apache-tomcat-xx.tar.gz

Setelah itu, jalankan Tomcat.

sudo /opt/tomcat/bin/startup.sh

Jangan lupa mengatur environment variable JAVA_HOME ke tempat instalasi Java SDK Anda sebelum menjalankan Tomcat.

Periksa keberhasilan instalasi Tomcat dengan cara browse ke http://localhost:8080

Deploy Aplikasi Java

Pada saat ini, kita sudah bisa mendeploy aplikasi Java ke Tomcat, tepatnya di folder [TOMCAT_HOME]/webapps. Untuk mudahnya, kita tidak akan instal aplikasi, tapi menggunakan tomcat-docs yang sudah disediakan Tomcat.

Install mod_jk

Instalasi mod_jk di Ubuntu sangat mudah, cukup lakukan sudo apt-get install libapache2-mod-jk

mod_jk akan diinstal dan langsung diaktifkan.

Sebelum mod_jk bisa bekerja dengan benar, kita harus melakukan sedikit konfigurasi. Saya membuat file konfigurasi di /etc/apache2/mods-available/jk.conf. Letak file konfigurasi tidak penting, yang penting adalah file tersebut dibaca oleh Apache. Silahkan baca-baca manual konfigurasi untuk distro Anda

Berikut adalah isi file /etc/apache2/mods-available/jk.conf.

JkWorkersFile /etc/apache2/mods-available/workers.properties
JkShmFile     /var/log/apache2/mod_jk.shm
JkLogFile     /var/log/apache2/mod_jk.log
JkLogLevel    info
JkLogStampFormat "[%a %b %d %H:%M:$S %Y] "

Seperti kita lihat di baris pertama, ada referensi ke file workers.properties. Kita akan bahas file ini di bagian selanjutnya.

Sisa file ini mendefinisikan konfigurasi log file.

Konfigurasi Workers

File workers.properties mendefinisikan semua worker yang tersedia. Worker adalah Tomcat yang akan melayani request ke aplikasi Java. Untuk mengaktifkan clustering, kita bisa mengkonfigurasi banyak worker sekaligus. Saat ini, sebagai contoh kita akan mengkonfigurasi satu saja dulu.

Berikut adalah isi file workers.properties

worker.list=worker1
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009

Konfigurasi Virtual Host

Terakhir, kita akan mengkonfigurasi virtual host agar Apache HTTPD dapat melakukan forwarding dengan benar. Pada contoh ini, kita akan mengarahkan request ke http://localhost/tomcat-docs agar dilayani oleh Tomcat. Berikut konfigurasi Virtual Hostnya.

<VirtualHost>
    JkMount /tomcat-docs/* worker1
</VirtualHost>

Jika ingin mempublikasikan banyak aplikasi Java sekaligus, duplikasi saja baris JkMount di atas agar mengarah ke masing-masing aplikasi kita yang sudah dideploy di dalam folder [TOMCAT_HOME]/webapps.

Konfigurasi virtual host di atas bisa digabungkan dengan konfigurasi lain seperti repository Subversion, Ruby on Rails, dan sebagainya. Cukup selipkan baris JkMount di dalam blok VirtualHost.

Letak konfigurasi Virtual Host berbeda antar distro. Di keluarga Debian, konfigurasinya ada di folder /etc/apache2/sites-available.

Setelah selesai, coba restart Apache HTTPD. Kalau semua berjalan lancar, dokumentasi Tomcat yang tadinya hanya bisa diakses di http://localhost:8080/tomcat-docs/ sekarang juga bisa diakses di http://localhost/tomcat-docs/. Perhatikan perbedaan portnya, yang belakangan dilayani oleh Apache HTTPD.

Selamat mencoba :D


Continuous Integration dengan Luntbuild

Setelah pada artikel sebelumnya kita menggunakan CruiseControl –sempat dikomentari sebagai XML Sit Ups– kali ini kita akan menggunakan Luntbuild, Continuous Integration Tools yang konfigurasinya tidak menggunakan XML sama sekali.

Sama seperti CruiseControl, untuk menjalankan Continuous Integration, kita memerlukan:

  • Repository Subversion yang berisi source code, lengkap dengan folder trunk dan release

  • Code Review, Unit Test, Integration Test, Coverage Test yang lengkap

  • File build.xml yang memiliki target untuk menjalankan semua test

Struktur folder repository juga masih sama seperti artikel sebelumnya. Kita tidak membuat baru, melainkan langsung menggunakan repository pada artikel sebelumnya.

Untuk menjalankan Luntbuild, kita memerlukan:

  • Luntbuild installer

  • Apache Ant 1.7.0

  • Apache Tomcat 5.5

  • Java SDK 6.0

Installer Luntbuild dapat diunduh di sini.

Cara instalasinya tidak sulit, begitu kita dapatkan file instalasinya, buka command prompt, dan masuk ke folder tempat installer tersebut berada. Kemudian jalankan installernya.

$ cd Downloads
$ java -jar luntbuild-1.4.2-installer.jar

Tampilan instalasi Luntbuild akan segera tampil. Klik saja Next untuk melewati Release Note dan License Agreement, sampai pada pertanyaan lokasi instalasi.

Lokasi Instalasi Luntbuild

Di komputer saya, saya pilih lokasi /opt/luntbuild-1.4.2 sebagai lokasi instalasi. Klik Next, dan lanjutkan ke konfigurasi database.

Instalasi Database

Di sini saya tidak mengubah setting apa-apa. Langsung saja lanjutkan sampai menemui pertanyaan username dan password administrator.

Setup Administrator Password

Saya masukkan luntbuild123 sebagai passwordnya. Kita juga akan ditanyai lokasi instalasi Tomcat agar Luntbuild bisa segera menginstal aplikasi webnya di sana. Entah apa yang terjadi, di komputer saya pilihan ini tidak berpengaruh apa-apa. Seharusnya installer akan membuat folder luntbuild di dalam [TOMCAT_INSTALL]/webapps. Setelah instalasi selesai saya tetap harus membuat folder luntbuild di dalam [TOMCAT_INSTALL]/webapps secara manual dan mengkopi semua isi [LUNTBUILD_INSTALL]/web ke dalamnya.

Setelah semua layar berhasil dilalui, instalasi selesai. Kita bisa langsung jalankan Tomcat, dan browse ke http://localhost:8080/luntbuild. Jika instalasi berjalan benar, maka kita akan disambut oleh Luntbuild

Halaman Depan Luntbuild

Secara default, kita login secara anonymous. Untuk bisa mengkonfigurasi project, kita harus logout dulu. Kita akan segera melihat halaman login.

Login Page

Masukkan username luntbuild dan password luntbuild123, sesuai yang kita isikan pada waktu instalasi. Kita akan melihat daftar builder yang masih kosong.

Segera klik tab Project, dan klik tombol New Project di kanan atas. User interface Luntbuild kurang intuitif, sehingga saya harus berusaha keras mencari tombol tersebut. Halaman New Project segera tampil.

Create New Project

Masukkan nama project dan berikan keterangan, lalu Save.

Selanjutnya, kita klik tab VCS.

No VCS Yet

Di tab ini kita melakukan konfigurasi untuk version control. Luntbuild mendukung berbagai merek version control. Pilih Subversion pada drop down yang tersedia.

Add New Subversion Repo

Untuk konfigurasi di komputer saya, isinya adalah sebagai berikut:

  1. Repository url base : svn://localhost

  2. Directory for trunk : trunk

  3. Directory for branches : kosongkan saja, karena project contoh ini tidak memiliki branch

  4. Directory for releases : releases/daily-build

  5. Username : endy. Ini adalah username yang digunakan Luntbuild untuk checkout dan membuat tag. Idealnya kita buatkan user khusus untuk Luntbuild

  6. Password : 123

  7. Quiet Period : kosongkan saja, ini tidak dibutuhkan untuk Subversion yang sudah mendukung atomic commit.

Ada sedikit keanehan Luntbuild. Dia mengharuskan kita membuat modul. Kalau dia tidak menemukan modul, maka akan keluar pesan error. Oleh karena itu, kita klik New Module, lalu langsung saja save tanpa mengisi apa-apa.

Hasil akhir dari tab VCS kita akan nampak seperti ini.

VCS Subversion

Berikutnya, kita masuk ke tab berikutnya untuk konfigurasi Builder.

No Builder Yet

Satu project bisa memiliki banyak builder. Dengan builder, kita bisa mengatur sejauh mana kita ingin melakukan build. Contoh beberapa build yang mungkin digunakan adalah:

  • Unit test: hanya menjalankan unit test saja, mungkin dijalankan setiap 1 jam sekali.

  • Full test: menjalankan semua jenis test, dijalankan sebelum jam makan siang dan sore sebelum pulang

  • Nightly/Daily build : dijalankan sekali sehari, selain menjalankan semua test, juga menghasilkan *jar atau *war. Jenis build ini biasanya dikonfigurasi untuk menghasilkan tag di version control apabila sukses.

Klik tombol New Builder untuk mengkonfigurasi build. Karena kita menggunakan Ant, pilih Ant pada dropdown yang tersedia.

Ant Builder

Pada contoh ini, kita akan mengkonfigurasi Build Jar. Sekali sudah paham konsepnya, tidak sulit untuk mengkonfigurasi jenis build yang lain. Berikut adalah nilai yang saya isikan:

  1. Name : Build Jar

  2. Command for Ant : /opt/apache-ant-1.7.0/bin/ant. Ini adalah path menuju Ant kita.

  3. Build script path : build.xml. Ini adalah nama file build.xml yang ada di project kita

  4. Build target : build-jar. Target Ant yang dipanggil pada saat build berjalan. Saya pilih target build-jar yang mengeksekusi semua test, kemudian membuat *.jar bila test tidak ada yang gagal.

  5. Field sisanya, biarkan saja sesuai default.

Terakhir, kita mengkonfigurasi Schedule, alias jadwal. Ini menentukan kapan build dieksekusi. Untuk Build Jar yang sudah kita konfigurasi, kita ingin dia dijalankan setiap jam 01 dini hari, setiap hari. Berikut isian konfigurasinya.

  1. Name : Nightly Build

  2. Description : build setiap jam 01 dini hari

  3. Next build version : nightly-${#currentDay=system.(year+"-"+numericMonth+"-"+dayOfMonth), #lastDay=project.var["day"].setValue(#currentDay), #dayIterator=project.var["dayIterator"].intValue, project.var["dayIterator"].setIntValue(#currentDay==#lastDay?#dayIterator+1:1), #currentDay}.${project.var["dayIterator"]}. Ini adalah label yang akan diberikan untuk setiap build. String di atas akan menghasilkan label nightly-2007-08-17.1 bila dijalankan pada tanggal 17 Agustus 2007.

  4. Trigger Type: cron. Luntbuild menyediakan dua jenis trigger. Pertama, simple trigger yang berbasis interval.

Dengan simple trigger, kita dapat menjadwalkan build untuk berjalan misalnya setiap dua jam, atau setiap 30 menit. Kedua, cron trigger, yang berbasis waktu. Dengan cron trigger, kita dapat menjalankan build setiap waktu tertentu, misalnya setiap Sabtu, atau setiap hari jam 1 pagi. Untuk menjalankan build setiap jam satu pagi, gunakan cron trigger dengan konfigurasi 0 0 1 * * ?. Luntbuild menggunakan pustaka Quartz untuk melakukan penjadwalan ini. Lebih lanjut tentang konfigurasi jadwal Quartz dapat dilihat di websitenya.

  1. Build necessary condition: vcsModified or dependencyNewer. Buat apa melakukan build kalau belum ada perubahan sejak terakhir kali build berjalan? Inilah maksud dari field ini. Dengan menyebutkan vcsModified, kita menjelaskan bahwa kalau tidak ada perubahan di dalam version control, tidak perlu lakukan build. Selain nilai ini, kita juga bisa menggunakan nilai always, yang artinya tetap build walaupun tidak ada yang commit.

  2. Associated builders. Di sini kita memilih builder mana yang ingin dijalankan.

  3. Associated post builder. Misalnya setelah build sukses dijalankan, kita ingin membuat installer yang siap diunduh. Kita bisa sebutkan hal tersebut di sini.

  4. Label strategy: Label if success. Hanya buat tag di version control bila build sukses.

  5. Notify strategy: notify if status change. Artinya, kirim pemberitahuan apabila status build berbeda dari sebelumnya, misalnya tadinya gagal menjadi sukses, atau sebaliknya.

Demikian sebagian nilai yang harus diisikan. Untuk field yang tidak saya jelaskan, silahkan baca keterangannya atau ikuti saja default.

Setelah semua konfigurasi selesai, kembali ke halaman depan untuk menyaksikan hasil pekerjaan kita. Di sana akan terpampang semua build yang sudah dikonfigurasi.

Daftar build

Kita bisa menjalankan build tersebut secara manual tanpa harus menunggu jatuh temponya. Biasanya kita lakukan build manual untuk mengetes konfigurasi. Build yang sedang berjalan ditandai dengan ikon roda gigi.

Building

Build yang gagal akan ditandai dengan warna merah, sedangkan build yang berhasil diwarnai hijau.

Build Successful

Bila build gagal, tentunya kita ingin melihat apa yang terjadi. Klik build info untuk menampilkan informasinya.

Build Info

Kurang detail? Buka saja lognya. Di sini akan terlihat di langkah yang mana build tersebut berhenti.

Build Log

Demikianlah cara penggunaan Luntbuild. Mudah-mudahan dengan menggunakan perangkat ini proyek software Anda bisa dikelola dengan baik dan teratur.


Cruise Control

Kalau Anda programmer Java, tentunya sudah tidak asing lagi dengan Tomcat atau Eclipse. Yang satu adalah application server, satunya lagi adalah Integrated Development Environment (IDE). Ada satu kesamaan dalam kedua produk ini, keduanya adalah proyek open source yang dikelola secara profesional.

Coba buka halaman download di website masing-masing proyek tersebut. Di sana bisa kita temukan beberapa jenis download, yaitu Nightly Builds dan Stable Builds. Stable builds adalah aplikasi yang sudah melalui berbagai fase testing dan dinyatakan lulus uji. Nightly builds, seperti namanya, adalah build yang dibuat setiap malam dan dirilis kepada publik agar bisa diuji coba beramai-ramai. Dengan besarnya jumlah penguji, diharapkan bug yang ada dalam aplikasi bisa ditemukan semuanya.

Build, dalam dunia Java, terdiri dari serangkaian aktifitas, diantaranya sebagai berikut:

  1. Menjalankan review kode otomatis

  2. Kompilasi kode program

  3. Menjalankan unit test

  4. Menjalankan integration test

  5. Menjalankan coverage test (kalau ada)

  6. Menjalankan platform test, untuk aplikasi yang berbeda antar OS (seperti Eclipse)

  7. Bila lulus semua ujian di atas, buat tag di version control

  8. Siapkan executable (*.jar, *.war, *exe, dan sebagainya)

  9. Publish executable di website supaya bisa didownload orang banyak

Wow, daftar aktifitasnya cukup banyak. Kalau ini dikerjakan setiap malam, betapa membosankan. Saya tidak dapat membayangkan ada satu orang yang ditugaskan khusus untuk melakukan hal ini. Pasti setelah beberapa hari saja dia akan bosan dan mengundurkan diri. Apalagi dalam project open source di mana sebagian besar kontributornya tidak dibayar.

Untuk itulah ada tools khusus untuk melakukan hal membosankan ini. Toolsnya disebut Continuous Integration Tools, berdasarkan tulisan Martin Fowler tentang Continuous Integration. Ada beberapa tools yang tersedia di pasaran, diantaranya:

Dalam tulisan kali ini, kita akan bahas tentang penggunaan CruiseControl, salah satu Continuous Integration Tool yang populer dan open source.

CI Tool yang baik haruslah memiliki fitur sebagai berikut:

  • Mendukung berbagai jenis version control, minimal Subversion dan CVS

  • Dapat menggunakan Ant atau Maven

  • Mendukung banyak project

  • Dapat mengelola artifact (hasil build)

  • Dapat memberitahu orang tentang hasil build (gagal atau sukses) melalui berbagai cara (email, instant message, sms, dan sebagainya)

  • Memiliki aplikasi pelaporan yang mudah diakses melalui web

Yang dilakukan oleh CI Tools adalah sebagai berikut:

  1. Periksa repository source code, apakah ada perubahan terbaru

  2. Bila ada, ambil perubahan terbaru tersebut

  3. Lakukan build (compile, testing, dsb)

  4. Laporkan hasilnya (gagal atau berhasil) melalui email atau media lainnya

  5. Buat juga laporan di website

  6. Bila sukses, publish artifact yang dihasilkan

Cruise Control membutuhkan beberapa file konfigurasi:

  • config.xml: Hanya satu, digunakan untuk mendaftarkan project yang akan dikelola

  • build-namaproject.xml : Satu per project. Bila kita punya tiga project, maka akan ada tiga build-namaproject.xml, dengan nama file disesuaikan dengan masing-masing project.Buildfile ini bertugas untuk sinkronisasi dengan version control dan membuat tag di version control

  • build.xml : Satu per project. Ini adalah buildfile yang biasa kita gunakan di project untuk melakukan kompilasi, menjalankan test, dan menghasilkan *jar atau *war.

Kita butuh satu folder kerja untuk CruiseControl melakukan tugasnya. Sebaiknya folder ini dipisahkan dari folder instalasi CruiseControl agar memudahkan kita pada waktu melakukan upgrade CruiseControl. Untuk kepentingan ilustrasi, folder instalasi CruiseControl akan ditulis [CC_INSTALL] dan folder kerja akan ditulis [CC_WORK].

Berikut adalah struktur folder [CC_INSTALL]:

Struktur Folder Instalasi CruiseControl

dan folder kerja [CC_WORK] akan berisi seperti ini:

Folder Kerja CruiseControl

Struktur folder di dalam repository Subversion kita seperti ini:

Struktur Folder Subversion

Pada contoh ini, kita akan menjalankan build untuk satu project TestingTraining dengan konfigurasi sebagai berikut:

  1. Memiliki dua build, yang satu berjalan setiap satu menit, satu lagi berjalan setiap jam 18.00 sore

  2. Version control yang digunakan adalah Subversion

  3. Bila build sukses, buat tag di folder releases/daily-build/build-NN-yyyyMMddhhmmss

Konfigurasi tersebut ditulis di config.xml sebagai berikut.

<cruisecontrol>
    <project name="TestingTraining" buildafterfailed="true">
      <listeners>
        <currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
      </listeners>

      <modificationset quietperiod="10">
          <svn RepositoryLocation="svn://172.16.2.252/trunk" />
      </modificationset>

      <schedule interval="60">
          <ant anthome="/opt/apache-ant-1.6.5"
               antWorkingDir="/home/endy/tmp/cruise-work/projects/${project.name}"
               buildfile="/home/endy/tmp/cruise-work/build-${project.name}.xml"
               target="build"
               uselogger="true"
               usedebug="false"
	       />
      </schedule>

      <log>
          <merge dir="/home/endy/tmp/cruise-work/projects/${project.name}/build/report/junit"/>
      </log>

      <publishers>
            <onsuccess>
                <antpublisher anthome="/opt/apache-ant-1.6.5"
                              antWorkingDir="/home/endy/tmp/cruise-work/projects/${project.name}"
                              buildfile="/home/endy/tmp/cruise-work/build-${project.name}.xml"
                            target="tag"
                            uselogger="true"
                            usedebug="false"
                />
		<artifactspublisher
		    dir="/home/endy/tmp/cruise-work/projects/${project.name}/dist"
		    dest="artifacts/${project.name}"/>
            </onsuccess>
        </publishers>
    </project>
</cruisecontrol>

Berikut adalah penjelasannya.

  • Konfigurasi CruiseControl dibuat dengan format XML. Tag paling luar adalah <cruisecontrol>

  • Satu file konfigurasi bisa memuat banyak project. Masing-masing project dikonfigurasi dalam tag <project>

  • Di halaman depan CruiseControl, akan ada status untuk masing-masing project. Status ini akan disimpan di file status.txt

  • Modification Set: Beberapa produk version control tidak mengenal atomic commit, misalnya CVS. Karena itu, bila kita commit (misalnya) 7 file sekaligus, bisa saja CruiseControl terbangun pada saat baru 3 file yang tercommit. Bila CruiseControl melakukan build pada saat itu juga, bisa dipastikan akan terjadi error. Oleh karena itu, kita suruh CruiseControl membatalkan build bila kurang dari 10 detik yang lalu ada orang yang commit. Tentu saja bila kita menggunakan Subversion, hal ini tidak relevan.

  • Schedule: Ini adalah daftar build schedule yang ingin kita daftarkan. Tag ini bisa memuat banyak build. Kita menggunakan interval 60 detik, yang artinya setiap menit CruiseControl akan melakukan build. Selain schedule yang berbasis interval, kita juga bisa mendaftarkan schedule yang menggunakan waktu tertentu, misalnya berjalan setiap jam 12 malam.

  • Merge log yang dihasilkan JUnit, tersimpan di folder build/report/junit ke dalam tampilan report CruiseControl.

  • Publisher : pada bagian ini kita daftarkan Ant Publisher pada kategori onsuccess. Artinya, kalau build sukses, jalankan target tag untuk membuat tag di dalam repository. Selain itu, kita juga menggunakan Artifact Publisher, yang berguna untuk mengkopi file *.jar yang dihasilkan ke dalam folder CruiseControl sehingga bisa didownload melalui website.

Di file konfigurasi tersebut, kita memanggil file build-TestingTraining.xml. Ini adalah isi dari file tersebut.

<project name="CruiseControl Builder"
    	 default="build"
         basedir="projects/TestingTraining">

  <property name="repository.url" value="svn://localhost"/>
  <property name="branch" value="trunk"/>
  <property name="release.folder" value="releases/daily-build" />
  <property name="svn.username" value="endy" />
  <property name="svn.password" value="123" />
  <property name="message" value="Created automatically by CruiseControl" />

  <path id="cc-classpath">
    <fileset dir="../../lib" includes="**/*jar"/>
  </path>  

  <target name="update">
    <java classname="org.tmatesoft.svn.cli.SVN" fork="true"
          classpathref="cc-classpath">
      <arg value="update"/>      
    </java>
  </target>

  <target name="build" depends="update">
    <ant inheritAll="false" target="build-jar"/>
  </target>

  <target name="tag">
    <java classname="org.tmatesoft.svn.cli.SVN"
              fork="true" classpathref="cc-classpath">
            <arg value="--username"/>
            <arg value="${svn.username}"/>

            <arg value="--password"/>
            <arg value="${svn.password}"/>

            <arg value="cp"/>
            <arg value="${repository.url}/${branch}"/>
            <arg value="${repository.url}/${release.folder}/${label}-${cctimestamp}"/>

            <arg value="-m"/>
            <arg value="${message}"/>

        </java>
  </target>

</project>

Penjelasannya sebagai berikut.

  • Kita menggunakan pustaka yang disediakan SVNKit (dulunya JavaSVN) agar bisa menggunakan Subversion melalui Ant. Sebetulnya tanpa SVNKit juga bisa, yaitu dengan target exec yang dimiliki Ant untuk memanggil perintah di command line. Dengan menggunakan SVNKit, kita tidak perlu menginstal Subversion command line di build server kita.

  • Ada tiga target utama dalam buildfile ini: update, build, dan tag. Target update berguna untuk mengambil perubahan terbaru dari repository ke folder kerja CruiseControl. Target build memanggil target build-jar di dalam build.xml yang dimiliki project. Target build-jar ini hanya bisa berhasil kalau semua test sudah berjalan dengan sempurna. Target tag berguna untuk membuat tag di repository. Kita bisa mengirim variabel yang dihasilkan CruiseControl, dalam hal ini ${label} dan ${cctimestamp}.

Agar file ini bisa dijalankan, kita harus memiliki folder projects/TestingTraining yang berisi hasil checkout dari repository. Mari kita checkout dulu.

$ cd projects
$ svn co svn://localhost/trunk TestingTraining

Untuk menguji apakah file build-TestingTraining.xml ini bisa dijalankan dengan baik, masuk ke folder projects/TestingTraining dan panggil file tersebut dari folder ini.

$ ant -f ../../build-TestingTraining.xml build
Buildfile: ../../build-TestingTraining.xml

update:
     [java] At revision 71.

build:

clean:
   [delete] Deleting directory /home/endy/tmp/cruise-work/projects/TestingTraining/build/bin
   [delete] Deleting directory /home/endy/tmp/cruise-work/projects/TestingTraining/build/debug
   [delete] Deleting directory /home/endy/tmp/cruise-work/projects/TestingTraining/build/cobertura
   [delete] Deleting directory /home/endy/tmp/cruise-work/projects/TestingTraining/build/report/junit
   [delete] Deleting directory /home/endy/tmp/cruise-work/projects/TestingTraining/build/report/pmd
   [delete] Deleting directory /home/endy/tmp/cruise-work/projects/TestingTraining/build/report/cobertura
   [delete] Deleting directory /home/endy/tmp/cruise-work/projects/TestingTraining/dist

prepare:
    [mkdir] Created dir: /home/endy/tmp/cruise-work/projects/TestingTraining/build/bin
    [mkdir] Created dir: /home/endy/tmp/cruise-work/projects/TestingTraining/build/debug
    [mkdir] Created dir: /home/endy/tmp/cruise-work/projects/TestingTraining/build/cobertura
    [mkdir] Created dir: /home/endy/tmp/cruise-work/projects/TestingTraining/build/report/junit
    [mkdir] Created dir: /home/endy/tmp/cruise-work/projects/TestingTraining/build/report/pmd
    [mkdir] Created dir: /home/endy/tmp/cruise-work/projects/TestingTraining/build/report/cobertura
    [mkdir] Created dir: /home/endy/tmp/cruise-work/projects/TestingTraining/dist

code-review:

compile:
    [javac] Compiling 11 source files to /home/endy/tmp/cruise-work/projects/TestingTraining/build/bin

compile-cobertura:
    [javac] Compiling 11 source files to /home/endy/tmp/cruise-work/projects/TestingTraining/build/debug
   [delete] Deleting: /home/endy/tmp/cruise-work/projects/TestingTraining/cobertura.ser
   [delete] Deleting directory /home/endy/tmp/cruise-work/projects/TestingTraining/build/cobertura
[cobertura-instrument] Cobertura 1.8 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
[cobertura-instrument] Instrumenting 7 files to /home/endy/tmp/cruise-work/projects/TestingTraining/build/cobertura
[cobertura-instrument] Cobertura: Saved information on 7 classes.
[cobertura-instrument] Instrument time: 110ms

unit-test:
    [junit] Running tutorial.testing.CalculatorTest
    [junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.047 sec
    [junit] Cobertura: Loaded information on 7 classes.
    [junit] Cobertura: Saved information on 7 classes.
    [junit] Running tutorial.testing.DayCounterTest
    [junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.055 sec
    [junit] Cobertura: Loaded information on 7 classes.
    [junit] Cobertura: Saved information on 7 classes.
    [junit] Running tutorial.testing.LoginServletTest
    [junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.159 sec
    [junit] Cobertura: Loaded information on 7 classes.
    [junit] Cobertura: Saved information on 7 classes.
    [junit] Running tutorial.testing.dbunit.PersonDaoMySQLTest
    [junit] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 0.153 sec
    [junit] Cobertura: Loaded information on 7 classes.
    [junit] Cobertura: Saved information on 7 classes.
[junitreport] Transform time: 564ms
[cobertura-report] Cobertura 1.8 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
[cobertura-report] Cobertura: Loaded information on 7 classes.
[cobertura-report] Report time: 231ms

coverage-test:
[cobertura-check] Cobertura 1.8 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
[cobertura-check] Cobertura: Loaded information on 7 classes.
[cobertura-check] All checks passed.

full-test:

build-jar:
      [jar] Building jar: /home/endy/tmp/cruise-work/projects/TestingTraining/dist/TutorialTesting.jar

BUILD SUCCESSFUL
Total time: 12 seconds

File build-TestingTraining.xml akan memanggil build.xml yang ada di dalam project kita. Berikut isi dari file ini.

<project name="TestingTraining" default="full-test" basedir=".">

	<property name="source.java" value="src/java"/>
	<property name="compile.normal" value="build/bin"/>
	<property name="compile.debug" value="build/debug"/>
	<property name="compile.cobertura" value="build/cobertura"/>
	<property name="distribution" value="dist" />

	<property name="report.junit" value="build/report/junit"/>
	<property name="report.pmd" value="build/report/pmd"/>
	<property name="report.cobertura" value="build/report/cobertura"/>

	<path id="devel.classpath">
		<pathelement location="${source.java}"/>
		<pathelement location="${compile.debug}"/>
		<fileset dir="lib" includes="**/*jar" />
		<fileset dir="ext" includes="**/*jar" />
	</path>

	<taskdef classpathref="devel.classpath" resource="tasks.properties"/>

	<target name="prepare">
		<mkdir dir="${compile.normal}"/>
		<mkdir dir="${compile.debug}"/>
		<mkdir dir="${compile.cobertura}"/>
		<mkdir dir="${report.junit}"/>
		<mkdir dir="${report.pmd}"/>
		<mkdir dir="${report.cobertura}"/>

		<mkdir dir="${distribution}" />
	</target>

	<target name="clean">
		<delete dir="${compile.normal}"></delete>
		<delete dir="${compile.debug}"></delete>
		<delete dir="${compile.cobertura}"></delete>
		<delete dir="${report.junit}"></delete>
		<delete dir="${report.pmd}"></delete>
		<delete dir="${report.cobertura}"></delete>
		<delete dir="${distribution}"></delete>
	</target>

	<target name="code-review" depends="prepare">
		<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpathref="devel.classpath"/>
    	<pmd targetjdk="1.5" shortFilenames="true" failonerror="true" failonruleviolation="true" rulesetfiles="pmd-ruleset.xml">                           
	      <formatter type="net.sourceforge.pmd.renderers.HTMLRenderer"
	                 toFile="${report.pmd}/pmd.html"
	      />
	      <fileset dir="${source.java}">
	          <include name="**/*.java"/>
	          <exclude name="**/*Test.java"/>
	      </fileset>
	    </pmd>
	</target>

	<target name="compile" depends="code-review">
		<javac
			srcdir="${source.java}"
			destdir="${compile.normal}"
			classpathref="devel.classpath">
		</javac>
	</target>

	<target name="unit-test" depends="compile,compile-cobertura">
		<junit haltonfailure="true" fork="true" printsummary="true">
			<classpath location="${compile.cobertura}"></classpath>
			<classpath refid="devel.classpath"/>
            <formatter type="xml"/>
            <batchtest todir="${report.junit}">
                <fileset dir="${compile.debug}" includes="**/*Test.class" excludes="**/*Abstract*.class"/>
            </batchtest>
		</junit>

		<junitreport todir="${report.junit}">
            <fileset dir="${report.junit}">
            <include name="TEST-*.xml"/>
            </fileset>
            <report format="frames" todir="${report.junit}/html"/>
        </junitreport>

		<cobertura-report
        	datafile="cobertura.ser"
        	srcdir="${source.java}"
        	destdir="${report.cobertura}"
        />
	</target>

	<target name="compile-cobertura" depends="prepare">
		<javac
			srcdir="${source.java}"
			destdir="${compile.debug}"
			classpathref="devel.classpath"
			fork="true"
			debug="true"
		/>
        <delete file="cobertura.ser"/>
    	<delete dir="${compile.cobertura}" />

        <cobertura-instrument todir="${compile.cobertura}">
            <fileset dir="${compile.debug}">
                    <include name="**/*.class"/>
                    <exclude name="**/*Test*.class"/>
            </fileset>
        </cobertura-instrument>
	</target>

	<target name="coverage-test" depends="unit-test">
		<cobertura-check datafile="cobertura.ser"
                         branchrate="75"
                         linerate="75"
                         haltonfailure="true"
        />

	</target>

	<target name="full-test" depends="code-review,coverage-test"></target>

	<target name="build-jar" depends="clean,full-test">
		<jar destfile="${distribution}/${ant.project.name}.jar"
		       basedir="${compile.normal}"
		       excludes="**/*Test.class"
		  />
	</target>
</project>

Seperti kita lihat di atas, file tersebut memiliki target build-jar yang hanya akan dilakukan apabila target code-review, compile, unit-test, coverage-test dilakukan dengan sukses.

Baiklah, persiapan sudah selesai, sekarang jalankan CruiseControl, dan lihat hasilnya. CruiseControl dijalankan melalui command prompt dengan mensuplai config.xml yang kita miliki.

$ cd [CC_INSTALL]
$ ./cruisecontrol.sh -configfile [CC_WORK]/config.xml

Bila tidak ada error, maka kita bisa browse ke http://localhost:8080

Ini adalah halaman depan. Memuat status masing-masing project.

Halaman Depan CruiseControl

Ini adalah halaman untuk TestingTraining.

Hasil Build

Seperti kita lihat, hasilnya build failed, karena database MySQL yang diperlukan untuk test belum dijalankan. Mari kita nyalakan dan buat test ini menjadi sukses. Setelah sukses, tampilannya seperti ini.

Hasil Build yang sukses

Kita bisa melihat hasil unit test di dalam tab Test Result.

Hasil JUnit

Berapa kali gagal, berapa kali sukses? Kita bisa lihat di tab Metric.

Metric tentang hasil build

Bila kita sudah menjalankan daily build ini selama beberapa waktu, di repository kita akan dihasilkan tag sesuai dengan build yang sukses. Berikut contohnya.

Daftar Tag yang dibuat CruiseControl

Demikianlah penggunaan CruiseControl. Dengan menggunakan CruiseControl ini, para programmer bisa dibebaskan dari tugas membosankan, sehingga waktunya bisa digunakan untuk hal lain yang lebih bermanfaat, seperti misalnya menulis blog atau menambah teman baru di Friendster.