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.

Membuat Installer dengan IzPack

Menggunakan izPack

Setelah kita selesai membuat aplikasi, what next? Tentunya mendistribusikan pada orang yang membutuhkan. Bagaimana caranya agar aplikasi kita mudah digunakan? Langkah pertama adalah dengan cara memudahkan proses instalasi. Dalam tulisan ini, kita akan mempelajari cara menggunakan izPack, framework untuk membuat installer aplikasi, sehingga menginstal aplikasi akan semudah menekan tombol Next.

Para programmer tentunya sudah familiar dengan berbagai framework installer. Ada yang komersil seperti InstallShield, dan ada juga yang gratis seperti NSIS dan izPack. Kelebihan izPack adalah dia berbasis Java, tidak seperti NSIS yang cuma bisa jalan di Windows.

Langkah pertama, mari kita download izPack. IzPack disediakan sebagai installer, sehingga kita harus menginstalnya dulu sebelum bisa digunakan.

Selanjutnya, mari kita lihat struktur folder project kita yang ingin dibuatkan installernya. Struktur folder saya seperti ini:

Struktur folder project

Setelah terinstal di komputer pengguna, saya ingin foldernya seperti ini:

Hasil yang diinginkan

Perhatikan bahwa folder src tidak perlu diikutkan. Pengguna dapat memilih apakah mau menginstal source code atau tidak. Jadi, folder source code adalah optional. Folder dokumentasi dan plugin tambahan juga biasanya diinstal secara optional.

Berikutnya, tentukan screen yang akan tampil selama proses instalasi, biasanya adalah :

  1. Salam pembukaan, menyatakan kepada pengguna bahwa proses instalasi akan dimulai.
  2. Informasi aplikasi, menjelaskan tentang aplikasi dan kegunaannya.
  3. License Agreement
  4. Pemilihan paket yang akan diinstal. Di sini pengguna dapat memilih apakah dia akan menginstal paket optional atau tidak.
  5. Tujuan instalasi. Pengguna menentukan di folder mana dia akan menginstal.
  6. Proses instalasi. Menunjukkan kemajuan proses instalasi. Biasanya menggunakan progress bar.
  7. Instalasi selesai. Menampilkan pesan sukses atau gagal.

Kerangka dari konfigurasi installer kita adalah sebagai berikut :

    <installation version="1.0">
        <info> </info>
        <guiprefs> </guiprefs>
        <locale> </locale>
        <resources> </resources>
        <panels> </panels>
        <packs> </packs>
    </installation>

Isi dari tag info adalah sebagai berikut

    <info>
        <appname>PlayBilling</appname>
        <appversion>1.1-RC3</appversion>
        <authors>
            <author name="Endy Muhardin" email="endy@artivisi.com"/>
            <author name="Anton Raharja" email="anton@ngoprek.org"/>
        </authors>
        <url>http://playbilling.sourceforge.net</url>
        <javaversion>1.5.0</javaversion>
    </info>

Bagian guiprefs menjelaskan tampilan yang akan digunakan. Kita akan pakai native saja, sesuai OS, supaya tampak alami. Kalau mau, kita dapat memilih beberapa tampilan Look & Feel yang tersedia.

    <guiprefs width="800" height="600" resizable="yes"/>

Berikutnya, pilihan bahasa instalasi. Untuk mudahnya, gunakan bahasa Inggris saja.

    <locale>
        <langpack iso3="eng" />
    </locale>

Untuk bagian resource, kita perlu mendefinisikan file-file yang dibutuhkan installer. File-file ini harus sudah ada.

    <resources>
        <res src="installer/application-description.html" id="HTMLInfoPanel.info"/>
        <res src="installer/gpl.txt" id="LicencePanel.licence"/>
    </resources>

Tentukan urutan screen seperti sudah dijelaskan di atas.

    <panels> 
        <panel classname="HelloPanel"/>
        <panel classname="HTMLInfoPanel"/>
        <panel classname="LicencePanel"/>
        <panel classname="PacksPanel"/>
        <panel classname="TargetPanel"/>
        <panel classname="InstallPanel"/>
        <panel classname="SimpleFinishPanel"/>
    </panels>

Bagian packs menentukan folder-folder yang akan diinstal. Sintaksnya mirip dengan Ant.

    <packs>
        <pack name="Base" required="yes">
            <description>Base System files</description>
            <fileset dir="." targetdir="$INSTALL_PATH">
                <include name="ext/jetty/*"/>
                <include name="webapp/**/*"/>
                <include name="*.sh"/>
                <include name="*.bat"/>
            </fileset>
        </pack>
        
        <pack name="Source" required="no">
            <description>Source code and test files</description>
            <fileset dir="." targetdir="$INSTALL_PATH">
                <include name="src/**/*"/>
                <include name="lib/**/*"/>
                <include name="*.xml"/>
            </fileset>
        </pack>
    </packs>

Variabel $INSTALL_PATH akan diisikan sesuai folder tujuan yang dipilih pengguna.

Ok, file konfigurasi installer kita sudah selesai. Sekarang tinggal diotomasi sehingga bisa dipanggil lewat Ant. Ada beberapa informasi yang dibutuhkan oleh izPack, yaitu:

  • lokasi konfigurasi instalasi (file xml yang baru saja kita buat)
  • nama file installer yang akan dihasilkan (misalnya MyProject-installer.jar)
  • jenis instalasi (desktop atau web)
  • base folder (folder acuan untuk menentukan lokasi relatif folder yang lain)
  • lokasi instalasi izPack

Berikut adalah target untuk Ant

    <taskdef name="izpack" 
             classpath="${izpack_install_dir}/lib/compiler.jar"
             classname="com.izforge.izpack.ant.IzPackTask"
    />
    <target name="build-installer">
        <izpack input="installer/playbilling-installer.xml"
                output="dist/playbilling-installer.jar"
                installerType="standard"
                basedir="."
                izPackDir="${izpack_install_dir}"
        />
    </target>

Setelah Ant target dijalankan, kita akan mendapatkan file playbilling-installer.jar di dalam folder dist. Mari kita coba jalankan file installer tersebut.

java -jar dist/playbilling-installer.jar

Kita akan disodori tampilan selamat datang

Screen Selamat Datang

Klik Next, dan kita akan melihat keterangan tentang PlayBilling. Keterangan ini langsung dicopy-paste dari homepage PlayBilling

Deskripsi Aplikasi

Next lagi, dan muncullah license agreement, yaitu GPL, yang diambil langsung dari website GPL.

GPL License

Screen selanjutnya adalah pilihan paket yang akan diinstal. Perhatikan bahwa paket wajib diburamkan dan tidak bisa diklik.

Pilihan paket instalasi

Setelah itu, kita akan ditanya lokasi instalasi.

Pilihan lokasi instalasi

Pilih lokasi yang sesuai, kemudian Next. Instalasi akan segera dimulai.

Proses Instalasi

Sukses … !

Sukses

Sebagai bonus, izPack juga akan membuatkan Uninstaller. Coba jalankan

java -jar /home/endy/PlayBilling/Uninstaller/uninstaller.jar

Uninstall

Klik OK, dan aplikasi akan dihilangkan dari komputer Anda.

Selamat mencoba. Semoga bermanfaat.


Tapestry vs Spring MVC

Dalam beberapa project terakhir, saya menggunakan Spring MVC untuk membuat presentation layer. Spring sangat fleksibel dan mudah dipelajari. Ini menambah fleksibilitas dalam menyusun tim programmer. Dengan kemudahannya, programmer yang sudah pernah membuat aplikasi web –baik menggunakan Struts, PHP, Ruby on Rails, atau bahkan yang cuma mengerti servlet– dapat beradaptasi dalam hitungan hari dan langsung produktif.

Di lain pihak, saya sering sekali mendengar kehebatan Tapestry. Para pendukungnya menggembar-gemborkan peningkatan produktifitas (baca: kecepatan membuat aplikasi) yang signifikan. Harga yang harus dibayar untuk produktifitas tersebut adalah kompleksitas. Tapestry terkenal sulit. Dia menggunakan paradigma event-driven. Jadi kita harus berpikir seolah-olah kita membangun aplikasi desktop dengan Swing atau VB. Kompleksitas ini menyebabkan kita tidak bisa sembarangan menyusun tim programmer. Butuh orang yang pemahaman Java-nya menengah ke atas agar aplikasi dapat diselesaikan dengan baik.

Setelah merasa nyaman menggunakan Spring di beberapa project terakhir, saya putuskan untuk keluar dari zona aman dan belajar Tapestry. Masa depan aplikasi web nampaknya adalah component-mania. Berbagai komponen siap pakai yang tersedia di pasaran (baik gratis maupun bayar) yang tinggal kita tempelkan di aplikasi kita, akan menjadi trend. Terutama dengan maraknya penggunaan Ajax belakangan ini. Pasti akan banyak orang yang membuat komponen Ajax-ready yang bisa langsung digunakan.

Prediksi ini diperkuat dengan presentasi Chuk Mun Lee di Sun Developer Day 2006 kemarin. Dia menunjukkan bagaimana cepatnya orang bisa membuat aplikasi web. Cukup mendownload kumpulan komponen dari jMaki, dan langsung menggambar di IDE. Kalau para pesaing bekerja seperti ini, sementara kita masih secara manual memparsing request, wah … kita akan ketinggalan.

Sebagai titik awal pelajaran, saya akan mulai dari Spring Framework. Ada beberapa kegiatan utama dalam pembangunan aplikasi web yang terjadi berulang-ulang. Dalam membandingkan Spring vs Tapestry, kegiatan-kegiatan ini akan menjadi kriteria penilaian. Dalam setiap kegiatan, saya akan memberi nilai dan menyatakan pemenangnya. Masing-masing kegiatan bobotnya berbeda, tergantung frekuensi kemunculannya dalam aplikasi.

Berikut adalah kriteria penilaiannya :

  1. Hello World. Yaitu kegiatan setup pertama kali sampai Hello World muncul. Bobotnya cuma 5%, karena kegiatan ini cuma dilakukan sekali saja di awal.
  2. Form binding. Bagaimana semua variabel dalam form bisa diambil sebagai satu domain object. Bobotnya 20%, karena ini adalah kegiatan wajib, inti dari aplikasi.
  3. Validasi form. Bagaimana mencegah invalid input, baik secara server-side maupun client-side. Bobotnya 20%, saudara kembar dengan Form Binding.
  4. Web Asset. Bagaimana penanganan image, css, javascript. Bobotnya 10%.
  5. Security. Bagaimana kemudahan mengaktifkan otentikasi dan otorisasi. Bobotnya 20%. Ini juga bagian yang penting. Kalau penanganan di sisi ini tidak elegan, akan banyak duplikasi kode bertebaran di dalam kode program.
  6. Ajax readiness. Bobotnya 10%.
  7. Reusability. Bagaimana kemudahan menggunakan komponen/library yang sudah ada. Bobotnya 10%.

Disclaimer: dalam kegiatan membandingkan ini, tidak ada tujuan untuk menjelekkan yang satu terhadap yang lainnya. Ini murni adalah pekerjaan harian software designer, yaitu memilih framework dan teknologi. Berbagai saran dan masukan dari pendukung masing-masing framework sangat diharapkan.

Hello World akan dibahas di artikel selanjutnya.


Ruthless Testing 4

Database testing merupakan suatu kegiatan yang sulit, apalagi dalam dunia Java. Bayangkan saja, untuk melakukan testing database, kita harus melakukan :

  1. Membuat skema database. Skema terdiri dari dari tabel, constraint, view, dan lainnya.

  2. Mengisi sampel data.

  3. Mengeksekusi query.

  4. Periksa ke database untuk memastikan hasilnya benar.

  5. Koreksi atau lanjutkan sesuai dengan hasil di nomer 4.

Semua kegiatan di atas sangat membosankan dan melelahkan untuk diulang-ulang setiap kali melakukan perubahan kode program. Akibatnya programmer menjadi bosan dan malas mengetes kode programnya. Padahal kode akses database merupakan salah satu bagian krusial dalam aplikasi.

Pada artikel ini kita akan membahas cara membuat kode akses database yang mudah dan cepat, dilengkapi dengan testing yang menyeluruh dan menyenangkan.

Saya biasanya menggunakan kombinasi Spring Framework dan Hibernate. Dengan kombinasi ini, kita dapat membuat aplikasi dengan sangat cepat.

Kita mulai dengan struktur tabel sederhana. Misalnya kita memiliki class Siswa, sebagai berikut:

Class Siswa

public class Siswa {
  private Integer id;
  private String nama;
  private Date tanggalLahir;

  public Integer getId() { return this.id; }
  public String getNama() { return this.nama; }
  public Date getTanggalLahir() { return this.tanggalLahir; }

  // setter method  
}

class ini akan disimpan di database dengan struktur tabel (MySQL) sebagai berikut:

mysql-schema.sql

CREATE TABLE TBL_SISWA (
  id_siswa INT PRIMARY KEY AUTO_INCREMENT, 
  nama VARCHAR(255), 
  tanggal_lahir DATE
);

Dengan menggunakan Hibernate, kita tidak perlu menulis SQL query. Sebagai gantinya, kita harus memberitahu Hibernate tentang skema database kita. Caranya adalah dengan menambahkan annotation sehingga class kita menjadi seperti ini:

Siswa.java

package tutorial.hibernate;
@Entity
@Table(name="TBL_SISWA")
public class Siswa {
  private Integer id;
  private String nama;
  private Date tanggalLahir;

  @Id
  @Column(name="id_siswa")
  @GeneratedValue(strategy=GenerationType.AUTO)
  public Integer getId() { return this.id; }

  public String getNama() { return this.nama; }

  @Column(name="tanggal_lahir")
  public Date getTanggalLahir() { return this.tanggalLahir; }
}

Pembaca yang teliti segera protes, “Ada yang ketinggalan!! Di atas public String getNama() belum ada @Column”
Tidak, itu bukan kelupaan. Tapi memang Hibernate dapat dengan cerdas menebak nama kolom. public String getNama akan diterjemahkan menjadi kolom nama. Bahkan seandainya nama tabel kita SISWA (bukannya TBL_SISWA), kita tidak perlu menuliskan @Table.

Oke. Sekarang mapping sudah selesai. Saatnya membuat kode untuk mengisi record (create), membaca record (read), mengubah record (update), dan menghapus record (delete). Empat serangkai operasi database ini dikenal dengan istilah CRUD.

Pertama, kita buat interface dulu, kode yang mendefinisikan operasi CRUD ini.

SiswaDao.java

package tutorial.hibernate;
public interface SiswaDao {
  public void create(Siswa s);
  public Siswa getById(Integer id);
  public void update(Siswa s);
  public void delete(Siswa s);  
}

Pembuatan interface berguna supaya kapan-kapan kalau kita sudah bosan dengan Hibernate, kita bisa mengganti dengan implementasi yang lainnya, misalnya iBatis atau JDBC biasa.

Berikut implementasi operasi CRUD dengan Spring dan Hibernate.

SiswaDaoHibernate.java

package tutorial.hibernate;
public class SiswaDaoHibernate extends HibernateDaoSupport implements SiswaDao {
  public void create(Siswa s){
    getHibernateTemplate().save(s);
  }

  public Siswa getById(Integer id){
    return (Siswa) getHibernateTemplate().load(Siswa.class, id);
  }
  
  public void update(Siswa s){
    getHibernateTemplate().update(s);
  }

  public void delete(Siswa s) {
    getHibernateTemplate().delete(s);
  } 
}

Lagi-lagi ada yang bertanya, “Kok tidak ada kode untuk menangani koneksi database? Mana connect dan disconnect dengan database? Mana kode untuk begin dan commit transaction?”

Semua kode birokratis yang ditanyakan barusan akan diinisialisasi melalui Spring Framework. Sebenarnya kita bisa saja menginisialisasi sendiri melalui kode program biasa. Tapi akan lebih efisien dan konsisten apabila kita menggunakan Spring Framework, seperti akan kita lihat sebentar lagi.

Berikut adalah deklarasi SiswaDaoHibernate, DataSource koneksi database, dan konfigurasi transaction :

belajar.xml

<beans>
    <bean id="siswaDaoAsli" class="tutorial.hibernate.SiswaDaoHibernate">
      <property name="sessionFactory"><ref bean="sessionFactory"/></property>
    </bean> 

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
      <property name="dataSource" ref="dataSource"/>
      <property name="annotatedClasses">
        <list>
          <value>tutorial.hibernate.Siswa</value>
    	</list>
    	</property>
    	<property name="hibernateProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop> 
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
            </props>
    	</property>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/belajar"/>
    	<property name="username" value="belajar"/>
    	<property name="password" value="belajar"/>
    </bean>

    <bean id="siswaDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager" ref="transactionManager"/>

    	<property name="target" ref="siswaDaoAsli"/>

    	<property name="transactionAttributes">

    		<props>

    			<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

    			<prop key="create*">PROPAGATION_REQUIRED</prop>
    			<prop key="update*">PROPAGATION_REQUIRED</prop>

    			<prop key="delete*">PROPAGATION_REQUIRED</prop>

    		</props>

    	</property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    	<property name="sessionFactory" ref="sessionFactory"/>
    </bean>
</beans>

Object yang kita gunakan adalah siswaDao. Object ini dirangkai dari berbagai object lain, diantaranya adalah koneksi database (dataSource), transaction interceptor (gunakan transaction untuk method create, update, delete*), dan transaction manager untuk mengelola transaction Hibernate.

Perhatikan kode ini:

<prop key="hibernate.hbm2ddl.auto">create</prop> 

Baris kode di atas akan men-drop seluruh tabel dan constraint di database, dan membuat ulang semuanya. Dengan demikian, database kita akan fresh seperti baru.

PERHATIAN!!! Jangan dijalankan di database production!!

Berikut adalah unit test untuk class SiswaDaoHibernate:

SiswaDaoHibernateTest.java

package tutorial.hibernate;
public class SiswaDaoHibernateTest extends TestCase {
    private static ApplicationContext ctx;
    private static SiswaDao dao;
    private static DataSource ds;
    private static Connection conn;
    
    static {
        ctx = new ClassPathXmlApplicationContext("belajar.xml");
        dao = (SiswaDao)ctx.getBean("siswaDao");
        ds = (DataSource)ctx.getBean("dataSource"););
    }

    public void setUp() throws Exception {
        conn = ds.getConnection();
    }
    
    public void tearDown() throws Exception {
        conn.close();
    }

    public void testCreate() throws Exception {
        Siswa endy = new Siswa();
        endy.setNama("Endy Muhardin");
        endy.setTanggalLahir(new SimpleDateFormat("dd-MM-yyyy").parse("17-08-1945"));
        dao.save(endy);
    
        // mari kita test
        String sql = "SELECT * FROM TBL_USER WHERE nama='Endy Muhardin'";
        
        ResultSet rs = conn.createStatement().executeQuery(sql);
        assertTrue("harusnya ada minimal satu record", rs.next());
        assertEquals("coba cek tanggal lahir", rs.getDate("tanggal_lahir"), new SimpleDateFormat("dd-MM-yyyy").parse("17-08-1945"));
    }
}

Nah, dengan adanya unit test di atas, kita tidak perlu lagi secara manual menjalankan kode test, kemudian memeriksa isi database.

Sekarang, bagaimana mengetes method getById? Apakah kita harus melakukan insert dulu baru kemudian menjalankan dao.getById(1) ?

Tidak perlu. Kita dapat menggunakan DBUnit untuk menginisialisasi sampel data. Kita buat sampel data (fixture) sebagai berikut:

siswa-fixture.xml

<?xml version="1.0" encoding="UTF-8"?>

<dataset>
    <TBL_USER id="99"
              nama="Khalisa Alayya"
              tanggal_lahir="2005-12-31"
    />
</dataset>

Sekarang, pastikan fixture di atas dijalankan sebelum setiap test dieksekusi. Caranya adalah dengan menambahkan kode berikut di method setUp:

public void setUp() {
    conn = ds.getConnection();
    
    // inisialisasi koneksi DBUnit
    DatabaseConnection dbUnitConn = new DatabaseConnection(conn);
    
    // inisialisasi fixture
    FlatXmlDataSet fixture = new FlatXmlDataSet(new File("siswa-fixtures.xml"));
    
    DatabaseOperation.CLEAN_INSERT.execute(dbUnitConn,fixture);
}

Kode ini :

DatabaseOperation.CLEAN_INSERT.execute(dbUnitConn,fixture);

akan menghapus semua data yang ada di database, dan mengisinya dengan satu record, yaitu Khalisa Alayya.

Sehingga kita dapat membuat kode test seperti ini:

public void testGetById() {
    Siswa khalisa = dao.getById(99);
    assertNotNull(khalisa);
    assertEquals("Khalisa Alayya", khalisa.getNama());
}

Berikut adalah Ant script untuk mengeksekusi test di atas:

build.xml

<project name="belajar-hibernate" default="test" basedir=".">

    <!-- silahkan lengkapi dengan target compile -->

    <target name="test" depends="compile">
		<junit haltonfailure="true" fork="true" printsummary="yes">             
            <classpath refid="project-classpath"/>
            <formatter type="xml"/>
            <batchtest todir="report/junit">
                <fileset dir="bin" includes="**/*Test.class"/>
            </batchtest>
        </junit>
	</target>

</project>

Silahkan jalankan berkali-kali sepuasnya. Rangkaian kode di atas akan dengan senang hati melakukan:

  1. Pembuatan schema

  2. Mengisi sampel data

  3. Menjalankan kode program

  4. Memeriksa hasilnya di database

Tidak perlu memelototi phpmyadmin lagi :D

Semoga bermanfaat.


Wiki

Bagi mereka yang belum tahu, wiki adalah website yang bisa diedit. Contohnya ada di Wikipedia

Biasanya wiki digunakan untuk membuat dokumentasi. Sifatnya yang bisa diedit siapa saja menyebabkan banyak orang bisa langsung berkontribusi mengisi wiki. Tidak perlu direpotkan dengan birokrasi aplikasi CMS seperti biasanya.

Ada banyak aplikasi wiki yang tersedia. Dibuat dalam berbagai bahasa pemrograman. Biasanya, aplikasi wiki diinstal di web server dan diakses oleh orang banyak.

Selain yang diinstal di web server, ada juga yang digunakan di desktop. Saya sendiri menggunakan Zim Desktop Wiki di komputer saya untuk menulis buku.

Buat yang malas (atau tidak bisa) menginstal, bisa menggunakan aplikasi wiki web based yang tidak perlu diinstal di server. Gunakan Tiddly Wiki. Wiki anda tinggal ditulis di komputer sendiri, kemudian diupload ketika sudah selesai.

Saking banyaknya orang yang membuat aplikasi wiki, akhirnya ada yang menyediakan one stop solution untuk memilih wiki. Silahkan kunjungi wikimatrix untuk membandingkan berbagai aplikasi wiki. Dia juga menyediakan wizard untuk membantu pemilihan aplikasi wiki yang sesuai.

Selamat mengeksplorasi. Semoga bermanfaat.


Technorati

Orang-orang pada rame Technorati. Pengen tau barang apa sih ini.

Buat yang udah merasakan manfaatnya, tolong kasi masukan dong.

Terima kasih.