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.

Internal Dependency dengan Ivy

Pada artikel sebelumnya, kita telah menyimpan artifact modul person-model di shared repository melalui mekanisme scp dengan private key. Pada artikel ini, kita akan menggunakan artifact person-model tersebut dalam modul person-dao-api. Pengambilan artifact dilakukan melalui mekanisme yang sama, yaitu scp dengan private key.

Berikut struktur folder modul person-dao-api.

Kita memiliki folder src yang berisi source code class PersonDao.java, yang isinya sebagai berikut.

package com.artivisi.tutorial.ivy.dao;

import java.util.List;

import com.artivisi.tutorial.ivy.model.Person;

public interface PersonDao {
	
	/**
	 * saves Person object into database. 
	 * If object is already exists (denoted by not-null ID field), 
	 * the existing record with the corresponding ID is updated. 
	 * If the object is new (denoted by null ID field), 
	 * new record is inserted.
	 * 
	 *  This method also set the ID field for new record.
	 * */
	public void save(Person person);
	
	/**
	 * fetch all person object in database.
	 * @return List of all person
	 * */
	public List getAll();
	
	/**
	 * fetch Person object with the speficied ID. 
	 * @param id identifier for person object
	 * @return Person object if there is record found for the speficied id, null otherwise
	 * */
	public Person getById(Long id);
}

Seperti terlihat dalam deklarasi import, class ini menggunakan class Person, yang sudah tersedia di shared repository. Kita akan melakukan build dengan menggunakan script build.xml. Isinya sama dengan yang ada di modul person-model, dengan perkecualian nama projectnya. Berikut isi build.xml.

<project name="person-dao-api" default="build">

	<property file="build.properties"/>
	
	<import file="${basedir}/../person-build/default.xml"/>
	<import file="${basedir}/../person-build/ivy-builder.xml"/>
	
</project>

Dependensi modul person-dao-api terhadap person-model kita deklarasikan dalam ivy.xml sebagai berikut.

<ivy-module version="1.0">
    <info organisation="com.artivisi" module="person-dao-api"/>
    
	<publications>
		<artifact name="${ant.project.name}" conf="api"/>
		<artifact name="${ant.project.name}-sources" conf="source" type="src" ext="jar"/>
	</publications>
    
    <dependencies>
        <dependency name="person-model" rev="latest.integration" conf="api"/>
    </dependencies>
</ivy-module>

Setelah build.xml dan ivy.xml selesai dibuat, kita bisa langsung melakukan build. Panggil target dari command line sebagai berikut:

ant build

Dari output perintah di atas terlihat bahwa Ivy akan menguraikan dependensi project dengan melakukan query ke semua resolver yang terdaftar. Setelah modul yang dibutuhkan (person-model) ditemukan, Ivy akan langsung mendonlodnya ke dalam folder lib. Karena isi folder lib sudah kita daftarkan ke dalam classpath, maka proses kompilasi akan berjalan dengan mulus. Berikut struktur folder person-dao-api setelah melakukan proses resolve.

Pada deklarasi dependensi di atas, terlihat bahwa modul person-dao-api memiliki dependensi terhadap modul person-model dengan revision latest.integration. Ini artinya Ivy akan menggunakan versi terbaru yang ada dalam repository. Untuk melihat kemampuan fitur ini, mari kita publish person-model dengan revision yang lebih baru.

Edit file build.properties dalam modul person-model, sehingga menjadi seperti ini.

build.version = 0.0.2
release.type = integration

Setelah itu, bersihkan hasil kompilasi terdahulu, dan publish modul person-model.

ant clean publish-local

Lalu, pindah ke modul person-dao-api, dan lakukan build lagi.

ant clean build

Terlihat pada output, bahwa modul person-model yang terbaru akan didonlod, dan yang versi lama akan dihapus. Sehingga isi folder lib akan terlihat sebagai berikut.

Ivy memiliki beberapa cara untuk menentukan mana artifak yang terbaru (latest), yaitu: latest-time, latest-revision, dan latest-lexico.

Dengan menggunakan latest-time, Ivy akan melihat tanggal dibuatnya suatu artifak. Kalau artifak berada di repository filesystem, maka tanggal sistem operasi akan digunakan. Bila repository diakses melalui http, Ivy akan melakukan query terhadap http server. Metode ini, walaupun efektif, relatif lambat bila akses jaringan ke repository tidak mumpuni.

Latest-revision membandingkan nama versi artifak, dan mengenali beberapa keyword. Dia dapat menentukan bahwa versi 1.0-alpha lebih baru daripada 1.0-dev1, dan 1.0 lebih baru daripada 1.0-rc1.

Sedangkan strategi latest-lexico hanya membandingkan berdasarkan urutan abjad. Jadi, 1.0-m1 akan lebih baru daripada 1.0-build135.

Ketiga strategi tersebut sudah built-in dalam Ivy dan tidak memerlukan konfigurasi lebih lanjut.

Selain revision, Ivy juga mengenal terminologi status. Secara default, Ivy menyediakan tiga status diurutkan dari yang paling stabil/mature, yaitu release, milestone, dan integration. Bila kita menggunakan latest.integration, Ivy akan mengambil artifak terbaru berstatus apapun dari repository. Bila kita gunakan latest.milestone, Ivy akan mengabaikan rilis integration, dan hanya akan membandingkan milestone dan release. Demikian juga bila kita gunakan latest.release, maka versi terbaru hanya akan dicari dari artifak berstatus release.

Cara deklarasi latest.integration di atas disebut dengan dynamic revision, karena nomer revision tidak secara eksplisit disebutkan. Selain menggunakan latest.integration, ada beberapa mekanisme lain, yaitu menggunakan +, dan menggunakan range.

Kita bisa menggunakan + untuk memilih revisi terbaru untuk rilis tertentu. Misalnya bila kita memiliki modul dengan revisi 1.0.1, 1.0.4, 1.0.7, dan 1.1.4 dalam repository, kita dapat menggunakan 1.0.+ untuk memilih versi terbaru dalam lini 1.0, yaitu 1.0.7.

Kita juga bisa menggunakan range, untuk menentukan revisi yang dapat digunakan, misalnya [1.0,2.2] akan memilih semua revisi yang sama atau lebih besar dari 1.0, tapi lebih kecil atau sama dengan 2.2. Lebih lengkap tentang ini bisa dilihat di dokumentasi Ivy tentang dependensi.

Demikianlah deklarasi dependensi dengan Ivy. Pada artikel ini, kita baru mencoba dependensi terhadap modul yang kita buat sendiri. Di artikel selanjutnya, kita akan lihat bagaimana mendeklarasikan dependensi terhadap modul eksternal seperti Spring Framework atau Hibernate.


Publish Modul ke Repository

Template project sudah dibuat pada artikel sebelumnya. Pada artikel ini, kita akan menghubungkan kedua modul ini dengan menggunakan Ivy.

Seperti kita ketahui, modul person-dao-api mempunyai dependensi terhadap modul person-model. Berikut gambarnya.

Dependensi ini terlihat di source-code PersonDao.java, yang melakukan import terhadap class Person, sebagai berikut.

package com.artivisi.tutorial.ivy.dao;

import java.util.List;

import com.artivisi.tutorial.ivy.model.Person;

public interface PersonDao {
	
	/**
	 * saves Person object into database. 
	 * If object is already exists (denoted by not-null ID field), 
	 * the existing record with the corresponding ID is updated. 
	 * If the object is new (denoted by null ID field), 
	 * new record is inserted.
	 * 
	 *  This method also set the ID field for new record.
	 * */
	public void save(Person person);
	
	/**
	 * fetch all person object in database.
	 * @return List of all person
	 * */
	public List getAll();
	
	/**
	 * fetch Person object with the speficied ID. 
	 * @param id identifier for person object
	 * @return Person object if there is record found for the speficied id, null otherwise
	 * */
	public Person getById(Long id);
}

Agar class diatas bisa dikompilasi dengan lancar, ada tiga hal yang harus dilakukan:

  1. Menyediakan lokasi yang dapat diakses oleh kedua modul

  2. Mempublikasikan distribusi modul person-model

  3. Mendeklarasikan dependensi person-dao-api terhadap person-model

Repository Ivy

Dalam dunia Ivy, file-file distribusi suatu modul, seperti person-model.jar, person-model-sources.jar, disebut dengan istilah artifact. Artifact ini diletakkan di lokasi tertentu, disebut dengan istilah repository. Konfigurasi tentang cara mengakses suatu repository disebut dengan istilah resolver. Ivy menyediakan berbagai resolver untuk berbagai metode/protokol, yaitu:

  • Filesystem : ini biasanya digunakan untuk folder di komputer lokal atau shared folder (NFS atau Windows Share)

  • URL : digunakan untuk mengakses repository melalui http

  • SSH : digunakan untuk mengakses repository melalui mekanisme scp

  • SFTP : menggunakan protokol FTP yang terenkripsi

  • VFS : menggunakan Apache Commons VFS sebagai backend. Mendukung apapun jenis filesystem yang didukung VFS, salah satunya sftp

  • Ibiblio : untuk mengakses repository maven2

Selain itu, Ivy juga menyediakan composite resolver, yaitu resolver yang dapat menampung resolver lainnya. Composite resolver ada dua:

  • Chain Resolver : menggabungkan beberapa resolver, sehingga kalau suatu artifak tidak ditemukan di resolver pertama, bisa melanjutkan pencarian ke resolver selanjutnya dalam chain

  • Dual Resolver : memisahkan resolver untuk menghitung dependensi, dan resolver untuk mendownload artifak.

Baiklah, ternyata Ivy bisa menangani macam-macam protokol. Tapi bagaimana cara kita mendesain repository yang baik? Bagaimana best-practicesnya? Mari kita bahas.

Desain Repository

Biasanya, kita memiliki dua jenis repository, internal dan external. Repository internal digunakan untuk menyimpan artifak yang dihasilkan project dalam organisasi. Sebagian atau semua artifak internal ini mungkin saja bersifat komersil atau proprietary, sehingga tidak dibuka untuk konsumsi publik.

Di internet tersedia repository yang bisa diakses semua orang. Pengguna Maven biasanya menggunakan repository ibiblio. Pengguna Spring OSGi biasanya menggunakan repository SpringSource, yang sudah berisi library yang OSGi compliant. Repository SpringSource kompatibel baik dengan Ivy maupun Maven. Untuk menghemat bandwidth internasional, organisasi kita bisa membuat mirror dari repository publik ini agar dependensi bisa diunduh dari jaringan lokal.

Pada artikel ini, kita hanya akan membahas tentang internal repository. Pembahasan tentang external repository akan dibahas pada artikel yang akan datang.

Internal Repository

Internal repository dibagi lagi menjadi dua kategori, yaitu local repository, dan shared repository.

Local repository berada di PC masing-masing programmer. Misalnya satu programmer mengerjakan dua modul yang saling berhubungan, misalnya person-model dan person-dao-api. Seringkali dia membuat perubahan di person-model yang akan digunakan di person-dao-api. Tapi karena kode programnya belum sempurna, dia tidak ingin merilis artifak tersebut ke anggota tim yang lain. Untuk kebutuhan ini, dia mempublikasikan artifact person-model ke local repo di PCnya dia sendiri, sehingga bisa diakses oleh modul person-dao-api.

Setelah person-model dan person-dao-api dibuat dan ditest secara menyeluruh, barulah programmer tersebut merilis artifak ke shared repo agar bisa digunakan rekan-rekannya.

Konfigurasi Internal Resolver

Untuk mengimplementasikan skenario di atas, kita pertama akan mendefinisikan repository local. Asumsikan saja repository ini akan disimpan di folder local-repo sejajar dengan modul-modul yang lainnya. Kita akan mengkonfigurasi resolvernya di file bernama ivysettings.xml, diletakkan di modul person-build dalam subfolder ivy. Berikut isinya.

<ivysettings>
	<settings defaultResolver="local" />

	<caches defaultCacheDir="${ivy.settings.dir}/../../ivy-cache" />
	<resolvers>		
		<filesystem name="local">
			<artifact
				pattern="${ivy.settings.dir}/../../local-repo/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
			<ivy
				pattern="${ivy.settings.dir}/../../local-repo/release/[organisation]/[module]/[revision]/[artifact]-[revision].xml" />
		</filesystem>
	</resolvers>
</ivysettings>

Ada beberapa hal yang harus dijelaskan di sini.

  • defaultResolver : ini adalah resolver yang digunakan oleh Ivy bila kita tidak secara eksplisit memilih resolver. Setting ini akan berguna nantinya bila kita mendeklarasikan dependensi

  • defaultCacheDir : menyebutkan lokasi cache di komputer lokal. Bila tidak dikonfigurasi, Ivy akan menyimpan cache di folder .ivy2/cache dalam home folder user.

  • Variabel ${ivy.settings.dir} : lokasi folder tempat file ivysettings.xml berada

  • Artifact Pattern : struktur folder tempat artifak disimpan

  • Ivy Pattern : struktur folder tempat metadata modul disimpan. Kita akan membahas tentang metadata ini nanti.

Selanjutnya, kita butuh repository yang bisa digunakan seluruh tim dan programmer dalam perusahaan. Ini berguna bila modul yang kita buat akan digunakan oleh tim lain. Untuk itu, kita akan mengkonfigurasi repository yang akan kita beri nama company. Agar aman, kita gunakan protokol scp untuk memindahkan file ke server. Otentikasinya menggunakan public/private key supaya semua artifact disimpan dengan nama user yang sama. Berikut konfigurasi resolvernya. Tambahkan di bawah resolver local.

<ssh name="company" keyFile="${user.home}/.ssh/id_rsa" host="nama-servernya" user="user-untuk-login-di-server">
	<ivy
		pattern="/lokasi-folder-di-server/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
	<artifact
		pattern="/lokasi-folder-di-server/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</ssh>

Kita telah memiliki dua resolver, satu untuk repository di komputer lokal, dan satu lagi repository yang disharing ke seluruh organisasi.

Target Resolve dan Publish

Setelah kita melakukan konfigurasi resolver, sekarang kita bisa mempublish artifact dari modul person-model. Untuk melakukan publishing, kita perlu membuat target di buildfile kita. Agar buildfile tetap rapi, kita buat file baru, yaitu ivy-builder.xml, diletakkan di modul person-build. File ini nantinya akan kita import di build.xml dalam masing-masing modul. Berikut isi file ivy-builder.xml

<project name="ivy-related-targets" xmlns:ivy="antlib:org.apache.ivy.ant">
	
	<target name="resolve" description="--> resolve and retrieve dependencies with ivy">
		<ivy:retrieve 
			sync="true"
			pattern="lib/[conf]/[artifact].[ext]"
		/>
	</target>
    
	<target name="clean-cache" description="--> clean the ivy cache">
		<ivy:cleancache />
	</target>


	<target name="publish-local" depends="build" description="--> publish this project in the ivy repository">
		<property name="revision" value="${build.version}" />
		<delete file="${build.dir}/ivy.xml" />
		<ivy:publish 
			artifactspattern="${dist.dir}/[artifact].[ext]" 
			resolver="local" 
			pubrevision="${revision}" 
			status="${release.type}" 
			update="true" 
			overwrite="true" 
		/>
		<echo message="project ${ant.project.name} released locally with version ${revision} and status ${release.type}" />
	</target>
	
	<target name="publish-company" depends="build" description="--> publish this project to company repository">
		<property name="revision" value="${build.version}" />
		<delete file="${build.dir}/ivy.xml" />
		<ivy:publish 
			artifactspattern="${dist.dir}/[artifact].[ext]" 
			resolver="company" 
			pubrevision="${revision}" 
			status="${release.type}" 
			update="true" 
			overwrite="true" 
		/>
		<echo message="project ${ant.project.name} released to company repo with version ${revision} and status ${release.type}" />
	</target>
    
	<target name="ivy-report" depends="resolve" description="--> generate dependency report">
		<ivy:report todir="build/ivy-report"/>
	</target>
	
</project>

Terlihat dari target publish di atas bahwa kita membutuhkan dua variabel untuk melakukan publish, yaitu ${revision} dan ${release.type}. Kedua variabel ini akan kita sediakan pada masing-masing modul.

Bila kita ingin mempublish artifact, terlebih dulu kita harus melakukan build. Kita juga memiliki target resolve untuk menentukan dependency. Target resolve ini dijalankan sebelum melakukan kompilasi. Dengan demikian, kita harus menyesuaikan target compile dalam default.xml agar menjalankan resolve sebelum compile. Ubah baris berikut dalam default.xml

<target name="compile" depends="prepare">
	<javac srcdir="${src.java.dir}" destdir="${compile.dir}" classpathref="compile.classpath" />
</target>

menjadi seperti ini.

<target name="compile" depends="resolve, prepare">
	<javac srcdir="${src.java.dir}" destdir="${compile.dir}" classpathref="compile.classpath" />
</target>

Mempublish Artifact

Sekarang kita ingin mempublish artifact yang dihasilkan modul person-model. Seperti kita ketahui pada artikel sebelumnya, bila kita menjalankan target build, akan dihasilkan dua jar dalam folder dist, yaitu person-model.jar dan person-model-sources.jar. Kita akan mempublish kedua artifak ini ke repository.

Pastikan file ivy-builder.xml sudah diimport dalam build.xml. Isi build.xml harusnya terlihat seperti ini.

<project name="person-model" default="build">
	<property file="build.properties"/>
	
	<import file="${basedir}/../person-build/default.xml"/>
	<import file="${basedir}/../person-build/ivy-builder.xml"/>
	
</project>

File tersebut mengacu pada file build.properties. Berikut isi file build.properties.

build.version = 0.0.1
release.type = integration

Kedua variabel di atas digunakan untuk mengisi variabel ${build.version} dan ${release.type} yang dibutuhkan target publish di atas. Setiap kali kita melakukan publish, kita harus menentukan versi dan jenis artifak tersebut.

Nomer versi (build.version) tidak sulit dipahami. Untuk menentukan mana yang lebih baru, tinggal dibandingkan versi major, minor, dan micronya. Release type membutuhkan penjelasan lebih lanjut.

Secara default, Ivy memiliki tiga jenis release, diurutkan dari yang paling experimental sampai yang paling stabil: integration, milestone, dan release. Kita juga bisa mendefinisikan jenis release sendiri, dengan menggunakan tag status dalam ivysettings.xml. Untuk kebutuhan kita, tiga status yang disediakan Ivy sudah memadai.

Selanjutnya, kita mendefinisikan beberapa metadata yang berkaitan dengan artifact yang ingin dipublish, yaitu:

  • Nama organisasi kita. Ini akan digunakan Ivy untuk mengatur struktur folder dalam repository

  • Nama modul yang akan dipublish

  • Daftar artifak yang akan dipublish. Satu modul bisa mempublish banyak artifak, misalnya: *.jar yang berisi hasil compile, javadoc, source-code, dsb

Metadata tersebut ditulis dalam file yang bernama ivy.xml. Diletakkan di sebelah build.xml. Berikut isinya.

<ivy-module version="1.0">
	<info organisation="com.artivisi" module="com.artivisi.tutorial.ivy.model"/>
	<publications>    
		<artifact name="${ant.project.name}"/>
		<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
	</publications>
</ivy-module>

Setelah semuanya lengkap, kita tinggal memanggil

ant publish-local 

untuk mempublish ke repository local, atau

ant publish-company

untuk mempublish ke repository shared. Bila dalam proses pembuatan private key kita menggunakan password, akan muncul dialog box yang menanyakan password. Isikan nama user yang digunakan untuk login ke server dan password dari private key kita. Sekali lagi, username yang dimasukkan adalah username di server, sedangkan password yang dimasukkan adalah password untuk private key kita sendiri. Jadi, kita tidak memasukkan password dari username di server.

Setelah dijalankan, kita akan melihat banyak file dalam repository. Ivy akan merilis: person-model-0.0.1.jar, person-sources-0.0.1.jar, dan ivy-0.0.1.xml yang berisi metadata dari rilis tersebut. Setiap file disertai signature md5 dan sha1 untuk kebutuhan verifikasi keabsahan file.

Berikut struktur folder keseluruhan.

Isi folder local repo sebagai berikut.

Isi folder person-build sebagai berikut.

Isi folder person-model sebagai berikut.

Pada artikel selanjutnya, kita akan membahas bagaimana cara mengambil artifact yang sudah dipublish tersebut.


Otomasi Build Process dengan Ant

Setelah pada artikel sebelumnya kita memahami mengapa dan kapan kita butuh Ivy, mulai dari artikel ini, kita akan menjalani langkah demi langkah supaya kegiatan build kita benar-benar terintegrasi dan efisien.

Pada artikel ini, kita akan membuat satu modul dulu, yang sama sekali tidak memiliki dependensi, yaitu modul person-model. Kita akan mengotomasi proses build untuk modul ini, kemudian menyiapkan infrastruktur lainnya sehingga siap untuk digunakan oleh modul lainnya.

Tiap modul yang kita buat, setara dengan satu project dalam IDE. Dalam IDE kita dapat mendaftarkan (membuat referensi ke) project lain dalam build-path sehingga tidak muncul peringatan compile error.

Berikut struktur folder modul person-model.

Tidak ada yang istimewa, kita memiliki build.xml disertai dengan folder src yang berisi source code java. Kita ingin agar struktur folder kita seragam di semua modul, jadi untuk seluruh modul nantinya strukturnya akan sama.

Kita hanya memiliki satu file source code sederhana, Person.java sebagai berikut.

package com.artivisi.tutorial.ivy.model;

import java.util.Date;

public class Person {
	private Integer id;
	private String name;
	private Date 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;
	}
	public Date getBirthdate() {
		return birthdate;
	}
	public void setBirthdate(Date birthdate) {
		this.birthdate = birthdate;
	}	
}

Sebagai rangkaian build, kita akan melakukan kegiatan sebagai berikut:

  • Bersihkan hasil kompilasi sebelumnya (clean)

  • Kompilasi (compile)

  • Distribusi, membuat file *.jar dari hasil kompilasi (build)

Rangkaian kegiatan tersebut kita tuliskan dalam file build.xml, sebagai berikut

<project name="person-model">
	
	<property name="src.java.dir" value="src/java" />
	<property name="compile.dir" value="build/bin" />
	<property name="dist.dir" value="build/dist" />

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

	<target name="prepare">
		<mkdir dir="lib" />
		<mkdir dir="build" />
		<mkdir dir="${compile.dir}" />
		<mkdir dir="${dist.dir}" />
	</target>

	<target name="clean">
		<delete dir="build" />
		<delete dir="lib" />
	</target>

	<target name="compile" depends="prepare">
		<javac srcdir="${src.java.dir}" destdir="${compile.dir}" classpathref="compile.classpath" />
	</target>

	<target name="build" depends="compile">
		<!-- binary distribution -->
		<jar destfile="${dist.dir}/${ant.project.name}.jar">
			<fileset dir="${compile.dir}">
				<exclude name="**/*Test.class"/>
			</fileset>
		</jar>
		
		<!-- source distribution -->
		<jar destfile="${dist.dir}/${ant.project.name}-sources.jar">
			<fileset dir="${src.java.dir}">
				<exclude name="**/*Test.java"/>
			</fileset>
		</jar>
	</target>
</project>

Selanjutnya, kita bisa coba melakukan build.

ant build

Nanti Ant akan menghasilkan file person-model.jar dan person-model-sources.jar dalam folder dist.

File build.xml ini nantinya akan kita gunakan di seluruh modul. Seluruh modul tentunya butuh kegiatan kompilasi dan membuat jar. Untuk menghindari perulangan dan copy-paste, kita simpan build.xml ini di folder bersama, kita beri nama saja person-build.

Karena digunakan di banyak modul, kita perlu melakukan penyesuaian sedikit, yaitu namanya kita ganti menjadi default.xml. Deklarasi nama project juga diganti, dari seperti ini:

<project name="person-model">

menjadi seperti ini:

<project name="default-build">

Struktur foldernya menjadi seperti ini

Selanjutnya, dalam build.xml di project person-model, kita hanya perlu melakukan import saja. Isi build.xml menjadi seperti ini:

<project name="person-model" default="build">
	
	<import file="${basedir}/../person-build/build.xml"/>

</project>

Silahkan coba lakukan build di dalam folder person-model. Seharusnya modul ini bisa dibuild tanpa error, sama seperti sebelumnya.

Bila modul person-model sudah lancar, kita bisa mulai mereplikasi modul-modul yang lainnya. Dengan semua modul sudah dibuatkan sesuai template, berikut struktur foldernya.

File build.xml di masing-masing project mirip dengan person-model, hanya dibedakan di baris pertama saja.

<project name="person-model">

Sesuaikan dengan nama modul masing-masing.

Persiapan template project kita sudah selesai. Sampai tahap ini, kita hanya memiliki satu source-code java, yaitu Person.java. Source-code ini akan dipaket menjadi file distribusi person-model.jar (binary) dan person-model-sources.jar (source-code). Pada artikel selanjutnya, kita akan mempublikasikan distribusi ini agar bisa digunakan oleh modul lain.


Build Management dengan Ivy

Pada posting sebelumnya, saya telah membahas tentang cara instalasi Ivy, dan juga sedikit pengantar tentang apa itu Ivy.

Ivy adalah dependency management tools. Dia mampu menangani dependensi antar modul dalam aplikasi. Tentunya penjelasan ini sangat abstrak. Baiklah mari kita lihat problem apa yang kita hadapi dalam membuat aplikasi, dan bagaimana Ivy menyelesaikan problem tersebut.

Peringatan : Bukan untuk pemula !!!

Rangkaian artikel ini diperuntukkan untuk Senior Developer, Team Leader, atau Architect.

Saya asumsikan pembaca sudah mahir menggunakan Ant, Linux, dan memiliki bandwidth yang besar.

Studi Kasus

Untuk contoh kasus, mari kita buat aplikasi sederhana dengan Spring MVC 2.5. Aplikasi ini bisa didonlod di sini.

Aplikasi sederhana ini terdiri dari 3 bagian utama, yaitu:

  • Domain Model

  • Kode Akses Database (DAO)

  • Tampilan (UI)

Hubungan dependensi antara ketiga bagian ini dapat digambarkan sebagai berikut:

Tanda panah dibaca sebagai “tergantung kepada”. Contohnya, modul DAO tergantung kepada modul Domain Model, sehingga untuk mengkompilasi modul DAO, kita harus punya modul Domain Model. Sebaliknya, untuk mengkompilasi modul Domain Model, kita tidak butuh modul DAO.

Dependensi, Pembagian Tim, dan Penjadwalan

Ketergantungan antar modul ini perlu dipertimbangkan dengan seksama, karena dari desain ketergantungan ini, kita dapat menentukan pembagian tim yang efisien. Idealnya masing-masing tim development dapat bekerja secara paralel dan tidak saling menunggu tim lain selesai.

Dengan skema dependensi seperti di atas, pembagian tugas antar tim kita tidak efisien, karena tim DAO harus menunggu tim DM selesai, baru dia dapat mulai. Demikian juga, tim UI harus menunggu tim DM dan juga tim DAO selesai, baru dia dapat mulai. Ini dapat dilihat di project schedule berikut.

Dengan schedule seperti ini, kita membutuhkan 11 minggu untuk development, karena modul UI dan DAO yang membutuhkan waktu lama harus dikerjakan secara serial.

Agar kita dapat bekerja secara paralel, kita dapat mengatur ulang dependensi sebagai berikut.

Kita menambahkan modul baru, yaitu DAO-API dan DAO-Impl. Modul DAO-API ini berisi interface dari modul DAO, tanpa implementasi. Implementasinya berada di modul DAO-Impl.

Pembagian yang baru ini didasarkan pada waktu pengembangan dari masing-masing modul. Modul DM dan DAO-API bisa dikembangkan dengan cepat, karena hanya berisi struktur data dan deklarasi method saja. Modul UI dan DAO-Impl butuh waktu lama, karena relatif kompleks dan membutuhkan banyak test.

Dengan skema baru, project schedule menjadi seperti ini.

Dengan skema di atas, kita dapat mengalokasikan agar tim DAO dan tim UI bersama-sama mengerjakan modul Domain Model dan DAO-API. Setelah selesai, tim UI dapat mengerjakan modul UI secara paralel dengan tim DAO yang mengerjakan modul DAO-Impl.

Durasi development dapat dikurangi menjadi 7 minggu saja.

Masalah dalam implementasi

Ok, kita sudah mendesain dependensi sedemikian rupa, sehingga bisa meminimasi idle time. Berarti kita sudah menjadi Development Team Leader yang canggih … benar??

Belum, yang kita lakukan ini baru setengah jalan. Mengelola tim yang bekerja paralel itu bukan pekerjaan yang mudah. Desain dependensi yang baik memungkinkan tim bekerja paralel. Tapi butuh perangkat tambahan agar mereka bisa berkoordinasi secara efisien.

Masalah terbesar dengan project multi-modul ini adalah bagaimana mengelola perubahan (Change Management). Developer yang berpengalaman pasti sudah tahu bahwa keinginan end-user selalu berubah. Perubahan ini menjadi masalah bila terjadi di modul yang digantungi banyak modul lain.

Contohnya, pada assessment awal, kita sudah mendefinisikan bahwa class Person memiliki tiga property, yaitu id, nama, dan tanggalLahir. Class Person ini kita tempatkan di modul Domain Model, yang digunakan oleh semua modul lain. Katakan saja misalnya kita rilis dengan versi 1.0.

Ternyata peta persaingan bisnis aplikasi contact berubah. Perusahaan pesaing menyediakan aplikasi yang tidak hanya menyimpan tanggal lahir, tapi juga nomer handphone. Tentunya kita harus buru-buru mengupgrade aplikasi (yang belum selesai dikerjakan) agar juga memuat data nomer handphone.

Nah, bagaimana mengelola perubahan ini agar kedua tim yang sedang bekerja (DAO-Impl dan UI) dapat menyesuaikan diri dengan mudah?

Implementasi yang paling sederhana bisa dilakukan dengan USB Flashdisk. Compile saja modul DM, kemudian copy ke flashdisk. Edarkan flashdisk tersebut ke seluruh tim … masalah selesai.

Cara flashdisk, walaupun bisa dilakukan, tapi tidak scalable. Jika dependensinya rumit (misalnya membuat aplikasi ERP), kita harus membuat satu departemen khusus untuk mengedarkan flashdisk.

Nah, inilah gunanya Ivy. Dengan Ivy, kita bisa membuat perubahan di class Person, kemudian menyuruh Ivy untuk mempublikasikannya ke lokasi tertentu dengan versi 1.1. Begitu tim lain melakukan kompilasi, Ivy secara otomatis akan mendeteksi bahwa ada update terbaru di modul Domain Model, mendownload versi terbaru, menghapus versi yang lama, baru melakukan kompilasi.

Ivy dapat mengelola dependensi antar modul dalam internal perusahaan, maupun dependensi dengan pustaka open-source. Contoh aplikasi kita di atas menggunakan pustaka dari Spring Framework, MySQL, Velocity, dan SiteMesh. Masing-masing pustaka tersebut memiliki dependensi lagi terhadap pustaka lain, misalnya Apache Commons dan Log4J.

Dengan menggunakan Ivy, kita hanya perlu mendeklarasikan dependensi langsung, yaitu Spring Framework, MySQL, Velocity, dan SiteMesh. Selanjutnya Ivy akan mencari tahu semua dependensi level kedua terhadap Jakarta Commons dan Log4J. Begitu kita melakukan kompilasi, Ivy akan terlebih dulu mengunduh semua dependensi dari internet, melakukan setting CLASSPATH, baru melakukan kompilasi.

Ivy juga memiliki fitur configuration. Dengan fitur ini, kita bisa membedakan dependensi untuk kompilasi, melakukan test, atau mendeploy aplikasi ke production.

Contohnya, bila kita menggunakan database, kita tidak perlu mendownload *.jar apapun untuk melakukan kompilasi. Pada saat kita test di IDE sendiri, kita gunakan database HSQLDB supaya ringan dan cepat. Untuk test oleh tim tester, kita gunakan database MySQL. Akhirnya, untuk UAT dan production, kita gunakan database Oracle.

Contoh lain, kita bisa mendefinisikan konfigurasi deployment dan delivery. Untuk deployment, kita menginstal aplikasi di tempat client. Tentunya kita hanya butuh *.jar saja. Lain halnya dengan delivery. Selain *.jar, kita juga harus memuat source code, javadoc, manual penggunaan, dan lainnya ke dalam DVD untuk diserah-terimakan dengan client.

Dengan menggunakan fitur configuration Ivy, kita dapat mendefinisikan berbagai kombinasi artifak yang dibutuhkan untuk berbagai situasi.

Pada artikel selanjutnya, kita akan mulai membuat modul Domain Model. Seluruh modul yang dibuat dalam rangkaian artikel ini bisa dibuat dengan Text Editor biasa. Tidak perlu IDE canggih semacam Netbeans, Eclipse, atau IDEA.

Stay tuned.


Login ssh dengan private key

Masih dalam rangka mengotomasi workflow di ArtiVisi, artikel ini akan membahas tentang cara memindahkan file antar komputer secara aman tapi otomatis.

Ada berbagai cara kita memindahkan file dari satu komputer ke komputer lain, diantaranya:

  • Menggunakan USB Flashdisk

  • Windows File Sharing

  • NFS

  • FTP atau SFTP

  • SCP

  • dan sebagainya

Cara pertama jelas tidak bisa diotomasi.

Cara kedua sampai keempat mengharuskan kita membuka write access tanpa password di komputer tujuan supaya bisa otomatis. Kalau ada passwordnya kan harus ada seseorang yang mengetik password tersebut. Jadi agak sulit mengotomasinya.

Oleh karena itu, kita akan membahas cara kelima, yaitu SCP. SCP –atau Secure Copy– adalah mekanisme copy file melalui protokol SSH. File yang dikirim terenkripsi. Untuk dapat masuk ke komputer tujuan, kita harus melakukan otentikasi. Jadi ini adalah cara yang relatif aman untuk mengirim file.

Tunggu dulu, katanya mau otomatis, tapi kok harus otentikasi?? Berarti harus ada yang mengetik password dong ….

Tidak juga, makanya saya menggunakan istilah otentikasi, bukan password. SSH dapat melakukan otentikasi dengan password maupun private key. Pada artikel ini, kita akan membahas tentang otentikasi private key.

Artikel ini dibuat menggunakan Ubuntu Hardy dengan openssh sudah terinstal.

Ada beberapa langkah yang harus kita lakukan, yaitu:

  1. Membuat private key di komputer pengirim

  2. Mengirim public key ke komputer tujuan

  3. Registrasi public key di komputer tujuan

Sebelum mulai, kita tentukan dulu studi kasusnya.

Di tengah pertarungan dengan Pain, guru genit Jiraiya tiba-tiba berhasil menemukan kelemahan musuhnya. Dia ingin mengirim file berisi informasi tersebut ke sang Hokage seksi, yaitu Tsunade. Rencananya, dia akan login ke server di markas dengan username Tsunade dan menyimpan file tersebut di folder /home/tsunade/important.

Tentunya Tsunade tidak akan memberitahukan passwordnya kepada Jiraiya, mengingat tabiatnya yang genit. Sebagai gantinya, dia mendaftarkan public key Jiraiya ke user accountnya di server, sehingga Jiraiya bisa login sebagai Tsunade, tanpa Tsunade harus memberitahukan passwordnya kepada Jiraiya.

Pada cerita di atas, kita punya dua komputer, yaitu laptopnya Jiraiya yang dibawanya kemanapun pergi, dan server di markas. Kita juga punya dua user, yaitu Jiraiya dan Tsunade.

Agar bisa melakukan public key authentication, tentunya hal pertama yang harus dilakukan adalah membuat public key.

Membuat public key

Jiraiya harus membuat public key di laptopnya sendiri. Public key selalu berpasangan dengan private key. Untuk membuat public key, Jiraiya mengetik perintah berikut di terminal.

ssh-keygen

Perintah tersebut akan menanyakan password untuk membuka private key. Jiraiya memilih tidak memberikan password. Sebetulnya ini kurang aman, karena kalau ada orang lain yang berhasil mendapatkan file private key, dia bisa login ke server di markas tanpa hambatan.

Tapi mau bagaimana lagi, hidup sebagai ninja sangat berbahaya. Kita tidak tahu apakah setelah pertarungan kita masih punya tangan untuk mengetik password.

Tekan enter beberapa kali sampai selesai. Berikut output yang dihasilkan.

Generating public/private rsa key pair.
Enter file in which to save the key (/home/jiraiya/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/jiraiya/.ssh/id_rsa.
Your public key has been saved in /home/jiraiya/.ssh/id_rsa.pub.
The key fingerprint is:
6f:7c:e2:52:f1:14:5d:2c:7f:3a:53:5e:fe:7f:98:9c jiraiya@laptop

Selanjutnya, kita harus kirimkan file public key ke Tsunade agar didaftarkan di accountnya. File public key ini bisa ditemukan di folder /home/jiraiya/.ssh/ dengan nama id_rsa.pub. Public key bisa dikirim melalui media apapun, misalnya email, usb flashdisk, burung hantu, maupun burung merpati.

Registrasi Public Key

Singkat kata, Tsunade telah menerima public key dari Jiraiya. Dia harus mendaftarkannya di accountnya agar Jiraiya bisa login.

Tsunade harus login dulu ke server dan menaruh file public key tersebut di sana. Misalnya dia letakkan di folder /home/tsunade/ dan nama filenya diubah menjadi jiraiya_id_rsa.pub.

Untuk meregistrasinya, Tsunade menggunakan perintah berikut di terminal.

cat /home/tsunade/jiraiya_id_rsa.pub >> /home/tsunade/.ssh/authorized_keys

Setelah itu, public key tidak lagi diperlukan, sehingga bisa dihapus.

rm /home/tsunade/jiraiya_id_pub

Selesai sudah konfigurasi di server, sekarang kita kembali ke pertarungan Jiraiya vs Pain.

Login dengan public key

Sesaat sebelum Jiraiya menekan tombol Send di Mozilla Thunderbird, Pain mendaratkan serangan mematikan ke Jiraiya, sehingga ybs tidak dapat menggerakkan tangannya. Jiraiya membatin, “Untung saja saya sudah setup public key di server dengan account Tsunade. Baiklah, mari kita scp saja.”

Untuk mengirim file tersebut melalui scp dengan public key, berikut adalah perintahnya

scp -i /path/menuju/private/key username@komputer-tujuan:folder-tujuan

Jadi, untuk mengirim file pain-secret.txt ke server dengan username tsunade, Jiraiya menggunakan perintah berikut

scp -i /home/jiraiya/.ssh/id_rsa pain-secret.txt tsunade@server:/home/tsunade/important/

File akan segera terkirim tanpa harus mengetik password.

Lebih lanjut tentang ssh dengan public/private key bisa dibaca di sini.

Lebih lanjut tentang nasib Jiraiya bisa dibaca di sini.

Selamat mencoba