Thursday, December 4, 2008

Django uygulamasını deploy etmek


Django uygulamamı deploy ederken karşılaştığım sorunu ve sorunun çözümünü paylaşmak istiyorum. Bilişim Terimleri sitesi deploy teriminin Türkçe karşılığı icin yerleştirme, konuşlandırma terimlerini kullanmış. Fakat bu karşılıklar bana biraz komik geldiği için ("Abi dün gece uygulamayı yerleştirirken problem oldu" gibi), ben "deploy" terimini Türkçeye çevirmeden kullanmaya devam edeceğim.

Bu yazıda bahsettiğim Django (cango diye okunuyor) Tatlı Kaçıklar dizisinde oynayan Mehmet Ali Erbil'in köpeği değil Python ile geliştirilmiş bir Web Framework. Python ile geliştirilmiş onca web framework arasından Django'yu seçmemin nedeni çok iyi bir dokümantasyona sahip olması (gerçekten çok iyi, abartmıyorum) ve web sitesinde yapılan şu Django tanımı oldu :

Web Framework For Perfectionists With Deadlines


Kısaca Django reklamı yaptıktan sonra ana meseleye gelelim. Yazının bundan sonrası Django ile ilgilenmeyenler için sıkıcı olabilir baştan söyleyeyim.
Django, uygulama ayarlarını setting.py dosyasından okur. Genelde development ve test yaparken kullandığınız ayarlar ile production ortamında kullandığınız ayarlar aynı olmaz. En basit örneği database ayarları: url, database adı, kullanıcı adı, şifre gibi.. settings.py dosyasını production sunucusuna upload ettikten sonra her seferinde gidip settings.py'de production ayarlarını yapmak bir süre sonra insanı bayar. Uygulamayı deploy ettikten sonra sağını solunu değiştirmeden çalıştırabilmek için setting.py dosyasına aşağıdaki numarayı çekiyoruz: (Dosyanın sonuna bu satırları yazıyoruz)

try:
from settings_local import *
except ImportError:
pass

Bu sayede Django settings.py'deki ayarları yükledikten sonra settings_local.py dosyamızdaki ayarları yükler. settings_local.py dosyasında development ve test platformunda kullanmak istediğimiz ayarları yazarız. settings_local.py dosyasını production sunucusuna upload etmemize gerek yok, çünkü bu dosyada çalışma ortamımızın production ortamından farkını yazıyoruz. Örnek bir settings_local.py dosyası şu şekildedir:

DEBUG = True

DATABASE_ENGINE = 'mysql'
DATABASE_NAME = 'tututil'
DATABASE_USER = 'nikli'
DATABASE_PASSWORD = 'drowssap
DATABASE_HOST = ''
DATABASE_PORT = ''

MEDIA_ROOT = '/var/www/GOF'
MEDIA_URL = 'http://localhost/gof/'

settings_local.py dosyası settings.py ile aynı klasörde olmalıdır. Dosyalarınızı FTP ile production sunucusuna gönderirken aman dikkat edin settings_local.py dosyasını göndermeyin.

Wednesday, October 29, 2008

Sonunda evlilik telaşı bitti

22 Ekim'de balayı tatilinden dönüp, 23 Ekim'de iş başı yaptım. Daha yeni yeni tatil havasından çıkıyorum. Söz verdiğim gibi yazılara evlilik sonrası daha çok zaman ayıracağım.

Geçenlerde günlüğe yeni bir yazı yazmak için kolları sıvamıştım ki ne göreyim TELEKOMÜNİKASYON İLETİŞİM BAŞKANLIĞI yine yapacağını yapmış; Blogger servisine Türkiye'den erişimi kapatmış. Tünel servisleri aracılığı ile erişim açık olmasına rağmen içimdeki yazma hevesini kırdığı için tatil sonrası ilk yazım bugüne kadar sarktı.

Balayı tatilimiz için Nurten'le Maldivler'e gittik. Yüzlerce adadan oluşan bu ülkede kendi bütçemize göre bir ada seçtik ve çantamızı sırtlanıp Maldivler'in yolunu tuttuk. Katar aktarmalı 10 saatlik bir uçak yolculuğundan sonra Maldivler'in başkenti Male'ye ulaştık. Male'den tatilimizi geçireceğimiz Sun Island adasına transferimiz de deniz uçağı ile oldu. Yeryüzündeki cennete benzetilen Maldivler'de 6 gün geçirdikten sonra gözümüz arkada kalarak dönüş yolculuğuna başladık.

http://picasaweb.google.com/ilkinulas/MaldivesEkim2008 adresinde tatilde çektiğimiz fotoğraflara göz atabilirsiniz.

Aşağıda fotoğraf makinası ile çektiğim ve benim ilginç olduğunu düşündüğüm videolar var. İlk video Sting ray denilen balığın (bu balığın türkçe ismini bilmiyorum. Bilen varsa söylesin lütfen) kendini insanlara nasıl sevdirdiğini, insanların elinden nasıl yemek yediğini görebilirsiniz.



İkinci videoda ise su altı kamerasına alternatif olarak su üstü kamerası ile yaptığım çekimleri izleyebilirsiniz :)



Maldivler bizim için unutulmaz bir deneyim oldu. İlk başta Maldivler'de bir hafta tatil için harcayacığımız para ile Türkiye'de bir ay tatil yaparız diye düşünüyordum. Fakat beyaz kumlar üzerinde güneşlenip,ılık okyanus suyunda mercanlar ve rengarenk balıklar arasında yüzdükten sonra balayımız için Maldivler'i seçmenin ne kadar iyi bir tercih olduğunu anladım.

Friday, September 26, 2008

Evleniyorum...


Selam dostlar, uzun zamandır yazamadım. Bu suskunluğumu güzel bir haberle bozayım dedim. 11 Ekim 2008'de memleketim Denizli'de evleniyorum. 8 Ekim 2008 Çarşamba günü İstanbul, Salon Palmina'da bir düğünümüz var, hemen sonrasında 11 Ekim Cumartesi Denizli'de nikah törenimiz var. Tüm dostlarım davetlidir.

Evlilik telaşı biter bitmez yazılarıma devam edeceğim.

Monday, August 11, 2008

Google Chart API artık QR Codes da destekliyor

Günlüğe ilk yazılarımı eklemeye başladığımda QRCode'lar ile ilgili bir yazı yayınlamıştım. QRCode'un ne olduğundan, nerelerde kullanılabileceğinden bahsetmiştim. Geçenlerde Google Code Blog'ta 'QR Codes now available on the Google Chart API' yazısını görünce bunu siz sevgili blog okuyucularımla paylaşayım istedim.
Google Chart API ile nasıl QR code oluşturulacağı bu sayfada detaylı bir şekilde anlatılmış. Ben olayı bir tane örnek vererek özetlemek istiyorum.
Google'a size bir QR code üretmesi için aşağıdaki bilgileri vermeniz gerekiyor:

  • chs : QR Code olarak çizilecek resmin boyutu (chart size)
  • cht : Chart tipi
  • chl : Encode edilecek text. (örneğin bir URL)
  • choe : output encoding (örneğin UF-8)
    QR Code grafiğini çizdirmek için http://chart.apis.google.com/chart adresine, yukarıda sıralanan parametrelerin gönderilmesi gerekiyor. Bunu HTTP GET ile yapabilirsiniz. http://www.oksijen.com URL'ini QR Code içine gömmek için aşağıdaki resim URL'ini kullanabiliriz.

    <img src="http://chart.apis.google.com/chart?cht=qr&chs=200x200&chl=http://www.oksijen.com"/>



    Yukarıdaki resmin içerisinde http://www.oksijen.com URL'inin gömülü olduğuna inanmayanlar
    herhangi bir J2ME QR Code okuyucusunu cep telefonlarına kurup resmin içinde ne kodlandığını görebilirler.

  • Saturday, June 28, 2008

    TechCrunch & Webrazzi MeetUp Notları

    Bugün TechCrunch & Webrazzi MeetUp'a katıldım. Conrad/Istanbul Otel'de gerçekleşen etkinlik ile ilgili notlarımı paylaşmak istiyorum. Arda Kutsal'ı böyle bir etkinlik düzenlediği için tebrik ediyorum. Titizlikle seçilmiş panel konuları ve konuşmacılar bu güneşli cumartesi gününü bir konferans salonunda sıkılmadan geçirmeme yetti. Yaklaşık 400 kişinin katıldığı MeetUp'ta 7 ana panel vardı:
    1. Sosyal platformlar ve sosyal medyada pazarlama
    2. Kullanıcı içeriğinin yarattığı Web 2.0 hukuku
    3. Online reklam pazarı ve geleceği
    4. Türkiye’de web girişimciliği, yatırım imkanları ve çıkış stratejileri
    5. StartupPad
    6. Grou.ps Global Announcement
    7. European Startup Scene

    Sosyal platformlar ve sosyal medyada pazarlama:


  • Kariyer.net'i temsilen gelen Pazarlama Direktörü Ayça Arsan, Kariyer.net'in reklam harcamalarının 40%'ının internet reklamcılığına ayrıldığını söyledi. Internet reklamcılığının Türkiye'deki reklam piyasasının 2%'sini oluşturduğunu duyduğumda çok şaşırdım.
  • Can Karatoprak, meşhur facebook uygulması Rakı Sofrası'nın yazarı, Rakı Sofrası'nın 1 milyon 50 bin üyesi olduğunu söyledi. Rakı Sofrası gibi basit bir uygulamanın bir şirketi çekip çevirecek paralar kazandırdığını duymak, dikkatimi yeniden facebook platformuna çevirmeme sebep oldu. Can Karatoprak'ın dediğine göre şirketi BizBilişim Çözümleri, Mey İçki ile sağlam bir reklam anlaşması yapmış.

    Kullanıcı içeriğinin yarattığı Web 2.0 hukuku


  • Ekşisözlük'ünde avukatlığını yapan Av. Başak Purut, Türkiye'de bazı savcıların Internet'in ne olduğundan haberlerinin olmadığını, bu savcılar yüzünden youtube ve wordpress'in uzun süreler kapalı kaldığını söyledi.
  • TBD İstanbul Yönetim Kurulu Üyesi Av. Ceyda Cimilli Akaydın, Türkiye'deki bilişim davalarında bilirkişi sıkıntısının yaşandığını söyledi. Ceyda Hanım, isteyenlerin mahkemelere bilişim alanında bilirkişi olmak için başvurabileceklerini ve bilirkişi olarak görev yapmaları durumunda dava başına 300-400 YTL gibi paralar alabileceklerini söyledi. Bu sözün üzerinde salondakilerin bir anda gözleri parladı :)
  • Günün en eğlenceli konuşması Radikal Gazetesi Teknoloji Editörü Serdar Kuzuloğlu'nun konuşmasıydı. Internetin Türkiye'ye gelişinden günümüze kadar geçen sürede kendisinin bilişim adına yaptıklarını anlattı. Radikal Gazetesindeki bir habere yapılan yorum yüzünden kapama davası açılması üzerine, veritabanından elle ilgili yorumu silip davadan yırttığını anlattığında salon kahkahalara boğuldu.

    Online reklam pazarı ve geleceği


  • Bu panel'de, yayın yapanlarla reklam verenleri buluşturan şirket sahipleri Okyar Özlüpınar - Yönetici Ortak, ReklamZ ve Sarper Danış – Ortak, ZapMedya konuşmalar yaptı. Türkiye'de, reklam verenlerin Internet'in gücünden habersiz olduklarından basettiler. Reklam alanında geridönüşün en fazla olduğu ortamın Internet ortamı olduğunu söylediler.
  • Google Türkiye Ülke Müdürü Bülent Hiçsönmez'in, Google AdWords hakkında konuşmaması ilgi çekiciydi. Herkesin bildiği klasik "Internet gelecektir" sözleriyle konuşmasını geçiştirdi.

    Türkiye’de web girişimciliği, yatırım imkanları ve çıkış stratejileri


  • Panelin bu bölümünde Türkiye'deki başarılı girişimcilere ve bu girişimcilerin deneyimlerine yer verildi.
  • Cember.net kurucusu Çağlar Erol, Çember.net'i eşi ile beraber yazdığını 2 sene gibi kısa bir sürede 4 milyon dolara XING'e sattığını anlattı.
  • Startup şirketlerinin ömürlerinin çok uzun olmadığından, sektörde palazlandıktan sonra devler tarafından satın alınmadıklarında web'in karanlık sayfalarında kaldıkları söylendi. Girişimcilere para yatıran LeVenture'nin kurucu ortaklarından Cem Dalgıç, startup'ların mutlaka bir çıkış planının olması gerektiği söyledi.
  • Devrim Demirel Blogcu.com'u Nokta A.Ş. nasıl sattığını, Ersan Özer de itiraf.com'u GittiGidiyor'a nasıl sattığını anlattı. Milyon dolarla ifade edilen rakamlar inanın başımı döndürmeye yetti.

    StartupPad


  • Kindo.com, Mekanist.net, Mülakat.Net ,TakasMerkezi.com, Wridea tanıtımları yapıldı.
  • Benim için günün en şok edici anı, Mekanist.net'in, yaklaşık bir sene önce karşılaştığım ve bunun yerli versiyonunu yapabilir miyim acaba diye düşündüğüm yelp.com'un yerli versiyonu olduğunu gördüğüm andı.
  • Sunday, June 1, 2008

    Barış Manço ve Cacık


    Çocukluğumdan beri severek dinlerim Barış Manço'yu. "Barış Manço ile 7'den 77'ye" programını her pazar izlerdik ailecek. Daha sonra "Adam Olacak Çocuk" ile çıktı karşımıza Barış Abi. Çok iyi hatırlıyorum, üniversitede hazırlık okuduğum zamanlarda, "Barış Manço Taksim Marmara Cafe'de cam kenarında oturuyor" diye bir haber geldi biz üniversite kantininde otururken. Yarım saatte Ortaköy'den Taksim'e uçtuk resmen. Baktik ki harbiden Barış Manço Marmara Cafe'de üst katta cam kenarında oturmuş birileriyle konuşuyor. Yarım saat boyunca The Marmara'nın karşısından izlemiştik Barış Abi'yi.

    Bu sabah "Sözüm Meclisten Dışarı" albümünden bir parçasını dinledim. Kendisini zaman zaman hıyar gibi hissedenler için bu parçanın sözlerini buradan paylaşmak istedim.

    Sözüm meclisten dışarı dostlar Söz-Müzik: Barış Manço
    CACIK 
    
    Bugünlerde kendimi hıyar gibi hissediyorum
    Hani dilim dilim doğrasalar beni
    Marmara Ege Karadeniz ve hatta Akdeniz cacık olur diyorum
    
    Derdim öylesine büyük ki dostlar
    Kırka yarıp yine kırka bölseler
    Ve kırk bostana gübre diye serpseler
    Kırkbin tane ot biter de kırkbin derde deva olur diyorum
    
    Ne oldu bana böyle durup dururken
    
    Oğlan aldı başını gitti kız zaten lafımı dinlemezdi
    Düğmem kopuk paçam sökük oramda buramda çengelli iğneler
    Bir de çengelli iğne nazar bozar derler
    
    Hanımın çorabı kaçık başında bigudiler
    Karabaş bile, karabaş bile suratıma bakıp bakıp havlıyor
    Öğünmek gibi olmasın ama dostlar
    Kendimi hıyar gibi hissediyorum
    
    Hani ince kıyım doğrasalar beni Akdeniz cacık olur diyorum
    
    Ve hatta Atlas okyanusu ve hatta Hint okyanusu
    Ve hatta hatta Büyük okyanus bile cacık olur diyorum
    Böyle cacığa rakı mı dayanır
    
    Çivi çiviyi söker derler soğuktan donanı buzla ovarlar
    Ben zaten yanmışım dostlar peki beni fırına mı koysalar
    Zeytin suyuna kuru ekmek böyle gelmiş böyle gidecek
    

    Saturday, May 31, 2008

    Python Sinema API'si

    Vizyondaki filmleri, şehirdeki salonları , filmlerin hangi salonda saat kaçta gösterildiğini bulmaya yarayan bir API yazmaya karar verdim. (Kardeşim ne derdin var durduk yere insan API yazmaya karar verir mi diyenleriniz olabilir. Neden böyle bir API'ye ihtiyaç duyduğumu ilerleyen yazılarda anlatacağım.) Filmler ile ilgili bilgileri, seçeceğim bir sinema sitesinden HTML ayıklama yöntemi (screen scraping) ile almaya karar verdim. Daha doğrusu bu şekilde yapmak zorunda kaldım çünkü delicious, flickr, digg gibi kullanıcılarına API desteği veren türkçe sinema sitesi bulamadım.

    Sinema sitelerini teker teker gezerken MyNet'in satın aldığı Beyazperde sitesi dikkatimi cekti.
    Beyazperde sitesi anlaşılabilir, temiz URL'ler kullanan ender türkçe sitelerden birisi. Ne demek istediğimi aşağıdaki URL'lere bakınca anlayabilirsiniz.
  • http://beyazperde.mynet.com/salon/10
  • http://beyazperde.mynet.com/film/3945
  • http://beyazperde.mynet.com/seanslar/3945/sehir/462
    Genelde sinemaya Mecidiyekö-Profilo AFM'de gidiyorum. Profilo AFM'nin Beyazperde'deki salon numarası 10 olduğu için gösterimdeki filmleri görmek için uzun uzun ana sayfaya git, şehir seç, salon seç işlemleri yapmıyorum. Firefox'ta adres çubuğuna http://beyazperde.mynet.com/salon/10 yazman AFM Profilo'da gösterimdeki filmlere erişmeme yetiyor.

    Beyazperde sitesindeki filmleri, salonları, seanslari html içerisinden ayıklayabilmek için BeautifulSoup html-xml parser'ını kullandım. BeautifulSoup'tan bu yazımda da bahsetmiştim. Ve ortaya sinema.py adresinden indirebileceğiniz basit bir sinema sever API'si çıktı.

    Örneklerle API'nin nasıl kullanıldığına bakalım.
    Vizyondaki filmleri listelemek için aşağıdaki örnekteki gibi vizyondakiFilmler fonksiyonu kullanılıyor.

    >>> import sinema
    >>> filmler=sinema.vizyondakiFilmler()
    >>> len(filmler)
    75
    >>> filmler.keys()
    [u'3922', u'3949', u'3920', u'3711', u'3808', u'3902', u'3835', u'3869', u'3941', u'3634',
    u'3867', u'3800', u'3946', u'3947', u'3916', u'3958', u'3956', u'3926', u'3734', u'2601',
    u'3910', u'3305', u'3911', u'3934', u'3948', u'2812', u'3964', u'3793', u'3845', u'1694',
    u'3855', u'3898', u'3918', u'3862', u'3978', u'3897', u'3805', u'3844', u'3780', u'3915',
    u'3873', u'3816', u'3847', u'3809', u'3833', u'3953', u'3952', u'3872', u'3950', u'3957',
    u'3930', u'3919', u'3945', u'3870', u'3936', u'3876', u'3932', u'3959', u'3885', u'3887',
    u'3837', u'3931', u'3883', u'3882', u'3612', u'3888', u'3871', u'3758', u'1683', u'3143',
    u'3962', u'3963', u'3960', u'3961', u'3825']
    >>> filmler['1694']
    u'Indiana Jones ve Kristal Kafatas\u0131 Krall\u0131'

    Seçilen filmin gösterimde olduğuşehirlerin listesi için filminGosterildigiSehirler fonksiyonu çağırılır.

    >>> sehirler=sinema.filminGosterildigiSehirler('1694')
    >>> sehirler
    {u'216': u'\u0130stanbul Anadolu', u'318': u'K\u0131r\u0131kkale', u'212': u'\u0130stanbul
    Avrupa', u'312': u'Ankara', u'452': u'Ordu', u'242': u'Antalya', u'454': u'Giresun', u'332':
    u'Konya', u'392': u'K\u0131br\u0131s', u'258': u'Denizli', u'252': u'Mu\u011fla', u'236':
    u'Manisa', u'256': u'Ayd\u0131n', u'232': u'\u0130zmir', u'248': u'Burdur', u'322': u'Adana',
    u'288': u'K\u0131rklareli', u'346': u'Sivas', u'326': u'Hatay', u'342': u'Gaziantep', u'422':
    u'Malatya', u'362': u'Samsun', u'284': u'Edirne', u'286': u'\xc7anakkale', u'324': u'Mersin',
    u'442': u'Erzurum', u'462': u'Trabzon', u'262': u'\u0130zmit', u'246': u'Isparta', u'266':
    u'Bal\u0131kesir', u'386': u'K\u0131r\u015fehir', u'264': u'Adapazar\u0131', u'226': u'Yalova',
    u'224': u'Bursa', u'222': u'Eski\u015fehir', u'282': u'Tekirda\u011f', u'272': u'Afyon', u'352':
    u'Kayseri', u'412': u'Diyarbak\u0131r', u'372': u'Zonguldak', u'374': u'Bolu'}
    >>> sehirler['212']
    u'\u0130stanbul Avrupa'

    Seçilen filmin şehirde hangi salonlarda gösterildiğini bulmak için sehirdekiSalonlar fonksiyonu kullanılıyor.

    >>> salonlar = sinema.sehirdekiSalonlar('1694','212')
    >>> salonlar
    [(u'1120', u'Adapazar\u0131 Cinebonus (Ada)', [u'11:00', u'12:15', u'13:30', u'15:00', u'16:15',
    u'17:45', u'19:00', u'20:30', u'21:45', u'23:15']), (u'1000', u'Akatlar Finansbank AFM
    Mayadrom', [u'11:15', u'13:45', u'16:15', u'18:45', u'21:15']), (u'1081', u'Bak\u0131rk\xf6y
    Cinebonus (Capacity)', [u'11:00', u'11:00', u'12:00', u'13:30', u'13:45', u'16:15', u'16:30',
    u'19:00', u'19:15', u'21:45', u'22:00', u'24:30', u'(Cuma, Cmts 24:45']), (u'1118',
    u'Beylikd\xfcz\xfc Cine Marka', [u'11:00', u'12:15', u'13:30', u'14:45', u'16:00', u'17:15',
    u'18:30', u'19:45', u'21:00', u'22:15']), (u'1082', u'Beylikd\xfcz\xfc Fox City', [u'11:30',
    u'14:00', u'16:30', u'19:00', u'21:30']), (u'1117', u'Esentepe Cinebonus (Astoria)', [u'11:00',
    u'12:15', u'13:30', u'14:45', u'16:15', u'17:30', u'19:00', u'20:15', u'21:45', u'(Cuma, Cmts
    23:0............

    >>> salonlar[11]
    (u'1067', u'\u0130stinye AFM Park', [u'10:50', u'11:30', u'12:25', u'13:55', u'14:15', u'15:30',
    u'17:00', u'17:15', u'18:30', u'19:00', u'20:00', u'21:30', u'21:50'])
    >>> salonlar[11][2]
    [u'10:50', u'11:30', u'12:25', u'13:55', u'14:15', u'15:30', u'17:00', u'17:15', u'18:30',
    u'19:00', u'20:00', u'21:30', u'21:50']

    Ve son olarak seçilen salonda gösterilen filmleri bulmak için salondakiFilmler fonksiyonu kullanılıyor.

    >>> filmler=sinema.salondakiFilmler('10')
    >>> filmler
    [(u'3945', u'88 Dakika', [u'11:50', u'14:20', u'16:50', u'19:30', u'21:50']), (u'1694', u'Indiana
    Jones ve Kristal Kafatas\u0131 Krall\u0131\u011f\u0131', [u'10:50', u'11:10', u'13:30', u'13:50',
    u'16:20', u'16:30', u'19:00', u'19:10', u'21:30', u'21:45']), (u'3964', u'O... \xc7ocuklar
    \u0131', [u'11:20', u'14:00', u'16:45', u'19:15', u'21:55']), (u'3949', u'\xd6l\xfcmc\xfcl
    Oyunlar', [u'11:30', u'14:10', u'16:40', u'19:20', u'22:00']), (u'3800', u'Sex and the City',
    [u'10:40', u'12:15', u'13:50', u'15:30', u'17:10', u'18:50', u'21:30', u'22:10', u'(Cuma, Cmts
    23:50'])]
    >>> filmler[0][1]
    u'88 Dakika'


    Söylemeden geçemeyeceğim python ile yazılan bu API dokumantasyonu ile beraber 100 satırı geçmiyor. Beyazperde sitesi gününbirinde html arayüzünde bir değişiklik yaparsa bu API'nin de güncellenmesi gerekiyor.

  • Sunday, May 25, 2008

    Takım çalışması üzerine atıp tutmalar...

    1997 yılında Galatarasay Üniversitesin'de Bilg. Mühendisliği okumak için İstanbul'a geldim. 2001 yılından beri de (yaklaşık 7 sene olmus) yazılım işinden para kazanıyorum. Üniversite yıllarında aylık masraflarımı çıkarmasa da bütçeye katkı sağlayacak part-time bir işim vardı. Üniversiteden mezun olduktan sonra bir yıl Galatasaray Üniversitesi'nde araştırma görevlisi olarak çalıştım. Bir şey araştırdığımız yoktu ama adı öyle işte "araştırma görevlisi". Özel sektörde çalışmanın benim için daha avantajlı olacağına karar verip 2003 yılında Oksijen'de , şimdiki adı ile Vodafone IT Hizmetleri, çalışmaya başladım.
    Ekip olarak bir yazılım projesi yürütme fırsatını ilk olarak Oksijen'de yakaladım. Daha önceden yaptığım işlerde hep tek başınaydım. Tasarım ile ilgili bütün kararlari kendim verip hangi teknolojileri kullanacağıma kendim karar veriyordum. Başarı da benimdi başarısızlık da. Yazılım dünyasında tecrübe kazandıkça takım çalışmasının bireysel çalışmalardan daha zevkli ve daha fazla tatmin edici olduğunu farkettim.

    Bazıları için bireysel başarılar, takım başarılarından daha fazla tatmin edici olabilir. Klasik bir söz vardır: Mutluluk paylaştıkça artar, acılar paylaştıkça azalır. Bence takım çalışmaları için de geçerlidir bu söz. Zor bir projenin stresi takımın bütün üyelerinin omuzlarına dağıldığında tek başına katlanılamayacak stresler takım olarak üstesinden gelinebilecek bir engel halini alır. Takım başarısının verdiği haz bana göre bireysel başarılardan daha fazladır. Tenis müsabakasında bir set almış bir sporcunun sevinciyle, futbol ya da basketbol maçını kazanmış bir takım elemanının sevincini karşılaştıracak olursanız ne demek istediğimi anlarsınız. Gol atan bir futbolcunun takım arkadaşlarına koşup gol sevinci yaşamasını bireysel sporların hangisinde görebilirsiniz?

    Takdir edilme isteği insanın doğasında vardır. Yaptığı başarılı bir iş sonunda takdir edilmeyen çoğu insan motivasyonunu kaybetmeye yakındır. Takım çalışması ile kazanılan başarılardan sonra takım üyeleri, bireysel başarılarda olduğu kadar, takdir edilme ihtiyacı hissetmezler. Kendi çalıştığım projelerden örnek vermek istiyorum. Bir işi başardığımızda, zor bir işi en iyi şekilde yaptığımızı takım arkadaşlarımın da bilmesi, harcanan emekten takım arkadaşlarımın da haberdar olması beni tatmin etmeye yetiyor. Ayrıca bir takdir edilme ihtiyacı hissetmiyorum. Takımdaki diğer arkadaşlar için de böyle olduğunu tahmin ediyorum.

    İşler yetişmediğinde fazla mesai yapmak takım olarak çalışıldığında zevkli bile olabiliyor bazen. Tek kişi ile yürütülen projelerde fazla mesailerin eziyet olduğunu hepimiz biliyoruz. İşlerin yetişmediği durumlarda yöneticilerin görünmez baskısı ve işi yetiştirememenin verdiği mahçupluk hissi takım olarak çalışıldığında katlanabilir seviyelere iner.

    Takım çalışmasının bir başka avantajı da sizin yazdığınız kod parçasının, projenin detaylarından haberdar takımın başka bir üyesi tarafından gözden geçirilebilmesidir. Tek kişilik projelerde "code review" yapacak kişi yine siz olacağınız için farklı bir gözün getireceği avantajlardan yararlanamayacaksınız.

    İşte benim takim çalışması ve bireysel çalışmalar ile ilgili naçizane fikirlerim bunlardır.

    Cep Telefonu ile RSS takip etmek isteyenler için : gozgezdir.appspot.com


    İki ay kadar önce Google, Appengine'i duyurduğunda hemen bir developer hesabı almak için başvurmuştum fakat appengine'e ilginin çok fazla olması sebebiyle benim başvurumu diğer binlerce başvuru gibi beklemeye almışlardı. Neyse lafı fazla uzatmayayım geçen hafta Google'dan appengine hesabımın aktif hale getirildiğini söyleyen bir mail aldım. Artık ben de google'ın alt yapısını kullanarak fantastik web uygulamaları geliştirebilecektim (Hem de beleş, cebimden kuruş para harcamadan.)

    Yazıya devam etmeden "Appengine de nedir yav?" diyenler için ufak bir açılama yapmak gerekiyor sanırım. Google Appengine, yazdığınız web uygulamalarını Google'ın kullandığı altyapıda çalıştırmanızı sağlayan bir sistemdir. Demek istediğim şu, siz uygulamanızı yazıyorsunuz ve Google sunucularına deploy ediyorsunuz. Google'ın veritabanlarını, Google'ın CPU'larını, Google'ın disklerini ve Google'ın bantgenişliğini kullanarak kullanıcılarınıza web uygulamanızdan servis veriyorsunuz.

    Google Appengine'i tasarlayan google mühendisleri programlama dili olarak Python'u seçmişler. Yani Appengine'e deploy edilecek web uygulamasının Python ile yazılması gerekiyor. İleriki sürümlerinde Appengine'in Ruby'yi de destekleceğinden bahsediliyor. Günün birinde Java'yı da desteklerler mi bilmiyorum ama ben Python ile web uygulamalarının ne kadar kolay yazıldığını gördükten sonra java ile bu işe bir daha hayatta bulaşmam :)

    Google Appengine üzerinden çalıştırmayı düşündüğünüz web uygulamasını kafanıza göre yazamıyorsunuz. Google yazdığınız uygulamanın Google mimarisi üzerinde ölçeklenebilir olması için bazı şartlar-kısıtlar koymuş. Uygulama geliştiricilerin bu şartlara uymasını bekliyorlar. Örneğin thread'leri kullanmak yasak, Filesystem işlemleri yapmak yasak,Socket açmak yasak. Bu yasakları koymuşlar ama uygulama geliştiricilere aşağıdaki imkanları veriyorlar:

    • 500 MB'a kadar ücretsiz alan.
    • Her ay 5 milyon sayfa görüntüleme. Günde 166 bin sayfa görüntüleme demek oluyor ki zaten böyle bir web uygulamanız varsa parasını verir Appengine premium hesabı alırsınız.
    • Uygulama geliştiriciler bir Google Appengine hesabı ile en fazla 3 uygulama çalıştırabilirler.
    • Socket açmak yasak demiştik fakat Appengine URL Fetch API'sini sunuyor. HTTP ve HTTPS destekliyor.
    • Filesystem işlemleri yapmak yasak demiştik fakat uygulamalar kendi upload ettikleri dosyaları okuyabilirler (konfigurasyon dosyalarını mesela)
    • Google Accounts : uygulamanızı halihazırda google'a üye olan herkes kullanabilir. Authentication Google User API ile yapılabilir.
    • Web uygulamaları Google Appengine Email servisini kullanarak mail atabilirler.
    • Appengine servislerinin bence en canalıcı olanı Datastore servisi. "Distributed data storage service" türkçeye nasıl çevireceğimi bilemedim. Datastore API'sini kullanarak milyonlarca kayıtlı tabloları Google sunucularında saklayabilirsiniz. (500MB'ı geçmemek şartıyla)
    • Python runtime environment kırpılarak kullandırılıyor. Socket açmak, thread başlatmak gibi servisler kapatılmış. Fakat diğer yasaklanmamış standart Python modülleri bir uygulama için yeterli oluyor. 3rd Party modulleri uygulamanız ile beraber upload ederek kullanabilirsiniz. Örneğin ben Gözgezdir'de RSS parser olarak UniversalFeedParser kullandım.
    • WSGI uyumlu web framework'leri ile çalışabiliyor. Kendi içinde Appengine WebApp Framework var. İsteyenler Django'yu da kullanabiliyorlar. Ben Appengine içinde gelen WebApp framework'ü kullanmayı tercih ettim.


    Hem Google'ın bu yeni servisini denemek hem de uzun zamandır düşündüğüm bir projeyi yapmak için kolları sıvadım. Cep telefonundan RSS okumak için ufak bir uygulama hazırladım. Halen BETA sürümünde olan bu uygulamanın ana fikri GPRS ile WEB' erişen küçük ekranlı cihazlar ile hesaplı bir şekilde RSS kaynaklarını dolaşmak. Uygulamayı tamamlamadan adresini buradan vermem doğru olur mu bilmiyorum ama belki bir kaç kişi kullanırsa tasarım ve kullanılabilirlik için benim aklıma gelmeyen fikirler verebilirler.
    Google yazdığınız appengine uygulamalarını uygulamaadi.appspot.com adresinden yayınlıyor. Fakat kendi alan adınızı alıp Google üzerinden yönlendirmesini yapmanız da mümkün. Ben yine pintilik yapıp alan adına para vermedim ve http://gozgezdir.appspot.com adresinden uygulamayı yayına aldım.
    İlgilenen arkadaşlar ile kaynak kodunu paylaşabilirim. Benim gibi yıllarca Java ile yazılım geliştirmiş Java'dan başka birşeyi gözü görmeyen yazılımcı arkadaşların python ile tanışıp böyle bir dil de varmış vay be demelerini isterim açıkçası :)

    Thursday, April 17, 2008

    Nişanlandım - I am engaged!

    Selam dostlar, 29 Mart 2008 tarihinde Nurten ile nişanlandık. Nişan resimlerini yeni yeni toparlayabildim. 19 gündür parmağımda yüzük ile dolaşıyorum ama hala alışamadım. Neyse zamanla bir yolunu bulup yüzüğü kenara kaldırırım nasıl olsa :D

    Resimde pişmiş kelle gibi sırıttığıma bakmayın, aslında çok stresli bir gündü benim için. Resimdeki o gülümsemenin sebebi tuzlu kahveyi içtikten sonra kızı almanın vermiş olduğu bir rahatlık olsa gerek.

    Bu arada resimdekileri tanıtayım, soldan sağa babam, Nurten, ben, annem ve kardeşim. Kardeşim de abimin başını bağladık sıra bana geldi diyerekten 32 dişini bizden esirgememiş, gözümden kaçmadı he he :) .

    Son olarak lafı fazla uzatmıyor ve darısı bekar arkadaşların başına diyorum.

    Thursday, April 3, 2008

    Command line English-Turkish dictionary

    I've been reading Python tutorials since I accidentally see the Zen of Python. I think I read enough. Now its time to get hands dirty. The best way to learn a language is to write code in that language. I have implemented a command line dictionary that fetches the Turkish definition of a given English word from the site Sesli Sözlük. The python script below uses BeautifulSoup HTML/XML parser for screen-scraping.
    This is sozluk.py. It reads program arguments and constructs a URL. After fetching URL content BeatifulSoup library is used to get desired parts of the HTML page. Finally results are printed to the stdout.

    from BeautifulSoup import BeautifulSoup
    import urllib2
    import sys
    import string


    def getDefinitions(keyword):

    url = 'http://www.seslisozluk.com/?word='+keyword
    c=urllib2.urlopen(url)
    soup=BeautifulSoup(c.read())
    results = soup.findAll(attrs={"class" : "dict_result"})[0].contents[1].contents
    definitions = [results[x] for x in range(len(results)) if ((x+2)%3)==0]
    return [definition.replace(' ', '') for definition in definitions]


    if __name__ == '__main__':
    if len(sys.argv)<=1:
    print 'missing keyword.'
    else:
    keyword =' '.join(sys.argv[1:])
    try:
    definitions = getDefinitions(keyword)
    for i in definitions:
    print i
    except:
    print "Failed to get definition of ",keyword

    This is sozluk.sh. It is used to call Python Interpreter and execute sozluk.py.

    #!/bin/bash
    #sozluk.py is in this folder.
    cd /home/ilkinulas/PITON/workspace
    python sozluk.py $1

    An example usage: Getting Turkish definition of word 'python'

    ilkinulas@tututil:~/PITON$ sozluk.sh python
    Bir proglama dili.
    i., zool. piton. piton yilani. yılan. yılan. i., zool. piton. apollon'un öldürdüğü dev yılan. piton.
    Delfi civarında Apollon tarafından öldürülmüş olan çok büyük bir yılan; piton, k, Python, piton, h, zool.
    : Gaia'nın doğurduğu efsanevi yılan. Yüz tane başı, alev kusan yüz tane ağazı vardı. Apollon tarafından öldürüldü.
    piton yılanı.
    piton yılanı.
    yılan.

    Sunday, March 30, 2008

    Ubuntu 8.04 HardyHeron Coming Soon

    Ubuntu 7.10 (Gutsy Gibbon) was announced October 2007 and will be supported till 2009. After four alpha releases, Ubuntu 8.04 Beta is released and available for download. The stable release of Ubuntu 8.04 (HardyHeron) is scheduled for April 2008. I am waiting for the stable relase to upgrage my ubuntu desktop edition.
    The new Ubuntu will come with

    • Gnome 2.22
    • Firefox 3.0
    • Linux kernel 2.6.24
    • Xorg 7.3 - "New Screen Resolution utility that allows users to dynamically configure the resolution, refresh rate, and rotation of a second monitor. This will be particularly handy for laptop users that connect to a projector or external monitor." - This is my favorite
    • and more

    Wednesday, March 12, 2008

    Monetary Calculations In Java


    float and double types are not suitable for monetary calculations in Java. If this is the first time you hear this you are in trouble.

    Floating point arithmetic which is used to represent float/double variables is inappropriate for exact results calculations. For example it is impossible to represent 0.1 (or any other negative power of ten) as a float or double exactly. Here is an example:
    float s1 = 0;
    for (int i = 0; i < 10; i++) {
    s1 += 0.10;
    System.out.println(s1);
    }
    
    This code prints:
    0.1
    0.2
    0.3
    0.4
    0.5
    0.6
    0.70000005
    0.8000001
    0.9000001
    1.0000001
    
    Here's another example: (0.1+0.1+0.1) == 0.3 What is the value of this statement true or false? since the sum (0.1+0.1+0.1) is not equal to 0.3 according to JVM, rounding up the sum would result in 4 which is not expected either. I think you got the point. We can not trust float or double variables if we want exact results. Applications that makes money/credit calculations needs exact results. To represent monetary values in Java you should use the BigDecimal class.
    BigDecimal bd = new BigDecimal("0");
    for (int i = 0; i < 10; i++) {
    bd = bd.add(new BigDecimal("0.10"));
    System.out.println(bd);
    }
    
    This java example prints (as expected):
    0.10
    0.20
    0.30
    0.40
    0.50
    0.60
    0.70
    0.80
    0.90
    1.00

    Friday, February 29, 2008

    QRCodes in action!

    Typing a web address with the number based keypads of cellphones is an annoying process for the majority of wireless subscribers that don't have phones with QWERTY keypads. If you don't agree try typing the address below with your cellphone:

    http://www.abc.net.au/cgi-bin/common/transform.pl?telstrawallaceml2html&wap&telstra~World&imode/2175963
    By using QRCodes wireless subscribers just SCAN & GO. Here is a small list of QRCodes usage:

    1. ORCodes & Facebook

    2. "Add to friends" link of facebook is encoded into a QRCode image and printed on a t-shirt. Seeing people around you, trying to scan your t-shirt with their cellphone will be freaky.



    3. QRKill - shooting game

    4. QRKill is a QRCode powered game. Players stick in their back a QRCode with his/her telephone and name encoded. They will have to kill each other capturing the code and sending an SMS. Players will be eliminated right after receiving a deathly SMS.


    5. Product Information

    6. McDonald's in Japan is printing the QRCodes on all its food packaging so customers know exactly what they are eating. By scanning the code with their cameraphone McDo lovers access all the information about the McFood including any potential allergy warnings.

      QRCodes are very popular in Japan. I haven't seen any printed QRCode in Turkey but according to me they'll catch attraction of Mobile advertisers soon. Here's another image that shows a product with information printed on it via QRCodes.



    Thursday, February 28, 2008

    memcached - Dağıtımlı (distributed) önbellek sistemi


    TBD BİLİŞİM TERİMLERİ KARŞILIKLAR SÖZLÜĞÜ'nde "distributed" kelimesinin karşılığı olarak "dağıtımlı" yazıyor. Önceleri "distributed" kelimesi için "dağıtık", "dağıtılmış" kelimelerini kullanırdım. Madem Türkiye Bilişim Derneği "dağıtımlı" diyor bundan sonra benim de bu kelimeyi kullanmak boynumun borcudur. Bu yazıyı, okuduğum bir çok ingilizce yazının bir özeti olarak yazıyorum. (Kendi fikirlerimi eklemeyi unutmadım tabi ki :)) Yazıda ingilizce terimlerin türkçe karşılıklarını kullanmaya özen gösterdim fakat yine de tam türkçe karşılığını bulamadığım terimler var.

    1. Nedir bu memcached denen şey?


    Memcached, Danga Interactive http://www.danga.com/ tarafından geliştirilmeye başlanmış bir "distributed object caching" sistemidir. Yüksek trafik alan dinamik web uygulamalarının veritabanına gitme sayısını azaltmayı, bu sayede de performansı artırmayı hedefleyen bir uygulamadır.
    Memcached'yi network üzerinden erişilebilen kocaman bir Hashtable olarak düşünebiliriz. Network üzerindeki herhangi bir makinadan "key" ile memcached'de sakladığımız bir nesneye, network üzerindeki başka bir makinadan aynı key ile ulaşabiliriz. Detaylar için okumaya devam...

    2. Lisansı nasıl?


    BSD License ile lisanslanmış. http://en.wikipedia.org/wiki/BSD_license

    3. Hangi işletim sistemlerinde çalışıyor?


    Linux, BSD, Windows üzerinde sorunsuz çalışıyor. Web sitesinde Solaris ile ilgili bir bilgi yok. Mail listelerine bakılınca solaris üzerinde çalıştırmak için taklalar atmak gerektiğini anlıyoruz. Memcached fazla CPU kullanmıyor ve tek ihtiyacı boş RAM. Bu yüzden memcached instance'larını çalıştıracağımız makinanın Sun sparc işlemcili bir makina olmasına gerek yok. Hızlı bir network kartı ve yeterli RAM'i olan linux PC'ler memcached çalıştırmak için yeterli olacaktır. (Ben testlerimi Ubuntu 7.10 kurulu Dell Laptop üzerinde yaptım :) )

    4. Nasıl kurulur?


    http://www.danga.com/memcached/download.bml adresinden indireceğiniz kaynak kodunu INSTALL ve README dosyalarını okuyarak derleyip kurabilirsiniz.
    http://blog.ajohnstone.com/archives/installing-memcached/ adresinde kurulum ile ilgili bir tutorial var.

    5. Nasıl çalıştırılır?


    Memcached bir konfigürasyon dosyasına ihtiyaç duymaz. Gerekli bütün ayarlar komut satırından memcached daemon'u çalıştırırken verilebilir.

    # ./memcached -d -m 2048 -l 192.168.43.47 -p 11211


    Bu komut memcached'ye der ki:
    • (-d) daemon olarak çalışacaksın
    • (-m 2048) 2GB'dan fazla RAM kullanamazsın.
    • (-l) 192.168.43.47 makinasında çalışıyorsun
    • (-p) 11211 portunu dinleyeceksin.

    Aynı makina üzerinde RAM yettiği sürece birden fazla memcached daemon çalıştırabiliriz (Farklı portlarda çalıştırmaya dikkat etmek lazım). Örneğin, herbiri 512MB kapasiteli 5 adet memcached daemon'u aynı makina üzerinde asaşıdaki şekilde başlatabiliriz. Toplamda 2.5GB'lık bir cache'imiz oluyor. (Dikkat böyle bir kullanım size mantıklı gelmeyebilir, sadece daemon'u nasıl başlattığımız göstermek için bir örnek.)

    # ./memcached -d -m 512 -l 192.168.43.47 -p 11211
    # ./memcached -d -m 512 -l 192.168.43.47 -p 11212
    # ./memcached -d -m 512 -l 192.168.43.47 -p 11213
    # ./memcached -d -m 512 -l 192.168.43.47 -p 11214
    # ./memcached -d -m 512 -l 192.168.43.47 -p 11215
    



    6. Memcached'ye uygulamalarımız nasıl erişecek?


    Memcached'nin network üzerinden erişilebilen büyük bir Hashtable olduğundan bahsetmiştik. Uygulamaların memcached'ye erişebilmeleri için çeşitli dillerde yazılmış istemci kütüphaneleri var. Java dışında Perl, Php, Python, Ruby, C# ve C için yazılımış istemciler mevcut. Bu adresten http://www.whalin.com/memcached/ java memcached client indirilebilir.
    Yazılımcılar gerek görürlerse memcached ile haberleşmeyi sağlayacak kendi istemci kütüphanelerini yazabilirler. Memcached ile client'lar arasındaki protokolün detaylarına http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt adresinden erişilebilir.

    7. Tipik bir kullanım örneği


    Örnek java kodu:

    public VpnAccount getVpnAccount(String msisdn) {
    VpnAccount account = memcachedClient.get(msisdn);
    if (account == null) {
    account = getAccountFromDatabase(msisdn);
    memcachedClient.set(msisdn, account);
    }
    return accout;
    }



    6. Nasıl çalışıyor? Nasıl "distribution" sağlıyor?


    Evet zurnanın "zıırrrt" dediği yere geldik. Memcached network üzerinden erişebileceğimiz boş RAM'i olan herhangi bir makinada çalıştırılabilir.

    Örnek 1: Bir veya birden fazla uygulamanın tek bir memcached instance kullandığı senaryo:


    Bu senaryonun üzerinde fazla durmaya gerek yok. Uygulamalara memcached'nin çalıştığı makinanin IP'si ve memcached'nin dinlediği port bilgisi verilir. Bütün uygulamalar aynı cache instance'ını kullanır. A uygulamasının K anahtar kelimesi ile cache'e gönderdiği V değerini, B uygulaması K anahtar kelimesini kullanarak alabilir.
    Bu mimarinin artıları:
    • Basit bir mimaridir. (Fakat tavsiye edilen mimari değil)
    • Merkezi bir cache olduğu için uygulamalar (java uygulamalarından bahsediyorum) kendi cache'lerini tutmak zorunda değil. Uygulamaların Cache'leri arasında tutarlılık kontrolleri yapmaya gerek yok.

    Eksileri:
    • Tek bir cache instance = SPOF (Single point of failure)

    Örnek 2:


    Birden fazla memcached daemon'ın kullanıldığı senaryo.


    192.168.43.47:11211, 192.168.43.48:11211 ve 192.168.43.49:11211 da çalışan 3 adet memcached daemon olsun. Bizim de bu cache'leri kullanan A,B ve C uygulamalarımız olsun. (IN uygulamalarından örnek verecek olursak GW1, GW2 ve GW3).
    memcached'nin çalışması iki adımdan oluşan hashing mekanizmasına bağlıdır.
    • 1. adımda client, memcached daemon listesini kullanarak "K" anahtarının hash'ini hesaplar. "K" ile saklanan "V" değerine ulaşmak için (ya da "K" ile "V" değerini saklamak için) hangi memcached server'a gideceğini bulur.

    • 2. adımda memcached serverda (çok büyük bir hashtable olarak düşünebiliriz demiştik) "K" anahtarının hash'i farklı bir algoritma ile hesaplanır. "Hashtable lookup" ile "V" değerine erişilir.

    Telekom uygulamasından bir örnek:
    MSISDN'leri anahtar olarak kullanıp VpnAccount nesnelerini memcached içinde saklayalım. MSISDN "905435022434", cache server listesine göre hash'lendiğinde "905435022434"e karşılık gelecek VpnAccount'un hangi memcached daemon'da saklanacağı bulunur. (Örneğin 192.168.43.48:11211).
    Uygulamaların hepsi anahtar kelimenin (yani MSISDN'in) hash'ini hesaplarken aynı hash algoritmasını kullanacağı için "905435022434" numaralı VpnAccount'a ulaşmak için bütün uygulamalar aynı memcached daemon'a gidecektir.
    Bu mimarinin artıları:
    • çok iyi ölçeklenebilir (highly scalable). Ne kadar çok RAM o kadar çok cache.
    • aklıma şimdilik başka birşey gelmiyor :D

    Eksileri:
    • (Eksi alanına birşeyler yazmış olmak için yazıyorum) birden fazla memcached instance'ı yönetmek zor olabilir.


    9. Authentication mekanizması nasıl işliyor?


    Memcached authentication kullanmıyor. Network üzerinden erişimi olan bütün client'lar "set" ve "get" yapabilirler. Üzerinde memcached çalışan host'ların firewall ayarlarının dikkatli yapılması gerekir.

    10. Failover senaryoları nasıl? Memcached failover destekliyor mu?


    memcached failover desteklemez. memcached'yi "set(key, value)" ve "get(key)" işlemlerini destekleyen bir Hashtable olarak olarak tanımlamıştık. Memcached instance'larının birbirlerinden haberleri yoktur. Network üzerinden kaç tane memcached'nin çalıştığı bilgisi sadece client'larda vardır. Memcached server grubundan bir server'ın çıkartılması ya da yeni bir server eklenmesi durumu ile başa çıkmak client'lara kalmıştır.
    A, B, C hostlarında çalışan 3 tane memcached server oldugunu varsayalım.

    hash("905435022434")%(["A","B","C"].length) --> A
    hash("905435022435")%(["A","B","C"].length) --> B
    hash("905435022436")%(["A","B","C"].length) --> C olsun.
    


    ("905435022434" anahtarı ile bir nesneyi aramak için A'ya, "905435022435" anahtarı ile bir nesneyi aramak için B'ye gidilecek.)
    Bu üç hosttan bir tanesi örneğin B erişilemez duruma gelirse, kullanılabilen 2 tane memcached server kalır. Bu durumda

    hash("905435022434")%(["A","C"].length) --> C
    hash("905435022435")%(["A","C"].length) --> A
    hash("905435022436")%(["A","C"].length) --> A olabilir.
    


    Yani 3 memcached server ile çalışırken "905435022434" anahtarı ile A hostuna yönlenilirken 2 memcached server ile çalışırken "905435022434" anahtarı ile C hostuna yönlenebiliriz. Bu da "905435022434" anahatarı ile cache içinde tutulan nesnenin artık geçersiz olduğu (DB'den yeniden çekilmesi gerektiği) anlamına gelir. Aynı tip senaryo memcached server grubuna yeni bir memcached server eklendiğinde de geçerlidir.
    Yukarıda bahsedilen problemi çözmek için "consistent hashing" algoritmaları geliştirilmiş. memcached client'ların verilen bir anahtar için hangi memcached server'a gideceklerini hesaplayan bu algoritmalar sayesinde memcached server grubundan bir server çıktığı zaman ya da yeni bir server eklendiği zaman "K" anahtarına karşılık gelen memcached server'ın değişmemesi sağlanmıştır.
    http://www.whalin.com/memcached/ adresindeki memcached client içerisinde "consistent hashing" algoritmasının implementasyonu mevcut.

    11. JVM, cache için extra hafızaya ihtiyaç duyacak mı?


    Memcached kullanıldığında nesneler JVM heap'te değil başka bir process'in (bu process JVM ile aynı host'ta ya da farklı bir host'ta olabilir) kullandığı hafıza alanında saklanır. Bugün cache kullanan uygulamalarımız için JVM'i başlatırken (yaklaşık olarak) 2GB heap kullan diyoruz (-Xms2048m -Xmx2048m). Memcached kullandığımız durumda JVM bu kadar geniş bir heap'e ihtiyaç duymayacaktır - çoğu uygulamalar için 256 MB'lık bir heap yeterli olur diye düşünüyorum. Bu sayede Garbage collection sureleri kısalacaktır. (Telekom uygulamalarında Garbage Collection suresinin ne kadar önemli olduğundan söz etmeye gerek yok.)

    12. Cache dolduğu zaman ne olur?


    Memcached'nin cache'i dolduğu zaman, cache'e yeni bir nesne eklemek istersek LRU (least recently used) yöntemiyle eski nesneler cache'den silinir.

    13. memcached'nin arkasında kim var?


    memcached kaynak kodu açık bir uygulama. Arkasında bizim gibi 100'lerce yazılımcı var. Daha da önemlisi bir çok yerde kullanıyor ve kendini kanıtlamış. http://developers.facebook.com/opensource.php adresinde, facebook yazılımcılarının memcached kaynak koduna yaptıkları katkılarla, memcached performansını 20% iyileştirdiklerinden bahsediliyor.
    Aşağıdaki listede memcached kullandıklarını söyleyen bir kaç tane web sitesi var. Bu sitelerin ortak özellikleri çok hit almaları ve her sayfada dinamik bilgiler göstermeleri.


    14. Daha detaylı bilgiye nereden ulaşabilirim?


    Bu adreste (http://www.socialtext.net/memcached/index.cgi?faq) memcached ile ilgili bir FAQ var.

    Wednesday, February 27, 2008

    QRCodes are becoming popular


    QRCodes are two dimensional barcodes that can be decoded (scanned) at high speed. QrCode is created by Japanese corporation Denso-Wave. These two dimentisional bar codes are very common in Japan. They are widely used in ad sector.
    URLs, Text, Phone numbers can be encoded in these codes. There are J2ME applications that can decode QRCodes. If you have a java enabled cell phone with a camera, install the application from http://reader.kaywa.com/ and begin scanning QRCodes.

    I am going to list what can be done with a QRCode reader and a QRCode generator later in this blog.

    The image in this post is an example of generated QR Code. Guess what is encoded in the picture. Install the QRCode reader and scan the image ;)

    Sunday, February 3, 2008

    RESTFUL URLs with Stripes

    I came across with Stripes Framework 3 months ago. At first I said "yet another java mvc framework" but after reading its documentation I was suprised to see how easy a web application can be developed with Stripes.

    I have developed several web applications with Struts and Spring MVC. The main difference between Stripes and other MVC frameworks is the configuration. Struts uses struts-config.xml to configure its action classes and its action forms. Stripes requires minimal configuration to get up and running (dispatcher conf. in web.xml), and zero configuration per ActionBean or page. If you have never heard of Stripes, go read Quick Start Guide, you will thank to me ;). I do not want to duplicate documentation in this post, because Stripes team has prepared a good and comprehensive documentation.

    In this post I will explain how you can create clean urls with Stripes. Stripes does not need a configuration file to configure its action beans. If you does not supply any additional configuration Stripes configures your web application with the default RuntimeConfiguration class. An ActionResolver is responsible for finding your action beans and mapping HTTP requests to action bean methods. Stipes uses NameBasedActionResolver by default.
    As it is explained here the default behaviour of NameBasedActionResolver is as follows:

    1. All classes that implements the ActionBean interface is searched. (By default Stripes searches for ActionBean implementations in classpath. But you can narrow the scope of this search by changing the default configuration. This will improve startup performance of your application. See ActionResolver Properties)
    2. Packages "web, www, stripes, action" and their parents are removed. For example "com.companyname.superproject.web.user.DetailsActionBean" becomes "user.DetailsActionBean".
    3. Action or ActionBean suffix is trimmed. "user.DetailsAactionBean" becomes "user.Details"
    4. "." is replaced with "/". "user.Details" becomes "/user/Details"
    5. Finally NameBasedActionResolver appends ".action" to the end of the action URL and we get "/user/Details.action"

    If you want to use clear URLs like "http://www.mysite.com/user/details/12345" instead of "http://www.mysite.com/user/Details.action?userid=12345" you must write your custom ActionResolver implementation. Don't worry it is very easy. First in your web.xml tell Stripes to use your ActionResolver class:
    web.xml

    <filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
    <init-param>
    <param-name>ActionResolver.UrlFilters</param-name>
    <param-value>/WEB-INF/classes</param-value>
    </init-param>
    <init-param>
    <param-name>ActionResolver.Class</param-name>
    <param-value>com.companyname.superproject.RestfulActionResolver</param-value>
    </init-param>
    </filter>

    Then write a class that extends NameBasedActionResolver. You only need to override getBindingSuffix() and getUrlBinding(String s) methods. An implementaion (RestfulActionResolver.java) is listed below:
    RestfulActionResolver.java

    package com.companyname.superproject.RestfulActionResolver
    import net.sourceforge.stripes.controller.NameBasedActionResolver;
    import org.apache.log4j.Logger;

    public class RestfulActionResolver extends NameBasedActionResolver {
    @Override
    protected String getBindingSuffix() {
    return "";
    }
    @Override
    protected String getUrlBinding(String s) {
    String urlBinding = super.getUrlBinding(s);
    return urlBinding.toLowerCase();
    }
    }

    RestfulActionResolver finds ActionBean instances and performs HTTP requests bindings for you.
    "com.companyname.superproject.web.user.DetailsActionBean" action bean is mapped to URL "/user/details".

    I used Stripes version 1.4.3 in the above example.
    I hope you enjoy the post.

    Tuesday, January 29, 2008

    Extended desktop configuration with xrandr

    I've got Ubuntu 7.10(gutsy) running pretty well on my DELL Latitude D505 laptop. I'll try to explain how I configured extended desktop with a NEC 19'' LCD monitor.
    We will use xrandr utility to configure multiple screens. Before using xrandr you must check xorg.conf file ( /etc/X11/xorg.conf )


    Section "Screen"
    Identifier "Default Screen"
    Device "Intel Corporation 82852/855GM Integrated Graphics Device"
    Monitor "Generic Monitor"
    Defaultdepth 24
    SubSection "Display"
    Depth 24
    Virtual 2304 1792
    Modes "1280x1024@75" "1024x768@60"
    EndSubSection
    EndSection

    The Virtual keyword is important. Sum of resolution widths and sum of resolution heights of two monitors are written in Virtual.
    For example : 1280 + 1024 = 2304 and 1024 + 768 = 1792

    You must restart X server after updating xorg.conf file. ctrl-alt-backspace is the shortcut to restart X. After successfully restarting X server, run the following commands:

    xrandr --output VGA --mode 1280x1024
    xrandr --output LVDS --mode 1024x768
    xrandr --output VGA --right-of LVDS
    First command sets VGA (19'' LCD monitor) resolution to 1280x1024. Second command sets laptop monitor resolution to 1024x768. The last command places the extended monitor (VGA) right of laptop monitor. That's it. At least it works for me.

    For more information check intellinuxgraphics.org site.

    Sunday, January 27, 2008

    How to change GNOME desktop wallpaper programmatically

    GNOME preferences are managed by the GConf tool. Each preference in the GConf repository is expressed as a key-value pair.
    gconftool-2 -R /desktop/gnome
    command displays all the preferences listed under "/desktop/gnome" repository. You can print the value of a key in the gnome preferences table with the "g" parameter.

    ilkinulas@tututil:~/bin$ gconftool-2 -g /desktop/gnome/screen/default/0/resolution
    1280x1024
    The "R" option prints all subdirectories and entries under a directory, recursively.

    ilkinulas@tututil:~$ gconftool-2 -R /desktop/gnome/background
    color_shading_type = solid
    secondary_color = #dadab0b08282
    primary_color = #000000000000
    picture_filename = /home/ilkinulas/Pictures/wallpaper/w1209.jpg
    picture_options = scaled
    picture_opacity = 100
    draw_background = true
    ilkinulas@tututil:~$ gconftool-2 -g /desktop/gnome/background/picture_filename
    /home/ilkinulas/Pictures/wallpaper/w1209.jpg
    You can set a key to a value with the option "s". When you set a gnome preference, applications that are interested in that preference will be notified. For example:

    gconftool-2 -t str -s /desktop/gnome/background/picture_filename "/home/ilkinulas/Pictures/wallpaper/64269-1.png"
    sets gnome desktop background picture to "/home/ilkinulas/Pictures/wallpaper/64269-1.png". The option "-t" is used to specify the type of the value of the key. If you execute the command above you will see that your desktop wallpaper is changed. Now lets write a shell script to randomly select a picture and set selected picture as wallpaper.

    #!/bin/bash
    DIR=/home/ilkinulas/Pictures/wallpaper
    counter=0;
    for i in `find $DIR -iname *.jpg -o -iname *.png -o -iname *.gif`
    do
    pictures[$counter]=$i;
    counter=$counter+1;
    done
    index=$((RANDOM%${#pictures[*]}));
    gconftool-2 -t str --set /desktop/gnome/background/picture_filename ${pictures[$index]};

    If you do not want to execute the above script manually you can add a new crontab entry. I start working in the office at 8 am and I use "00 09 * * *" as my crontab schedule, I want my wallpaper change everyday at 9 am, if my computer is on ;)

    If the way I explain in this post is too complicated you can try Webilder. Webilder delivers stunning wallpapers to your Linux desktop, directly from Flickr and Webshots. You choose what keywords (tags) to watch for, and photos are automatically downloaded to your computer. Webilder can also change the wallpaper every few minutes.

    Saturday, January 26, 2008

    ReadWriteLock example in Java

    Writing multithreaded java applications is not a piece of cake. Extra care must be taken because bad synchronization can bring your application to its knees. The JVM heap is shared by all the threads. If multiple threads need to use the same objects or static class variables concurrently, thread access to shared data must be carefuly managed. Since version 1.5, utility classes commonly useful in concurrent programming is included in the JSDK.

    In Java synchronized keyword is used to acquire a exclusive lock on an object. When a thread acquires a lock of an object either for reading or writing, other threads must wait until the lock on that object is released. Think of a scenerio that there are many reader threads that reads a shared data frequently and only one writer thread that updates shared data. It's not necessary to exclusively lock access to shared data while reading because multiple read operations can be done in parallel unless there is a write operation.

    In this post i'll give an example usage of ReadWriteLock interface which is introduced in the Java 1.5 API Doc. In Java Api Documentation it says :

    A ReadWriteLock maintains a pair of associated locks,
    one for read-only operations and one for writing.
    The read lock may be held simultaneously by multiple reader threads,
    so long as there are no writers. The write lock is exclusive.

    Reader threads can read shared data simultaneously. A read operation does not block other read operations. This is the case when you execute an SQL SELECT statement. But write operation is exclusive. This means all readers and other writers are blocked when a writer thread holds the lock for modifing shared data.


    Writer.java This class represents a thread that updates shared data. Writer uses WriteLock of ReadWriteLock to exclusively lock access to dictionary.


    01 package deneme.readwritelock;
    02 
    03 
    04 public class Writer extends Thread{
    05   private boolean runForestRun = true;
    06   private Dictionary dictionary = null;
    07   
    08   public Writer(Dictionary d, String threadName) {
    09     this.dictionary = d;
    10     this.setName(threadName);
    11   }
    12   @Override
    13   public void run() {
    14     while (this.runForestRun) { 
    15       String [] keys = dictionary.getKeys();
    16       for (String key : keys) {
    17         String newValue = getNewValueFromDatastore(key);
    18         //updating dictionary with WRITE LOCK
    19         dictionary.set(key, newValue);
    20       }
    21       
    22       //update every seconds
    23       try {
    24         Thread.sleep(1000);
    25       catch (InterruptedException e) {
    26         e.printStackTrace();
    27       }
    28     }
    29   }
    30   public void stopWriter(){
    31     this.runForestRun = false;
    32     this.interrupt();
    33   }
    34   public String getNewValueFromDatastore(String key){
    35     //This part is not implemented. Out of scope of this artile
    36     return "newValue";
    37   }
    38 }



    Reader.java This class represents a thread that reads share data.


    01 package deneme.readwritelock;
    02 
    03 public class Reader extends Thread{
    04   
    05   private Dictionary dictionary = null;
    06   public Reader(Dictionary d, String threadName) {
    07     this.dictionary = d;
    08     this.setName(threadName);
    09   }
    10   
    11   private boolean runForestRun = true;
    12   @Override
    13   public void run() {
    14     while (runForestRun) {
    15       String [] keys = dictionary.getKeys();
    16       for (String key : keys) {
    17         //reading from dictionary with READ LOCK
    18         String value = dictionary.get(key);
    19         
    20         //make what ever you want with the value.
    21         System.out.println(key + " : " + value);
    22       }
    23       
    24       //update every seconds
    25       try {
    26         Thread.sleep(1000);
    27       catch (InterruptedException e) {
    28         e.printStackTrace();
    29       }
    30     }
    31   }
    32   
    33   public void stopReader(){
    34     this.runForestRun = false;
    35     this.interrupt();
    36   }
    37 }



    Dictionary.java This is a simple and thread safe dictionary. Read operations are managed through ReadLock and write operations (updates) are managed throuh WriteLock.


    01 package deneme.readwritelock;
    02 
    03 import java.util.HashMap;
    04 import java.util.concurrent.locks.Lock;
    05 import java.util.concurrent.locks.ReentrantReadWriteLock;
    06 
    07 public class Dictionary {
    08   
    09   private final ReentrantReadWriteLock readWriteLock = 
    10     new ReentrantReadWriteLock();
    11 
    12   private final Lock read  = readWriteLock.readLock();
    13   
    14   private final Lock write = readWriteLock.writeLock();
    15   
    16   private HashMap<String, String> dictionary = new HashMap<String, String>();
    17   
    18   public void set(String key, String value) {
    19     write.lock();
    20     try {
    21       dictionary.put(key, value);
    22     finally {
    23       write.unlock();
    24     }
    25   }
    26   
    27   public String get(String key) {
    28     read.lock();
    29     try{
    30       return dictionary.get(key);
    31     finally {
    32       read.unlock();
    33     }
    34   }
    35 
    36   public String[] getKeys(){
    37     read.lock();
    38     try{
    39       String keys[] new String[dictionary.size()];
    40       return dictionary.keySet().toArray(keys);
    41     finally {
    42       read.unlock();
    43     }
    44   }
    45   
    46   public static void main(String[] args) {
    47     Dictionary dictionary = new Dictionary();
    48     dictionary.set("java",  "object oriented");
    49     dictionary.set("linux""rulez");
    50     Writer writer  = new Writer(dictionary, "Mr. Writer");
    51     Reader reader1 = new Reader(dictionary ,"Mrs Reader 1");
    52     Reader reader2 = new Reader(dictionary ,"Mrs Reader 2");
    53     Reader reader3 = new Reader(dictionary ,"Mrs Reader 3");
    54     Reader reader4 = new Reader(dictionary ,"Mrs Reader 4");
    55     Reader reader5 = new Reader(dictionary ,"Mrs Reader 5");
    56     writer.start();
    57     reader1.start();
    58     reader2.start();
    59     reader3.start();
    60     reader4.start();
    61     reader5.start();
    62   }
    63   
    64 }