Sansür, bir toplumun kendine olan güvensizliğini yansıtır ve otoriter rejimlerin belirgin bir özelliğidir.

--Potter Stewart

25.10.2012

PrimeFaces Datatable Column Custom Sorting

Popüler JSF kütüphanesi PrimeFaces'ın geliştiriciye yetenekli JSF bileşenleri sunduğu aşikar. Bu yeteneklerinin çoğunu showcase sayfasında sergilese de gözden kaçan gizli kalmış yeteneklerini de her zaman elinizin altında bulunması gereken kapsamlı dökümanını inceleyerek yakalayabilirsiniz.

Bunlardan biri de datatable bileşeninin sıralama yeteneğinde saklı. Basitçe kullanıcılarınızı ilk isimleri ile sıralamak istediğinizde işinize yarayacak aşağıdaki örnek, kullanıcının tam ismine göre sıralama yapmak istediğinizde elinizi bağlıyor:

<p:column width="100" sortBy="#{user.firstName}" >
 <f:facet name="header">
  <h:outputText value="Kullanici ismi" />
 </f:facet>

 <h:outputText value="#{user.firstName}" />
</p:column>

Yukardaki örnekte sortBy="#{user.firstName} #{user.lastName}" yapmamıza izin verilmiyor. Hemen akla kullanıcı tam ismini döndüren bir getter metot yazarak  sortBy="#{user.fullName}" yapmak akıllara gelebilir. Bu metot iki kolonu concat ederek bize tam ismi döndürmesi açısından kullanışlı olsa da ne var ki veri erişim katmanında Hibernate gibi bir kütüphane kullanıyorsanız buna da izin yok. Hibernate veritabanında ilişkilendirebileceği fullName isimli kolon bulamadığı için şikayet ediyor.

Bu aşamada yardımımıza datatable bileşeninin sortFunction yeteneği yetişiyor:

<p:column width="100" sortBy="#{user.firstName}" sortFunction="#{userBean.fullNameSorter}" >
 <f:facet name="header">
  <h:outputText value="Kullanici ismi" />
 </f:facet>

 <h:outputText value="#{user.fullName}" />
</p:column>

userBean managedbean'i içinde metodun gerçekleştirimi de şu şekilde basit bir compare işlemi ile halledilebilir:

public int fullNameSorter(User user1, User user2){
 Collator trCollator = Collator.getInstance(new Locale("tr","TR"));
 return trCollator.compare(user1.getFullName(), user2.getFullName());
}

Bol Javalar..

6.08.2012

Maven-Jetty Plugin ve EL ile Method Expression'lara Parametre Geçme

<plugin>
 <groupId>org.mortbay.jetty</groupId>
 <artifactId>jetty-maven-plugin</artifactId>
 <version>${jetty-maven-plugin.version}</version>
 <configuration>
  <scanIntervalSeconds>10</scanIntervalSeconds>
  <stopPort>8075</stopPort>
  <stopKey>STOP</stopKey>
 </configuration>
 <dependencies>
  <dependency>
   <groupId>org.glassfish.web</groupId>
   <artifactId>el-impl</artifactId>
   <version>2.2</version>
  </dependency>
 </dependencies>
</plugin>
Bu kadar..

26.07.2012

JSF MyFaces ve Spring Core

Uzunca bir süre projelerimizde ve JSF eğitimlerimizde JSF'in referans gerçekleştirimi olan Mojarra'yı kullandık. Referans gerçekleştirimi olmasına rağmen Mojarra'da yer alan hatalar ve MyFaces gerçekleştirimine kıyasla yavaş çalışması sonucu yeni projelerimizde MyFaces kullanmaya karar verdik.

Basitçe bu geçişi Mojarra'nın api ve impl .jar'larını MyFaces'inkilerle değiştirerek yapabiliyorsunuz. Lakin MyFaces, Mojarra'ya göre bazı noktalarda farklı davranabiliyor. Bu davranışı da web.xml'de ekleyebileceğiniz çeşitli context-param'larla düzenleyebiliyorsunuz.

MyFaces JSF'i Spring ile kullanırken karşılaştığımız bir farklılık da viewScope tanımlı managedBean niteliklerine bağımlılık aktarımı yapılan Spring bean'lerin ikinci istekte null değerler almasıydı. Bunun sebebi de MyFaces'in varsayılan olarak session'daki JSF state'ini her defasında serialize/deserialize etme çabasıydı. İkinci istek işlenirken deserialize yapılan JSF beanlerine Spring beanleri aktarılamadığı için NullPointerException hatası alıyorsunuz.


<context-param>
  <param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
  <param-value>false</param-value>
 </context-param>


şeklinde yazılan context-param değeri bu sıkınıtının önüne geçiyor. Buna benzer MyFaces parametrelerin listesini buradan inceleyebilirsiniz..

4.06.2012

OmniFaces

JSF ile haşır neşir olup da BalusC'yi duymayan yoktur herhalde. Forumlarda, özellikle stackoverflow.com'da yanıtlanmayan JSF sorusu kalmayacak diye inat etmiş Balus ve bir arkadaşı (Arjan Tijms) JSF'in eksikliklerini masaya yatırıp bunları nasıl yamarız diye düşünürlerken olsa gerek geçen günlerde OmniFaces'in ilk sürümünü yayınladılar.

Showcaselerinde PrimeFaces kullanan ekibin çalışmasına göz attığımda JSF'te can sıkan, baş ağrıtan belli başlı sorunlara pratik çözümler getirmelerinin yanı sıra bizim de "Bunları bir kütüphane haline getirsek de projelerde tekrar tekrar yazmasak" dediğimiz ama bir türlü fırsat bulamadığımız, her JSF projesinin olmazsa olmaz metotlarını da OmniFaces'a eklediklerini gördüm.

Session öldüğünde Ajax istemiyle geldiğinde alınan ViewExpiredException hatasına getirilen çözüm (FullAjaxExceptionHandler), script ve style dosyalalarını birleştirerek HTTP requestlerini azaltmayı hedefleyen CombinedResourceHandlerSelectItemsConverterRequiredCheckboxValidator, form validation mekanizmasına getirdikleri validateAllOrNonevalidateOneOrMore vb. yapılar, sıklıkla kullanılan işlevleri util sınıfı ve EL fonksiyonu olarak toparlamaları kütüphanenin ilk dikkat çeken özellikleri.

Kapattığı boşluklar ve karşıladığı küçük ihtiyaçlarla elimin altında olsun her JSF projesinde kullanırım diyeceğiniz bir çalışma olmuş. Göz atmanızda fayda var.

22.03.2012

PrimeFaces ve Security Kullanımı

Web projelerinde güvenlik olmazsa olmaz özelliklerden biri. Bu özelliği basitçe web.xml içerisinden yapılan güvenlik ayarlarıyla sunucunuz yardımıyla sağlayabileceğiniz gibi Spring Security benzeri çözümler de sundukları seçeneklerle daha çok tercih edilir durumda.

Her ne kadar erişim ayarları yapılırken tüm içeriğin giriş yapmayan kullanıcı için kısıtlanması tercih edilen yöntem ise de başta "giriş sayfası" olmak üzere benzer sayfalara ve bunların kullandığı kaynaklara erişim izni verilmesi gerekir. Giriş sayfasında PrimeFaces bileşeni kullanıyorsanız sayfanızın erişmeye çalışacağı ve erişim izni olmadığı için de kullanamayacağı başlıca adres "/javax.faces.resource/*" olacaktır.

PrimeFaces ile yazılmış bu gibi sayfaların kullanıcı giriş yapmadan düzgün bir şekilde görüntülenmesi için yukardaki adresin güvenlik ayarlarınızda erişime açılmış olması gerekmektedir.

web.xml içerisinde örnek bir güvenlik kısıtlaması şu şekilde yapılmalıdır:

 <security-constraint>
  <display-name>excluded</display-name>
  <web-resource-collection>
   <web-resource-name>All Access</web-resource-name>
   <url-pattern>/javax.faces.resource/*</url-pattern>
  </web-resource-collection>
 </security-constraint>
 
 <security-constraint>
  <display-name>Default Security Constraint</display-name>
  <web-resource-collection>
   <web-resource-name>Default</web-resource-name>
   <url-pattern>/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
   <role-name>ROLE_ADMINISTRATOR</role-name>
  </auth-constraint>
 </security-constraint>

Aynı şekilde Spring Security kullandığınız bir projede erişim ayarları şu şekilde yapılabilir:

        <intercept-url pattern="/javax.faces.resource/**" filters="none" />
        <intercept-url pattern="/**" access="ROLE_ADMINISTRATOR" />

Erişim kısıtlarını kendi geliştirdiğiniz bir yöntemle ele alıyorsanız PrimeFaces sayfaları için bu hususu umutmamanızda fayda var..

21.03.2012

JSF'te ActionListener / Action Çağrılma Sırası

JSF'e yeni başlayan birinin gün olur aklına düşen bir sorudur: commandButton ile ilişkilendirdiğim actionListener'lar ve action hangi sırada çağırılır.

Öncelikle, hatırlatma niteliğinde;

- Bir command bileşenine birden fazla actionListener yazabiliyorken sadece bir action yazabileceğinizi
- action ile bağlanan metotta döndürdüğünüz String ile sayfa yönlendirme işlemi yapabileceğinizi (void ya da "return null;" olursa aynı sayfada kalır) fakat actionListener metotlarında bu özelliğin olmadığını
- actionListener metodunun "ActionEvent" parametresi alabileceğini, action'a bağlanan metota ise el-2.2.jar'dan itibaren Expression Language ile istediğiniz parametreyi geçebileceğinizi

belirteyim.

Çağırılma sırasını ise Yiğit Darçın bizim için hemencecik test etmiş ve blogunda yayınlamış. Test edip deneyimlemekten üşenenler faydalanabilir ;)

24.12.2011

equals() Metodu Nasıl Yazılmalı

Yeni mezun meslektaşlarımızın List, Map, Set türevi veri yapılarında nesneleri konumlandırdıkları zaman nesnelerin equals ve hashCode metotlarını hazırlamamaları yaygın bir unutkanlıktır. List bileşenine koydukları nesne nedense contains() kontrolünde kaybolur. Sorunun çözümünü de benzer olayları önceden yaşamış ve kaynağını az çok tahmin eden tecrübeli yazılım geliştiricen gelir. equals ve hashCode metotlarının sınıf için gerçekleştirimi unutulmuştur. List ve Set sınıfları barındırdıkları nesnelerin eşitliğini bu iki metodu kullanarak kontrol ederler

Ne var ki equals ve hashCode metotları tecrübeli geliştiriciler tarafından bile olması gerektiği gibi hazırlanmıyor. Bu yazımda "Effective Java" kitabında yer alan "Item 8: Obey the general contract when overriding equals" bölümüne referans göstererek ideal bir equals metodunun nasıl olması gerektiğine değineceğim.

İlk uyulması gereken kural basit. "En iyi kod yazılmamış kodtur" ilkesini benimsemiş gibi: Eğer sınıfın her bir nesnesi sadece kendisiyle eşit olacak ise, yani sadece aynı bellek alanına referans gösteren değişkenler birbirleriyle eşit olacak ise sınıfta equals metodu yazmayın. Her Java sınıfının türediği Object sınıfının equals metodu bu bellek kontrolünü üstlenmiş durumda zaten. Böyle bir durumu oluşturabilecek senaryolar ise şu şekilde listeleniyor:


  • Sınıfın değerleri üzerinden işlevsel bir eşitlik gerektirmiyorsa: Örneğin java.util.Random sınıfının görevi rasgele sayılar üretmektir. İki Random sınıfının birbirine eşit olup olmaması işlevsel bir eşitlik kontrolü gerektirmez.
  • Atasınıfın (superclass) ihtiyacı karşılayacak şekilde gerçekleştirilmiş halihazırda bir equals metodu varsa: Çoğu Set gerçekleştirimi AbstractSet, List gerçekleştirimleri ise AbstractList atasınıfının equals metodunu kullanır.
  • Eğer sınıf private ve package-private ise ve sınıflar üzerinde eşitlik kontrolü yapılmayacağı kesinse
  • Sınıfın sadece yaratılmış tek bir olgusu olacak ise: Singleton sınıflar
Özetle sınıfımızın eşitliği, barındırdığı değişkenlerin değerleri üzerinden anlam ifade ediyorsa ve atasınıfta bu değerler üzerinden eşitlik kontrolünü sağlayacak metot yoksa sınıfımız için bir equals metodu yazmalıyız.

Peki ideal bir equals nasıl olmalı? Matematik derslerinden hatırlayacağımız aşağıdaki 5 kriteri de karşılamalı:

  • Dönüşlü: null olmayan her x değeri için x.equals(x)'in sonucu true olmalı.
  • Simetrik: null olmayan her x, y değerleri için x.equals(y) sadece y.equals(x) sonucu true ise true olmalı.
  • Geçişli: null olmayan her x, y, z değerleri için x.equals(y) ve y.equals(z) sonuçları true ise x.equals(z) sonucu da true olmalı.
  • Tutarlı: eşitlik kontrolünde kullanılan değişkenlerin değerleri değişmediği sürece x.equals(y) sonucu her zaman ya hep true ya da hep false olmalı.
  • null olmayan her x için x.equals(null) sonucu hep false olmalı
Bu eğlenceli matematiksel kriterleri sağlayamayan her equals metodu uygulamamızda sıkıntı yaratacak ve hatanın kaynağı bulmak da belki uzunca bir süre mümkün olmayacaktır. Bu kriterlere biraz yakından bakalım:


Dönüşlü
Basitçe, her bir nesne kendisine eşit olmalı. Bu kriteri bozacak bir gerçekleştirimde, List'e eklediğimiz nesnemizi contains metoduyla sorguladığımızda karşılaşacağımız sonuç olumsuz olacaktır.

Simetrik
Simetri kriterini aşağıdaki örnek kod kırıyor. (Örnek kodlar Effective Java kitabından alınmıştır):

// Broken - violates symmetry!
   public final class CaseInsensitiveString {
       private final String s;
       public CaseInsensitiveString(String s) {
           if (s == null)
               throw new NullPointerException();
           this.s = s;
}
       // Broken - violates symmetry!
       @Override public boolean equals(Object o) {
           if (o instanceof CaseInsensitiveString)
               return s.equalsIgnoreCase(
                   ((CaseInsensitiveString) o).s);
           if (o instanceof String)  // One-way interoperability!
               return s.equalsIgnoreCase((String) o);
           return false;
       }
       ...  // Remainder omitted
   }

 
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");String s = "polish";

 
Burada cis.equals(s) kontrolü true dönerken s.equals(cis) kontrolü ise false değerini dönecektir. Problemin kaynağı CaseInsensitiveString sınıfının equals metodu, karşılaştırmayı büyük/küçük harf ayırtmaksızın yaparken, String sınıfının equals metodunun bu ayrımı yapmamasından kaynaklanmaktadır.

List list = new ArrayList();
list.add(cis);

Yukarıdaki gibi bir senaryoda yapılan list.contains(s) kontrolü kodun üzerinde çalıştığı Java gerçekleştirimine göre true ya da false dönebilir. "Benim bilgisayarımda çalışıyordu" dememek için hastalıklı kodu düzeltmeliyiz. Karşılaştırmada String sınıfı aradan çıkartılmalı:

@Override public boolean equals(Object o) {
       return o instanceof CaseInsensitiveString &&
           ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);



Geçişli
Bu kriteri kırmak da çok zor değil. Point sınıfından türeyen bir ColorPoint sınıfında koordinatlara ek olarak getirilen renk değişkeni equals metoduna eklenince olanlar oluyor:

public class Point {
       private final int x;
       private final int y;
       public Point(int x, int y) {
this.x = x;
this.y = y; }
       @Override public boolean equals(Object o) {
           if (!(o instanceof Point))
               return false;
           Point p = (Point)o;
           return p.x == x && p.y == y;
}
       ...  // Remainder omitted
}

public class ColorPoint extends Point {
       private final Color color;
       public ColorPoint(int x, int y, Color color) {
           super(x, y);
           this.color = color;
       }
       ...  // Remainder omitted
   }
// Broken - violates transitivity!
   @Override public boolean equals(Object o) {
       if (!(o instanceof Point))
           return false;
       // If o is a normal Point, do a color-blind comparison
       if (!(o instanceof ColorPoint))
           return o.equals(this);
       // o is a ColorPoint; do a full comparison
       return super.equals(o) && ((ColorPoint)o).color == color;
   }

  
ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
p1 ile p2, p2 ile p3 birbirlerine eşit iken p1 ile p3 birbirine eşit olmalıyken maalesef eşitlik kontrolü bize false değerini dönecektir. Java kütüphanesinde bu kriteri kıran sınıflar bulmak mümkün. Örneğin Date ve Timestamp sınıfları. Ayrıntılı değerlendirmeyi "Effective Java" kitabından edinebilirsiniz. 

Bu sorun için çözüm ise gene bir Object Oriented tasarım ilkesini adres gösteriyor. "is-a ilişkisi yerine has-a" ilişkisi kullanmak. Point sınıfından ColorPoint sınıfı türetmek yerine ColorPoint sınıfı içerisinde Point sınıfının referansını barındırsın.


Tutarlılık
Eşitlik kontrolü yapılacak değişkenler her zaman tutarlı değerlere sahip olmalıdır. Örneğin java.net.URL sınıfının eşitlik kontrolü IP adresi ile ilişkilendirilen host name değişkeni üzerinden yapılıyor. Bu yüzden ağ erişiminin yanı sıra host name ile ilişkilendirilmiş IP adresi de değişebilir. Sağlıklı bir karşılaştırma her zaman yapılamayacaktır.


Equals metodu için bu olmazsa olmazların yanında sorguyu hızlandıracak ve kesirli değerler için equals yerine Float.compare() ya da Double.compare() kullanmanız gerektiğini söyleyen bir çok minik ipucunu "Effective Java" kitabında bulabilirsiniz. Kitabın Java ile geliştirim yaparken dikkat edilmesini önerdiği diğer konular da her Java geliştiricisi tarafından sindirilip kod yazarken uygulanmalı. Kitaptan bir tane edinin derim :)



25.11.2011

JSF ve Spring Security ile Önceden Kimlik Kontrolü Yapılmış Kullanıcı için Login Sayfasının Tekrar Gösterilmemesi

Başlık biraz uzun oldu ama anlatacağım konuyu başlıkta ancak bu kadar özetleyebildim. Özetin açıklamasını yapmaya çalışırsak: web uygulamasına giriş yapmış, yani kimliğini doğrulatmış bir kullanıcı, belirli sayfalara istekte bulunduğunda (örneğin login sayfası için tekrar istek gönderirse) zaten yetkilendirildiğini belirleyip isteği başka bir sayfaya yönlendirmek (örneğin login sayfasından sonra gösterilecek index sayfasına) istiyoruz.

Spring Security'de bu işin kolay yolu var mıdır diye küçük bir araştırma yaptık ama ya doğru kelimeleri yine bulamadık ki blog başlığı seçerken de yaşadım aynı sıkıntıyı ( :) ) ya da diğerler geliştiriciler böyle bir gereksinime ihtiyaç duymamışlar. İlki seçenek akla daha yatkın..

Çalışma arkadaşım Basri Kahveci'nin önerdiği ve bir projede uyguladığı yöntem de aklıma yatmayınca (Çalışıyor ama biraz kulağı tersten tutmak gibiydi :)) Küçük bir "Nasıl yapabiliriz?" düşünmesinin ardından aslında basit bir JSF PhaseListener kullanarak yapılabileceğini farkettim.

Bildiğiniz üzere JSF yaşam döngüsü temel olarak 6 adımdan oluşur (Yaşam döngüsünü detayıyla incelemek isterseniz BalusC bu konuda emek harcamış):




Kısaca hatırlamak gerekirse Request parametresi barındıran bir istek bu 6 adımdan geçerek kullanıcıya hedef sayfa döndürülür. Bir sayfaya yapılan ilk istek, yani parametre barındırmayan bir istek ise ilk adımdan sonra yaşam döngüsünü dolaşmak yerine Render Response adımına yönlendirilip kullanıcının istediği sayfa gösterilir. Her adımda gerçekleşen işlemleri isterseniz başka bir blog girdisinde bahsederiz.

JSF bize bu adımların arasına girebilmek için PhaseListener interface'ini kullanıma sunuyor. Görüldüğü gibi PhaseListener'da 3 metot mevcut. İkisi yaşam döngüsü adımlarının herbirine girerken ve herbirinden çıkarken işletilecek kodları barındırırken, üçüncü metot da hangi adımda bu listener'ın çalıştırılması gerektiğini belirtmemize olanak sağlıyor. Bu metotta PhaseId.ANY_PHASE gibi bir değer döndürerek her bir adım için listenerın tetiklenmesini sağlayabileceğimiz gibi PhaseId.RENDER_RESPONSE benzeri bir değerle belirli bir adımda tetiklenmesini de sağlayabiliyoruz.

Bizim senaryomuzda da login sayfamız render olmadan önce bir PhaseListenerın tetiklenmesini sağlayarak, kullanıcının önceden sisteme giriş yapıp yapmadığını belirleyip gerekiyorsa başka bir sayfaya yönlendirme işlemini yapmak mümkün:

public class AuthPhaseListener implements PhaseListener {

 @Override
 public void afterPhase(PhaseEvent event) {
 }

 @Override
 public void beforePhase(PhaseEvent event) {
  User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  if(user != null){
   FacesContext.getCurrentInstance().getExternalContext().redirect("main");
  }

 }

 @Override
 public PhaseId getPhaseId() {
  return PhaseId.RENDER_RESPONSE;
 }

}

beforePhase() metodu içinde Spring Security'nin SecurityContextHolder'ı ile sessionda kayıtlı bir kullanıcı  olup olmadığını kontrol ettik. Eğer bir kullanıcı nesnesi elde edebiliyorsak FacesContext ile de basitçe yönlendirme işlemini yapıyoruz.

Geriye yazdığımız bu listener sınıfını projemize entegre etmeye geldi. Eğer listener tanımını faces-config.xml içerisinde yaparsak her sayfa için işletilecek yaşam döngüsünde sınıfımız tetiklenecektir. Bizim istediğimiz sadece login sayfasında bunu tetiklemek. O yüzden tanımı listenerı kullanmak istediğimiz sayfa bazında yapmamız gerekli. JSF sayfamıza şu tag'i eklemek yeterli:

<f:phaseListener type="com.prime.dasgin.AuthPhaseListener"/>


JSF kullanılmayan projelerde benzer bir mantık web.xml'de tanımlanan Servlet Filter'lar ile de gerçekleştirmek mümkündür. Login sayfasına yapılacak olan bir istekte devreye girecek bir Filter tanımlamak ve benzer bir süreci içinde işletmek sorunu çözer.



16.10.2011

MySQL'in String karşılaştırması

Var olan bir projeye sonradan katıldıysanız ya da proje ekibinizde veritabanı işlerini yöneten bir takım arkadaşınız varsa MySQL'in string karşılaştırmasının nasıl çalıştığı ile ilgili bilginiz olmayabilir. Ve vakit gelip de yeni bir projenin veritabanını oluşturma ihtiyacı duyduğunuzda ve iş başa düştüyse MySQL karakter encoding ve string karşılaştırmayla ilgili anlatacağım bu konu size zaman kazandıracaktır.

MySQL'de nonbinary string tipleri (char, varchar, text) büyük küçük harf ayırmaksızın karşılaştırılır. Örneğin username like 'admin' gibi bir sorgu koşulunda dönen sonuçlar içinde username'i admin, ADmin, ADMIN, vb. olan kayıtlar da getirilecektir.

Böyle bir sonucu engellemek için ya veritabanı oluşturulurken schema tanımının collation tipini ya da sorunu yaşadığınız kolonun collation tipini binary olacak şekilde ayarlamanız gerekiyor:

CREATE DATABASE mobileme CHARACTER SET utf8 COLLATE utf8_bin;

ya da

ALTER TABLE table_name MODIFY username VARCHAR(25) CHARACTER SET utf8 COLLATE utf8_bin;


Varolan schema'da böyle bir sıkıntınız varsa tüm schema'nın collate tipini değiştirmek için aşağıdaki sorgunun ürettiği sorgucukları işletmeniz yeterli:

SELECT CONCAT('ALTER TABLE `', t.`TABLE_SCHEMA`, '`.`', t.`TABLE_NAME`, '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;') as stmt
FROM `information_schema`.`TABLES` t
WHERE 1
AND t.`TABLE_SCHEMA` = 'schema_name'
ORDER BY 1;

25.04.2011

Türkçe Maven Rehberi

"Kahvenin Hatırı" adlı blogta Sonatype'ın Maven belgelerinin Türkçe'ye çevrilmiş hallerine ulaşabilirsiniz. Son blog girdilerinde Maven ile ilgili örnekler de mevcut. Emeği geçen arkadaşa teşekkür ederiz:

http://kahveninhatiri.blogspot.com/2011/04/turkce-maven-rehberi.html