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