Java New Generation Concurency API
Posted on Monday, May 14, 2012Pasti sering menggunakan Download Accelerator kan? Dibalik layar aplikasi jenis ini melakukan proses download dengan cara memecah tugas download 1file menjadi beberapa bagian (task). Kemudian disiapkan beberapa pekerja (thread) yg menampung tugas-tugas tadi tanpa menunggu satu selesai baru yg lainnya jalan (non blocking) tapi langsung dijalankan secara bersama-sama. Hasil dari masing-masing thread akan digabungkan lagi menjadi 1 file utuh.
Java telah lama memiliki implementasi solusi seperti kasus tadi dengan Java Thread API. Java old Thread API, bukannya ga bagus, tapi “Is There a Reason to Use the Old Threading API?” ketika sejak Java 5 telah diciptakan Pustaka sakti yg dilupakan banyak org: Java New Generation Concurency API.
Lupakan jelimetnya berurusan dengan keyword ini di Thread: wait, notify, synchronize, join. Mari berkenalan dengan Pustaka Sakti generasi terbaru!
Contoh kasus aplikasi Currency Converter, aplikasi ini akan menghitung kurs beberapa mata uang terhadap USD.
Berikut ini fungsi yg digunakan untuk mendapatkan nilai kurs terharap USD melalui Yahoo Finance, sesuai dengan mata uang yg ingin dicari :
private HashMap getRates(String ccy) throws Exception {
URL url = new URL("http://finance.yahoo.com/d/quotes.csv?e=.csv&f=sl1d1t1&s=USD"
+ ccy + "=X");
BufferedReader reader = new BufferedReader(new InputStreamReader(
url.openStream()));
String data = reader.readLine();
String[] dataItems = data.split(",");
Double rate = Double.valueOf(dataItems[1]);
HashMap ccyRate = new HashMap();
ccyRate.put("CCY", ccy);
ccyRate.put("RATE", rate);
return ccyRate;
}
Saya akan mulai dengan Strategi yg pertama, dengan Metode Sekuensial (berurutan)
final String[] CURRENCY = new String[] { "IDR", "MYR", "AUD", "SGD", "THB" };
void checkRateSequential() throws Exception {
System.out.println("checkRateSequential :");
long start = System.nanoTime();
for (String ccy : CURRENCY) {
HashMap ccyRate = getRates(ccy);
System.out.println("Value of $1 in " + ccy + " is " + ccyRate.get("RATE"));
}
long end = System.nanoTime();
System.out.println("Time (seconds) taken " + (end - start) / 1.0e9);
}
checkRateSequential : Value of $1 in IDR is 9218.0 Value of $1 in MYR is 3.0717 Value of $1 in AUD is 0.9976 Value of $1 in SGD is 1.2526 Value of $1 in THB is 31.193 Time (seconds) taken 14.404746959
Metode ini paling umum digunakan, aplikasi akan melakukan request berulang-ulang berurutan untuk mendapatkan nilai kurs, satu request selesai baru request yg lain dijalankan (blocking IO), sampai selesai.
Dengan metode ini hanya ada satu proses IO yg berjalan. Sayang banget, CPU udah multicore, internet udah ngebut kok disia-siakan resource yg ada ?
Ok, Strategi berikutnya, Menjalankan beberapa request sekaligus secara bersamaan. Saya akan gunakan pustaka sakti Java Concurent API
void checkRateAllAtEnd() throws Exception {
System.out.println("checkRateAllAtEnd :");
long start = System.nanoTime();
ExecutorService executorPool = Executors.newCachedThreadPool();
List<Callable> tasks = new ArrayList<Callable>();
for (final String ccy : CURRENCY) {
tasks.add(new Callable() {
public HashMap call() throws Exception {
return getRates(ccy);
}
});
}
final List<Future> listRates = executorPool.invokeAll(tasks, 3600, TimeUnit.SECONDS);
for (Future rate : listRates) {
HashMap ccyRate = rate.get();
System.out.println("Value of $1 in " + ccyRate.get("CCY") + " is " + ccyRate.get("RATE"));
}
executorPool.shutdown();
long end = System.nanoTime();
System.out.println("Time (seconds) taken " + (end - start) / 1.0e9);
}
checkRateAllAtEnd : Value of $1 in IDR is 9218.0 Value of $1 in MYR is 3.0717 Value of $1 in AUD is 0.9976 Value of $1 in SGD is 1.2526 Value of $1 in THB is 31.193 Time (seconds) taken 3.782909569
Wow, ada penambahan kecepatan hingga lebih dari 3 kali lipat! Dengan memanfaatkan Pustaka Concurency, aplikasi dapat melakukan beberapa request sekaligus secara bersama-sama. Tidak perlu harus nganggur menunggu task yg satu selesai.
Penjelasan singkat mengenai penggunaan Java Concurency diatas:
ExecutorService executorPool = Executors.newCachedThreadPool();
Membuat Object Executor yg akan menjalankan task request. Executor ini sebagai tempat penampung beberapa thread (pekerja keras nan gigih yg siaga), diatas saya memakai “newCachedThreadPool” dengan ini Java akan secara otomatis membuat beberapa thread sesuai kebutuhan dan kemampuan sistem, jika sudah tidak aktif akan kembali ke pool.
Namun apabila ingin menentukan secara manual berapa jumlah thread yg diinginkan, bisa menggunakan ini “Executors.newFixedThreadPool(10)” tapi pastikan jangan sampai jumlah thread lebih kecil dari jumlah core CPU (ya iya lah!)
List<Callable> tasks = new ArrayList<Callable>();
for (final String ccy : CURRENCY) {
tasks.add(new Callable() {
public HashMap call() throws Exception {
return getRates(ccy);
}
});
}
Pada baris ke-1 difungsikan untuk menampung task yg akan dikerjakan oleh executor. Untuk itu perlu di isi, task apa aja yg ingin dijalankan. List ini harus berisi daftar object Callable, yaitu object yg mengimplement interface Callable. Object dengan interface Callable memiliki method call() yg nantinya akan dijalankan oleh pekerja-pekerja keras yg ngantor di Executor!
List<Future> listRates = executorPool.invokeAll(tasks, 3600, TimeUnit.SECONDS);
Eksekusi seluruh task yg ada tidak boleh lebih dari 3600 detik, jika lebih maka pekerjaan dihentikan (timeout)
for (Future rate : listRates) {
HashMap ccyRate = rate.get();
System.out.println("Value of $1 in " + ccyRate.get("CCY") + " is " + ccyRate.get("RATE"));
}
Objek Future disini merupakan representasi hasil dari object yg akan di hasilkan oleh thread yg mengeksekusi task. Java bisa memprediksi masa depan, hoho :)
Jadi for looping disini bertugas untuk mengumpulkan hasil dari masing-masing thread yg telah susah payah bekerja tidak boleh lebih dari 3600 detik tadi! Mudah bukan? gampang digunakan dan enak dibaca.
Nah ada lagi satu strategi yg akan saya perkenalkan. Metode diatas mengumpulkan hasil dari masing-masing thread secara berurutan dari thread yg pertama ke yg terakhir, Apa yg terjadi jika thread terakhir ternyata lebih berotot sehingga dia lebih dulu selesai ? Harusnya dia yg dapat medali penghargaan kan ?
Dengan Strategi ini, siapa thread yg duluan selesai dia yg akan diambil langsung hasilnya, jadi ga bisa diprediksi dengan pasti urutannya, tergantung kekuatan otot masing-masing pekerja!! haha.
void checkRateAsItComplete() throws Exception {
System.out.println("checkRateAsItComplete :");
long start = System.nanoTime();
ExecutorService executorPool = Executors.newCachedThreadPool();
CompletionService compService = new ExecutorCompletionService(executorPool);
for (final String ccy : CURRENCY) {
compService.submit(new Callable() {
public HashMap call() throws Exception {
return getRates(ccy);
}
});
}
for (int i=0; i<CURRENCY.length; i++)
future = compService.take();
HashMap ccyRate = future.get();
System.out.println("Value of $1 in " + ccyRate.get("CCY") + " is " + ccyRate.get("RATE"));
}
executorPool.shutdown();
long end = System.nanoTime();
System.out.println("Time (seconds) taken " + (end - start) / 1.0e9);
checkRateAsItComplete : Value of $1 in MYR is 3.0717 Value of $1 in SGD is 1.2526 Value of $1 in AUD is 0.9976 Value of $1 in THB is 31.193 Value of $1 in IDR is 9218.0 Time (seconds) taken 3.933620191
Waktu yg dicapai ga jauh beda dengan strategi sebelumnya, agak lebih lama dikit, lihat juga urutan selesai disini gak beraturan MYR diurutan pertama sedangkan IDR posisi buncit :( Tapi ini karena contoh kasus dengan data yg simple, bukan data ribuan, puluhan ratusan ribu, strategi ini tentu lebih cepat. Dimana thread yg telah selesai bekerja harus langsung balik ke pool (Executor) untuk menjalankan task berikutnya yg udah ngantri di pool.
CompletionService compService = new ExecutorCompletionService(executorPool);
for (int i=0; i<CURRENCY.length; i++) {
Future future = compService.take();
HashMap ccyRate = future.get();
Perbedaannya ada disini, gunakan jurus ajaib CompletionService. Lalu perhatikan for looping nya tidak menggunakan ordered list tapi menyerahkan kepada aplikasi, thread mana yg lebih dulu selesai, dialah yg akan diambil. Ini baru Fair!
Baris kode lengkap yg telah digabungkan menjadi satu class dapat di lihat disini https://gist.github.com/2689054
Scrapy + Selenium + Headless Firefox
Posted on Friday, June 24, 2011Scrapy is a fast high-level screen scraping and web crawling framework, used to crawl websites and extract structured data from their pages. It can be used for a wide range of purposes, from data mining to monitoring and automated testing
Selenium Remote Control is a client/server system that allows you to control web browsers locally or on other computers, using almost any programming language and testing framework
Scrapy memiliki kekurangan, yaitu ketidakmampuannya untuk scraping web yang mengharuskan adanya pemanggilan fungsi javascript, satu-satunya cara adalah dengan menggunakan Selenium
Jalankan Selenium-rc :
$ java -jar selenium-server-standalone-2.0rc2.jar
Buat file: test_selenium.py
from selenium import selenium
from scrapy.spider import BaseSpider
from scrapy.http import Request
import time
import lxml.html
class SeleniumSprider(BaseSpider):
name = "selenium"
allowed_domains = ['selenium.com']
start_urls = ["http://localhost"]
def __init__(self, **kwargs):
print kwargs
self.sel = selenium("localhost", 4444, "*firefox","http://selenium.com/")
self.sel.start()
def parse(self, response):
sel = self.sel
sel.open("/index.aspx")
sel.click("id=radioButton1")
sel.select("genderOpt", "value=male")
sel.type("nameTxt", "irfani")
sel.click("link=Submit")
time.sleep(1) #wait a second for page to load
root = lxml.html.fromstring(sel.get_html_source())
Lalu eskekusi
$ ./python test_selenium.py
Namun menjadi menjengkelkan jika setiap kali menjalankan script selenium akan membuka browser firefox untuk mensimulasikan perintah yg ada di script tsb.
Maka gunakan virtual framebuffer untuk mematikan window firefox. Jalankan perintah ini :
$ sudo aptitude install xvfb $ sudo aptitude install xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic $ sudo mod u+s `which Xvfb` $ Xvfb :99 -ac & $ xclock -display :99 & $ xwd -out xfvbtest.xwd -root -display :99 $ xwud -in xfvbtest.xwd
Jalankan Selenium-rc :
$ DISPLAY=:99 java -jar selenium-server-standalone-2.0rc2.jar
Maka setiap kali script dijalankan selenium tidak akan membuka window firefox tapi sebenarnya menjalankannya dibelakang layar (virtual)
40 Lines URL Shortener App
Posted on Wednesday, May 4, 2011Here is my another 40 lines simple ruby app, URL Shortener App.
I use a Base 62(0-9, A-Z, a-z) numbering system to represent the row id. And Fortunately, Ruby already has a library for it, The alphadecimal gem :)
%w(sinatra dm-core dm-migrations haml alphadecimal uri).each { |lib| require lib}
get '/' do haml :index end
get '/:url' do redirect Url.first(:id => params['url'].alphadecimal).origin end
post '/' do
uri = URI::parse(params['origin'])
raise "Invalid URL" unless uri.kind_of? URI::HTTP or uri.kind_of? URI::HTTPS
@url = Url.first_or_create(:origin => uri.to_s)
haml :index
end
class Url
include DataMapper::Resource
property :id, Serial
property :origin, String
def alias() self.id.alphadecimal end
end
configure do
DataMapper.setup(:default, "sqlite:///development.sqlite3")
DataMapper.auto_migrate!
end
__END__
@@ index
%html
%head
%title Klik - Url Shortener App
%body
%h1 Klik - Url Shortener App
- unless @url.nil?
%code= @url.origin
%a{:href => env['HTTP_REFERER'] + @url.alias}= env['HTTP_REFERER'] + @url.alias
%form{:action => '/', :method => 'POST'}
%input{:type => 'text', :name => 'origin'}
%input{:type => 'submit', :value => 'Pendekin!'}
Hello guys, its been a while since i last blogged about coding related stuff.
Now, I’d like to show you How simple and fun The Ruby Programming Language is.
I wrote Todolist with Sinatra (web framework) + DataMapper (db persistent) + Haml (view template) The total number of lines is just only 41, in a single file including the view template :)
require 'sinatra'
require 'dm-core'
require 'dm-migrations'
require 'haml'
class Todo
include DataMapper::Resource
property :id, Serial
property :text, String
end
configure do
DataMapper.setup(:default, "sqlite:///development.sqlite3")
DataMapper.auto_migrate!
end
get '/' do
@todos = Todo.all
haml :index
end
post '/' do
Todo.create(:text => params['todo'])
redirect '/'
end
__END__
@@ index
!!!
%html
%head
%title Todo
%body
%h1 Todo
%ul
- @todos.each do |todo|
%li= todo.text
%form{:action => '/', :method => 'POST'}
%input{:type => 'text', :name => 'todo'}
%input{:type => 'submit', :value=> 'Todo!'}
The World’s First Batik Train
Posted on Sunday, May 1, 2011This train is the first batik train in the world!! cool


Bandung - West Java - Indonesia
(Source: hasnatsaniya.blogspot.com)
Risih dengan penulisan nilai mata uang dollar di Media
Posted on Monday, April 18, 2011http://investasi.kontan.co.id/v2/read/1302851098/64979/Kuartal-I-2011-penjualan-alat-berat-INTA-lampaui-total-penjualan-tahun-lalu
“Perkiraan nilai total penjualan US$ 167 juta,” kata Fred, Jumat (15/4).
INTA tahun lalu mencatat lonjakan pendapatan sebesar 55% menjadi Rp 1,8 triliun dan laba bersih melesat 121% menjadi Rp 83 miliar dibandingkan 2009.
Tahun ini perseroan menargetkan pendapatan naik 63% menjadi US$ 2,9 triliun dibandingkan 2010. Sementara itu, laba bersih 2011 ditargetkan naik 88% menjadi US$ 155,8 miliar dibandingkan tahun lalu.
Sudah terlalu lama unek-unek ini ingin diungkapkan, sejak bertahun-tahun yg lalu (sejak saya mulai *serius* membaca berita anggap saja 10 thn yg lalu).
(Source: vaindream)
Assalamualaikum wr.wb, Salam kenal mas irfani, nama saya Dany. Saya saat ini sedang belajar CakePHP. Untuk naming convention cakephp mengenai nama table haruslah menggunakan kata jamak(diakhiri S/ES), trus bagaimana jika kita mempunyai table BUKU apakah harus dipaksa menjadi BUKUS agar mematuhi naming conventionnya??? Apakah di CakePHP tidak ada settingannya mengenai hal tersebut? Saya dah coba cari, tapi blom ketemu. Atas perhatiannya, saya ucapkan terima kasih. Alamat email saya dadan2383@yahoo.com (from Anonymous)
Waalaikumsalam wr rb. Hi Danny.
Coba ubah property $useTable pada class Model anda. Isi dengan nama table yg diasosiakan dengan Model tsb.
Menanam Harta Karun (finplan)
Posted on Wednesday, February 9, 2011Di masa kecil, saya termakan oleh dongeng, majalah anak, film kartun tentang kisah mencari harta karun. Selalu menarik, lucu, dan menyenangkan. Punya harta yang banyak secara mendadak. Tumpukan koin-koin emas perak didalam peti selalu membayangi.

![Hello guys, its been a while since i last blogged about coding related stuff.Now, I’d like to show you How simple and fun The Ruby Programming Language is.
I wrote Todolist with Sinatra (web framework) + DataMapper (db persistent) + Haml (view template) The total number of lines is just only 41, in a single file including the view template :)
require 'sinatra'
require 'dm-core'
require 'dm-migrations'
require 'haml'
class Todo
include DataMapper::Resource
property :id, Serial
property :text, String
end
configure do
DataMapper.setup(:default, "sqlite:///development.sqlite3")
DataMapper.auto_migrate!
end
get '/' do
@todos = Todo.all
haml :index
end
post '/' do
Todo.create(:text => params['todo'])
redirect '/'
end
__END__
@@ index
!!!
%html
%head
%title Todo
%body
%h1 Todo
%ul
- @todos.each do |todo|
%li= todo.text
%form{:action => '/', :method => 'POST'}
%input{:type => 'text', :name => 'todo'}
%input{:type => 'submit', :value=> 'Todo!'}](http://25.media.tumblr.com/tumblr_lf6jxcm6mE1qb6uwyo1_500.jpg)

