Cumartesi, Kasım 17, 2012

destek elemanının halinden anlamayana empati anketi


bilişim sektöründe müşteriye yakın insan bilgisayara yakın insanı, bilgisayara yakın insan da müşteriye yakın insanı bir türlü anlayamaz, mıknatısın ters kutubu misali birbirlerini iterek uzaklaşırlar.

teknik insanların hayatta kalmak için zaman içinde geliştirmiş olduğu bir takım davranışlar, bu insanlarla işi olan yöneticiler, satışçılar, müşteri yöneticileri tarafından umarsızlık, tembellik ve şımarıklık olarak yorumlanır.

destek insanlarının içinde bulunduğu durumu anlamayı kolaylaştıracak minik bir anketim olacak:

1.) bir saat içinde evden iş için bir belge hazırlayıp göndermeniz gerek, tam bu anda içeride bebeğiniz ağlamaya başlıyor, eşiniz de o sırada içeride uyumakta. ilk tepkiniz ne olur?

a. işinizi bırakıp bebeğin yanına gitmek
b. işinizi bırakıp eşinizi uyandırmaya gitmek
c. işinize devam edip eşinizin uyanmasını beklemek

2.) daha önce hiç alt değiştirmediniz, bebeğin altı değişmesi gerek, içeride eşiniz uyuyor, ne yaparsınız?

a. bebeğin ağlamasına kulak tıkar, eşimin uyanmasını beklerim.
b. bebeğin ağlamasına kulak tıkar, internetten nasıl alt değiştirilir videoları izlemeye başlarım.
c. eşimin üzerine su döker, uyandırırım.
d. eşimi yatağında dürte dürte nasıl alt değiştirilir anlatmasını sağlarım.
e. doğaçlama yapmaya çalışır, yalan yanlış bir şekilde bebeğin altını değiştiririm.

3.) üçüz bebeğiniz var, tek başınasınız, hepsi bir anda ağlamaya başladı, ne yaparsınız?

a. duvar kenarına çömelir, ağlarım.
b. önce en çok ağlayanla ilgilenirim, diğeri sesini daha çok yükseltecek olursa elimdekini bırakıp ona geçerim.
c. kafamda rastgele bir sıra belirlerim, o sırayla derdi neymiş bakar müdahale ederim, diğerlerinin ağlamasına kulağımı tıkarım.
d. kafamda rastgele bir sıra belirlerim, o sırayla önce hepsinin derdi neymiş anlarım, sonra durumlarına göre tekrar sıralama yaparım, müdahale etmeye başlarım.

burada durumu zorlaştıran şey hangi bebeğe bakılacağına karar vermek değil, hangi 2 bebeğe bakılmayacağına karar vermektir. anne ne karar verecek olursa olsun diğer 2 bebeğe bakmıyor olduğunun vicdan azabını üstünde taşır. aynı anda 3 bebeğin ağlamasının anneleri üzerinde yaratacağı tahribat ile dışarıdan bir insan üzerinde yaratacağı tahribat aynı olmayacaktır. anne zaman içinde duruma uyum sağlar. dışarıdan bir insan, annenin bebeklerin ağlamasına olan duyarsızlığını gaddarca bulabilir. halbuki annenin akıl sağlığını korumasının tek yolu bu duyarsızlığı edinmesidir.

ağlayan bir bebeğin beyninizde yarattığı tahribat neyse, bir destek insanına sürecin dışına çıkarak gönderdiğiniz her istek e-postasının, başına gidip yaptığınız amaçsız dırdırın yarattığı tahribat da odur.

burada üzerinde yük oluşmuş insanı rahatlatmak için aynı işleri yapabilecek bir yardımcı bulabilirsiniz. eğer öyle bir durumunuz yoksa yapabileceğiniz bir başka şey de hangi işin önce yapılacağına karar verme yükünü o kişinin üzerinden almaktır.

alt değiştirmek bir anne için kolay bir iştir, zor olan hangi bebeğin altının değiştirileceğine karar vermektir. bazen bu karar süreci işin kendisinden uzun sürebilir.

Perşembe, Eylül 06, 2012

bir haftalık babalık tecrübesi


insanlar daha sonra sorduklarında yol gösterebilmek için bilgiler ve duygular tazeyken yazayım.

doğum için gayrettepe florance nightingale hastenesini ve dr. herman işçi'yi tercih ettik. seçimlerimizden de son derece memnun kaldık.

hastanenin doğum bölümü hemşireleri 5 dakika içinde odada bitebilme özelliğine sahiptiler. yanınızda hiç anne, baba, akraba olmadan 2 kişi hastaneye girip 3 kişi olarak çıkabilirsiniz. hemşireler çocuğun bakımı ile ilgili her türlü eğitimi uygulamalı olarak veriyorlar.

toplam maliyet de şöyle oldu:
hastane: ~ 3.500 TL
doktor: 2.750 TL

hastanenin doğum sırasında, tam anestezi olmadığı sürece, eşi içeriye kabul ediyor ve fotoğraf çekmesine izin veriyor olması bir artıydı.

koç allianz'in doğum poliçeli sigortasını yaptırmış olduğumuzdan cebimizden, 3 senelik sigorta karşılığı olarak yaklaşık 3.000 TL çıkmış oldu. normalde 2 senelik süre yeterli, biz zamanları tutturamadık.

herman bey avrupa florance nightingale'e bağlı çalışıyor. dışarıdan doktor getirtmişiz gibi bir durum oluştuğundan, doktor parasını önce cebimizden ödeyip sonra sigortadan almak durumunda kaldık, eğer avrupa hastanesini tercih etmiş olsaydık doktor parası doğrudan sigorta tarafından karşılanacaktı.

3.500 TL'lık hastane masrafının 550 TL'lık kısmı yeni doğan işlemleri için alınıyor, sigortamız 450 TL'ye kadar bu masrafları karşılıyordu. özetle toplamda doğum için cebimizden 100 TL nakit çıkmış oldu. tabi doğum öncesi muayeneler ve testlerin parasını cebimizden verdik, bunlar poliçe kapsamında değildi.

sigorta konusunda eray bostancı bize yardımcı oldu. doğum sonrası rüzgar'ı yaklaşık 350 TL'ye esra'nın poliçesine ekledik. bu şekilde "bizim çocuk" kapsamında ömür boyu yenileme garantisiyle rüzgar'ın özel sağlık sigortası başlamış oldu. yalnız sigorta işlemleri için önce kimlik çıkarmak gerekiyor.

normal doğum istememize rağmen son dakika da sezeryana dönmek zorunda kaldık. halk arasında "5 dakikada çocuğu alıveriyorlar" şeklinde bahsedilen operasyonun esas uzun süren kısmının toplama kısmı olduğuna da şahit olmuş oldum, toplamda 45 dakika kadar bir zaman aldı ameliyat. normalde aynı ameliyatı başka bir nedenle yapıyor olsalar muhtemelen hastayı 1 hafta hastaneden çıkarmazlar, ama doğum nasıl güçlü bir motivasyonsa gerçekten anne 2 gün içinde ayağa kalkıp yürüyebiliyor. epidural anesteziyi tercih etmemizden ötürü 1 hafta baş ağrısı gibi bir yan etki yaşadık. oturma, kalkma, yürüme işlevlerinin normale dönmesi 1 haftayı geçti.

aylardır "şu çocuk sağ salim doğsa" diye beklediğiniz doğum anının mutluluğunu yaklaşık 1 saat yaşayabiliyorsunuz. 1 saat sonunda odanızda "bebeğin emmesi lazım" diye bir hemşire bitiveriyor ve mücadele başlıyor. bizimki planlı bir sezeryan olmadığından çocuk alındığında normal doğum başlamıştı, dolayısıyla esra'nın süt problemi olmadı. ameliyat sonrası ilk emzirmeler tam bir işkence ve sinir harbi. "insanlar içgüdüsel olarak emme yetisiyle doğar" sözü de yalanmış, bildiğiniz çocuğa emmesini gösteriyorsunuz. ama haklarını vermek gerekirse gayet iyi birer öğrenci olarak doğuyorlar. ne varki ciddi bir ameliyattan çıkmış bir insan olarak çocuğu doğru düzgün tutamazken bu emzirme eğitimini vermek çok kolay olmuyor.

biz önce mücadeleden bir yıldık, "acıkınca illa emecek" kuramına sarılır gibi olduk bir ara, ama hayat hiç öyle değilmiş. beslemezsen çocuk uykuya veriyor kendini, uykusu ağırlaştıkça emmesi güçleşiyor. her 2-3 saatte bir çocuğu emmeye zorlamak gerekiyormuş, neyse allem ettik küllem ettik bir şekilde çocuğu emzirmeyi başardık. burada da başınızda bir akraba ve hemşire ordusu oluyor, annenin işi psikolojik olarak hiç kolay değil.

birinci gün çocuk gayet güzel 3'er saat arayla 15'er dakika süt emdi. biz başarmış olduğumuzu zannederken üçüncü gün çıkıp gelen bir çocuk doktorunun, çocuğun sarılık değerinin sınırda olduğunu, çocuğun iyi beslenmediğini söylemesiyle tam bir yıkım yaşadık.

anneden süt geliyor, çocuk da emiyor gibi görünmesine rağmen, göğüs ucundaki delikler henüz tam açılmadığından yeterli beslenememiş. deliklerin açılması için paşanın daha kuvvetli emmesi gerek, çocuğun kuvvetlenmesi için de daha iyi beslenmesi gerek. durum çıkmaza girmişti. çıkmazı göğüs pompası ve bir adet şırıngayla aştık. göğüs pompasıyla deliklerin açılmasını sağladık, delikler açılmış olmasına rağmen rüzgar umudunu yitirmiş olacak emmeyi reddediyordu, bunun üzerine hemşire'nin de yönlendirmesiyle sağdığımız sütü şırıngaya doldurup verdik, şırıngayı hafif hafif rüzgar'ın ağzına sıkarken, çocuk içgüdüsel emme hareketine başlıyordu. sütün tadını aldıktan sonra hemen annesinin göğsüne koyduk ve normal sürece geri döndük.

şırınga işini denemek çok tedirgin etmişti, çünkü bir kere kolayına alışırsa hiç bir zaman memeden emmeme gibi bir risk söz konusu. o yüzden en son çare olarak başvurmak gerek.

anne sütünün büyülü etkisini anında görüyorsunuz, çocuk bir yudum içiyor, cildindeki problemler düzelmeye başlıyor, bir yudum daha alıyor gözleri açılmıya başlıyor, inanılmaz bir hızla kendine geliyor. 5. gün sonunda yapılan kan testinde bilirubin değerinin 13'ten 12'ye düştüğünü gördük, iyileşme süreci başlamıştı.

özetle ilk hafta endişeler silsilesi halinde geçiyor. çocuk sağ salim doğacak mı? doğdu emebilecek mi? emdi tuvaletini yapabilecek mi? tek derdiniz gaz çıkarmak haline geldiği zaman iş rayına oturmuş demek oluyor.

şöyle geriye dönüp baktığımda olağandışı herhangi bir problem yaşamadığımızı görüyorum, ama o an için inanılmaz bir endişe yaşıyorsunuz, bunu anlatmak kolay değil, normal bir duygu değil çünkü. iş tatlıya bağlandığı andaki mutluluk da normal bir mutluluk değil. her bir birim endişenin sonunda 2 birim mutluluk tadıyorsunuz, bu hesapla sonunuzun aşırı doz mutluluktan olması çok olası. zaten öyle olmasa çekilecek çile değil gerçekten. yaşanan her şey çok mucizevi, ama bir mucizenin bu kadar sık yaşanıyor olması bir çelişki, olağan mucize doğum.

bir insan tuvaletini yapabiliyor diye bu kadar mutluluk yaşamak normal değil tabi, düşünme şeklinizdeki onarımı mümkün olmayan hasarin hemen farkına varıyorsunuz. baba olmuşsunuz. bakkaldan ekmek alırken gaipten bebeğinizin kokusunu duyuyorsunuz, hem de çok keskin bir şekilde. sıcakta trafikte takılmış otobüsün güneş gören tarafında kalmış olsanız bile yüzünüze anlamsız bir gülücük yapışmış olabiliyor. hayatınızda hiç şarkı mırıldanmamış bir insan da olsanız, internette ninni repartuarınızı genişletmeye çalışırken buluyorsunuz kendinizi.

neredeyse sesimin güzel olduğuna beni bile inandıracaksın, canım oğlum. iyi ki doğdun!

Salı, Haziran 26, 2012

parkyeri'ne göre misiniz? parkyeri size göre mi?

parkyeri'nde, hazır yetişmiş insan gücü transfer etmek yerine, potansiyeli olan insanları seçip yetiştirmeyi tercih ediyoruz. çünkü şirketteki farklı kültür başka firma deneyimi olan arkadaşların uyum problemi yaşamasına neden olabiliyor. uyum konusundaki en büyük direnç noktası da "bu benim işim değil!" psikolojisi oluyor.

bir çeşit savunma mekanizması olarak geliştirilen bu psikolojinin gerekçeleri kişiden kişiye değişebiliyor:

* iş kilitlemeye çalışıyorlar
* bu benim uzmanlaşmak istediğim konu değil
* bu işi yapmaya yetkin değilim

parkyeri'nde işin atandığı değil seçildiği bir ortam yaratmaya çalışyoruz. herkes kartvizitinde istediği başlığı kullanmakta özgürdür. dolayısıyla kişiden beklentiyi müdürü değil de kişi kendi belirlemiş olur. bu kurgu sayesinde insanların potansiyelleri altında kalmasına ve üzerilerinde kapasitelerini aşan bir iş beklentisi oluşmasına engel olmaya çalışıyoruz.

parkyeri çalışanı olarak amacımız iş takibi sistemindeki işleri bitirmek, parkyeri yönetiminin amacı ise bu işlerin hiç tükenmemesini sağlamak. iş listesi şirketin devamlılığını sağlıyor olmalı ve çalışanı mutlu etmeli ki şirketin kurgusu çalışsın.

iş listesinde sadece muhasebe işleri kaldığını varsayalım ve şirkette o sırada yalnız 2 programcı çalışıyor, bu durumda muhasebeci alınıncaya kadar muhasebe işleri durmuyor, programcılar hoşlarına gitmese de muhasebe işlerini aralarında paylaşmak durumunda. bir muhasebe elemanı alınacak olursa bu işleri isteyerek üzerine çekeceğinden mutsuz olan 2 programcıyı özgür kılmış oluruz. ama alınan muhasebe elemanı isterse ve zamanı kalırsa üzerine bir programlama işi de alabilir. bunun önünde bir engel yok.

"iş kilitlenme" konusunu bu bilgiler ışığında değerlendirebilirsiniz. şirketin çarklarının dönmeye devam edebilmesi için herşeyden önce hevesinize ihtiyacı var.

"uzmanlaşma" konusuna gelecek olursak bu konuda insanların biraz aceleci davrandığını düşünüyorum ve genelde ne istendiği o kadar da net olmuyor. "ne konuda uzmanlaşmak istiyorsun?" dendiğinde "java" cevabını alabiliyorsunuz. öncelikle biz parkyeri'nde dil öğretmiyoruz programlama öğretiyoruz. dolayısıyla bir mutfak robotunu çözme hızınız herhangi bir dile olan hakimiyetinizden daha fazla anlam taşıyor bizim için. "uzmanlaşma için ne kadar zaman ayırdın kendine?" diye sorduğunuzda, gelen cevapların ortalaması 5 yıla tekabül ediyor. açıkcası kendi adıma 5 yılda herhangi bir konuda (mesela çömlek yapımı) kendimi uzman olarak konumlandırabileceğimi sanmıyorum. demek ki uzman kelimesinden herkesle aynı şeyi anlamıyorum. yola bu şekilde başlayan insanlar genelde 5 yıl sonunda kendilerini analist veya proje yöneticisi olarak buluyorlar.

insan mezun olduğunda, bir kavramlar bulutu içine düşüyor: backend, frontend, mobil, java, .net, python, perl, javascript, actionscript, objective-c, network, sistem yöneticiliği, ağ programcılığı, gömülü programlama, proje yönetimi, analiz vs. bu kavram bolluğu karşısında insanın ister istemez odağını daraltma psikolojisine girmesi gayet doğal. ama 7-8 yıllık bir tecrübenin ardından şunu söyleyebilirim her yol roma'ya çıkıyor.

önce bu kavramları 2 grup altında topluyorsunuz: backend ve frontend. bu 2 dağdan hangisine tırmanacağınıza karar verip tırmanmaya başlıyorsunuz. bazıları daha yolun ortasında yoruluyor ben buraya yerleşiyorum diyor. eğer yola devam ederseniz tırmandığınız dağın zirvesinin bir son olmadığını başka bir dağın yamacı olduğunu görüyorsunuz, yola devam ediyorsunuz ve geriye dönüp baktığınızda bulunduğunuz yükseklikten hem 'backend' hem 'frontend' dağında neler olduğunu rahatça görebiliyorsunuz. nereden tırmandığınızın bir önemi kalmıyor. sakın ilk dağların eteğinde yorulup yerleşik hayata geçen insanların yorumlarının sizi durdurmasına izin vermeyin.

"yetkinlik" meselesine gelecek olursak, sizden bilmenizi değil öğrenmenizi, en doğrusunu değil elinizden gelenin en iyisini yapmanızı bekliyoruz. herşeyden önemlisi hevesinizi koruyabilmeniz. işe burun kıvırmak, diğerlerinin yaptığını hor görmek gibi davranışların başkalarının motivasyonunu kırma gibi bir yan etkisi olduğunu unutmamak gerek.

bahsetmeye çalıştığım durumu çok güzel özetleyen şu videoyla yazımı sonlandırmak istiyorum:

Cumartesi, Ekim 22, 2011

crawlajax

ara motorlarının arası javascript ve ajax ile hiç iyi olmamıştır. HTML ve verinin aynı kaynaktan sunuluyor olmasından hiç hazetmeyen bir insan olarak SEO konusu çoğu zaman uygulama mimarisini belirlerken elimi kolumu bağlamıştır.

neyse ki google sonunda bu probleme şöyle bir çözüm sundu:

http://code.google.com/web/ajaxcrawling/docs/getting-started.html

eğer URL'inizde hashbang kullanırsanız, google hashbang'ten sonrasını alıp _escaped_fragment_ parametresiyle sunucunuza gönderiyor ve döndüğünüz cevabı indeksleyebiliyor.

sunucunuzda bir tarayıcı motoru kullanarak yazılmış veya HTML birim testleri için tasarlanmış araçlarla sayfanın hashbang'li adresini tarayıcıdan açmışcasına işliyorsunuz ve elde ettiğiniz DOM XML'ini cevap olarak basıyorsunuz.

google bunun için de htmlunit'i önermiş. yalnız bizim uygulamamız biraz ağır bir javascript uygulaması olduğundan HTML'in sunucuda işlenmesi çok uzun sürüyor ve performans problemlerine yol açıyordu.

bu noktada, performans konusunda, kullanılan araçtan bağımsız 3 tavsiyem olacak:
  • CSS renderi iptal edin ve CSS dosyalarına istek yapmadığınızdan emin olun.
  • resim, flash vs. isteklerini iptal edin.
  • sunucudan yaptığınız isteklerde kendinize özel bir user agent belirleyin, bu şekilde javascript tarafında isteğin googlebot isteği olduğunu farkedebilir, ve gereksiz işlemlerin önüne geçebilirsiniz (facebook ve google analytics istekleri gibi, bunlar sayfanın işlenme süresini oldukça olumsuz etkiliyor).
performans problemleri yaşanınca canlı sayfa işlemeye alternatif olarak, önden tüm siteyi dolaşıp sitenin DOMunun anlık bir görüntüsünü kaydetme ve googlebot geldiğinde statik içerik sunma yoluna yöneldik.

bu tarafta sayfanın işlenmesi işi için phantomjs'den faydalandık, sitenin dolaşımı için de kısa bir python kodu yazdık:


uygulama şu an sadece bizim ihtiyaçlarımızı görecek şekilde yazıldı, lütfen kendi ihtiyaçlarınız için projeyi çatallamaktan çekinmeyin.

ajax ve seo anahtar kelimeleri ile arama yaptığımda bir çok öneri bulabiliyorum ama çalıştırılmaya hazır, ürün haline getirilmiş bir şey bulamadım. bu projenin buradaki boşluğu dolduracağını umut ediyorum.

Pazar, Temmuz 03, 2011

sağ beyin, rüya ve ilham üzerine

'pragmatic thinking and learning' kitabından aldığım şevkle bir süredir rüyalarımın kaydını tutmaya başladım.


1 dk. önce gördüğüm rüyayı yazıya dökmekten aciz olduğumu farkettim. bir şey aynı anda hem bu kadar net hem bu kadar bulanık nasıl olabilir? görüntüler çok net ama kağıda dökülebilecek bir şey yok ortada.

kitaba göre beynimizin iki çalışma yöntemi var, biri kontrolümüzde olan sıralı çalışma şekli (sol beyin - left brain - linear brain), diğeri ise bilinçli kullanamadığımız kaotik olan yöntem (sağ beyin - right brain - rich brain).

benim anladığım, beynimizin sağ tarafı aslında hiçbir zaman durmuyor, sürekli veri işlemekle (ilişkilendirmekle) meşgul, bazen bu veri işleme sırasında bilgiler rastlantısal olarak sıralı hale gelebiliyor. eğer bu sıralı hale gelme esnasında uyuyorsak bunun adına rüya diyoruz, uyanıksak da ilham.

sıralı olmayan herhangi birşeyi ne hatırlayabiliyoruz ne de ifade edebiliyoruz. bu yüzden gördüğümüz rüyaya dair parçalar kafamızda net olabiliyor ama bir türlü birleştiremiyoruz. birleştiremeyince ifade edemiyoruz, ifade edemedikçe bulanıklaşıyor.

bir rüyamda 'ben şimdi bunu yarın nasıl tasvir edeceğim' derdine düşüp, rüyayı görürken bir yandan beynime de gördüklerimi kazımaya çalıştım. işin üzerine gittikçe rüyaların üzerinde bir çeşit kontrol yeteneği geliştirmeye başlıyorsunuz. bir keresinde de uykuya dalma anında rüyanın başlangıcında olduğumu farkettim, henüz sol beyin yerini sağ beyine bırakmadan, uyanık bir bilinçle rüyayı yaşayabildim ve hatta yönlendirebildim. şimdi bunu görmek istiyorum diye düşünüyorum, görmek istediğim şey hayaldan öte bir netlikle karşıma geliyor. bu sırada ne uyuyorum ne de uyanığım. insanların gündüz rüyası dedikleri şey bu olsa gerek.

her insanda bu rüya yönlendirme yeteneği bir refleks olarak mevcut. film gibi rüyaları da bu şekilde açıklıyorum, sürekli faaliyet halinde olan sadece sağ beynimiz değil, sol beynimiz de biz uyuyorken bile çalışıyor. sağ beynin uyku sırasında önüne attığı küçük sıralı parçaları birleştirip bir hikaye yaratabiliyor kendine.

bu rüya ve ilham yönetme işini bir çözebilsek rahatlayacağız muhtemelen. aslında bir çok sorunun cevabı hali hazırda beynimizin içinde bir yerlerde var ama bir türlü su yüzüne çıkaramıyoruz. beynimizde sakladıklarımız sadece kendi tecrübelerimiz de değil, atalarımızın tecrübelerini de beynimizin bir yerinde tuttuğumuza inanıyorum.

insan doğduğunda beyni boş mudur? dolu mu? bence boştur. ama zamanla dolar. nasıl insan büyüdükçe burnu ebeveynlerine ve atalarına benzemeye başlıyorsa, beyin de şüphesiz benzer bir süreçten geçiyor. beynimiz annemize benzedikçe annemizin hatıralarına da sahip olabilir miyiz?

bilgiyi beyinde nasıl saklıyoruz, beynimizde tuttuğumuz bilgiler beynimizin fiziksel yapısında bir değişikliğe yol açıyor mu? hayatımız boyunca kendi beynimizde oluşturduğumuz sinir ağlarının yapısını bir şekilde gelecek nesillere aktarabiliyor muyuz?

aktarabildiğimizi bir düşünün. bu durumda beynimiz aslında 1 milyon yaşında bir insanın tecrübesine sahip, nesiller boyu aktarılmış bir bilgi yumağı. bu hayatı erkek-kadın, zengin-fakir, güçlü-güçsüz, hasta-sağlıklı her türlü role bürünerek defalarca yaşamışız. aslında hepimizin beyninde duran bu bilgi hepimizi ölümsüz kılıyor aynı zamanda.

15 yaşında tecrübe ettiğiniz bir aşk macerasını 30 yaşındaki bilincinizle tekrar yaşadığınızı düşünün, tahmin edemeyeceğiniz ne gelebilir başınıza? bir de bu ilişkiyi 1 milyon yaşında bir insan olarak tekrar yaşadığınızı düşünün.

beynimizde 1 milyon yıllık bir tarih var, hem de her açıdan görüşümüz olan bir tarih. aynı dönemin padişahının da köylüsünün de hatıraları beynimizin içinde mevcut. çünkü soy ağacınızın bir dalı saraydan geçerken, bir dalı da bir köye uzanıyor.

tabi hiç bir koşulda bu aşk macerasını 15 yaşındaki halimiz kadar heyecanla yaşayamayız, belki de bu yüzden hatırlamamayı seçiyoruz, yaşama heyecanımızı kaybetmemek için.

Salı, Mart 08, 2011

jmockit ile ideal birim test

projeler genelde büyük bir enerji ile başlar. hele bir de çevik programlama ve test güdümlü programlama söylemlerinin büyüsüne kapılmış bir ekibiniz varsa, harıl harıl birim test yazılmaya başlanır.

zaman problemi olmayan bu enerjik ekip hiç bir şeyden üşenmez. sahte sunucular havalarda uçuşur, veritabanında çılgın sahte veriler oluşturulur. testi yazılmamış tek bir metod bulamazsınız.

sonra projelerin ortalarına doğru, yazılan kodları birleştirme aşamasına gelindiğinde, odak geliştirmek değil birleştirmek olduğundan yeni test eklenmemeye başlanır. varolan testler ise sınırlı ölçüde güncellenmeye devam eder. metodun kapsamı genişlemişse, genişleyen kapsam için test eklenmez, sadece daha önce yazılmış testlerin geçmesinin sağlayacak şekilde en az değişiklik felsefesiyle test güncellemeleri yapılır.

projenin sonuna doğru ise ekibimizde ne enerji ne de zaman kalmıştır. ama kod değişmeye devam eder. bu zamanlarda projede "önce çalışsın" havası hakim olur. yazılmış olan testler patlamaya başladıkça yorum haline getirilir.

ve gün gelir, bakmışsınız, elinizde hiç kayda değer bir birim test kalmamış, projenin başında harcadığınız onca çabayı çöpe atmışsınız.

bu duruma düşmemek için birim testler konusunda ekibin bilinçlendirilmesi şart. herkesin şu gerçekleri kabul etmesi gerek:

  • yaz ve unut birim testi yoktur. test kodunu yazarken bir gün bu kodun başkası tarafından güncellenmesi gerekeceğini unutma.
  • birim test yazımı kodun kendisinin yazımından daha uzun sürer, daha çok emek ister.
  • birim test ile fonksiyonel test ayrı şeylerdir. birim test ile test etmeye çalıştığımız kendi yazdığımız kod, kullandığımız veritabanının veya bağlandığımız web servisinin kodu değil!
  • test ettiğin metod alt metodlar çağırıyorsa, alt metodun yerine getirdiği işlevin doğrulamasını burada yapma! sadece alt metodun doğru paremetrelerle çağrılıp çağrılmadığını ve alt metodun döndüğü değerlere testini yazdığımız fonksiyon doğru tepki veriyor mu kontrolleri için test yaz.
  • testleri yorum haline getirmek testleri geçirme yöntemi değildir.
yine zaman içerisinde bu duruma düşmemek için kendimce geliştirmiş olduğum bir takım prensiplerim mevcut:
  • test kodundan harici bir sunucuya, veritabanına vs. bağlanmaya çalışma.
  • mümkünse birim testler ön ayar gerektirmesin. projenin kodlarını çekip "ant clean test" demek birim testleri çalıştırmak için yeterli olsun.
  • uygulamayı üzerinde geliştirdiğin "framework"'u birim testler için ayağa kaldırma.
  • projenin tüm birim testlerini çalıştırmak 1 dk.'yi geçmesin.
bunlar güzel idealler, peki pratikte bunu nasıl yapıyoruz, örneğin tek yaptığı iş veritabanından bir sorgu yapmak olan metodun testini veritabanına bağlanmadan nasıl yazacağız? cevap: sahte nesnelerle.

sahte nesne yaratmak için, özellikle javada bir çok gelişmiş kütüphane mevcut. yukarıda söylediğim şartları yerine getirebilmeniz için kullandığınız kütüphanenin şu özellikleri deskteklemesi gerek:
  • 'constructor' üzerine yazabilmelisiniz (metod içerisinde "new" ile yaratılmış nesneleri sahte nesnelerle değiştirebilmek için).
  • final/static/native/private metod ve değişkenlerinin üzerine yazabilmelisiniz.
  • test ettiğiniz sınıfın bir kısmını sahte hale getirebiliyor olmalısınız.
tüm bunları yapabildiğim kütüphane jmockit oldu. yine jmockit ekibinin, biraz yanlı da olsa, sahte nesne kütüphanelerinin özelliklerini karşılaştırdığı şu tablodan, seçtiğiniz kütüphanenin yeteneklerini kontrol edebilirsiniz.

ben burada jmockit üzerinden bazı pratik örnekler vermeye çalışacağım:
  • sınıfın bir kısmının üzerine yazmak.
  • statik metodun üzerine yazmak.
  • private değişkene değer atamak.
  • metod içerisinde new ile yarattığın bir nesnenin üzerine yazmak.

jmockit ile sahte nesnesi üretilecek bir interface:
public interface IFoo {
public String bar();
}

jmockit ile 'constructor'i üzerine yazılacak 'concrete' sınıf:
public class Foo implements IFoo {
public Foo() {
}

public String bar() {
return "foobar";
}
}
birim testi yazılacak örnek sınıf:
public class Sample {

private String privateField = "sample";
private IFoo fooNotInitiated = null;
private IFoo fooInitiatedAtConstructor = null;
private IFoo fooInitiatedAtDefinition = new Foo();

public Sample() {
this.fooInitiatedAtConstructor = new Foo();
}

public Sample(IFoo fooPassed) {
this.fooInitiatedAtConstructor = fooPassed;
}

public static String staticMethodA() {
return "a";
}

public static String staticMethodBCallingA() {
return staticMethodA() + ",b";
}

public String instanceMethodUsingPrivateFieldAndNewFoo() {
Foo foo = new Foo();
return privateField + ":" + foo.bar();
}

public String instanceMethodUsingPrivateFooNotInitiated() {
return fooNotInitiated.bar();
}

public String instanceMethodUsingPrivateFooInitiatedAtDefinition() {
return fooInitiatedAtDefinition.bar();
}

public String instanceMethodUsingPrivateFooInitiatedAtConstructor() {
return fooInitiatedAtConstructor.bar();
}
}
örnek birim test:
import junit.framework.TestCase;
import mockit.Mock;
import mockit.Mockit;
import mockit.MockUp;
import static mockit.Deencapsulation.setField;

public class SampleTest extends TestCase {

private Sample instance;

protected void setUp() {
instance = new Sample();
}

public void testStaticMethodA() {
assertEquals("a", Sample.staticMethodA());
}

public void testStaticMethodBCallingA() {
Mockit.setUpMock(Sample.class, new Object() {
@Mock
public String staticMethodA() {
return "m";
}
});
assertEquals("m,b", Sample.staticMethodBCallingA());
}

public void testOverridingNewOperator() {
new MockUp() {
@Mock
void $init() {
}

@Mock
public String bar() {
return "moobar";
}
};
assertEquals("sample:moobar", instance.instanceMethodUsingPrivateFieldAndNewFoo());
setField(instance, "privateField", "mample");
assertEquals("mample:moobar", instance.instanceMethodUsingPrivateFieldAndNewFoo());
assertEquals("moobar", instance.instanceMethodUsingPrivateFooInitiatedAtConstructor());
assertEquals("moobar", instance.instanceMethodUsingPrivateFooInitiatedAtDefinition());
instance = new Sample(new Foo());
assertEquals("sample:moobar", instance.instanceMethodUsingPrivateFieldAndNewFoo());
assertEquals("moobar", instance.instanceMethodUsingPrivateFooInitiatedAtConstructor());
assertEquals("moobar", instance.instanceMethodUsingPrivateFooInitiatedAtDefinition());
}

public void testCreatingMockFromInterface() {

IFoo mockFoo = new MockUp() {
@Mock
public String bar() {
return "moobar";
}
}.getMockInstance();
try {
assertEquals("moobar", instance.instanceMethodUsingPrivateFooNotInitiated());
fail("expecting null pointer exception here!");
} catch(java.lang.NullPointerException e) {
}
setField(instance, "fooNotInitiated", mockFoo);
assertEquals("moobar", instance.instanceMethodUsingPrivateFooNotInitiated());
assertEquals("foobar", instance.instanceMethodUsingPrivateFooInitiatedAtDefinition());
assertEquals("foobar", instance.instanceMethodUsingPrivateFooInitiatedAtConstructor());
instance = new Sample(new Foo());
assertEquals("foobar", instance.instanceMethodUsingPrivateFooInitiatedAtConstructor());
assertEquals("foobar", instance.instanceMethodUsingPrivateFooInitiatedAtDefinition());
setField(instance, "fooNotInitiated", mockFoo);
setField(instance, "fooInitiatedAtConstructor", mockFoo);
setField(instance, "fooInitiatedAtDefinition", mockFoo);
assertEquals("moobar", instance.instanceMethodUsingPrivateFooNotInitiated());
assertEquals("moobar", instance.instanceMethodUsingPrivateFooInitiatedAtDefinition());
assertEquals("moobar", instance.instanceMethodUsingPrivateFooInitiatedAtConstructor());
}
}


derleyip birim testleri çalıştıran kod:
#!/bin/sh
javac -cp .:jmockit/jmockit.jar:junit-4.9b2.jar Sample.java SampleTest.java && java -javaagent:jmockit/jmockit.jar -cp jmockit/jmockit.jar:junit-4.9b2.jar:. org.junit.runner.JUnitCore SampleTest

testStaticMethodA:

Kendi ürettiği string bir değeri dönen basit bir metod. yazdığımız test de üretilen string değerin ne olduğunu test ediyor.

testStaticMethodBCallingA:

burada B metodu kendi içerisinde A'yı çağırıyor. kendi ürettiği string bir değeri A'nın ürettiğiyle birleştirip dönüyor. kurada yazılacak birim testte A'nın ne ürettiğiyle ve nasıl ürettiğiyle ilgilenmiyoruz aslında. doğrulamamız gereken B'nin ürettiği string değer ve A'nın dönüş değerini nasıl kullandığı. dolayısıyla staticMethodA'nın üzerine yazıp döndüğü değere müdahale ediyoruz. staticMethodA veritabanına veya bir webservisine bağlanan bir metod olabilirdi. Bu fonksiyonu sahte hale getirerek tüm bu bağımlılıklardan da kurtulmuş oluyoruz.

aynı zamanda bu örnekle "sınıfın bir kısmının üzerine yazmak" ve "statik metodun üzerine yazmak" kavramlarını da açıklamış olduk. "staticMethodA" sahte hale getirilirken birim testini yazdığımız "staticMethodBCallinA" metodunun orjinal halini çağırabildik. burada statik metodu sahte hale getirme yöntemi önemli ("Mockit.setUpMock(Sample.class, new Object(){...});" şeklinde). benim denediğim diğer yöntemlerde (new Object yerine Class kullanmak mesela) çeşitli problemler yaşadım (testin birinde sahte hale getirdiğim bir metodun başka bir testte orjinal halini kullanamamak gibi).

testOverridingNewOperator:

bu test içinde yaptığımız new operatörü üzerine yazma işini her birim test arabirimi desteklemiyor, kişisel olarak başlıca jmockit tercih sebebimdir.

dünya'da herkes ideal kod yazmıyor malesef, kullandığınız her 'framework' veya kütüphane doğru enjeksiyon noktaları bıraksa muhtemelen çok ihtiyacımız olmazdı bu özelliğe. eğer değişken tipi olarak 'interface' değil de 'concrete' sınıflar kullanırsanız ve yarattığınız nesnelerin üzerine yazılabilecek herhangi bir yöntem sunmazsanız (örneğin bir metodun içinde bir nesne yaratıyorsunuz, yeni nesneden bir metod çağrısı yapıp, nesneyi çöpe atıyorsunuz), insanlara new operatörünün üzerine yazmak dışında bir şans bırakmazsınız.

örnekteki testte $init fonksiyonuyla hiç parametre almayan 'constructor'in üzerine yazmış olduk. o satırdan itibaren çalıştırılan her "new Foo();" işleminde gerçek 'constructor' yerine sahte 'constructor' çalıştırılacaktır. yaratılan nesne'den yapılan metod çağrılarında eğer sahtesi yazılmış bir metod varsa sahtesi, yoksa orjinali çalıştırılacaktır. gerçek 'constructor'in çağrılması istendiği durumlar için jmockit gerçek sınıftan yaratılan bir nesneyi enkapsüle ederek bir çözüm sunmaktadır. burada detaya inmemek için bu konuya girmiyorum.

dikkat ederseniz 'Sample' sınıfının nesnesi 'setup' fonskiyonunda yaratılmış olmasına rağmen, 'Sample' sınıfının alanlarının tanımlanması aşamasında ve 'constructor'inda yaratılmış 'Foo' nesneleri de sahte nesneler olarak gelmiş (bar fonksiyon çağrıları 'foobar' yerine 'moobar' dönmüş).

yine bu testte "private değişkene değer atamak" örneğini de 'privateField' değişkeni üzerinden vermiş olduk. 'Spring' gibi sınıf değişkenleri "bean" ayarları okunduktan sonra oluşturulan nesnelerle doldurulan bir 'framework' kullanıyorsanız bu özellik çok işinize yarayacaktır.

testCreatingMockFromInterface:

new operatörünün üzerine yazmak güzel ama getirdiği bir dezavantaj var. ya o sınıftan yaratılan her nesnenin üzerine yazmak istemiyorsak? örneğin test etmek istediğiniz metod java.io.BufferedReader sınıfından bir nesne yaratıyor ve sizin de bu sınıfın sahtesini yazmanız gerek. eğer new operatörü üzerine yazarsanız sistemdeki her BufferedReader nesnesini sahte hale getirmiş olursunuz. örneğin üzerine çalıştığınız 'framework' bir yerde BufferedReader nesnesi yaratıyorsa kodunuz çalışmayacak hale gelecektir.

bu örnekte 'IFoo' interface'inden bir sahte sınıf yaratılıyor ve önce hiçbir yerde ataması olmayan "fooNotInitiated" değişkenine bu sahte nesne atanıyor. "fooNotInitiated" örneği annotasyonlarla yaratılan sınıf değişkenleri için güzel bir çözüm. bu sırada "fooInitiatedAtConstructor" ve "fooInitiatedAtDefinition" değişkenleri orjinal 'Foo' nesnesini barındırmaya devam ediyor. daha sonra bu değişkenlerin de üzerine yazılabileceğine örnek olması açısından sahte nesne bu değişkenlere atanarak aynı fonksiyon çağrıları tekrar yapılıyor ve dönüş değerlerinin "moobar" olarak değiştiği görünüyor.

bu yöntemde en bağlayıcı nokta 'concrete' sınıflardan 'mock' nesne yaratılamaması! yani 'Sample' sınıfının "fooNotInitiated" değişkeninin tipi 'IFoo' değil de 'Foo' olsaydı elimizden birşey gelmezdi. 'concrete' sınıfı sahte hale getirmenin tek yolu $init fonksiyonunu kullanmak. yani kullandığınız kütüphane değişken tipi olarak 'concrete' sınıflar kullanmışsa elinizi kolunuzu bağlamış oluyor.

özetle kullandığınız birim test çatılarının yetenekleri de bir yere kadar, bir yerde yine gelip insan faktörüne takılabiliyorsunuz.

test edilebilir kod yazmak ya da yazmamak! işte bütün mesele bu!


Salı, Ocak 18, 2011

kartaca'ya geçtim

kartaca parkyeri çatısı altında kurulmuş bir şirket. askerden döndüğümde yine parkyeri çatısı altında kurulan bir diğer şirket zeitin'de çalışmaya başlamıştım. zeitin parkyeri'nden sermaye alınarak kuruldu, kartaca ise parkyeri'nin hali hazırda yaptığı projeleri devraldı.


zeitin sonrasında mynet'e geçiş hikayemi şu blog girdimde özetlemeye çalışmıştım.

mynet'te geçen mutlu 1.5 yılın ardından çok da kolay olmayan bir kararla kartaca'ya geçerek parkyeri çatısı altına geri dönmüş oldum.

aslında çok "geri dönmek" denilemez, çünkü her ne kadar kartaca parkyeri'nin işlerini devralmış olsa da iş yapış yöntemleri olarak kendine bambaşka bir yol çizmiş durumda. takımlar ve hedefler üzerine kurulu yeni bir düzen getirilmiş.

mynet'te gerek yaptığım işten tatmin olmam gerekse iş arkadaşlarımla mutlu olmam geçiş kararımı oldukça güçleştirdi. bu kararı kendimce şu nedenlere dayanarak rasyonalize ettim:
  • gelir ve gelir arttırma konuları
  • sistem mühendisliğine geçme isteğim
  • eğitim bütçesi olması
  • seminerlere katılım/seminer verme teşviği
  • özgür yazılım dünyasına yakınlık
  • bir kütüphanesinin olması
  • bedava çikolata
  • teknoloji şirketi olması
  • şirket etiği ve vizyonunun olması
gelir ve gelir arttırma konuları: kartaca'nın şu anda bana sunduğu şartları mynet sunamadı. mynet'te geliştirici pozisyonu için belirlenmiş bir maaş seviyesi mevcut, bu seviyenin üzerine çıkamıyorsunuz, yazılım tarafında da oturmuş bir yönetim kadrosu olduğundan terfi gibi bir durum da söz konusu değil, dolayısıyla gelirimi arttırma konusunda biraz önüm kapanmıştı. bu açıdan kartaca'da geleceğimin daha parlak olacağını düşündüm, büyümekte olan bir firmaya zamanında yapılan bir geçiş olacağı kararını verdim.

sistem mühendisliğine geçme isteğim: askerlik sonrası aldığım bir kararı 3 yıl sonra uygulama şansım olacaktı. bunu aslında mynet'te de yapabilirdim, ama mynet'te sistem tarafında mutlu olabileceğime dair şüphelerim vardı, sistem tarafının dünyası ile yazılım tarafının dünyası bambaşka mynet'te. 1.5 yılda mynet'te 3 defa mesaiye kalmışlığım ya vardır ya yoktur, sistemde ise insanların sürekli mesai yapıyor olması gibi sebepler geçiş yapmama engel oluyordu. kartaca'ya geçişim ile birlikte ruslar sıcak denizlere inemeden ben sistem mühendisliğine geçebildim ve hatta yeni sıfatımı da kendimce belirledim: "Site Güvenilirlik Mühendisi (Site Reliability Engineer - SRE)". "yazılım geliştiricisi" sıfatıyla proje yöneticisi veya müdür olmadan maaşınızı arttırmanız çok mümkün değil malesef, insan yönetmekten uzak durmak ve işin mutfağında kalmak isteyen bir insan olarak yeni sıfatımın amaçlarıma daha iyi hizmet edeceğini düşünüyorum. ve her zaman yazılım geliştirmeye hobi olarak devam edebilirim.

eğitim bütçesi olması: kartaca'da herkesin bir eğitim bütçesi kumbarası var, aylık 100$ atılıyor bu kumbaraya. kendi seçtiğin herhangi bir eğitim için bu bütçeden faydalanabiliyorsun. açıkcası sırf bu bütçeden faydalanayım diye muhtelif sertifikaları toplamayı düşünüyorum yavaş yavaş. normalde maaş olarak verseler bana bu parayı eğitim bütçesi adı altında, eğitime harcamazdım muhtemelen, bu şekilde olunca ağaç zorla eğilmiş oluyor. şirketinin eğitimini düşünüyor olması ve alacağın eğitim konusunda da seni özgür bırakıyor olması güzel bir şey.

seminerlere katılım/seminer verme teşviği: çalışanları seminer versin/alsın diye ittiren bir şirket olması.

özgür yazılım dünyasına yakınlık: parkyeri ve çatısı altında kurulan şirketler hep türkiye'deki özgür yazılım camiasının yanında olmuştur, maddi ve manevi olarak. bu da güzel bir çevre edinme şansı veriyor insana.

bir kütüphanesinin olması: bunun değerini kaybedince anladım. okuduğu kitabı bir daha okumayan bir insan olarak kitap biriktirme işini hep anlamsız bulmuşumdur. okurken itu'nun, çalışırken de parkyeri'nin kütüphanesini sömürdüğümden mynet'e geçince bir boşluğa düştüm. kitaba para vermek, evde kütüphane oluşturmak gibi yeni kavramlar girdi hayatıma. kartaca'da teknik olsun/olmasın istediğiniz kitabı söylüyorsunuz alıyorlar. kitap dışında yurt içi/dışı birçok derginin üyeliğinden de faydalanabiliyorsunuz. teknik olarak herhangi bir konuda bir kitaba ihtiyacınız olduğunda, aradığınız kitabı daha önce birilerinin sipariş etmiş olmasının hazzı bambaşka birşey. teknik olmayan kitaplar da kitap okuma faaliyetini sosyal bir hale dönüştürmesi nedeniyle önemli.

bedava çikolata: mynet'te maaşımızın bir kısmını yatırdığımız gündelik "saat 5" çaylarımız vardı, bu molalarımızda en çok aradığım şey parkyeri'ndeki bedava abur cubur/içeçek dolabımız olurdu. böylece hep insanlara bahsini ettiğim dolabıma kavuşmuş oldum. Böyle uygulamalar şirketler için çok önemli, çok fazla bir maliyet yaratmadan çalışanlarınızın kalbini fethetmenin en kolay yolu.

teknoloji şirketi olması: bir şirketin teknoloji satıyor olması ile kullanıyor olması apayrı şeyler. kartaca doğrudan adam-gün satıyor, dolayısıyla şirketin pazarladığı doğrudan çalışanlarının teknik yetenekleri ve şirkette birikmiş olan bilgi. dolayısıyla ben bilgimi ve yeteneklerimi arttırdığım sürece şirketin değeri de artmış oluyor. doğrudan bir kazan-kazan ilişkisi söz konusu. mynet ise üyelik ve reklam gelirleriyle ayakta duran bir şirket. teknik adam olarak şirketin başarısındaki rolünüz biraz daha küçük oluyor bu durumda. yarın bir gün mynet tüm teknik işlerini rahatlıkla taşeronlaştırabilir mesela, ama kartacanın varoluş sebebi barındırdığı teknik personel.

şirket etiği ve vizyonunun olması: askerliğim öncesi parkyeri'nde bir vizyon/misyon belirleme çalışması yapmıştık. kartaca bu çalışmanın çıktısını sahiplenmiş ve bu yolda köklü değişiklikler yapmış. şirket bir yandan ayakta durabilmenin gereklerini yerine getirebilirken bir yandan da belirlediği vizyon doğrultusuna ilerlemeyi başarabilmiş. ve bunlar başarılırken parkyeri zamanında benimsenmiş etik değerler korunabilmiş. kartaca için çok rahatlıkla şunları söyleyebilirim: çalışanın sosyal haklarını gasp etmez (stajyerlere bile sigorta yapılıp, maaş bağlanır, kartaca çapında bunu yapan başka şirket olduğunu sanmıyorum türkiye'de), vergi kaçırmaz, kaçak yazılım kullanmaz, yasa dışı iş yapmaz. türkiye'de bunları kendi şirketi için şüphe etmeden söyleyebilecek kaç çalışan var?

php'de MCRYPT_3DES ve MCRYPT_MODE_CBC ile şifrelemenin java'daki karşılığı

php'de TripleDES şifreleme algoritması ile CBC mod kullanarak ürettiğiniz şifrelenmiş sözcüğün aynısını java'da üretmek için internette arandığınızda karşınıza iki anahtar sözcük çıkacaktır:

  • DESede (algoritma olarak)
  • DESede/CBC/PKCS5Padding (dönüşüm metodu)
burada yanıltıcı olan dönüşüm metodu, php "PKCS5Padding" desteklemediğinden şifrelemek için verdiğiniz sözcüğün sonuna, kendince, 8'in katı kadar karakter sayılı hale getirecek şekilde boş karakterler ekliyor. Dolayısıyla java'da da dönüşüm metodu olarak "DESede/CBC/NoPadding" kullanıp, eksik karakter hatası[1] almamak için de php'nin yaptığını elle yapmanız gerekli.

aşağıdaki şifreleme için kullanılan php kodunun java karşılığını daha da aşağısında bulabilirsiniz.

PHP:

$plaintext = "Some-plain-text-message-to-be-symetrically-encrypted";
$deskey = "secret word with 24 byte";
$ivkey = "12345678";
$td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, $deskey, $ivkey);
$encrypted_data = mcrypt_generic($td, $plaintext);
mcrypt_generic_deinit($td);
echo strtoupper(bin2hex($encrypted_data));
mcrypt_module_close($td);


JAVA:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.apache.commons.codec.binary.Hex;
import java.util.Date;
import java.util.Arrays;
import java.text.SimpleDateFormat;
import java.text.ParsePosition;

public class Encrypt {

public static void main(String[] args) throws Exception {
String plainText = "Some-plain-text-message-to-be-symetrically-encrypted";
String desKey = "secret word with 24 byte";
String ivKey = "12345678";
String algorithm = "DESede";
// String transformation = "DESede/CBC/PKCS5Padding";
String transformation = "DESede/CBC/NoPadding";
byte[] keyValue = desKey.getBytes("UTF-8");
byte[] ivValue = ivKey.getBytes("UTF-8");
DESedeKeySpec keySpec = new DESedeKeySpec(keyValue);
IvParameterSpec iv = new IvParameterSpec(ivValue);
SecretKey key = SecretKeyFactory.getInstance(algorithm).generateSecret(keySpec);
Cipher encrypter = Cipher.getInstance(transformation);
encrypter.init(Cipher.ENCRYPT_MODE, key, iv);
// byte[] input = plainText.getBytes();
byte[] input = getPaddedBytes(plainText);
byte[] encrypted = encrypter.doFinal(input);
System.out.println(new String(Hex.encodeHex(encrypted)).toUpperCase());
}

public static byte[] getPaddedBytes(String s) throws java.io.UnsupportedEncodingException {
int n = s.length();
n = n + (8 - (n % 8));
byte[] src = s.getBytes("UTF-8");
byte[] dst = Arrays.copyOf(src, n);
return src;
}
}



[1] javax.crypto.IllegalBlockSizeException: Input length not multiple of 8 bytes