05 Oct 2008
Pada rangkaian artikel sebelumnya, kita telah membahas tentang:
-
Instalasi Ivy
-
Build Management dengan Ivy
-
Otomasi Build Process dengan Ant
-
Publish Modul ke Repository
-
Deklarasi internal dependency
-
Deklarasi external dependency
-
Ivy Configuration
Semua konsep dan pengetahuan di atas memungkinkan kita untuk menggunakan Ivy secara efektif untuk mengelola pembuatan aplikasi. Tetapi ada sedikit hal yang masih mengganjal, yaitu borosnya bandwidth yang digunakan selama build process dilakukan.
Pada artikel kali ini, kita akan membahas cara membuat mirror repository, sehingga pengambilan artifak dapat dilakukan dari server internal maupun harddisk lokal, sehingga tidak memboroskan bandwidth.
Di artikel sebelumnya, kita telah membahas tentang deklarasi resolver. Resolver adalah konfigurasi yang menyatakan lokasi repository. Ada beberapa jenis resolver yang disediakan Ivy:
-
Filesystem : repository Ivy di harddisk lokal
-
URL : repository Ivy yang diakses melalui protokol http
-
SSH : repository Ivy yang diakses melalui protokol scp/ssh
-
SFTP : repository Ivy, diakses melalui protokol sftp
-
VFS : repository Ivy, kompatibel dengan file system yang didukung oleh Apache Commons VFS, diantaranya WebDAV, FTP, ZIP, dan sebagainya
-
Ibiblio : repository Maven 2, diakses melalui protokol http
Sebagai contoh kasus, kita akan membuat mirror dari repository ivy milik SpringSource dan repository Maven2 di Ibiblio.
Pertama, kita deklarasikan dulu resolver untuk kedua repository sumber.
Konfigurasi ini dibuat dalam ivysettings.xml.
SpringSource memiliki dua repo berbeda, untuk artifak yang dihasilkannya sendiri (Spring Portfolio) disebut dengan release, dan untuk pustaka external (Hibernate, JSF, dsb; disebut dengan external. Berikut konfigurasi untuk kedua repo SpringSource, sesuai dengan dokumentasinya:
<url name="springsource.release.repo.resolver">
<ivy pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
<url name="springsource.external.repo.resolver">
<ivy pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
Dan ini adalah resolver untuk Ibiblio.
<ibiblio name="maven2" m2compatible="true" />
Repository SpringSource akan kita mirror di http://repo.artivisi.com/ivy/springsource/
dengan struktur folder nama-organisasi/nama-modul/nomer-revisi/[artifak]-[revisi].[extension]
. Folder tersebut berada di mesin repo.artivisi.com
dalam folder /var/www/repo.artivisi.com/ivy/springsource
. Berikut konfigurasi repo mirror-springsource.
<ssh name="springsource.mirror.resolver" keyFile="${user.home}/.ssh/id_rsa" host="repo.artivisi.com" user="${mirror.host.user}">
<ivy pattern="/var/www/repo.artivisi.com/ivy/springsource/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="/var/www/repo.artivisi.com/ivy/springsource/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</ssh>
Repository Ibiblio akan kita mirror di http://repo.artivisi.com/ibiblio
dengan struktur folder berbeda, untuk metadata sebagai berikut: [nama-organisasi]/[nama-modul]/ivys/ivy-[nomer-revisi].xml
, dan untuk artifak sebagai berikut: [nama-organisasi]/[nama-modul]/[jenis]s/[artifak]-[nomer-revisi].[extension]
. Folder tersebut berada di mesin repo.artivisi.com
dalam folder /var/www/repo.artivisi.com/ibiblio
. Berikut konfigurasinya:
<ssh name="ibiblio.mirror.resolver" keyFile="${user.home}/.ssh/id_rsa" host="repo.artivisi.com" user="${mirror.host.user}">
<ivy pattern="/var/www/repo.artivisi.com/ibiblio/[organisation]/[module]/ivys/ivy-[revision].xml"/>
<artifact pattern="/var/www/repo.artivisi.com/ibiblio/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]"/>
</ssh>
Setelah kita memiliki dua repository sumber dan dua repository tujuan, kita buat target untuk melakukan mirroring. Di Ivy, kegiatan mirroring ini disebut dengan istilah install. Kita memiliki tiga target install, untuk ibiblio, SpringSource release, dan SpringSource external. Berikut deklarasi targetnya, kita masukkan di ivybuilder.xml
bersama dengan target untuk resolve dan publish.
<target name="install-springsource-release" description="--> install dependency from springsource repo">
<ivy:install
organisation="${organisation}"
module="${module}"
revision="${revision}"
from="${springsource.release.repo.resolver}"
to="${springsource.mirror.resolver}"
transitive="true"
overwrite="true"
/>
</target>
<target name="install-springsource-external" description="--> install dependency from springsource repo">
<ivy:install
organisation="${organisation}"
module="${module}"
revision="${revision}"
from="${springsource.external.repo.resolver}"
to="${springsource.mirror.resolver}"
transitive="true"
overwrite="true"
/>
</target>
<target name="install-ibiblio" description="--> install dependency from ibiblio maven2 repo">
<ivy:install
organisation="${organisation}"
module="${module}"
revision="${revision}"
from="${ibiblio.repo.resolver}"
to="${ibiblio.mirror.resolver}"
transitive="true"
overwrite="true"
/>
</target>
Seperti kita lihat di atas, deklarasi target install cukup generik, dengan menggunakan variabel yang bisa di-override pada saat runtime. Untuk target install-springsource-external
, kita gunakan repo external SpringSource sebagai sumber, dan mirror-springsource sebagai tujuan. Target install-springsource-release
dan install-ibiblio
juga mirip.
Sekarang saatnya kita coba. Mari kita install Hibernate Annotations versi 3.3.1.GA. Modul ini ada di repository SpringSource external.
Langkah pertama, cari dulu modul yang kita inginkan. Kita bisa gunakan halaman search yang telah disediakan. Dari hasil pencarian, kita menemukan modul yang diinginkan, berikut dengan deklarasi dependensinya. Sekarang kita telah mengetahui nama organisasi, nama modul, dan nomer revisinya.
ant install-springsource-external -Dorganisation="org.hibernate" -Dmodule="com.springsource.org.hibernate.annotations" -Drevision="3.3.1.ga"
Ivy akan melakukan resolve dan melihat semua dependensi dari Hibernate Annotations versi 3.3.1.GA. Setelah itu, Ivy akan mendonlodnya ke cache lokal, untuk kemudian diupload melalui scp ke lokasi mirror.
Terakhir, tentunya mirror repository ini akan diakses orang menggunakan http, bukan ssh. Jadi kita harus buatkan resolver untuk mengakses http://repo.artivisi.com/ivy/springsource
dan http://repo.artivisi.com/ibiblio
. Berikut konfigurasinya, kita satukan menggunakan chain-resolver.
Demikianlah rangkaian tutorial tentang penggunaan Ivy. Mungkin banyak pembaca yang membatin,
Buat apa repot-repot, pakai Netbeans atau Eclipse kan juga bisa bikin jar/war.
Biar saya kasih bocoran sedikit tentang dapur ArtiVisi. Kami akan mengadopsi SOA (Service Oriented Architecture) dalam semua produk dan project kami. Adopsi ini dilakukan di level mikro dengan menggunakan platform OSGi. Karakteristik utama aplikasi SOA dengan OSGi adalah aplikasi akan terdiri dari banyak modul-modul kecil yang saling berinteraksi. Contohnya bisa dilihat di diagram modul yang disajikan di awal artikel.
Memecah aplikasi besar menjadi modul-modul kecil tidak terlalu sulit. Siapapun yang mengenal keyword import
dan konsep CLASSPATH di Java bisa melakukannya. Yang sulit adalah mengelola kegiatan development dan integrasi antar modul. Oleh karena itu, penggunaan dependency management seperti Ivy atau Maven2 adalah hal yang wajib.
Pembaca bisa memilih apakah akan menggunakan Maven2 ataupun Ivy. Konsep dasarnya mirip, tapi implementasinya berbeda. Jadi, kalau sudah menguasai Ivy, Maven2 tidak sulit. Demikian juga sebaliknya. Silahkan mencari yang sesuai dengan kebutuhan tim Anda.
03 Oct 2008
Rangkaian artikel sebelumnya telah memberikan kita pemahaman yang memadai untuk menggunakan Ivy. Pada artikel ini, kita akan membahas satu fitur Ivy yang walaupun tidak wajib dikuasai, tapi sangat penting, karena dapat membuat konfigurasi modul kita menjadi fleksibel. Fitur ini dalam dunia Ivy dikenal dengan istilah configuration
.
Sebagai paket standar kualitas di ArtiVisi, kami menggunakan Cobertura untuk melakukan coverage test. Sayangnya Cobertura dan Hibernate tidak kompatibel. Hibernate menggunakan pustaka asm
dengan versi 1.5.3. Sedangkan Cobertura juga menggunakan asm
, dengan versi 2.2.1. Bila kedua versi kita campur, maka akan terjadi error karena Java VM kebingungan menentukan versi mana yang akan digunakan.
Untuk mengatasi masalah ini, kita menggunakan fitur configuration
Ivy. Kita membuat konfigurasi bernama test
yang membawa asm
versi 2.2.1 sesuai kebutuhan Cobertura. Selain itu, kita juga membuat konfigurasi bernama runtime
dengan asm
versi 1.5.3 untuk digunakan Hibernate. Hal ini dimungkinkan karena Cobertura hanya kita gunakan pada saat test.
Contoh lain penggunaan configuration
adalah kombinasi modul untuk merakit aplikasi. Misalnya, kita memiliki modul-modul berikut dalam aplikasi kita:
-
model
-
dao.api
-
dao.hibernate
-
dao.jdbc
-
ui.web
-
ui.desktop
Kita dapat menggunakan konfigurasi Ivy untuk membentuk 4 kombinasi aplikasi, yaitu:
Fitur configuration juga bisa digunakan untuk mengatur rilis artifak, sehingga dari satu paket source-code, kita bisa membuat rilis:
-
Hanya source-code (source only)
-
Hanya hasil kompilasi (binary only)
-
Paket dokumentasi (javadoc, reference)
-
Paket komplit (source, binary, dokumentasi)
Kombinasi rilis ini umum kita temui dalam pustaka open-source populer seperti Spring Framework atau Hibernate.
Masih banyak lagi skenario penggunaan configuration. Silahkan kembangkan imajinasi Anda untuk penggunaannya. Pada artikel ini, kita akan membahas skenario kombinasi aplikasi seperti ilustrasi di atas.
Kita telah memiliki modul person-model, dengan deklarasi dependensi (ivy.xml) sebagai berikut:
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-model"/>
<configurations>
<include file="${basedir}/../person-build/ivy/ivyconfigurations.xml" />
</configurations>
<publications>
<artifact name="${ant.project.name}" conf="api"/>
<artifact name="${ant.project.name}-sources" conf="source" type="src" ext="jar"/>
</publications>
</ivy-module>
Daftar konfigurasi yang kita miliki didefinisikan dalam file ivyconfigurations.xml dan di-include dari masing-masing ivy.xml. Berikut isi ivyconfigurations.xml.
<configurations>
<conf name="compile" description="dependency for compile time only" />
<conf name="api" description="Domain Model and API only"/>
<conf name="impl" description="implementation of APIs"/>
<conf name="source" description="source code only"/>
</configurations>
Kita juga punya modul person-dao, interface yang digunakan sebagai patokan implementasi akses database, dengan dependensi sebagai berikut:
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-dao-api"/>
<configurations>
<include file="${basedir}/../person-build/ivy/ivyconfigurations.xml" />
</configurations>
<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>
Modul person-dao ini memiliki dua jenis implementasi, yaitu dengan JDBC:
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-dao-jdbc"/>
<configurations>
<include file="${basedir}/../person-build/ivy/ivyconfigurations.xml" />
</configurations>
<publications>
<artifact name="${ant.project.name}" conf="impl"/>
<artifact name="${ant.project.name}-sources" conf="source" type="src" ext="jar"/>
</publications>
<dependency name="person-dao-api" rev="latest.integration" conf="api"/>
</dependencies>
</ivy-module>
dan dengan Hibernate:
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-dao-hibernate"/>
<configurations>
<include file="${basedir}/../person-build/ivy/ivyconfigurations.xml" />
</configurations>
<publications>
<artifact name="${ant.project.name}" conf="impl"/>
<artifact name="${ant.project.name}-sources" conf="source" type="src" ext="jar"/>
</publications>
<dependency name="person-dao-api" rev="latest.integration" conf="api"/>
</dependencies>
</ivy-module>
Dengan memisahkan interface dan implementasi DAO, kita bisa langsung membuat dua jenis user interface, yaitu yang berbasis web sebagai berikut:
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-ui-springmvc"/>
<configurations>
<include file="${basedir}/../person-build/ivy/ivyconfigurations.xml" />
</configurations>
<publications>
<artifact name="${ant.project.name}" conf="impl"/>
<artifact name="${ant.project.name}-sources" conf="source" type="src" ext="jar"/>
</publications>
<dependency name="person-dao-api" rev="latest.integration" conf="api"/>
</dependencies>
</ivy-module>
dan berbasis desktop sebagai berikut:
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-ui-swing"/>
<configurations>
<include file="${basedir}/../person-build/ivy/ivyconfigurations.xml" />
</configurations>
<publications>
<artifact name="${ant.project.name}" conf="impl"/>
<artifact name="${ant.project.name}-sources" conf="source" type="src" ext="jar"/>
</publications>
<dependency name="person-dao-api" rev="latest.integration" conf="api"/>
</dependencies>
</ivy-module>
Seperti kita lihat, untuk bisa membuat UI, kita hanya membutuhkan modul dao saja.
Untuk merangkai aplikasi ini, kita membuat satu modul yang tidak berisi source-code Java sama sekali, melainkan hanya deklarasi dependensi saja.
Berikut konfigurasi untuk aplikasi desktop dengan JDBC.
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-apps"/>
<configurations>
<conf name="person-app-desktop-jdbc" description="Application with desktop UI and JDBC backend"/>
</configurations>
<dependencies>
<dependency name="person-dao-jdbc" rev="latest.integration"
conf="person-app-desktop-jdbc->impl"/>
<dependency name="person-ui-swing" rev="latest.integration"
conf="person-app-desktop-jdbc->impl"/>
</dependencies>
</ivy-module>
dan ini adalah konfigurasi untuk aplikasi web dengan Hibernate.
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-apps"/>
<configurations>
<conf name="person-app-web-hibernate" description="Application with web-based UI and Hibernate backend"/>
</configurations>
<dependencies>
<dependency name="person-dao-hibernate" rev="latest.integration"
conf="person-app-web-hibernate->impl"/>
<dependency name="person-ui-springmvc" rev="latest.integration"
conf="person-app-web-hibernate->impl"/>
</dependencies>
</ivy-module>
Deklarasi lengkap konfigurasi dan dependensi untuk keempat kombinasi aplikasi sebagai berikut.
<ivy-module version="1.0">
<info organisation="com.artivisi" module="person-apps"/>
<configurations>
<conf name="person-app-web-jdbc" description="Application with web-based UI and JDBC backend"/>
<conf name="person-app-desktop-jdbc" description="Application with desktop UI and JDBC backend"/>
<conf name="person-app-web-hibernate" description="Application with web-based UI and Hibernate backend"/>
<conf name="person-app-desktop-hibernate" description="Application with desktop UI and Hibernate backend"/>
</configurations>
<dependencies>
<dependency name="person-model" rev="latest.integration" conf="*->api"/>
<dependency name="person-dao-api" rev="latest.integration" conf="*->api"/>
<dependency name="person-dao-jdbc" rev="latest.integration"
conf="person-app-web-jdbc->impl;person-app-desktop-jdbc->impl"/>
<dependency name="person-dao-hibernate" rev="latest.integration"
conf="person-app-web-hibernate->impl;person-app-desktop-hibernate->impl"/>
<dependency name="person-ui-swing" rev="latest.integration"
conf="person-app-desktop-jdbc->impl;person-app-desktop-hibernate->impl"/>
<dependency name="person-ui-springmvc" rev="latest.integration"
conf="person-app-web-jdbc->impl;person-app-web-hibernate->impl"/>
</dependencies>
</ivy-module>
Kita dapat merakit aplikasi dengan ant sebagai berikut
Nanti pada folder lib akan terbentuk empat folder sesuai konfigurasi. Isi dari masing-masing folder mencerminkan paket aplikasi sesuai kombinasi yang kita inginkan.
Seperti kita lihat, dengan menggunakan fitur configuration, kita dapat merangkai empat kombinasi aplikasi dengan mudah. Tentunya aplikasi kita harus dirancang secara modular supaya mudah dikombinasikan seperti cerita di atas.
Artikel berikutnya akan menutup seri Ivy ini dengan cara membuat repository dalam organisasi kita. Dengan adanya repository lokal, kita dapat menghemat bandwidth internasional karena kebutuhan dependensi dapat ditangani oleh server internal.
Kita akan melihat kemampuan Ivy untuk mengadaptasi repository baik yang memiliki metadata Ivy, maupun Maven.
01 Oct 2008
Pada artikel sebelumnya, kita telah menghubungkan dua modul berbeda dengan deklarasi dependensi. Tapi hal itu belum cukup, karena pada aplikasi yang sebenarnya, kita akan banyak menggunakan pustaka yang tidak kita buat sendiri.
Agar kita bisa menggunakan pustaka luar tersebut, kita harus mengetahui alamat repository yang menyimpan pustaka yang kita inginkan, berikut dependensinya. Misalnya, bila kita ingin menggunakan Hibernate, kita harus mengetahui repository yang memuat artifak hibernate.jar dan juga dependensinya seperti asm.jar, cglib.jar, dan segudang *.jar lainnya.
Selanjutnya, untuk menghemat bandwidth perusahaan, kita dapat menaruh pustaka yang sering digunakan tersebut dalam jaringan perusahaan. Dengan demikian semua orang yang akan menggunakan *.jar tersebut tidak perlu mendonlod dari lokasi asalnya, tapi cukup dari jaringan lokal. Pembuatan mirror ini akan kita diskusikan di artikel terakhir dalam seri ini.
Ada dua masalah utama yang kita hadapi dalam menggunakan pustaka luar. Pertama, bagaimana mencari dan memilih repository yang baik. Kedua, bagaimana mendaftarkannya dalam project kita.
Mencari dan memilih repository merupakan hal yang krusial. Para pengguna Maven, tools untuk dependency management seperti Ivy, memiliki repository yang dapat diakses melalui internet. Akan tetapi, ada satu masalah besar, isinya tidak lengkap dan deklarasi metadatanya banyak yang berantakan. Sebagai ilustrasi, dari dokumentasi Hibernate kita mengetahui bahwa hibernate.jar membutuhkan asm.jar, commons-collection.jar, dan cglib.jar. Tapi di repository Maven, kadang hanya tersedia asm.jar. Artifak commons-collection.jar ada di deklarasi dependensi, tapi filenya tidak ada. Bahkan cglib.jar sama sekali tidak dicantumkan dalam deklarasi dependensi. Nah, kira-kira seperti itulah kondisi repository yang tidak dipelihara dengan baik. Metadata dependensinya tidak akurat, dan koleksi artifaknya tidak lengkap.
Untungnya –SpringSource, perusahaan yang membekingi Spring Framework– telah membuatkan repository yang cukup mumpuni. Mereka menjamin bahwa repository tersebut transitively complete. Artinya, bila hibernate.jar membutuhkan commons-collection.jar dan commons-collection.jar membutuhkan commons-lang.jar, kita bisa yakin bahwa ketiga .jar tersebut ada dalam repository. SpringSource menyediakan metadata untuk ivy dan maven2. Jadi selain kita pengguna Ivy, pengguna Maven2 juga bisa menggunakan repository tersebut. Semua *.jar yang ada dalam repo ini sudah dijamin OSGi compliant. Ini akan sangat berguna apabila kita membangun di platform OSGi. Yang paling penting, repository ini memiliki fasilitas pencarian.
Bila dependensi yang kita inginkan tidak tersedia di repository SpringSource, kita masih dapat mencari ke repository Maven2 yang disediakan oleh ibiblio.
Baiklah, masalah pertama sudah terpecahkan. Sekarang mari kita selesaikan masalah kedua, yaitu menggunakan kedua repository tersebut dalam aplikasi kita.
Repository ini, sama dengan internal repository yang sudah kita bahas pada artikel terdahulu, akan dideklarasikan dalam file ivysettings.xml. Berikut adalah entri untuk repository SpringSource.
<url name="springsource.release.repo.resolver">
<ivy pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
<url name="springsource.external.repo.resolver">
<ivy pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
Cara konfigurasi ini juga bisa dilihat di situs repository SpringSource.
Dan ini adalah konfigurasi untuk repository iBiblio.
<ibiblio name="maven2" m2compatible="true" />
Kita dapat menyuruh Ivy untuk terlebih dulu mencari di repo SpringSource. Bila ditemukan, hentikan pencarian. Bila tidak ditemukan, lanjutkan ke iBiblio. Untuk mengaktifkan mekanisme ini, gunakan ChainResolver sebagai berikut.
<chain name="external-repository">
<chain name="springsource-repository" returnFirst="true">
<url name="springsource.release.repo.resolver">
<ivy pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
<url name="springsource.external.repo.resolver">
<ivy pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
</chain>
<chain name="ibiblio.repo.resolver">
<ibiblio name="maven2" m2compatible="true" />
</chain>
</chain>
Atribut returnFirst
digunakan untuk menghentikan pencarian bila artifak sudah ditemukan.
Setelah repository kita daftarkan, kita tinggal mengkonfigurasi dependensi di project kita. Modul person-dao-impl akan menggunakan Spring Framework 2.5.5.A dan juga Hibernate 3.2.6.ga. Kita dapat mencari di situs repository SpringSource untuk mendapatkan deklarasi dependensi yang sesuai untuk Spring Framework dan Hibernate.
Berikut adalah file ivy.xml dari modul person-dao-impl.
<ivy-module version="1.0">
<info organisation="com.artivisi" module="com.artivisi.tutorial.ivy.dao.impl"/>
<publications>
<artifact name="${ant.project.name}"/>
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
</publications>
<dependencies>
<dependency
org="com.artivisi"
name="com.artivisi.tutorial.ivy.dao.api"
rev="latest.integration"
/>
<dependency
org="org.springframework"
name="org.springframework.orm"
rev="2.5.5.A"
/>
<dependency
org="org.hibernate"
name="com.springsource.org.hibernate"
rev="3.2.6.ga"
/>
</dependencies>
</ivy-module>
Terlihat dari ivy.xml bahwa modul ini memiliki dependensi terhadap modul person-dao-api, yang mana memiliki dependensi terhadap person-model. Dengan fitur transitive-dependency yang dimiliki Ivy, kita tidak perlu mendeklarasikan dependensi terhadap person-model, karena sudah secara otomatis didaftarkan melalui person-dao-api.
Modul ini memiliki satu source code, yaitu PersonDaoHibernate, sebagai berikut.
package com.artivisi.tutorial.ivy.dao.impl;
import java.util.List;
import com.artivisi.tutorial.ivy.model.Person;
import com.artivisi.tutorial.ivy.dao.PersonDao;
@Transactional
public class PersonDaoHibernate implements PersonDao {
private SessionFactory sessionFactory;
@Autowired
public void setSessionFactory(SessionFactory sf) {
this.sessionFactory = sf;
}
public void save(Person person) {
sessionFactory.getCurrentSession()
.saveOrUpdate(person);
}
@SuppressWarnings("unchecked")
public List<Person> getAll(){
return sessionFactory.getCurrentSession()
.createCriteria(Person.class)
.list();
}
public Person getById(Long id){
return (Person) sessionFactory.getCurrentSession()
.get(Person.class, id);
}
}
Seperti kita lihat, kode program tersebut menggunakan @Transactional dari Spring Framework, dan SessionFactory milik Hibernate. Mari kita lakukan kompilasi.
PERHATIAN!!! Perintah build ini akan mengakses internet dan mendonlod semua *.jar yang dibutuhkan. Ini akan memakan banyak bandwidth internasional Anda.
Dari output kita bisa lihat bahwa Ivy menemukan dependensi yang dibutuhkan di repository SpringSource, dan kemudian mendonlodnya. Hasil donlod akan disimpan dalam cache, sehingga bila kita melakukan build lagi, tidak perlu mendonlod berkali-kali.
Demikianlah penjelasan tentang dependensi eksternal. Pada artikel selanjutnya, kita akan bahas tentang configuration, fitur yang sangat canggih dari Ivy.
28 Sep 2008
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:
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
.
Lalu, pindah ke modul person-dao-api, dan lakukan build lagi.
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.
10 Sep 2008
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:
-
Menyediakan lokasi yang dapat diakses oleh kedua modul
-
Mempublikasikan distribusi modul person-model
-
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
untuk mempublish ke repository local, atau
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.