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

--Potter Stewart

14.11.2008

En Basit Tasarım Örüntüsü Singleton (mu?)

Singleton örüntüsü bir sınıfın sadece tek bir nesnesi olması gerektiği durumlarda kullanılır ve sınıfın ihtiyaç duyulduğu zaman yaratıldığı en bilindik kullanım şekli şu şekildedir:

public class DateUtils {


private static DateUtils instance = null;


private DateUtils() {}


public static DateUtils getInstance() {


if(instance == null)


instance = new DateUtils();


return instance;


}

...

}


Böyle bir kullanım çoklu işletim dizilerinde (multithreads) sorunlara neden olabilmektedir. Aynı anda iki farklı işletim dizisinin instance niteliğini kontrol etmesi ve null görmesi sonucu iki farklı olgunun yaratılma olasılığı vardır. Bunun önüne iki şekilde geçilebilir. İlki sınıfın static niteliğinin uygulama ayağa kalkarken yaratılması:


public class DateUtils {


private static DateUtils instance = new DateUtils();


private DateUtils() {}


public static DateUtils getInstance() {

return instance;

}

...

}

İkincisi ise getInstance() yönteminin synchronize hale getirilmesi. Tüm yöntemin senkron hale getirilmesi, sadece nesnenin null durumunda kontrol edilmesi gereken, nesne yaratıldıktan sonra da performansa olumsuz etki yapan bir durum olduğu için kullanımından kaçınılmaktadır. Bunun yerine instance niteliği null ise senkronu başlatan bir yöntem tercih edilmiştir:


public class DateUtils {


private static DateUtils instance;


private
DateUtils() {}

public static DateUtils getInstance () {


if(instance == null) {


synchronized(DateUtils.class)
{


if(instance == null)


instance = new DateUtils();


}


}


return instance;


}

}

Ne var ki “Double-Checked Locking” olarak bilinen bu yöntem JDK 5’ten önce çalışmıyor. Ayrıntılı bilgiye “The Double-Checked Locking is Broken Declaration” bağlantısından ulaşabilirsiniz. JDK 5 ile gelen volatile bu
sıkıntıya derman oluyor:


public class DateUtils {


private volatile static DateUtils instance;


private DateUtils() {}


public static DateUtils getInstance () {


if (instance == null) {


synchronized(DateUtils.class)
{


if (instance == null)


instance = new DateUtils();


}


}


return instance;


}

}

Singleton örüntüsünde dikkat edilmesi gereken bir başka husus ise sınıf Clonable arayüzünü gerçekleştiren bir başka sınıftan türetilmişse ortaya çıkıyor. Singleton sınıfımızın clone() yöntemi ile çoğaltılmasının önüne geçmek istiyorsak bu metodun üstüne yazmamız gerekiyor:

public Object clone() throws CloneNotSupportedException {


throw new CloneNotSupportedException();

}

13.11.2008

Hibernate Data Filtering (Süzme)

Verilerin tamamını değil de belirli kriterlerle süzülüp işimize yarayacakları veritabanından sorgulamak günlük hayatta çokça karşılaştığımız bir senaryo. Örneğin “Abone” nesnesinin belirli tarihten önceki ödenmemiş faturalarına erişmek istediğimiz zaman abone.getFaturalar()
şeklinde bir erişim, eğer bir süzme uygulanmazsa abonenin tüm faturalarını getireceği için Java kodlarıyla süzme işlemini yapmamızı gerektirecektir. Buna benzer ya da veri güvenliği (kullanıcıya sadece görmesi gereken verilerin gösterilmesi) gibi senaryolarda Hibernate Data Filtering, Java kodlarında gerçekleştirmemiz gereken süzme maliyetinden kurtulmamıza olanak sağlıyor.


Öncelikle süzme işleminde kullanacağımız süzgeci, biricik ismiyle tanımlamamız gerekiyor:

@org.hibernate.annotations.FilterDef(name="faturaOdemeFilter",


parameters={
@ParamDef(name="donem",type="integer"),
@ParamDef(name="kalanTutar",type="double"),
@ParamDef(name="durumConditions",type="long")

})


Tanımladığımız süzgecin ismi faturaOdemeFilter ve dinamik süzme işlemi yapabilmemiz için üç tane parametre alıyor. Parametrelerin kurulma işlemine birazdan değineceğiz. Bu tanım global nitelikli olduğu için herhangi bir sınıfta ya da package-info.java içerisinde yazılabilir. Yazıldığı sınıfın davranışında bir etkisi yoktur fakat uygun olan tanımın ilgili sınıfta yazılmasıdır. Süzgeç tanımımız olgusunu da süzme işleminin yapılacağı sınıfta ya da ilişkide yazılması gerekmektedir:

@Entity
@Table(name="FATURA")

@Filter(name="faturaOdemeFilter", condition="ODEME_DONEMI <= :donem and FATURA_DURUM in (:durumConditions) and KALAN_TUTAR >:kalanTutar")

public class Fatura {...}


Birden fazla süzgeç @org.hibernate.annotations.Filters ile gruplanabilir. Yukarıdaki gibi sınıf üzerine yazılmış süzgeç olgusu


List<Fatura> filteredItems = session.createQuery("from Fatura").list();


List<Fatura> filteredItems = session.createCriteria(Fatura.class).list();


Deyimlerinde çalışacaktır. Id ile sorgulamalarda (id'yi biliyorsak süzmeye de gerek yok) ve nesne niteliklerinin sorgulanmasında sınıf üzerine yapılan tanım çalışmayacaktır. abone.getFaturalar()deyimi gibi abonenin ilişkili olduğu faturaların süzülmesi için süzgeç olgusunun ilişki üzerinde tanımlanması gerekmektedir:


@Entity
@Table(name = "ABONE")


public class Abone {


@OneToMany(mappedBy="abone")
@Filter(name="faturaOdemeFilter", condition="ODEME_DONEMI <= :donem and FATURA_DURUM in (:durumConditions) and KALAN_TUTAR > :kalanTutar")


private Set<Fatura> faturalar = new HashSet<Fatura>();


...

}


Süzgeç tanımının kullanılması için Hibernate Session’ında süzgeç aktif hale getirilerek varsa aldığı parametreler kurulmalıdır. Veriler çekildikten sonra da süzgeç tekrar pasif hale getirilebilinir. (JPA API’sinde EntityManager filtrelemeye destek sağlamadığı için Hibernate arayüzü kullanılmalıdır.)


List<Long> durumConditions = new ArrayList<Long>();

durumConditions.add(FaturaDurumEnum.ODENMEMIS.getValue());
durumConditions.add(FaturaDurumEnum.
PARCALI_ODENIYOR.getValue());


Filter filter = session.enableFilter("faturaNormalOdemeFilter");


filter.setParameter("donem",donem).setParameter("kalanTutar",new Double(0)).setParameterList("durumConditions", durumConditions);


Set<Fatura> faturalar = abone.getFaturalar();


session.disableFilter(("faturaNormalOdemeFilter");



23.10.2008

Char Tipi ve Hibernate

Bir veritabanına erişirken Char tipinde tanımlanmış bir kolona
rastladım. Birkaç ufak takla atma, alan nesnesinde kolona karşılık
gelen niteliği String tanımalama gibi girişimlerim sonuç vermeyince
Google başvurmak kaçınılmaz oldu. Hibernate'in sitesinde yer alan bir sayfada
UserType arayüzünden gerçekleştirilmiş bir sınıfın bu işi çözeceği
belirtilmiş. Sınıfı aşağıdaki annotationlar ile denediğimde artık Char
tipine sorunsuz ulaşabildim:

(Burada Char.class, UserType’tan gerçekleştirilmiş sınıfımız)

@Entity
@Table(name="ABONE_DURUM")
@TypeDef(name = "char", typeClass = Char.class)
public class AboneDurum{

@Id

@Column(name="ABONE_DURUM_KODU")
private Long id;

@Column(name="ACIKLAMA")
private String aciklama;

@Type(type="char")
@Column
(name="KISA_ACIKLAMA", length=3)
private String kisaAciklama;

...



13.08.2008

POJO vs. EJB 3.0

POJO ve EJB, Java EE geliştiriminde tercih edilen iki ayrı programlama modeli. Bu iki teknoloji, EJB 3.0'ın öğrenilmesi zor ve karmışık programlama modelini terkederek POJO tabanlı bir model seçmesiyle aynı eksende ilerleyecek gibi duruyor. Ama hala göz ardı edilemeyecek farklılıklar mevcut. POJO programlama modelini temel alan Spring çatısını kendine örnek alan EJB ile POJO'yu kıyaslarken elde ettiğim sonuçları özetlemeye çalıştım:


POJO

EJB 3

Basitlik

Mimarinin geliştiriminde göz önünde tutulan nesne yönelimli ilkeler sayesinde öğrenilmesi ve kullanılması kolay bir yapıya sahip. Basitlik ve tutarlılık ön planda.

Getirdiği mimari artılarına karşın EJB 1.0 sürümünden bu yana değişen programlama modeli ve öğrenim maliyeti projeler için dezavantaj oluşturuyor.

Test Edilebilirlik

POJO yapısı sayesinde Bağımlılık aktarımı (Dependency Injection) örüntüsü kullanılarak bileşenlerin kolay ve hızlı test edilebilirliğine imkan sağlıyor. Testler containerdan bağımsız yapılabiliyor.

Bağımlılık aktarımının sadece JNDI nesneleri ile yapılabilmesi, Session ve message-driven beanlerinin POJO olmalarına rağmen EJB Container’a ihtiyaç duymaları sebebiyle test edilebilirlik kısıtlanıyor. Bu da geliştirim sürecini olumsuz etkiliyor.

Geliştirim Ortamı

Tomcat gibi hafif (lightweight) containerlarla geliştirilen web uygulaması hızlıca çalıştırılabiliyor.

Geliştirilen uygulamanın çalıştırılabilmesi için ağır uygulama sunucularına ihtiyaç duyuyor. Projenin ihtiyaç duymadığı özellikler de uygulama sunucusu ile ayağa kaldırılıyor.

Collections Desteği

Hibernate çatısı ile persistence katmanında Set<String>, Set<Integer> gibi entity beanlerden oluşmayan collectionlara doğrudan destek sağlıyor.

Persistence katmanında sadece entity beanleri için collection desteği sağlıyor. Set<String>, Set<Integer> gibi collectionlara doğrudan destek sağlamıyor.

Konfigürasyon

Spring Bean configuration dosyalarına ihtiyaç duyuyor. Annotation desteği sunuyor.

Spring Bean configuration dosyalarından daha karmaşık bir yapıya sahip EJB descriptor dosyalarına ihtiyaç duyuyor. Annotation desteği sunuyor.

Persistence API

Kendine ait bir persistence api olmamasına karşın Template örüntüsü ile desteklenen; Hibernate, Ibatis, JPA gibi popüler çatılarla ve JDBC ile kolayca entegrasyona izin veren bir mimari sağlıyor.

EJB 3 ile Entity beanlerin yerini Hibernate, Toplink, JDO gibi persistence API’lerden devşirilen JPA belirtimi alıyor. Belirtim olması sebebiyle belirtimin yetersiz kaldığı yerlerde geliştirim sağlayıcısına bağımlı kalınabilmektedir.

Transactions

Dağıtık transaction mimarisini JTA ile destekliyor. Spring AOP çatısı ile annotationlar kullanılarak 7 farklı Propagation-bazlı transaction tanımlanabiliyor. Transactionlar XML konfigürasyon dosyalarında ya da aspect yaklaşım kullanılarak tanımlanıyor. Spring JDBX, Hibernate ve JTA ile entegre olabiliyor.

Dağıtık transaction mimari geliştirimine olanak sağlıyor. Session beanlerin tüm public metodlarına transaction mantığı otomatik olarak uygulanıyor. Annotation tabanlı bir transaction yönetimi bulunuyor. Sadece JTA’yı destekliyor. JTA Tomcat gibi hafif containerlarda çalışmıyor.

Güvenlik

ACEGI güvenlik çatısıyla hem nesne method çağrımı hem de HTTP istemlerinde doğrulama ve yetkilendirme yapılabiliyor. Rol-bazlının yanı sıra ACL-bazlı yetkilendirme de sağlıyor. Uygulama sunucusundan bağımsız standart bir güvenlik katmanı sağlıyor.

Tanımlı method-düzeyi rol-bazlı yetkilendirme, programatik rol belirleme özelliklerine sahip. EJB belirtiminin yeterli olmaması sebebiyle, güvenlik gerçekleştirimini uygulama sunucusuna bırakıyor. Böylece güvenlik katmanı uygulama sunucuna bağımlı hale geliyor.
















































Summation of Persistence Comparison Between Spring and EJB 3.0.

Feature

Spring

EJB 3.0

Simple ORM Persistence

Implementation

Hibernate, JPA, JDO, TopLink, iBatis

JPA (providers include Hibernate, Kodo and Toplink)

JDBC Support

--

Mapping

XML, Annotations

Annotations, XML

Cache Propagation

Thread local

Transaction

Extended cache scoping

Open session in view

Extended persistence context


Equivalent Transaction Management Functionality in Both Spring and EJB 3.0.

Feature

Spring

EJB 3.0

Declarative Transactions

Programmatic Transactions

Demarcation

AOP

Session bean methods

Supported Transaction Types

JDBC, Hibernate, JTA

JTA

Support for Distributed Transactions

(With JTA)

Configuration

XML

Transactional by default, override with annotations or XML


Teknolojilerin Google Trend’ten elde edilen yıllara göre arama sonuçlarının karşılaştırma tabloları:

ejb

Frame2

1.00

pojo

Frame3

0.16



ejb

Frame4

1.00


hibernate

Frame5

1.84

jpa

Frame6

0.24



Sonuç olarak Java EE uygulamalarda EJB'nin tercih edilmesini gerektirecek tek durum dağıtık transaction yönetimi ihtiyacı. POJO (dolayısıyla Spring) bu ihtiyaca karşılık veremiyor. Bunun dışındaki senaryolarda değişen trendler de göz önüne alınarak POJO programlama modelini temel alan çözümlerin (Spring, Hibernate, vs.) (EJB 3.0 gibi kopyalarının yerine :) ) seçilmemesinde hiç bir neden yok.

Kaynaklar

11.07.2008

Spring 2.5.3 ve Apache CXF ile Anlaşma-Sonra(Contract-Last) Yaklaşımlı Web Service Geliştirme


Bir önceki girdide Spring Web Service kullanarak anlaşma-önce web service geliştirme yaklaşımından ve bu yaklaşımının artılarından bahsetmiştik. WSDL belgesinin değişmesinin istemci ve sunucu kodlarını etkileyeceği için önce WSDL belgesinin oluşturulması gerektiğine değinmiştik. Bunun yanında anlaşma-önce yaklaşımın kullanılması geliştiriciyi Web Service geliştiriminin ayrıntılarından uzak tutacağı için küçük çaplı projelerde avantaj sağlayabilir. Bu girdide de Apache CXF kullanarak anlaşma-önce yaklaşımla basit bir web service geliştireceğiz.

Apache CXF’nin kullanacağı kütüphaneleri indirdikten sonra projemiz için gerekli olanları .zip kütüğünün lib dizininde yer alan WHICH_JARS kütüğünde bulabilirsiniz. Temel kullanım için gerekli olan kütüphanelerin listesi şu şekildedir:

- cxf.jar

- commons-logging.jar

- geronimo-activation.jar (Or the Sun equivalent) *

- geronimo-annotation.jar (Or the Sun equivalent) *

- geronimo-javamail.jar (Or the Sun equivalent) *

- geronimo-stax-api.jar (Or the Sun equivalent) *

- neethi.jar

- jaxb-api.jar *

- jaxb-impl.jar

- stax-utils.jar

- XmlSchema.jar

- wstx-asl.jar *

- wsdl4j.jar

- xml-resolver.jar

* Bu kütüphaneler Java 6 Update 4’den sonra JDK içinde mevcuttur.

Ayrıca Java sınıflarımızda Annotation kullanacağımız için Aegis ve JAX-WS kütüphalerine de ihtiyacımız var:

- jaxen.jar

- jdom.jar

- stax-utils.jar

- geronimo-ws-metadata.jar [6]

- jaxws-api.jar [6]

- saaj-api.jar [6]

- saaj-impl.jar [6]

- asm.jar

Örnek servis olarak kişi listesi döndüren bir servis yazalım:

package com.blogspot.mdasgin.examples.model;

import org.apache.cxf.aegis.type.java5.IgnoreProperty;

public class Kisi {

private String isim;

private String soyisim;

private Integer kimlikNo;

public Kisi() {

}

public Kisi(String isim, String soyisim, Integer kimlikNo) {

this.isim = isim;

this.soyisim = soyisim;

this.kimlikNo = kimlikNo;

}

public String getIsim() {

return isim;

}

public void setIsim(String isim) {

this.isim = isim;

}

public String getSoyisim() {

return soyisim;

}

public void setSoyisim(String soyisim) {

this.soyisim = soyisim;

}

/*

* @IgnoreProperty tells Aegis that I don't want getTamIsim() to show up in the auto-generated WSDL.

* It's just a read-only convenience method.

*/

@IgnoreProperty

public String getTamIsim() {

return soyisim + ", " + isim;

}

public Integer getKimlikNo() {

return kimlikNo;

}

public void setTamIsim(Integer kimlikNo) {

this.kimlikNo = kimlikNo;

}

}

@IgnoreProperty annotation’ı sayesinde Aegis, otomatik üretilen WSDL belgesinde bu niteliği göz ardı edecek. Web service arayüzü ve geliştirimi de şu şekilde olacaktır:

package com.blogspot.mdasgin.examples.contractlast.services;

import java.util.List;

import javax.jws.WebParam;

import javax.jws.WebService;

import com.blogspot.mdasgin.examples.model.Kisi;

/*

* @WebService annotation bu arayuzun bir web service arayuzu oldugunu belirtir.

*/

@WebService

public interface KisiService {

List<Kisi> getKisiList();

/*

* @WebParam annotation allows us to use an HTTP parameter called "message" to reference the operation's argument

* instead of having to call it "arg0", which is the default.

*/

void addKisi(@WebParam(name = "kisi")Kisi kisi);

}

---

package com.blogspot.mdasgin.examples.contractlast.services.impl;

import java.util.ArrayList;

import java.util.List;

import javax.jws.WebService;

import com.blogspot.mdasgin.examples.contractlast.services.KisiService;

import com.blogspot.mdasgin.examples.model.Kisi;

/*

* @WebService annotation bu sinifi bir web service geliştirimi olarak isaretler. Ayrica kullandigi arayuzu de belirtir.

*/

@WebService(endpointInterface="com.blogspot.mdasgin.examples.contractlast.services.KisiService")

public class KisiServiceImpl implements KisiService {

public List<Kisi> getKisiList() {

List<Kisi> kisiList = new ArrayList<Kisi>();

kisiList.add(new Kisi("Mustafa", "Dasgin", "11111111111"));

kisiList.add(new Kisi("Huzur", "Oncu", "22222222222"));

return kisiList;

}

public void addKisi(Kisi kisi) {

System.out.println(kisi.getTamIsim());

}

}

Bu aşamadan sonra web.xml’de gelen web service istemlerinin yönlendirilebilmesi için gerekli tanımları eklemeliyiz:

<servlet>

<servlet-name>CXFServlet</servlet-name>

<servlet-class>

org.apache.cxf.transport.servlet.CXFServlet

</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>CXFServlet</servlet-name>

<url-pattern>/webservices/*</url-pattern>

</servlet-mapping>

Tanımdan da anlaşılacağı gibi sunucumuza “/webservices/*” örüntüsünde gelen istemler CXFServlet’e yönlendirilecektir. Spring bean tanımlarımızı da yapalım:

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:cxf="http://cxf.apache.org/core"

xmlns:jaxws="http://cxf.apache.org/jaxws"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://cxf.apache.org/core

http://cxf.apache.org/schemas/core.xsd

http://cxf.apache.org/jaxws

http://cxf.apache.org/schemas/jaxws.xsd">

<import resource="classpath:META-INF/cxf/cxf.xml" />

<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<!-- CXF'nin loglama ozelligi kullanilabilir fakat performans soz konusu ise kullanilmamasi onerilir.

-->

<cxf:bus>

<cxf:features>

<cxf:logging />

</cxf:features>

</cxf:bus>

<!-- The service bean -->

<bean id="kisiService"

class="com.blogspot.mdasgin.examples.contractlast.services.impl.KisiServiceImpl" />

<!-- Aegis data binding -->

<bean id="aegisBean"

class="org.apache.cxf.aegis.databinding.AegisDatabinding"

scope="prototype" />

<bean id="jaxws-and-aegis-service-factory"

class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean"

scope="prototype">

<property name="dataBinding" ref="aegisBean" />

<property name="serviceConfigurations">

<list>

<bean

class="org.apache.cxf.jaxws.support.JaxWsServiceConfiguration" />

<bean

class="org.apache.cxf.aegis.databinding.AegisServiceConfiguration" />

<bean

class="org.apache.cxf.service.factory.DefaultServiceConfiguration" />

</list>

</property>

</bean>

<!-- Service endpoint -->

<!-- See http://incubator.apache.org/cxf/faq.html regarding CXF + Spring AOP -->

<jaxws:endpoint id="kisiServiceEndPoint"

implementorClass="com.blogspot.mdasgin.examples.contractlast.services.impl.KisiServiceImpl"

implementor="#kisiService" address="/kisiws">

<jaxws:serviceFactory>

<ref bean="jaxws-and-aegis-service-factory" />

</jaxws:serviceFactory>

</jaxws:endpoint>

</bean>

Sunucu tarafımız hazır. Aşağıdaki istemlerle deneme yapabilirsiniz:

  • http://localhost:8080/denemeWSProje/webservices (Hizmet veren web servis listesi)

  • http://localhost:8080/denemeWSProje /webservices/kisiws?wsdl (Üretilen wsdl belgesi)

  • http://localhost:8080/denemeWSProje /webservices/kisiws/getKisiList

  • http://localhost:8080/denemeWSProje /webservices/kisiws/addKisi?kisi=...

Sunucuya bağlanacak istemci tarafında tanımlanması gereken spring bean’leri de şu şekilde olmalıdır (Sunucu tarafında tanımlanan aegisBean ve jaxws-and-aegis-service-factory beanleri de istemci tarafında tanımlanmalıdır):

<bean id="client"

class="com.blospot.mdasgin.examples.contractlast.services.KisiService"

factory-bean="clientFactory" factory-method="create" />

<bean id="clientFactory"

class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">

<property name="serviceClass"

value=" com.blogspot.mdasgin.contractlast.services.KisiService" />

<property name="address"

value="http://localhost:8080/denemeWSProje/webservices/kisiws" />

<property name="serviceFactory"

ref="jaxws-and-aegis-service-factory" />

</bean>

Kaynaklar: