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.

Ruthless Testing 5

Setelah beberapa seri artikel Ruthless Testing, ternyata lebih banyak yang skeptis daripada yang antusias. Respon yang paling banyak ditemukan adalah:

Wah, ini konsep yang sangat menarik. Tentunya akan sangat baik jika diterapkan. Sayang sekali, di tempat saya tidak bisa, karena … [berbagai alasan dan kesulitan] …

Hmm … saya mengerti perasaan Anda. Been there, done that. Saya pernah jadi programmer, dan juga pernah jadi project manager. Saya mengerti beberapa keberatan dan kesulitan implementasinya.

Daripada berkeluh kesah tiada guna, mari kita lihat satu persatu masalahnya.

Programmer : Bikin unit test? Nambah kerjaan aja. Lagian test code kan gak dideliver ke client

Jawaban saya: Unit test itu adalah investasi Anda. Bayangkan beberapa bulan/tahun yang akan datang, client datang ke kita, meminta perbaikan untuk error dalam aplikasi. Padahal kita sedang sibuk mengerjakan proyek lain yang sama sekali berbeda. Hampir seluruh kode sudah kita lupakan. Bahkan untuk method sederhana seperti ini

calc.divide(x, y);

kita sudah lupa bagaimana behaviournya kalau menghadapi pembagian dengan nol, bilangan negatif, atau bilangan tidak bulat. Dan seperti biasa, kita tidak membuat komentar Javadoc.

Unit test to the rescue. Tinggal buka unit test untuk kode di atas, dan kita akan melihat bagaimana kode tersebut menghadapi bilangan negatif, nol, dan kasus-kasus aneh lainnya.

Next ..

Programmer : Aplikasi saya mengakses jaringan/database/email. Sulit dites

Jawaban saya: Itu namanya integration testing, bukan unit testing. Walaupun demikian, tetap bisa dites secara otomatis. Tapi butuh sedikit investasi tambahan.

Misalnya, kita punya method seperti ini:

public void process(Order order) {
    // coba lihat customernya, periksa saldonya
    Customer c = order.getCustomer();
    if(c.getBalance() < 0) {
        // handle error
    }
    
    // saldo ok, simpan order ke database
    String sql = "INSERT INTO tbl_order VALUES (?, ?, ?)";

    // kirim email konfirmasi
    String dest = c.getEmail();
    // kode send email tidak ditunjukkan
}

Seperti kita lihat, ada beberapa integration point di sini, yaitu akses database dan koneksi ke mail server.

Untuk mengotomasi pengetesan method ini, kita gunakan teknik yang sudah teruji dari 400 tahun lalu: divide et impera. Yang tidak tidur di kelas waktu pelajaran Sejarah pasti tau teknik ini. Sekarang teknik ini sudah diadaptasi ke dunia programming dengan istilah Refactoring. Silahkan google keyword tersebut untuk lebih jelasnya.

Pada contoh kita di atas, kita me-refactor kode akses database dan koneksi mail server ke class yang berbeda. Kita pisahkan kode tersebut ke class tersendiri. Berikut adalah interfacenya:

public interface OrderDao {
    public void save(Order o);
}

dan

public interface MailSender {
    public void send(String from, String to, String message);
}

Implementasinya disisakan buat latihan di rumah.

Sehingga method kita menjadi seperti ini:

public void process(Order order) {
    // coba lihat customernya, periksa saldonya
    Customer c = order.getCustomer();
    if(c.getBalance() < 0) {
        // handle error
    }
    
    orderDao.save(order);
    
    mailSender.send(adminEmail, c.getEmail(), "Order sudah dikirim");
}

Dengan adanya kedua class tambahan ini, kita bisa menggunakan Mock Object untuk memalsukan object orderDao dan mailSender. Kita bisa buat kedua object ini selalu sukses, atau selalu gagal, tergantung skenario yang mau dites.

OrderDao sendiri bisa dites dengan menggunakan teknik pengetesan database. MailSender dapat ditest dengan menggunakan Dumbster, mail server palsu.

Kasus seperti ini biasanya terjadi karena kodenya sudah terlanjur campur aduk. Apabila unit test diterapkan dari awal project, programmer terkondisi untuk membuat kode yang mudah dites, sehingga effort untuk refactor tidak terlalu besar.

Tujuan utama kita adalah semua tes bisa dijalankan otomatis, independen (tes satu tidak mempengaruhi tes lain) dan repeatable(bisa diulang-ulang dengan hasil yang konsisten). Tidak harus menggunakan JUnit. Bisa pakai tools buatan sendiri.

Keberatan berikutnya datang dari project manager.

Project Manager : Berarti saya harus alokasikan waktu tambahan untuk membuat unit test, wah bisa-bisa saya dimarahi client/bos.

Jawaban saya: Bos… programmer Anda pasti membuat kode test, walaupun tidak otomatis. Ini sudah naluri dasar manusia.

Berikut contohnya:

public void process(Order order) {
    // coba lihat customernya, periksa saldonya
        
    // saldo ok, simpan order ke database
    String sql = "INSERT INTO tbl_order VALUES (?, ?, ?)";
  
    // coba periksa ... nanti kita hapus lagi
    String coba = "SELECT * FROM tbl_order WHERE .. ";

    // kirim email konfirmasi
    String dest = c.getEmail();
    // kode send email tidak ditunjukkan
}

Jadi, daripada dia pasang kode test di kode program, dan nantinya dihapus lagi, kan sayang. Lebih baik suruh saja untuk bikin unit test betulan. Keuntungan berikutnya, dengan banyaknya unit test, kesalahan coding akan lebih cepat ditemukan, sehingga pada tahap testing oleh client, lebih sedikit bug yang ditemukan. Dengan demikian, alokasi waktu untuk rework akan lebih sedikit.

Project Manager : Kalau terlalu banyak unit test, nanti aplikasinya menjadi rumit.

Jawaban saya: Justru sebaliknya. Aplikasi yang banyak unit testnya akan menjadi lebih modular dan tidak terlalu kusut struktur internalnya. Dengan demikian, kode program menjadi lebih mudah dipelihara dan dikembangkan.

Terakhir, ada beberapa tips dari saya agar penerapan unit testing dapat berjalan dengan optimal:

  • siapkan infrastruktur sebelum mulai coding
  • sering-sering refactor agar kode mudah dites
  • gunakan coverage testing sebagai pelengkap, agar kekurangan unit test cepat terdeteksi
  • terapkan daily build otomatis yang menjalankan semua tes

Demikian, semoga bermanfaat


Presentasi Subversion

Hari Sabtu, 2 Desember 2006 pukul 10.00 WIB, saya akan memberikan presentasi tentang Subversion di pertemuan rutin programmer Java (JaMU - Java Meet Up).

Adapun tempat kejadian presentasi (TKP) beralamat di: SUN Microsystem Indonesia Lantai 13, Gedung Wisma Metropolitan I (WTC Sudirman) Jakarta

Subversion adalah aplikasi version control. Aplikasi ini mirip dengan file server, artinya dia bisa digunakan untuk menyimpan file, dan bisa diakses jika ingin mengambil file tersebut. Bedanya dengan file server biasa (FTP server, Samba, atau Windows Sharing), aplikasi version control menyimpan riwayat perubahan semua yang kita simpan di dalamnya. Kalau kita menyimpan satu file, kemudian isinya kita modifikasi (tambah baris, hapus, ganti nama, dan sebagainya), maka setiap perubahan tersebut dicatat oleh version control.

Tidak hanya dicatat, kita juga bisa mengembalikan kondisi file sesuai keinginan. Kita bisa melihat versi awal dari file tersebut, atau nama file sebelum diganti, dan semua titik penting lain di masa lalu. Singkatnya, dalam urusan menyimpan file, version control mirip dengan mesin waktu.

SIlahkan datang ke JaMU minggu ini. Gratis, tidak dipungut biaya.

Materi yang akan dibawakan adalah:

  1. Konsep Version Control
  2. Penggunaan sehari-hari
  3. Tag, Branch, Merge
  4. Aplikasi pelengkap
  5. Keterbatasan Subversion
  6. Aplikasi sejenis (kompetitor)
Presentasi dapat didownload [di sini [PDF 710KB]](http://endy.artivisi.com/downloads/writings/Subversion-presentation-20061129.pdf “Presentasi Subversion”).

BerkeleyDB Java Edition

Beberapa waktu yang lalu, ada posting menarik tentang Google yang menggunakan BerkeleyDB untuk mengelola user accountnya. Karena penasaran, saya lalu mencoba mendownload dan bermain-main dengan BerkeleyDB versi Java (Java Edition).

BerkeleyDB adalah produk embedded database open source yang populer. Belum lama ini dia diakuisisi oleh Oracle, sehingga membuat produk ini makin terkenal lagi.

Pada tulisan kali ini, kita akan mencoba menggunakan BDB Java Edition.

Tapi jangan salah paham dengan namanya. BerkeleyDB adalah database, tapi bukan relational database. Artinya, dia tidak memiliki fasilitas SQL. Walaupun demikian, dia mendukung ACID transaction. Salah satu contoh penggunaan BDB adalah pada aplikasi version control Subversion.

Kalau tidak ada SQLnya, bagaimana kita melakukan operasi CRUD? Jawabnya adalah, anggap saja BDB sebagai Map. Kita bisa menggunakan Map seperti ini:

    Map myMap = new HashMap();
    myMap.put(1, "Endy");

Maka object String akan disimpan dengan key 1. Object ini dapat diambil dengan kode:

    String data = myMap.get(1);

Bagi mereka yang sudah pernah mendengar Object Database tentunya sudah familiar dengan konsep seperti ini. Salah satu implementasi konsep ini adalah db40 atau Prevayler.

Sekarang, coba kita gunakan contoh kasus yang standar. Ada class Person yang akan kita buatkan operasi CRUD-nya. Berikut kodenya:

Person.java

    package tutorial.berkeleydb;
    
    import java.util.Date;
    
    import com.sleepycat.persist.model.Entity;
    import com.sleepycat.persist.model.PrimaryKey;
    
    @Entity
    public class Person {
            
            @PrimaryKey(sequence="ID")
            private Integer id;
            private String name;
            private Date birthdate;
            private String email;
            
            public Person(){
                    
            }
            
            public String getEmail() {
                    return email;
            }
            public void setEmail(String address) {
                    this.email = address;
            }
            public Date getBirthdate() {
                    return birthdate;
            }
            public void setBirthdate(Date birthdate) {
                    this.birthdate = birthdate;
            }
                    
            public Integer getId() {
                    return id;
            }
            public void setId(Integer id) {
                    this.id = id;
            }
            public String getName() {
                    return name;
            }
            public void setName(String name) {
                    this.name = name;
            }
    }

Perhatikan bahwa untuk menjadikan class Person persistent, kita hanya perlu menambahkan annotation, mirip dengan JPA atau Hibernate. Sekarang mari kita definisikan interface CRUD-nya.

PersonDao.java

    package tutorial.berkeleydb;
    
    import java.util.List;
    
    public interface PersonDao {
            public void save(Person p);
            public void delete(Person p);
            public Person getById(Integer id);
            public List<Person> getAll();
    }

Dan berikut adalah implementasinya.

PersonDaoBerkeleyDB

    package tutorial.berkeleydb;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    import com.sleepycat.je.DatabaseException;
    import com.sleepycat.persist.EntityCursor;
    import com.sleepycat.persist.EntityStore;
    import com.sleepycat.persist.PrimaryIndex;
    
    public class PersonDaoBerkeleyDB implements PersonDao {
    
            private PrimaryIndex<Integer, Person> personPrimaryKey;
            
            public PersonDaoBerkeleyDB(EntityStore storage) {
                    try {
                            personPrimaryKey = storage.getPrimaryIndex(Integer.class, Person.class);
                    } catch (DatabaseException e) {
                            e.printStackTrace();
                    }
            }
            
            public void delete(Person p) {
                    try {
                            personPrimaryKey.delete(p.getId());
                    } catch (DatabaseException e) {
                            e.printStackTrace();
                    }
            }
    
            public List<Person> getAll() {
                    List<Person> result = new ArrayList<Person>();
                    try {
                            EntityCursor<Person> allPerson = personPrimaryKey.entities();
                            Iterator<Person> iter = allPerson.iterator();
                            while (iter.hasNext()) {
                                    result.add(iter.next());
                            }
                            allPerson.close();
                    } catch (DatabaseException e) {
                            e.printStackTrace();
                    }
                    return result;
            }
    
            public Person getById(Integer id) {
                    try {
                            return personPrimaryKey.get(id);
                    } catch (DatabaseException e) {
                            e.printStackTrace();
                    }
                    return null;
            }
    
            public void save(Person p) {
                    try {
                            personPrimaryKey.put(p);
                    } catch (DatabaseException e) {
                            e.printStackTrace();
                    }		
            }
    
    
    }

Sebagai perbandingan, berikut kode CRUD menggunakan Hibernate dengan bantuan Spring.

    public class PersonDaoHibernate extends HibernateDaoSupport implements PersonDao{
        public void save(Person p){
            getHibernateTemplate().save(p);
        }
        
        public void delete(Person p){
            getHibernateTemplate().delete(p);
        }
        
        public Person getById(Integer id){
            Person result = null;
            try {
                    result = (Person) getHibernateTemplate().load(Person.class, id);
                    getHibernateTemplate().initialize(result);
            } catch (ObjectRetrievalFailureException e) {
                    log.log(Level.WARNING, "no object with id:"+id+" in database");
            }
            return result;
        }
        
        public List<Person> getAll(){
            return getHibernateTemplate().find("from Person p");
        }
    }

Oh iya, contoh kode BDB di atas dapat diujicoba dengan membuat Main class sebagai berikut:

Main.java

    package tutorial.berkeleydb;
    
    import java.io.File;
    
    public class Main {
            private static EntityStore store;
            private static Environment env;
            
            public static void main(String[] args) throws Exception {
                    openDatabase();
                    
                    Person endy = new Person();
                    endy.setName("Endy Muhardin");
                    endy.setBirthdate(new SimpleDateFormat("dd-MM-yyyy").parse("17-08-1945"));
                    endy.setEmail("endymuhardin@yahoo.com");
                    
                    PersonDao dao = new PersonDaoBerkeleyDB(store);
                    dao.save(endy);		
                    System.out.println("======== Create one person with ID:"+endy.getId()+" ========");
                    
                    System.out.println("======== Display all person from database ========");
                    List<Person> all = dao.getAll();
                    for (Person person : all) {
                            displayPerson(person);
                    }
                    
                    System.out.println("======== Add one more person to database ========");
                    Person khalisa = new Person();
                    khalisa.setName("Khalisa Alayya");
                    khalisa.setBirthdate(new SimpleDateFormat("dd-MM-yyyy").parse("31-12-2000"));
                    khalisa.setEmail("me@khalisa.web.id");
                    dao.save(khalisa);
                    
                    System.out.println("======== Display all person from database ========");
                    all = dao.getAll();
                    for (Person person : all) {
                            displayPerson(person);
                    }
                    
                    System.out.println("======== Get person by ID "+endy.getId()+" from database ========");
                    Person endyFromDb = dao.getById(endy.getId());		
                    displayPerson(endyFromDb);
                    
                    System.out.println("======== Change person data with ID "+endy.getId()+" from database ========");
                    endyFromDb.setEmail("endy.muhardin@gmail.com");
                    dao.save(endyFromDb);
                    
                    System.out.println("======== Display person with ID "+endy.getId()+" after update ========");
                    Person endyFromDb2 = dao.getById(endy.getId());		
                    displayPerson(endyFromDb2);
                    
                    closeDatabase();
            }
            
            private static void displayPerson(Person person) {
                    System.out.println("ID : "+person.getId());
                    System.out.println("Name : "+person.getName());
                    System.out.println("Birthdate : "+person.getBirthdate());
                    System.out.println("Address : "+person.getEmail());
            }
            
            private static void openDatabase(){				
                    try {			
                            EnvironmentConfig config = new EnvironmentConfig();
                            config.setAllowCreate(true);
                            config.setTransactional(true);
                            
                            env = new Environment(new File("database"), config);
                            
                            StoreConfig storeConfig = new StoreConfig();
                            storeConfig.setAllowCreate(true);
                            storeConfig.setTransactional(true);
                            
                            store = new EntityStore(env, "PersonDatabase", storeConfig);			
                    } catch (DatabaseException e) {
                            e.printStackTrace();
                    }			
            }
            
            private static void closeDatabase() {
                    try {
                            store.close();
                            env.close();
                    } catch (DatabaseException e) {
                            e.printStackTrace();
                    }
            }
    }

Selain contoh kode yang ditampilkan di sini, sebetulnya BDB juga mendukung asosiasi One to One, One to Many, dan Many to Many. Sayang sekali saya belum sempat coba.

Dari percobaan sederhana ini, kita dapat menarik beberapa sisi positif dan negatif dari BerkeleyDB, juga kemungkinan kasus yang tepat dan tidak tepat untuk menggunakan BDB.

Positif

  • Ukuran jar kecil. Hanya butuh 1.1 MB untuk jar-nya BDB. Bandingkan jika kita gunakan MySQL + Hibernate. Jar mysql-connector 430KB, hibernate.jar 2MB, hibernate-annotation.jar 300KB, dependensi lainnya seperti ehcache, jta, cglib, asm, dan lainnya bisa mencapai 7MB total.
  • Tidak perlu berkutat dengan SQL. Object oriented 100%
  • Karena embedded, sangat cepat. Akses data instan langsung ke sumbernya. Bandingkan dengan rantai MySQL-network-hibernate-spring-aplikasi. Bagaikan langit dan bumi.

Negatif

  • Karena embedded, sulit dimanipulasi dengan tools lain. Berbeda dengan database RDBMS yang bisa diakses menggunakan command line, web-based interface, atau database management tools.
  • Karena tidak support SQL, tidak bisa dimanipulasi aplikasi lainnya. Misalnya aplikasi reporting.
  • Untuk sharing data dengan aplikasi lain, kita harus buatkan API via Java atau XML.
  • BDB mengandalkan Serialization untuk menyimpan object ke dalam database. Siapapun yang sudah pernah belajar Serialization pasti tahu bahwa penyakitnya ada pada class evolution. Kalau class kita berubah –misalnya menambah atau mengurangi field, apalagi rename class– BDB akan bingung, dan kita harus menulis kode program untuk melakukan migrasi.

Kapan menggunakan BDB

  • Data yang disimpan hanya digunakan sendiri. Misalnya penyimpanan data session HTTP, cache untuk username dan password, dsb.
  • Butuh kecepatan extra tinggi dan pemrograman yang mudah. Misalnya kita membuat server SMS gateway atau message processing. Butuh kecepatan tinggi, dan datanya kecil kemungkinan digunakan aplikasi lain.
  • Anda termasuk orang yang living on the edge. Selalu menggunakan tools yang aneh dan terbaru.

Kapan menggunakan RDBMS

  • Data harus bisa diakses aplikasi lain seperti reporting.
  • Data berjumlah besar, sehingga sulit untuk dimigrasi apabila terjadi refactoring. (Ingat masalah Serialization)
  • Perusahaan sudah terlanjur beli Oracle atau DB2.

Demikianlah .. semoga bermanfaat. :D


Menggunakan Eclipse BIRT [bagian 1]

Programmer beraliran komersial tentunya sudah tidak asing dengan perlengkapan reporting seperti Crystal Report. Reporting tools ini berguna untuk membuat laporan dari sumber data yang sudah ada.

Cara kerjanya sederhana. Bagi yang belum pernah menggunakan aplikasi reporting dan ingin memahaminya secara sederhana, dapat menggunakan fasilitas mail merge yang dimiliki aplikasi office seperti Microsoft Word atau OpenOffice Writer.

Intinya, kita membuat satu template. Di dalam template tersebut kita isikan variabel-variabel yang kita ingin tampilkan. Pada saat dijalankan (runtime), variabel ini akan digantikan oleh template processor dengan data yang diambil dari sumber data eksternal.

Contoh umum penggunaan fasilitas mail merge adalah untuk mencetak label alamat pada undangan. Kita definisikan variabel yang akan tampil, misalnya:

  • Nama
  • Alamat
  • Kota

Kemudian, kita sediakan file lain di spreadsheet (MS Excel atau OO Calc) yang memuat data tersebut. Data yang disimpan bisa saja ratusan atau ribuan. Untuk Open Office, database yang digunakan tidak hanya dari spreadsheet, tapi bisa juga dari database betulan seperti MySQL atau Oracle.

Setelah template dan data siap, aplikasi dijalankan untuk melakukan merge. Hasilnya adalah ratusan label yang sudah terisi dengan data dari database.

Logika yang sama digunakan oleh perlengkapan reporting seperti Crystal Report dan kompetitor lainnya. Biasanya template dibuat dalam format XML. Selanjutnya, prosesnya sama. Desain templatenya, kemudian siapkan datanya.

Saya tidak akan membahas Crystal Report di sini. Kita akan membahas aplikasi reporting yang gratis, yaitu Eclipse BIRT. Sebagai bagian dari Eclipse, aplikasi ini gratis dan berkualitas tinggi.

Fitur BIRT juga tidak kalah dengan aplikasi reporting lain, diantaranya adalah:

  1. Berbagai pilihan datasource; RDBMS, XML, Text File, dan lain-lain. Lain-lain maksudnya di sini adalah sumber lain yang dapat diakses melalui scripting language BIRT.
  2. Berbagai pilihan output; PDF, XLS, CSV, HTML, XML adalah format yang didukung. Untuk format lain, asalkan berbasis text, bisa menggunakan output XML yang kemudian diproses lagi menggunakan XSLT.
  3. Berbagai pilihan integrasi. BIRT dapat dijalankan sebagai standalone report server, atau juga diembed (digabungkan) dengan aplikasi kita. Bila dijalankan sebagai standalone server, BIRT dapat berkomunikasi dengan aplikasi dengan bahasa pemrograman yang berbeda, misalnya PHP, Ruby, .Net, atau yang lainnya.
  4. Visual Designer. Eclipse sudah melengkapi BIRT dengan database explorer, drag-and-drop query builder, dan fitur canggih lainnya. Kita akan lihat fitur ini dalam screenshot di bawah.
  5. Integrated dengan IDE. Bila kita menggunakan Java, kita bisa coding di Eclipse, dan membuat report di Eclipse juga. Jadi tidak perlu menggunakan beberapa tools yang berbeda.

Baiklah, mari kita coba saja.

Pertama, kita harus donlod dulu dari homepagenya. Ukurannya relatif besar, sekitar 200 MB. Tapi jangan khawatir, ada mirrornya di server Universitas Indonesia. Kalau kita sudah punya Eclipse terbaru, bisa langsung gunakan fitur updatenya. Jalankan downloadnya sebelum pulang kantor. Dengan demikian, besok pagi ketika kita datang, BIRT sudah terinstal.

Setelah terinstal, kita bisa langsung menggunakannya. BIRT terinstal lengkap bersama contoh database. Tutorial cara penggunaannya juga cukup jelas dan lengkap. Sayangnya tidak ada screenshotnya. Jangan khawatir, pada artikel ini, saya akan sediakan screenshotnya.

Berikut adalah tampilan Eclipse pada saat sudah dijalankan.

Eclipse Interface

Selanjutnya, kita langsung membuat project baru. Klik File > New, kemudian pilih Report Project

Create Report Project

Beri nama projectnya. Kemudian klik Next. Eclipse akan menanyakan apakah kita ingin bekerja dalam Report Perspective. Jawab saja Yes.

Report perspective tampil seperti screenshot berikut

Report Perspective

Perhatikan di sebelah kiri ada tiga tab : Pallete, Data Explorer, dan Library. Kita akan gunakan tab ini untuk mendesain report.

Kemudian, mari kita buat report pertama kita. Datasource yang akan digunakan sudah disediakan Eclipse sebagai database sample. Sesuai tutorial Eclipse, kita akan membuat laporan yang berisi daftar nama pelanggan, dikelompokkan berdasarkan provinsi (State) dan kota (City).

Untuk membuat report baru, klik File > New > Report. Kalau pilihan Report belum ada, pilih Others dan cari di daftar yang tersedia, dalam kategori Business Intelligence and Reporting Tools.

Create Report

Beri nama reportnya

Define Report

Kemudian pilih templatenya. Supaya lebih seru, kita akan gunakan Blank Template

Blank Template

Selanjutnya, template report kita tampil di layar dalam Design View. Kita dapat berganti ke berbagai view melalui tab di bawah editor report.

Setelah report tampil, kita dapat menambahkan label. Tambahkan saja satu label untuk judul, yaitu Laporan Data Pelanggan. Jenis huruf, rata tengah, dan setting lainnya dapat dilakukan melalui toolbar yang ada di bagian bawah.

Add Label

Sebelum melangkah lebih jauh, kita perlu mendefinisikan Data Source untuk report ini. Buat Data Source baru melalui panel sebelah kiri.

Create DataSource

Pilihan datasource akan muncul. Kita akan menggunakan database sample yang sudah ada. Untuk project betulan, kita dapat gunakan database atau sumber data yang lainnya.

Select DataSource

Beri nama Sample di kolom Data Source Name, kemudian klik Finish.

Dari datasource yang ada, kita dapat mendefinsikan Data Set. Data Set ini adalah sebagian dari isi Data Source yang akan kita gunakan dalam report.

Create Dataset

Muncul pilihan datasource dan jenis data set. Kita bisa menggunakan lebih dari satu datasource dalam satu report. Untuk kali ini, cuma ada satu data source. Pilih tipe data set SQL Select Query. Jangan lupa beri nama yang deskriptif untuk dataset yang dibuat.

Create Dataset

Setelah kita tekan Next, akan muncul database explorer di panel kiri, dan SQL editor di kanan. Kita dapat melakukan drag and drop pada layar ini.

Database Explorer

Edit SQL menjadi seperti ini:

select * 
from

Kemudian drag-and-drop tabel Customer ke sebelah kanan from, sehingga kodenya menjadi seperti ini:

select * 
from CLASSICMODELS.CUSTOMERS

Klik Next. Selanjutnya muncul Data Set Editor.

DataSet Editor

Kita tidak melakukan perubahan apa-apa di sini. Tapi bila ingin tahu apa isi tabelnya, kita dapat melihat Preview Result seperti ini:

Preview DataSet

Klik OK. Dataset siap digunakan.

Selanjutnya, kita akan tampilkan data pelanggan dalam bentuk tabel. Pilih Table di Pallete, dan letakkan di report. Kita akan tampilkan 4 kolom dan 1 baris detail. Kolom yang nantinya akan ditampilkan adalah:

  1. Provinsi
  2. Kota
  3. Nama Pelanggan
  4. Nomer Telepon

Tampilan yang dihasilkan adalah seperti ini:

Initial Table

Dari seluruh data pelanggan yang ada, kita akan kelompokkan berdasarkan provinsi, kemudian kota. Untuk itu, kita tambahkan Group di tabel. Caranya, klik tombol pemilih tabel, kemudian klik kanan di baris detail.

Insert Group

Group Editor akan muncul. Beri nama State, kemudian klik OK.

Group Editor

Hasilnya akan tampak seperti ini

Group by State

Untuk mengisikan State ke Group Row tersebut, drag-and-drop dari panel kiri ke kolom paling kiri di baris Group Header Row. Layar Select Data Binding akan muncul, langsung saja klik OK.

State Data Binding

Kita perlu memasukkan Group Row satu lagi untuk City. Caranya sama, yaitu dengan menambahkan Group Row di bawah Group Header State.

Insert Group Below

Beri nama City, kemudian klik OK. Lalu pilih field city dari Data Explorer di sebelah kiri, dan pasang di kolom kedua di Group Header Row 2. Hasilnya seperti ini:

Group by City

Terakhir, masukkan field CUSTOMERNAME dan PHONE ke Detail Row. Kolom judul CUSTOMERNAME kurang enak dibaca, jadi kita bisa ganti labelnya di baris paling atas menjadi Customer Name. Hasil akhirnya adalah seperti ini:

Final Design

Report kita sudah selesai. Silahkan disave, kemudian lihat previewnya. Kalau semua dilakukan dengan benar, kita akan melihat tampilan seperti ini:

Report Preview

Kita akan lihat bahwa data pelanggan sudah diurutkan dan dikelompokkan berdasarkan State dan City.

Kalau kita tekan menu File, kita akan menemui pilihan View Report in Web Viewer, as HTML, dan as PDF. Silahkan gunakan sesuai kebutuhan.

Export Format HTML PDF

Selamat mencoba.


Membuat Gantt Chart dengan JFreeChart

JFreeChart adalah library untuk menghasilkan chart dengan Java. Berbagai chart bisa dihasilkan, dari Pie Chart, Bar Chart, dan sebagainya.

Pada artikel ini, kita akan mencoba membuat Gantt Chart. Gantt chart adalah diagram yang menunjukkan rangkaian task, tanggal mulai, selesai, dan persentase kemajuannya. Bagi mereka yang pernah menggunakan aplikasi manajemen proyek pasti tau apa itu Gantt Chart.

Berikut output yang kita inginkan Gantt Chart

Diagram di atas dihasilkan dari sumber data sebagai berikut

Aktivas Tanggal Mulai Tanggal Selesai Persentase Selesai
UML Design 01-01-2006 03-01-2006 100 %
Coding 02-01-2006 03-01-2006 75 %
Testing 03-01-2006 14-01-2006 50 %
Integrate 04-01-2006 25-01-2006 25 %

Untuk mengubah data tersebut menjadi chart, berikut langkah-langkah dan kode yang digunakan.

Pertama, kita harus buat data tersebut menjadi Task object.

Task design = new Task("UML Design", toDate("01-01-2006"), toDate("03-01-2006"));
Task coding = new Task("Coding", toDate("02-01-2006"), toDate("03-01-2006"));
Task test = new Task("Testing", toDate("03-01-2006"), toDate("14-01-2006"));
Task commit = new Task("Integrate", toDate("04-01-2006"), toDate("25-01-2006"));

Untuk memudahkan konversi tanggal, saya buat method seperti ini

private static Date toDate(String date) throws ParseException {
    return formatter.parse(date);
}

kemudian, kita set persentase kemajuan task.

design.setPercentComplete(1);		
coding.setPercentComplete(0.75);
test.setPercentComplete(0.50);
commit.setPercentComplete(0.25);

Task dapat dikelompokkan menjadi TaskSeries.

TaskSeries codingTasks = new TaskSeries("Coding Activities");
codingTasks.add(design);
codingTasks.add(coding);
codingTasks.add(test);
codingTasks.add(commit);

Dan kumpulan TaskSeries disebut TaskCollection

TaskSeriesCollection allTasks = new TaskSeriesCollection();
allTasks.add(codingTasks);

TaskCollection ini digunakan untuk membuat chart.

JFreeChart chart = ChartFactory.createGanttChart("Coba Gantt Chart", "Task", "Tanggal", allTasks, false, false, false);

Terakhir, kita gambar chart menjadi file PNG

ChartUtilities.saveChartAsPNG(new File("output/gantt.png"), chart, 400, 300);

Selain menjadi PNG, kita juga bisa menghasilkan file JPEG

ChartUtilities.saveChartAsJPEG(new File("output/gantt.jpg"), chart, 400, 300);

Demikianlah cara membuat chart dengan JFreeChart. Selain Gantt chart masih banyak lagi fitur JFreeChart yang bagus. Silahkan download dan coba