Pada kesempatan kali ini kita akan membuat sebuah aplikasi sederhana yang berfungsi untuk membackup database-dalam hal ini Postgresql- tentu saja dengan menggunakan Lazarus dan Freepascal. Aplikasi ini sangat sederhana yang berfokus pada bagaimana memanggil sebuah aplikasi eksternal dengan parameter-parameter tertentu.
Secara detail, hal-hal yang akan kita pelajari dari pembuatan aplikasi ini adalah bagaimana :
- memanggil aplikasi eksternal
- menentukan parameter-parameternya
- mensetting environment variable
- menulis pada input stream
- membaca pesan/verbose output proses
Ok, langsung saja, seperti biasa silahkan membuat project baru di Lazarus anda, dan buatlah 1 buah form dengan tampilan kira-kira seperti di bawah ini.
Alur aplikasi ini kita buat sederhana saja. Hanya ada 1 tombol dan 1 memo. User akan menekan tombol tersebut yang kemudian memanggil tool/aplikasi backup database. Hasil verbose proses kemudian akan ditampilkan ke dalam memo. Jika proses backup berhasil, maka akan memunculkan sebuah dialog OK begitupun dengan gagal, akan memunculkan dialog Error.
Buatlah event on click pada tombol tersebut, dengan cara double klik tombol tersebut pada tampilan desain form. Hasilnya seperti di bawah ini.
procedure TForm1.Button1Click(Sender: TObject);
begin
end;
Sebelum coding lebih lanjut, untuk memanggil aplikasi eksternal, kita dapat menggunakan beberapa cara (silahkan googling jika ingin tahu). Cara yang akan kita gunakan kali ini adalah menggunakan kelas TProsess. Oleh karena itu, pada bagian atas, silahkan tambahkan unit proses, seperti contoh di bawah ini.
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
process;
Sekarang kembali ke dalam procedure TForm1.Button1Click di bagian bawah. Tekan tombol Ctrl h untuk kembali ke posisi cursor sebelumnya. Deklarasikan sebuah variable dengan tipe TProcess yang akan digunakan untuk menjalankan aplikasi eksternal, misalkan diberi nama lProc.
NOTE: saya terbiasa memberikan prefix l di depan variabel lokal dengan tujuan agar mudah dikenali. Dan Anda tidak harus mengikuti kebiasaan ini.
procedure TForm1.Button1Click(Sender: TObject);
var
lProc: TProcess;
begin
lProc :=TProcess.Create(nil);
lProc :=TProcess.Create(nil); Artinya kita meng-instansiasi kelas TProcess menjadi object lProc, dimana object tersebut tidak ada yang memiliki (nil).Kenapa Nil dan bukan Self atau yang lain, jawabannya karena object tersebut akan kita destroy menual, sehingga Nil, Self atau yang lain tidak akan menjadi soal.
Kemudian tambahkan option proses poUsePipes agar TProcess membaca verbose proses. Jika anda tidak ingin membaca output proses, maka option ini bisa anda hilangkan.
lProc :=TProcess.Create(Self);
lProc.Options:=[poUsePipes];
Sekarang bagian yang paling penting. Tool/aplikasi backup database Postgres yang biasa digunakan adalah pg_dump. Format perintahnya kira-kira seperti di bawah ini.
pg_dump -U [nama_user] -h [host] -p [port] -F [format backup] -v -f [nama file] [nama_database]
-v : verbose, ini harus untuk memunculkan log proses backup
Contoh
pg_dump -U postgres -h localhost -p 5432 -F custom -v -b -f file_backup.backup db_sekolah
Property TProcess pertama yang harus anda tentukan adalah nama executable nya, dalam hal ini adalah pg_dump.
lProc.Executable:='pg_dump';
Bisa dilakukan dengan cara seperti di atas, akan tetapi hal ini tidak disarankan. Nama executable harus beserta absolute path nya. Misalkan jika di Linux, file pg_dump biasanya berada pada /usr/bin/pg_dump.
Nah, Untuk mendapatkan file beserta absolute pathnya, bisa dilakukan dengan menggunakan fungsi FindDefaultExecutablePath . Oleh karena itu, kita modifikasi kode di atas menjadi seperti di bawah ini.
var
lProc: TProcess;
lApp : String;
begin
lApp :=FindDefaultExecutablePath('pg_dump');
if lApp='' then
begin
ShowMessage('pg_dump tidak ditemukan');
exit;
end;
lStr :=TStringStream.Create('');
lProc :=TProcess.Create(nil);
lProc.Options:=[poUsePipes];
lProc.Executable:=lApp;
Perhatikan kode di atas, kita tambahkan variabel lApp yang menyimpan file executable aplikasi beserta absolute pathnya. Jika aplikasi tidak ditemukan, maka tidak perlu dilanjutkan, langsung exit saja dengan memunculkan error dialog;
Selanjutnya tambahkan parameter-parameternya ke dalam property parameter.
lProc :=TProcess.Create(nil);
lProc.Options:=[poUsePipes];
lProc.Executable:=lApp;
lProc.Parameters.Add('-U');
lProc.Parameters.Add('postgres');
lProc.Parameters.Add('-h');
lProc.Parameters.Add('localhost');
lProc.Parameters.Add('-p');
lProc.Parameters.Add('5432');
lProc.Parameters.Add('-F');
lProc.Parameters.Add('custom');
lProc.Parameters.Add('-b');
lProc.Parameters.Add('-v');
lProc.Parameters.Add('-f');
lProc.Parameters.Add('latihan.backup');
lProc.Parameters.Add('db_sekolah');
Pada Contoh kode di atas, kita akan membackup database yang bernama db_sekolah yang berada di localhost dengan menggunakan user postgres dan hasilnya disimpan ke dalam file latihan.backup yang berada di dalam folder yang sama dengan aplikasi ini. Silahkan sesuaikan dengan setting anda.
Pada saat proses ini dieksekusi, kita akan membaca output yang dihasilkan pg_dump. Output proses ini dapat dibaca melalui Output stream atau Stderr stream. Untuk aplikasi ini, kita menggunakan Stderr. Oleh karena itu, siapkan terlebih dahulu variable-variable yang akan digunakan untuk membaca stream, yaitu :
procedure TForm1.Button1Click(Sender: TObject);
const
BUF_SIZE = 2048; // Buffer size for reading the output in chunks
var
lProc : TProcess;
lApp : String;
lBytesRead : longint;
lBuffer : array[1..BUF_SIZE] of byte;
lStr : TStringStream;
begin
BUF_SIZE : konstanta ukuran buffer, biasanya = 2048 bit
lBytesRead : digunakan untuk mencatat berapa byte stream yang berhasil di baca. Jika gagal atau stream sudah habis, maka akan menghasilkan nilai 0
lBuffer : digunakan sebagai penyimpan stream sementara
lStr : digunakan sebagai penyimpan stream Stderr sebagai string
Selanjutnya lakukan eksekusi proses dan baca hasilnya.
...
lProc.Parameters.Add('-f');
lProc.Parameters.Add('latihan.backup');
lProc.Parameters.Add('db_sekolah');
// eksekusi proses
lProc.Execute;
// lakukan pembacaan output dari Stderr Stream sampai selesai
repeat
// baca stream output dan simpan ke dalam buffer
lBytesRead :=lProc.Stderr.Read(lBuffer,BUF_SIZE);
// simpan stream sebagai string
lStr.Write(lBuffer,lBytesRead);
// simpan verbose ke dalam Memo
Memo1.Text:=lStr.DataString;
// refresh form agar perubahan isi Memo terlihat langsung
Application.ProcessMessages;
// stream habis ditandai dengan byteread = 0
until lBytesRead=0;
if lProc.ExitStatus=0 then
ShowMessage('Proses OK')
else
ShowMessage('Proses Fail');
// cleanup memori yang sudah tidak diperlukan
lProc.Free;
lStr.Free;
end;
Output/hasil verbose aplikasi eksternal dapat dibaca via Output Stream atau Stderr Stream. Untuk kasus ini, hasil verbose dibaca dari Stderr Steam.Application.ProcessMessages diperlukan untuk memaksa refresh form agar tampilan Memo diupdate. Jika anda tidak menambahkan ini, maka selama proses Memo akan tetap terlihat kosong, dan baru akan berubah setelah proses backup selesaiUntuk mengetahui apakah eksternal aplikasi apakah berjalan dengan baik, salah satunya dapat diketahui dari ExitStatus-nya. Biasanya jika berjalan dengan baik/sukses maka ExitStatus-nya pasti 0. Sedangkan selain itu maka artinya terjadi error.
Sampai di sini, aplikasi ini sudah bisa berjalan, asalkan database tersebut dapat di akses tanpa password.
Jika database anda dapat diakses tanpa menggunakan password, maka silahkan anda compile dan jalankan aplikasi ini. Jika semuanya lancar, maka aplikasi ini sudah bisa membackup database yang anda inginkan.
Lalu bagaimana jika database tersebut hanya dapat diakses dengan password ? Maka kita harus menambahkan “informasi” password tersebut ke dalam aplikasi kita. Ada 2 cara yang saya ketahui, yaitu dengan menggunakan Environment variable dan dengan menginput password dengan input stream.
- Menginput password dengan Environment VariableIni adalah salah satu fitur dari Posgresql, dan belum tentu ada pada database lain. Untuk menginput password dalam koneksi ke Postgresql, dapat dengan menggunakan Environment variable yang bernama PGPASSWORD. Artinya, jika PGPASSWORD terdapat di dalam environment variable, maka Postgresql akan menggunakannya sebagai password untuk koneksi. Untuk informasi lebih lanjut, silahkan melihat di sini : Menggunakan PGPASSWORDJadi, yang akan kita lakukan adalah menambahkan PGPASSWORD ke dalam environment variable Process. Caranya adalah sebagai berikut.
lProc.Environment.Append('PGPASSWORD=rahasia'); lProc.Execute;
Kode di atas artinya, kita menambahkan sebuah environment variable yang bernama PGPASSWORD yang bernilai rahasia. rahasia ini adalah contoh password dari user postgres.Cara ini telah saya coba dan berhasil berjalan di lingkungan Linux. Akan tetapi akan GAGAL di lingkungan Windows.Jika cara ini gagal, maka hal ini kemungkinan dikarenakan semua environment variable telah dihilangkan dan diganti hanya dengan PGPASSWORD pada awal proses append Environment. Oleh karena itu, cara mengatasinya adalah dengan menambahkan terlebih dahulu semua environment variable yang ada, baru kemudian menambahkan PGPASSWORD. Caranya adalah sebagai berikut.Tambahkan variabel baru untuk melakukan looping. Kemudian looping dan baca semua environment variable yang ada.var ... lIdx : integer; ... begin ... // baca semua environment variable yang ada dengan looping for lIdx:=0 to GetEnvironmentVariableCount-1 do begin // tambahkan ke property Environment dari TProcess lProc.Environment.Append(GetEnvironmentString(lIdx)); end; // tambahkan PGPASSWORD yang berisi password user lProc.Environment.Append('PGPASSWORD=rahasia'); // eksekusi proses lProc.Execute; ...
Sampai di sini seharusnya kita sudah bisa menjalankan aplikasi backup dengan dengan baik.Berdasarkan link Postgresql di atas. Teknik ini tidak direkomendasikan dikarenakan masalah keamanan password. Password yang tersimpan di dalam environment variable ini secara teori bisa dilihat oleh user dengan menggunakan perintah ps. - Menginput password dengan Input stream
Pada saat anda mengakses suatu database yang dilindungi dengan password, maka akan muncul prompt input password. Nah prompt input ini pada TProcess dapat diakses atau ditulisi dengan menggunakan stream Input. Berikut ini adalah caranya:Tambahkan variable untuk menyimpan nilai password.var ... lPass : string; ...
Setelah TProcess dieksekusi, tulis/kirimkan password tersebut ke dalam pg_dump via Input stream.... // eksekusi proses lProc.Execute; // input password, ingat tambahkan LineEnding di akhir password sebagi tanda // akhir password lPass :='rahasia'+LineEnding; // kirimkan password ke pg_dump via input stream lProc.Input.Write(lPass[1],Length(lPass)); ...
Sampai di sini, jika semua lancar maka aplikasi anda akan dapat berjalan dengan baik. Silakan anda coba.Untuk Linux, jika anda menggunakan cara ini, maka JANGAN jalankan aplikasi tersebut via terminal, melainkan langsung ke aplikasi-nya dengan cara double klik. Karena jika anda menjalankan via terminal, maka input akan diredirect/muncul di terminal sehingga anda harus menginput password di sana.
Gagal membackup database |
Berhasil membackup database |
Berikut ini adalah kode program eksekusi secara lengkap.
procedure TForm1.Button1Click(Sender: TObject);
const
BUF_SIZE = 2048; // Buffer size for reading the output in chunks
var
lProc : TProcess;
lBytesRead : longint;
lBuffer : array[1..BUF_SIZE] of byte;
lStr : TStringStream;
lApp : String;
lPass : string;
lIdx : integer;
lEnv : TStringList;
begin
lApp :=FindDefaultExecutablePath('pg_dump');
if lApp='' then
begin
ShowMessage('pg_dump tidak ditemukan');
exit;
end;
lStr :=TStringStream.Create('');
lProc :=TProcess.Create(nil);
lProc.Options:=[poUsePipes];
lProc.Executable:=lApp;
lProc.Parameters.Add('-U');
lProc.Parameters.Add('postgres');
lProc.Parameters.Add('-h');
lProc.Parameters.Add('localhost');
lProc.Parameters.Add('-p');
lProc.Parameters.Add('5432');
lProc.Parameters.Add('-F');
lProc.Parameters.Add('custom');
lProc.Parameters.Add('-b');
lProc.Parameters.Add('-v');
lProc.Parameters.Add('-f');
lProc.Parameters.Add('latihan.backup');
lProc.Parameters.Add('db_sekolah');
{ Cara ini adalah dengan menggunakan Environmen variable
// baca semua environment variable yang ada dengan looping
for lIdx:=0 to GetEnvironmentVariableCount-1 do
begin
// tambahkan ke property Environment dari TProcess
lProc.Environment.Append(GetEnvironmentString(lIdx));
end;
// tambahkan PGPASSWORD yang berisi password user
lProc.Environment.Append('PGPASSWORD=rahasia');
}
// eksekusi proses
lProc.Execute;
{ Cara ini adalah dengan menggunakan input stream}
// input password, ingat tambahkan LineEnding di akhir password sebagi tanda
// akhir password
lPass :='rahasia'+LineEnding;
// kirimkan password ke pg_dump via input stream
lProc.Input.Write(lPass[1],Length(lPass));
// lakukan pembacaan output sampai selesai
repeat
// baca stram output dan simpan ke dalam buffer
lBytesRead :=lProc.Stderr.Read(lBuffer,BUF_SIZE);
// simpan stream sebagai string
lStr.Write(lBuffer,lBytesRead);
// simpan verbose ke dalam Memo
Memo1.Text:=lStr.DataString;
// refresh form agar perubahan isi Memo terlihat langsung
Application.ProcessMessages;
// stream habis ditandai dengan byteread = 0
until lBytesRead=0;
if lProc.ExitStatus=0 then
ShowMessage('Proses OK')
else
ShowMessage('Proses Fail');
// cleanup memori yang sudah tidak diperlukan
lProc.Free;
lStr.Free;
end;
Selanjutnya, silahkan modifikasi aplikasi anda sesuai dengan keperluan anda. Misalkan, informasi hostname, port dan lain-lain tidak dihardcoded, melainkan dibaca dari suatu file config.
Sekian tip/trik kali ini. Selamat mencoba, semoga yang sedikit ini bermanfaat.