Kategori arşivi: Java

Java 9 ile Modül Bazlı Yazılım

Bir yazılım sisteminde karmaşaya, bağımlılıklara ve kodun bakım ve geliştirilmesi sürecine hakim olabilmenin bir yolu da komponent ya da modül bazlı yazılım yapmaktan geçmektedir. İdeal şartlarda bir modül tek bir görevi yerine getirir ve tek sorumluluk prensibi göz önünde bulundurularak implemente edilmiştir. Modül iç dünyasını gizli tutar ve kullanımını modül API (application programming interface) olarak isimlendirilen tanımlı giriş, çıkış kanalları ya da başka bir deyişle kullanım arayüzü aracılığı ile sağlar. Kullanım arayüzleri modülün hangi işlemleri gerçekleştirdiğini soyut olarak tanımlarken, bu işlemlerin nasıl gerçekleştirildikleri modül içinde yer alan implementasyonlarda yer almaktadırlar. Kısaca bir modül kullanıcısı için bir kara kutudur. Bu şekilde kullanıcısını etkilemeden, iç implementasyonu değiştirmek mümkündür, çünkü kullanıcı iç implementasyona değil, kullanıcı arayüzüne bağımlıdır. Kullanıcı arayüzleri sahip oldukları yapıyı koruyabildikleri sürece, modül üzerinde yapılan değişiklikler kullanıcıyı etkilemez. Bu şekilde tanımlı kullanıcı arayüzleri aracılığıyla esnek olarak birbirine bağlı olan uygulama parçaları geliştirmek ve bu parçalar üzerinde uygulamanın genelini etkilemeden gerekli değişiklikleri yapmak mümkündür.

Java 9 öncesinde modül bazlı yazılımın nasıl yapıldığını ya da benim bu konuda nasıl çalıştığımı ve karşılaştığım sorunları bir örnek üzerinde göstermek istiyorum. Vereceğim örnekte loglama işlemini gerçekleşiren bir modül yer almaktadır. Bu modülün kullanım arayüzü (API) Logger.java isimli sınıfta tanımlıdır.

[source language=”java”]

package com.kurumsaljava.logger;

public interface Logger {

void log(LogLevel level, String log);

}
[/source]

Logger bir interface sınıftır ve modülün yaptığı işlemi tek bir soyut metot olarak ihtiva etmektedir. Bu yapı bize genel olarak modülün hangi tür bir işlemden sorumlu olduğunu tanımlama imkanı sağlamaktadır. Bunu yaparken herhangi bir implementasyon detayı kullanma zorunluluğumuz bulunmamaktadır. Bu şekilde tamamen soyut bir tanımlama yapmış olmaktayız. Oluşturduğunuz API ne sorusuna cevap verirken, nasıl sorunun cevabını iç implementasyona bırakmaktadır.

Loglama işleminin nasıl gercekleştirildiği bir implementasyon detayıdır ve modül dışında yer alanları ilgilendirmez/ilgilendirmemeldir. Modülün ana görevlerinden birisi, bu implementasyon detayını gizlemek olmalıdır. Bu ona implementasyon üzerinde istediği şekilde değişiklik yapma imkanını sunarken, modül kullanıcılarının bu değişikliklerden doğrudan etkilenmelerinin önüne geçen bir unsur olacaktır.

Modülün sorumlu olduğu işlemi su şekilde implemente edebiliriz:

[source language=”java”]
package com.kurumsaljava.logger;

import com.kurumsaljava.logger.LogLevel;
import com.kurumsaljava.logger.Logger;

class StdoutLoggerImpl implements Logger {

@Override
public void log(LogLevel level, String log) {
System.out.println(level.name() + " – " + log);
}
}
[/source]

Burada dikkatinizi sınıfın tanımlandığı satıra çekmek istiyorum. Sınıfı public olarak tanımlamadım, çünkü modül dışındaki dünyanın bu sınıfa erişimini engellemek istiyorum. Modülün kara bir kutu olabilmesi için bu gerekli bir adım. Bana kalmış olsa bu sınıfı private olarak tanımlardım. Ama bu ne yazık ki mümkün değil, çünkü bir interface sınıfın tüm metotları ya public ya da package private olmak zorundadır yani implementasyon sınıfı private kullanarak metotların görünürlüğünü daha da daraltamaz. Bu yüzden yukarıda yer alan implementasyon sınıfını en az package private olarak tanımlayabilmekteyim. Bu StdoutLoggerImpl sınıfının sadece aynı paket içinde yer alan sınıflar tarafından erişilebilir olduğu anlamına gelmektedir. Ama reflection kullanarak, bu sorunu aşabiliriz yani sadece paket içinde kullanılabilir olan bir sınıfın reflection aracılığı ile başka bir paket içinde yer alan bir sınıf tarafından kullanılabilir olmasını sağlayabiliriz.

[source language=”java”]
package com.kurumsaljava.application;

import java.lang.reflect.Constructor;

import com.kurumsaljava.logger.LogLevel;
import com.kurumsaljava.logger.Logger;

public class Main {

public static void main(String[] args) throws Exception {

Class<Logger> c = (Class<Logger>)Class.forName("com.kurumsaljava.logger.StdoutLoggerImpl");

Constructor<?> constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
Logger logger = (Logger)constructor.newInstance();
logger.log(LogLevel.DEBUG, "Hello World");
}
}
[/source]

Görüldüğü gibi başka bir paket içinde yer alan Main sınıfı aslında modülün iç dünyasının bir parçası olan ve dış dünyanın görmemesi gereken bir implementasyon sınıfını kullanabilmektedir. Buna ne derleyici ne de yorumlayıcı engel olabilmiştir. Bu durum modüler bir yapı oluşturmamıza engel olmakta ve modüllerin birer kara kutu olmaları gerektiği prensibini çiğmemektedir. Modül bir kara kutu olduğunu düşünerek hareket ettiğinde, örneğin StdoutLoggerImpl yerine başka bir sınıf oluşturulduğunda, Main sınıfı çalışmaz hale gelecektir. Oysaki bizim istediğimiz modül kullanım yapısı şu şekilde olmalıdır:

[source language=”java”]
package com.kurumsaljava.application;

import com.kurumsaljava.logger.LogLevel;
import com.kurumsaljava.logger.LoggerFactory;

public class Main {

public static void main(String[] args) throws Exception {

LoggerFactory.instance().log(LogLevel.DEBUG, "Hello World");
}
}
[/source]

Main sınıfı logger modülene ve sunduğu hizmete erişebilmek için LoggerFactory sınıfını kullanmaktadır. LoggerFactory şu yapıya sahiptir ve iç implementasyonu API aracılığı ile kullanılabilir hale getirmektedir:

[source language=”java”]
package com.kurumsaljava.logger;

public class LoggerFactory {

public static Logger instance() {
return new StdoutLoggerImpl();
}
}
[/source]

LoggerFactory sınıfı StdoutLoggerImpl sınıfı ile aynı paket içinde bulundugundan, LoggerFactory bu sınıfı kullabilmektedir. İç implementasyonu kullanılabilir hale getirmek için API’nin bir parçası olan böyle bir sınıfı oluşturmamız gerekmektektedir. Bu durumda modülün kullanım arayüzünü oluşturan sınıflar Logger, LogLevel ve LoggerFactory sınıflarıdır yani modül kullanıcıları modülün sunduğu hizmeti sadece bu üç sınıf aracılığı ile kullanabilirler. Ama reflection örneğinde gördüğümüz gibi iç implementasyona erişmek her zaman mümkündür yani Java dilinde gerçek modüller oluşturmak aslında mümkün değildir.

StdoutLoggerImpl sınıfı gizleme imkanımız gerçekten yok mu? Bir de şöyle denesek:

[source language=”java”]
package com.kurumsaljava.logger;

public class LoggerFactory {

public static Logger instance() {
return new StdoutLoggerImpl();
}

private static class StdoutLoggerImpl implements Logger {

@Override
public void log(LogLevel level, String log) {
System.out.println(level.name() + " – " + log);
}
}
}
[/source]

Java’da bir sınıf bünyesinde başka bir sınıf tanımlamak (inner class) ve sınıfı private olarak deklare etmek mümkün. Modülü yine şu şekilde kullanabiliriz:

[source language=”java”]
package com.kurumsaljava.application;

import com.kurumsaljava.logger.LogLevel;
import com.kurumsaljava.logger.LoggerFactory;

public class Main {

public static void main(String[] args) throws Exception {

LoggerFactory.instance().log(LogLevel.DEBUG, "Hello World");
}
}
[/source]

Peki reflection? Ne yazık ki bu şekilde bile reflection ile iç sınıflara erişim mümkündür:

[source language=”java”]
public static void main(String[] args) throws Exception {

Class<Logger> c = (Class<Logger>)Class.forName("com.kurumsaljava.logger.LoggerFactory$StdoutLoggerImpl");

Constructor<?> constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
Logger logger = (Logger)constructor.newInstance();
logger.log(LogLevel.DEBUG, "Hello World");
}
[/source]

Sonuç? Java 9 öncesi dilin sahip oldugu özellikleri kullanarak kara kutu gibi çalışan modüller oluşturmak mümkün değil. Biz ne kadar paket bazında sınıf erişimini engellemeye çalışsak da, classpath içinde yer alan her sınıfa erişim bir şekilde mümkündür. Bize burada ne jar dosya yapısı, ne public/private direktifleri ne de tüm sınıfların yer aldığı classpath destek vermektedir. Peki Java dilinde modül nasıl oluşturabiliriz? Bu OSGI (Open Services Gateway initiative) ile mümkün, ama Java 9 ile daha da kolay, çünkü modüller için gerekli yapılar dilin ve derleyicinin bir parçası haline geldi.

Java 9 sürümünün getirdiği yeniliklerin başında yeni modül sistemi geliyor. Şimdi logger modülünün gerçek bir modül olarak Java 9 ile nasıl implemente edilebileceğini yakından inceleyelim.

Öncelikle bir takım sınıfların bir modül teşkil ettiğini tanımlamamız gerekiyor. Bu amaçla modül tanımlayıcısı (module descriptor) olarak isimlendirilen bir sınıf oluşturmamız gerekiyor. Logger modülü için bu sınıfı şu şekilde tanımlayabiliriz:

[source language=”java”]
module logger {
}
[/source]

Modül tanımlayıcı sınıfının ismi module-info.java ismini taşımaktadır. Yukarıdaki örnekte logger isminde bir modül tanımladık. Modülün yer alacağı dizin yapısı şu şekildedir:

[source language=”java”]
src
|__logger
|__module-info.java
|__com
|_kurumsaljava
|__logger
|__Logger.java
|__LoggerFactory.java
|__LogLevel.java
|__StdoutLoggerImpl.java
[/source]

Modüle ait tüm parçalar src/logger dizininde yer almaktadır. Modülü tanımlamak için src/logger/module-info.java sınıfını oluşturduk. Modül implementasyonu /src/logger/com/kurumsaljava/logger dizininde yer almaktadır. Şimdi logger modülünü kullanan yeni bir modül oluşturulım. Application ismini taşıyan bu modül, logger modülünde yer alan loglama servisini kullanmaktadır.

[source language=”java”]
src
|__logger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__Logger.java
| |__LoggerFactory.java
| |__LogLevel.java
| |__StdoutLoggerImpl.java
|__application
|__module-info.java
|__com
|_kurumsaljava
|__application
|__Main.java
[/source]

Uygulamamız iki modülden oluşmaktadır ve şu şekilde (windows powershell ile) derlenmektedir:

[source language=”java”]

javac -d classes –module-source-path src $(dir src -r -i "*.java")

[/source]

Birinci parametre (-d) ile derlenen sınıfların hangi dizinde yer aldığını, ikinci parametre ile (–module-source-path) derlemek istediğimiz modül kodlarının hangi dizinde yer aldığını tanımladık. Bu işlemin ardından ekran çıktısı şu şekilde olacaktır:

[source language=”java”]
PS C:\Users\acar\eclipse-workspace-java9\modules> javac -d classes –module-source-path src $(dir src -r -i "*.java")
C:\Users\acar\eclipse-workspace-java9\modules\src\application\com\kurumsaljava\application\Main.java:3: error: package com.kurumsaljava.com.logger is not visible
import com.kurumsaljava.com.logger.LogLevel;
^
(package com.kurumsaljava.com.logger is declared in module logger, but module application does not read it)
C:\Users\acar\eclipse-workspace-java9\modules\src\application\com\kurumsaljava\application\Main.java:4: error: package com.kurumsaljava.com.logger is not visible
import com.kurumsaljava.com.logger.LoggerFactory;
^
(package com.kurumsaljava.com.logger is declared in module logger, but module application does not read it)
2 errors
[/source]

Hatanın ne olduğunu daha iyi anlayabilmek için tekrar Main sınıfına bir göz atalım:

[source language=”java”]
package com.kurumsaljava.application;

import com.kurumsaljava.logger.LogLevel;
import com.kurumsaljava.logger.LoggerFactory;

public class Main {

public static void main(String[] args) {
LoggerFactory.instance().log(LogLevel.INFO, "Hello World");
}

}
[/source]

Main sınıfı LogLevel ve LoggerFactory sınıflarını kullanmaktadır. Bunlar logger modülünün kullanım arayüzünü oluşturan sınıflardır. Lakin derleyici bu sınıflar için “not visible” yani görülmeyen sınıflar hatasını vermektedir. Derleyici bu sınıfları bulamamaktadır, çünkü logger modülü tanımlayıcısında (module-info.java) bu sınıflar kullanıma açılmamıştır yani logger modülü bu hali ile tam bir kara kutudur ve kullanımı imkansızdır. Logger modülünü kullanılabilir hale getirmek için kullanım arayüzünü oluşturan sınıfları diğer modüllerin erişebileceği şekilde tanımlamamız gerekmektedir. Bu işlemi şu şekilde gerçekleştirebiliriz:

[source language=”java”]
— logger/module-info.java

module logger {
exports com.kurumsaljava.logger;
}
[/source]

Bu tanımlama ile com.kurumsaljava.logger paketinde yer alan sınıfların hepsini diğer modüller tarafından kullanılabilir hale getirmiş olduk. Logger modülünü kullanmak isteyen her modülün şu şekilde bu isteği dile getirmesi gerekmektedir:

[source language=”java”]
— application/module-info.java

module application {
requires logger;
}
[/source]

Derleme işlemi tamamlandıktan sonra, uygulamayı oluşturan Main sınıfını şu şekilde koşturabiliriz:

[source language=”java”]

java –module-path classes -m application/com.kurumsaljava.application.Main

[/source]

Bu hali ile bir modülün sahip olması gerektiği yapıya çok yaklaşmış olduk. Modülün iç dünyasını paket bazında kullanıma sunabildigimiz için logger modülünün yapısını aşağıdaki şekilde değiştirmek faydalı olacaktır, aksi taktirde bu konfigürasyon ile application/Main sınıfı doğrudan StdoutLoggerImpl sınıfına erişebilmektedir. Bu dogal olarak modülün bir kara kutu gibi işlev görmesinin önünde bir engel oluşturmaktadır.

[source language=”java”]
src
|__logger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__impl
| | |__StdoutLoggerImpl.java
| |__api
| |__Logger.java
| |__LoggerFactory.java
| |__LogLevel.java

— logger/module-info.java

module logger {
exports com.kurumsaljava.logger.api;
}
[/source]

Modüler uygulamamızı koştururken –module-path parametresi dikkatinizi çekmiş olabilir. Aynı şekilde modülleri derlerken –module-source-path parametresini kullanmıştık. Java’nın yeni modül sistemi classpath yerine modülü oluşturan sınıfları yüklemek için module-path dizin yapısını kullanmaktadır. Module path bir ya da daha fazla ve derlenen modülleri ihtiva eden dizinlerden oluşmaktadır. Derleme işlemi esnasında modüle ait sınıflar module-source-path ile tanımlanmatadır. Modüler bir uygulama koşturulduğunda, modülün module-path içinde bulunan ilk sürümü kullanılmakta ve diğerleri göz ardı edilmektedir. Module-path ve classpath birlikte kullanılabilmektedir.

Oluşturulan modülleri jar dosyalarında tutmak münkün. Lakin her modül için ayrı bir jar dosyası oluşturulması gerekmektedir. Modüler uygulamamiz için aşağidaki şekilde modül jar dosyaları oluşturabilir ve bu jar dosyaları aracılığı ile uygulamamızı koşturabiliriz.

[source language=”java”]
PS C:\Users\acar\eclipse-workspace-java9\modules> jar –create –file module/application.jar –main-class com.kurumsaljava.application.Main -C classes/application .
PS C:\Users\acar\eclipse-workspace-java9\modules> jar –create –file module/logger.jar -C classes/logger .
PS C:\Users\acar\eclipse-workspace-java9\modules> jar –create –file module/logger.jar -C classes/logger .
PS C:\Users\acar\eclipse-workspace-java9\modules> java –module-path module -m application/com.kurumsaljava.application.Main
INFO – Hello World
[/source]

Oluşturdugumuz modüller arasındaki bağımlılıkları şu şekilde kontrol edebiliriz:

[source language=”java”]
PS C:\Users\acar\eclipse-workspace-java9\modules> jdeps –module-path modules module/*.jar
application
[file:///C:/Users/acar/eclipse-workspace-java9/modules/module/application.jar]
requires mandated java.base (@9.0.4)
requires logger
application -> java.base
application -> logger
com.kurumsaljava.application -> com.kurumsaljava.logger logger
com.kurumsaljava.application -> java.lang java.base
logger
[file:///C:/Users/acar/eclipse-workspace-java9/modules/module/logger.jar]
requires mandated java.base (@9.0.4)
logger -> java.base
com.kurumsaljava.logger -> java.io java.base
com.kurumsaljava.logger -> java.lang java.base
com.kurumsaljava.logger -> java.lang.invoke java.base
PS C:\Users\acar\eclipse-workspace-java9\modules>
[/source]

Görüldügü gibi application.jar dosyasında yer alan modül logger modülü yanı sıra java.base modülüne bağımlıdır. Aynı şekilde logger modülü de java.base modülüne bağımlıdır. Burada Java bünyesinde yer alan sınıfların da artık modüler bir yapıda olduğunu görmekteyiz. Sadece ihtiyaç duyulan sınıfların yer aldığı modülleri kullanarak, Java uygulamalarını daha az hafıza ihtiyacı duyan Java ortamlarında koşturmak mümkün hale gelmiştir. Tüm java modüllerine şu sekilde erişebiliriz:

[source language=”java”]
PS C:\Users\acar\eclipse-workspace-java9\modules> java –list-modules
java.activation@9.0.4
java.base@9.0.4
java.compiler@9.0.4
java.corba@9.0.4
java.datatransfer@9.0.4
java.desktop@9.0.4
java.instrument@9.0.4
java.logging@9.0.4
java.management@9.0.4
java.management.rmi@9.0.4
java.naming@9.0.4
java.prefs@9.0.4
java.rmi@9.0.4
java.scripting@9.0.4
java.se@9.0.4
java.se.ee@9.0.4
java.security.jgss@9.0.4
java.security.sasl@9.0.4
java.smartcardio@9.0.4
java.sql@9.0.4
java.sql.rowset@9.0.4
java.transaction@9.0.4
java.xml@9.0.4
java.xml.bind@9.0.4
java.xml.crypto@9.0.4
java.xml.ws@9.0.4
java.xml.ws.annotation@9.0.4
jdk.accessibility@9.0.4
jdk.attach@9.0.4
jdk.charsets@9.0.4
jdk.compiler@9.0.4
jdk.crypto.cryptoki@9.0.4
jdk.crypto.ec@9.0.4
jdk.crypto.mscapi@9.0.4
jdk.dynalink@9.0.4
jdk.editpad@9.0.4
jdk.hotspot.agent@9.0.4
jdk.httpserver@9.0.4
jdk.incubator.httpclient@9.0.4
jdk.internal.ed@9.0.4
jdk.internal.jvmstat@9.0.4
jdk.internal.le@9.0.4
jdk.internal.opt@9.0.4
jdk.internal.vm.ci@9.0.4
jdk.jartool@9.0.4
jdk.javadoc@9.0.4
jdk.jcmd@9.0.4
jdk.jconsole@9.0.4
jdk.jdeps@9.0.4
jdk.jdi@9.0.4
jdk.jdwp.agent@9.0.4
jdk.jlink@9.0.4
jdk.jshell@9.0.4
jdk.jsobject@9.0.4
jdk.jstatd@9.0.4
jdk.localedata@9.0.4
jdk.management@9.0.4
jdk.management.agent@9.0.4
jdk.naming.dns@9.0.4
jdk.naming.rmi@9.0.4
jdk.net@9.0.4
jdk.pack@9.0.4
jdk.policytool@9.0.4
jdk.rmic@9.0.4
jdk.scripting.nashorn@9.0.4
jdk.scripting.nashorn.shell@9.0.4
jdk.sctp@9.0.4
jdk.security.auth@9.0.4
jdk.security.jgss@9.0.4
jdk.unsupported@9.0.4
jdk.xml.bind@9.0.4
jdk.xml.dom@9.0.4
jdk.xml.ws@9.0.4
jdk.zipfs@9.0.4
[/source]

Aşağıdaki şekilde sadece uygulamamızı ve ihtiyaç duyduğumuz Java modüllerini ihtiva eden bir Java sürümü oluşturabiliriz:

[source language=”java”]
PS C:\Users\acar\eclipse-workspace-java9\modules> jlink –module-path "%JAVA_HOME%/jmods;module" –add-modules application –output distribution
PS C:\Users\acar\eclipse-workspace-java9\modules> .\distribution\bin\java -m application
INFO – Hello World
[/source]

Jlink ile oluşturduğumuz Java sürümü sadcee 35MB büyüklügünde olup, uygulamamızı ihtiva etmekle birlikte, onu koşturmak için gerekli tüm yeteneklere sahiptir. Bunu 300MB büyüklügünde olan JDK9 sürümü ile kıyasladığımızda, Java ile modüler yazılımın ve Java’nın sahip olduğu kendi modüler yapının Java 9 ile yapılacak yeni projelerde hafıza kullanımını ne oranda düşürebileceği görülmektedir.

Modül tanımlayıcılarının (module descriptor) içeriğine aşağidaki şekilde ulaşmak mümkündür. Bu kullanmak istediğimiz modüllerin kullanım arayüzleri hakkında fikir edinmemizi kolaylaştıracaktır.

[source language=”java”]
PS C:\Users\acar\eclipse-workspace-java9\modules> jar –describe-module –file=module/logger.jar
logger jar:file:///C:/Users/acar/eclipse-workspace-java9/modules/module/logger.jar/!module-info.class
exports com.kurumsaljava.logger
requires java.base mandated

PS C:\Users\acar\eclipse-workspace-java9\modules> jar –describe-module –file=module/application.jar
application jar:file:///C:/Users/acar/eclipse-workspace-java9/modules/module/application.jar/!module-info.class
requires java.base mandated
requires logger
contains com.kurumsaljava.application
main-class com.kurumsaljava.application.Main

PS C:\Users\acar\eclipse-workspace-java9\modules>
[/source]

Son olarak servis modüllerinden bahsetmek istiyorum. Şimdiye kadar incelemiş olduğumuz modül sisteminde bir sorun mevcut. Logger modülünün birden fazla implementasyonu düşünülebilir. Örneğin bir RemoteLoggerImpl sınıfı ile logları bir sunucuya gönderebiliriz. Bu durumda ikinci bir modül oluşturarak, Logger sınıfını yeniden implemente etmemiz gerekmektedir. Lakin bu durumda modül kullanıcısının bağımlılık sayısı artacaktır. İncelediğimiz örnekte application modülü logger modülünü kullanmaktadır. İsmi remotelogger olan yeni bir modül oluşturduğumuz taktirde, application modülü bu yeni modülü kullanmak isteğinde, modül tanımlayıcısı şöyle bir yapıya bürünecektir:

[source language=”java”]
— application/module-info.java

module application {
requires logger;
requires remotelogger;
}
[/source]

Oluşturacağımız her yeni logger implementasyonu için bu liste uzayıp, gidecektir. Modüller arasında bu şekilde bir bağımlılık oluşturmak, modüllerin bakımını ve geliştirilmesini zora somaktadır. Hizmet kullanan (consumer) ve hizmet sunan (provider) modülleri birbirlerine bağımlı olmayacak şekilde ayırt etmemiz gerekmektedir. Aksi taktirde bu bağımlılıkların yönetimi imkansiz hale gelecektir. Bunu sağlamak için servis katalog (service catalog) ya da servis kayıt merkezi (service registry) yapısını kullanabiliriz.

Servis kayıt merkezini hizmet alan ve hizmet sunan modüller arasında bir aracı katman olarak düşünebiliriz. Hizmet sunan modüller servis kayit merkesine başvurarak, sundukları hizmeti kaydettirirler. Aynı şekilde hizmet almak isteyen modüller de servis kayıt merkezine başvurarak, istedikleri servisi edinmeye çalışırlar. Burada ki en önemli nokta, hizmet sunan ve hizmet alan modüllerin birbirlerini tanımamalarıdır ve daha önce bahsetmiş olduğum bağımlılığın oluşmamasıdır. Modüller sadce servis kayıt merkezini tanırlar ve işlemleri onun aracılığı ile gerçekleştirirler.

Şimdi logger modülünü iki değişik implementasyon sunacak şekilde yeniden oluşturalım ve application modülünün bu iki modüle bagımlı olmadan, mevcut logger implementasyonları servis kayıt merkezi aracılığı ile nasıl kullanabileceğini bir örnek üzerinde inceleyelim.

Logger modülünü soyut olarak yani sadece api sınıflarını ihtiva edecek şekilde şu şekilde tanımlayabiliriz:

[source language=”java”]
src
|__logger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__api
| |__Logger.java
| |__LogLevel.java

— logger/module-info.java

module logger {
exports com.kurumsaljava.logger.api;
}

— Logger.java

package com.kurumsaljava.logger.api;
public interface Logger {

public void log(LogLevel level, String log);

}
[/source]

Akabinde stdout ve remote logger implementasyonlarını yeni iki modül bünyesinde aşağıdaki şekilde implemente edebiliriz:

[source language=”java”]
src
|__logger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__api
| |__Logger.java
| |__LogLevel.java
|__remotelogger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__remote
| |__RemoteLoggerImpl.java
|__stdoutlogger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__stdout
| |__StdoutLoggerImpl.java

— remotelogger/module-info.java

module remotelogger {
requires logger;
provides com.kurumsaljava.logger.api.Logger with com.kurumsaljava.logger.remote.RemoteLoggerImpl;
}

— stdoutlogger/module-info.java

module stdoutlogger {
requires logger;
provides com.kurumsaljava.logger.api.Logger with com.kurumsaljava.logger.stdout.StdoutLoggerImpl;
}
[/source]

Modül tanımlayıcısı bünyesinde provides direktifi ile bir modül başka bir modül tarafından tanımlanmış ve export edilmiş bir api sınıfını implemente edebilmektedir. Provides ile oluşturduğumuz implementasyonlar servis kayıt merkezinde Logger sınıfı için iki servis implementasyonu kaydı oluşturacaktır. Kısaca iki yeni modül “biz bu servisi sağlayabiliyoruz” şeklinde bir açıklamada bulunarak, gerekli servis kayıtlarının oluşturulmasını sağlamaktadırlar. Peki bu modüllere doğrudan bir bağımlılık oluşturmadan, servis implementasyonlarını nasıl kullanabiliriz? Şu şekilde:

[source language=”java”]
src
|__logger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__api
| |__Logger.java
| |__LogLevel.java
|__remotelogger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__remote
| |__RemoteLoggerImpl.java
|__stdoutlogger
| |__module-info.java
| |__com
| |_kurumsaljava
| |__logger
| |__stdout
| |__StdoutLoggerImpl.java
|__application
| |__module-info.java
| |__com
| |_kurumsaljava
| |__application
| |__Main

— application/module-info.java

module application {
requires logger;
uses com.kurumsaljava.logger.api.Logger;
}

— Main

package com.kurumsaljava.application;

import java.util.ServiceLoader;

import com.kurumsaljava.logger.api.Logger;
import com.kurumsaljava.logger.api.LogLevel;

public class Main {

public static void main(String[] args) {
Iterable<Logger> services = ServiceLoader.load(Logger.class);
for(Logger logger: services) {
logger.log(LogLevel.DEBUG, "Hello World");
}
}
}
[/source]

Application isimli modül modül tanımlayısı bünyesinde uses direktifi ile hangi modülü kullanmak istediğini ifade etmektedir. Bu Logger soyut sınıfını baz alan soyut bir tanımlamadır. Application modülü kesinlikle somut bir sınıf ya da modül implementasyonuna işaret etmemektedir. Bu şekilde modüller arasındaki bağı daha esnek hale getirmek ve modüller arası alışverişi servis kayıt merkezi aracılığıyla yürütmek mümkün hale gelmektedir.

Mevcut servis implementasyonlarına erişim yani servis kayıt merkezine başvuru ServiceLoader sınıfı aracılığı ile gerçekleşmektedir. Main örneginde Logger.class sınıfını implemente eden modüller load() metodu aracılığı ile servis kayıt merkezinden alınmakta ve bu şekilde arzu edilen servis modül bünyesindeki tanımlı kullanıcı arayüzü aracılığı ile koşturulabilmektedir. ServiceLoader örneğinde görüldüğü gibi LoggerFactory gibi bir sınıfın kullanımı gereksiz hale gelmiştir, çünkü factory (bknz. factory tasarım şablonu) görevini servis kayıt merkezi üstlenmektedir.

Java, 9 sürümü ile gerçek anlamda modüler uygulamaların oluşturulması mümkün kılan bir araç haline gelmiştir. Yeni uygulamaların bu avantajı kullanabilmeleri ümidiyle …


EOF (End Of Fun)
Özcan Acar

JVM Nasıl Çalışır Yazı Serisi – Java Dilinde Neden Göstergeçler (Pointer) Yok?

Java’da göstergeçler var, ama C dilinde olduğu şekilde işlemiyorlar. Bu yazımda Java’da göstergeçlerin neden olmadığını aktarmaya çalışacağım.

C dilinde aşağıdaki şekilde bir göstergeç tanımlaması yapilabilmektedir:

[sourcecode language=”java”]

int i, *ptr;
ptr = &i;
*ptr=10;
[/sourcecode]

Bu örnekte i isminde ve int veri tipinde bir değişken ve yine int veri tipine sahip ve ptr isminde bir göstergeç tanımladık. C dilinde göstergeçler bir hafıza alanına işaret ederler. Yukarıdaki örnekte ptr in sahip olduğu değer i değişkeninin hafızadaki adresidir. ptr üzerinde yapılan her işlem i değişkenini etkileyebilir, çünkü ptr dolaylı olarak i değişkeninin sahip olduğu değere işaret etmektedir. Göstergeç aritmetiği sayesinde hafıza alanlarını doğrudan adreslemek ve o hafıza alanlarında yer alan değerler üzerinde işlem yapmak mümkündür.

Java’da göstergeç tanımlamaları ve hafıza alanları üzerinden doğrudan işlem yapmak mümkün değildir, çünkü:

  • Java sanal makinesi (Java Virtual Machine) hafıza alanını kendisi yönetir.
  • Java’da array ve list nesnelerinin uzunlukları JVM tarafından kontrol edilir. Olmayan array elemanları üzerinde işlem yapmaya çalışmak ArrayIndexOutOfBoundsException hatasını oluşturur.
  • Java dilinde array dilin bir parçasıdır ve string işlemleri için String sınıfı kullanılır. C dilinde göstergeçler aracılığı ile bu yapılar oluşturulur ve dilin bir parçası değildirler.
  • Java nesne referanslarını JVM bünyesinde yer alan garbage collector aracılığı ile yönetir.

Java dilinde göstergeçler sadece nesnelere işaret eden değişken isimleri olarak kullanılırlar, ama:

  • Bu değişkenler üzerinde göstergeç aritmetiği yapılamaz
  • Sadece nesnelere işaret ederler, adres alanlarına değil

Java’da bu nesne göstergeçlerine referans ismi verilmektedir.

Aşağıdaki Java kodunu koşturduğunuzda NullPointerExceptin oluşmaktadır:

[sourcecode language=”java”]

Nesne nesne = null;
nesne.birMethod();
[/sourcecode]

Peki neden oluşan hatanın ismi NullPointerException seklindedir de NullReferenceException değildir? Bu sorunun cevabını bende bilmiyorum. Java’da NullReferenceException olmalıydı. NullPointerException sınıfının kaynak koduna baktığımızda, sınıfın kim tarafından geliştirildiği görememekle birlikte, JDK1.0 dan beri Java’nın bir parçası olduğunu görmekteyiz. Bu sanırım Java dilini oluşturanların C/C++ kökenli olmalarından kaynaklanıyor. Ama C# dilinde NullReferenceException isminde ve aynı vazifeyi gören bir sınıf mevcut. C# dil geliştiricileri aynı hatayı yapmamış.

[sourcecode language=”java”]

/**
* @author unascribed
* @version %I%, %G%
* @since JDK1.0
*/
public
class NullPointerException extends RuntimeException {
}
[/sourcecode]

Şüphesiz Java dilinde göstergeçlerin ve göstergeç aritmetiğinin olmaması Java’da geliştirilen kodun daha kolay anlaşılır olmasını sağlamaktadır. C kodu çok kısa zamanda göstergeçlerin yerli, yersiz kullanılmalarından dolayı kaotik bir yapıya bürünebilir. Ama bunun karşılığında göstergeçler yardımı ile donanıma çok yakın kalınarak, uygulama geliştirmek mümkündür. Java’da bu ne yazık ki mümkün değil. Sizinle donanım arasında her zaman JVM olacak.


EOF (End Of Fun)
Özcan Acar

java.lang.IncompatibleClassChangeError: Implementing class Nedir ve Analizi Nasıl Yapılır?

Bu hata genelde bir interface ya da üst sınıf değişikliğe uğradıktan sonra, classpath içinde bu sınıfı eski haliyle implemente etmiş/genişletmiş bir sınıfın, üst sınıf tekrar kullanılarak derlenmeden kullanılması sonucu ortaya çıkan bir hata türüdür.

Çalıştığım projede bir web uygulamasını deploy ederken hatanın şu hali ile karşılaştım:

[source language=’java’]
javax.servlet.ServletException: Error instantiating servlet class xxx.xxx.emobility.webapi.EMobilityUMAdminServlet
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    com.vw.mbbc.dispatcher.access.webapi.WebServerSimulator.invoke(WebServerSimulator.java:140)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    java.lang.Thread.run(Thread.java:744)
root cause

java.lang.IncompatibleClassChangeError: Implementing class
    java.lang.ClassLoader.defineClass1(Native Method)
    java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2918)
    org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1174)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1669)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1547)
    org.hibernate.ejb.Ejb3Configuration.(Ejb3Configuration.java:107)
    org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:124)
    javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:48)
    javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32)
[/source]

Bu hatayı analiz etmek için HibernatePersistence sınıfının 124. satırından yola çıkıyoruz. Bu satırda Ejb3Configuration sınıfından bir nesne oluşturuluyor. Ejb3Configuration sınıfının 107. satırına göz attığımızda AnnotationConfiguration sınıfından bir nesne oluşturulduğunu görüyoruz. Stacktrace e göz attığımızda sınıf yükleyicisinin (classloader) bu sınıfı classpath içinde bulmak üzere işlem yaptığını görüyoruz.

AnnotationConfiguration sınıfı Configuration sınıfını genişletiyor. IncompatibleClassChangeError hatasının oluşmasının ana sebebi ise şu: org.hibernate.cfg.Configuration sınıfı AnnotationConfiguration sınıfı ile aynı jar dosyası içinde değil. Eğer aynı jar dosyasında olsalardı Classloader doğru Configuration sınıfını bulurdu ve IncompatibleClassChangeError hatası oluşmazdı. Benim ortamında org.hibernate.cfg.Configuration sınıfının iki değişik sürümü iki değişik jar dosyasındaydı ve classloader tesadüf eseri kullanımda olan AnnotationConfiguration sınıfı için değişikliğe uğramış olan Configuration sınıfını önce buldu ve yükledi. Classloader uyumsuzluğun farkına vardığı için IncompatibleClassChangeError hatası oluştu. 

Bu sorunu çözmek için değişikliğe uğramış olan üst sınıfı bulup, classpath dan uzaklaştırmak gerekiyor. Benim örneğimde iki Configuration sınıfı bulunmakta. Çoğu zaman classpath içinde değişikliğe uğramış bir üst sınıf yer alacaktır. Bu durumda üst sınıfı genişleten alt sınıfı tekrar derleyerek bu sorun çözülebilir.


EOF (Enf Of Fun)
Özcan Acar  

Yazılımcının Verimliliğini Artıran Eclipse Ayarları

Çoğu projede yazılım geliştirme ortamı olarak Eclipse’i kullanıyorum. Bu yazımda yazılım yaparken verimliliğimi artırdığını düşündüğüm Eclipse ayarlarını sizinle paylaşmak istiyorum. Kullandığım Eclipse sürümü 4.3.1 Kepler.

Hiç sevmediğim bir şey varsa, o da formatsız koddur. Bir Eclipse editöründeki bir kodu CTRL+SHIFT+F ile formatlamak mümkün. Ama bunu yazılımcı değil, Eclipse otomatik olarak yapmalı.

Bu ayarı Window –> Preferences –> Java –> Editor –> Save Actions bölümünde yapabiliriz.

Resim 1

Prefecences panelini açtıktan sonra üst kısımda yer alan arama alanına save kelimesini girmeniz, resim 1 de yer alan ekrana ulaşmanızı sağlayacaktır. Kodu otomatik formatlamak için Perform the selected actions on save kutusunun işaretli olması gerekiyor. Bu kutuyu işaretledikten sonra diğer opsiyonlar aktif hale gelir. Kodu formatlamak için Format source code kutusunu işaretlemek yeterli olacaktır.

Save Actions panelindeyken diğer gerekli gördüğüm ayarları da yapalım isterim.

Bu paneldeki Additional actions kutusunu işaretleyelim ve Configure butonuna tıklayalım. Karşımıza resim 2 de yer alan panel çıkacaktır.

Resim 2

Code Style tabındayız. Ben burada Use blocks in if/while/for/do statmens –> Always ayarını kullanıyorum. Şu şekilde bir if bloğu oluşturduğumda

[source language=”java”]
if(i>0)
doIt();
[/source]

bu Eclipse tarafından otomatik olarak şu şekilde formatlanır.

[source language=”java”]
if(i>0){
doIt();
}
[/source]

Buradaki maksat tek satırlık if/while/for/do yapılarını küme parantezi kullanarak bir blok haline getirmektir. İlk başlarda ben bu tür tek satırlık yapılarda küme parantezlerini kullanmazdım, çünkü bu bana koddan bir satır tasarruf etmemi sağlardı. Lakin sonraları çalıştığım birçok projede firma yazılım geliştirme politikası olarak küme parantezlerinin kullanılması koşulunu takip etme zorunluluğu doğduğundan ve Sonar gibi statik kod analizi araçlarının küme parantezi olmayan bloklardan hoşlanmadıklarından, ben de tek satırlık yapılarda küme parantezleri kullanmaya başladım. Bu bir alışkanlık meselesi. Çok kısa zamanda insan kullandığı yeni yapılara alışıyor ve bırakmak istemiyor. Bunu bir mezhep kavgasına dönüştürmenin bir anlamı yok anlayacağınız ;-)

Yazdığım kodda sınıfların, metotların, değişkenlerin, metot parametrelerinin ve metot gövdesinde kullanılan değişkenlerin mümkün mertebe final olmalarına dikkat ederim. Final olan yapılar genel olarak değiştirilemez olduklarından, istenmeden değiştirilmeleri mümkün değildir. Örneğin final olan bir değişkene yeniden bir değer atanamaz. Paralel çalışan (multi threaded) bir uygulamada final olan yapılar threadlar arasında sıkıntı olmadan paylaşılabilir.

Ben eğer şöyle bir metot yazdı isem

[source language=”java”]
public void doIt(int i) {
int y = i;
System.out.println(y);
}
[/source]

bu metodun Eclipse tarafından şu şekilde formatlanmasını arzularım:

[source language=”java”]
public void doIt(final int i) {
final int y = i;
System.out.println(y);
}
[/source]

Bunun Eclipse tarafından yapılması sağlamak için resim 2 de yer alan Use modifier final where possible kutusunun işaretlenmesi gerekmektedir. Burada üç seçenek bulunmaktadır. Eğer private olan değişkenlerin final olmasını istiyorsak Private fields, metot parametrelerinin final olmasını istiyorsak Parameter ve metot gövdesindeki değişkenlerin final olmasını istiyorsak Local variables seçeneğini aktif hale getiriyoruz.

Resim 3,4 ve 5 de Additional Save Actions panelindeki ayarlarım yer alıyor.

Resim 3

Resim 4

Resim 5

Resim 5 de yer alan Remove unused imports seçeneği koddan kullanılmayan import direktiflerinin silinmesini sağlamaktadır. Bir Java sınıfı bünyesinde zaman içinde import direktifleri ile birçok sınıf kullanılır. Bu sınıf yeniden yapılandırıldığında kullanımda olan bazı sınıflar ihtiyaç dışı kalabilir. Yazılımcılar genelde bu şekilde kullanım dışı kalan import direktiflerini görmediklerinden koddan uzaklaştırmazlar. Ben böyle bir sınıfı editörüme yüklediğimde yaptığım ilk kayıt işlemi (save) ile kullanım dışı olan tüm import direktifleri koddan silinir ve kod formatlanır.

Faydalı bulduğum diğer bir ayar ise, hangi küme parantezi bloğunda olduğumu doğrudan görebilmektir. Resim 6 da hangi blok içinde isem, o bloğun küme parantezleri öne çıkmış durumda. Bu ayarı yapmak için resim 7 de görülen Editor paneline gitmemiz ve orada Bracket highlighting seçeneklerinden Enclosing bracket seçeneğini seçmemiz gerekiyor. Bu bizim içinde bulunduğumuz kod bloğunu doğrudan görmemizi sağlayacaktır.

Resim 6

Resim 7

Java’da biliyorsunuz satır sonunda ; işaretini kullanma zorunluluğu var. Bunu da Eclipse bizim için yapabilir. Resim 8 de yer alan Automatically insert at correct position {} ve ; işaretlerinin doğru yerlerde Eclipse tarafından koda eklenmesini sağlamaktadır.

Resim 8

Eclipse altında CTRL+F11 tuşlarına bastığınızda bir uygulamayı koşturabilirsiniz. Genelde ben bir uygulamayı test ederken bu tuş kombinasyonunu şıkça kullanıyorum. Lakin kaynak kod üzerinde yaptığım değişiklikler CTRL+F11 tuşlarına bastığımda otomatik olarak kaydedilmiyor ve böylece yaptığım değişiklikler koşturduğum uygulamaya yansımıyor. Ayrıca CTRL+F11 her zaman en son koşturduğum uygulamayı koşturmayabilir. Uygulamayı koşturmadan önce değişiklikleri kaydetmek ve uygulamayı yeniden derlemek için resim 9 da yer alan ayarların yapılmasında fayda vardır.

Resim 9

Ne zaman iyi bir yazılımcı oluruz? Kullandığımız yazılım geliştirme ortamına sunduğu birçok kısa yol tuşlarıyla hakim olduğumuz zaman, çünkü bu bizim yazılımcı olarak verimliliğimizi artırır.


EOF (End Of Fun)
Özcan Acar

Java Generics Get ve Put Prensibi

Java Generics and Collections kitabında Get and Put isminde bir prensipden bahsediliyor. Java Generics wildcard kullanımı bu prensip takip edildiğinde daha kolaylaşıyor. Prensip şöyle:

Eğer bir yapıdan (liste, map vs.) nesne ediniyorsanız (get) extends wildcard, bir yapıya nesne ekliyorsanız (put) super wildcard kullanın. Bir yapı üzerinde bu iki işlemi birden yapıyorsanız, o zaman wildcard kullanmayın.

Bunu bir örnek ile açıklayalım. Aşağıdaki metot bir listeden diğer bir listeye nesneleri kopyalamak için kullanılan jenerik bir metot.

[source language=”java”]
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}
[/source]

copy metodunun ilk parametresi nesnelerin kopyalanacağı, yani put yapılacak olan liste, ikinci parametresi nesnelerin alındığı, yani get edildiği liste. Şu şekilde bir listeyi diğer bir listeye kopyalayabiliriz:

[source language=”java”]
List<Object> objs = Arrays.<Object>asList("String", 1, 3.14);
List<Integer> ints = Arrays.<Integer>asList(1,2);
copy(objs, ints);
[/source]

dest isimli parametre put işlemi yapıldığı için super wildcard’ını, src isimli parametre get yapıldığı için extends wildcard’ını kullanıyor.

Bir iterator kullanıldığında bir yapıdan get işlemi ile değerler edinilir. Bu durumda extends wildcard kullanılmalı. Aşağıdaki örnekte sum metodu bir listede yer alan Number sınıfı ve altsınıflarından oluşan nesnelerin değerlerini topluyor. Buna göre bu metodu sum(Arrays. asList(1, 2)) ya da sum(Arrays. asList(1.1, 2.2)) şeklinde koşturmak mümkün. Eğer extends wildcard kullanılmasaydı Integer ya da Double nesneler ihtiva eden bir listeyi sum metoduna parametre olarak vermek mümkün olmazdı.

[source language=”java”]
public static double sum(Collection<? extends Number> nums) {
double s = 0.0;
for (Number num : nums)
s += num.doubleValue();
return s;
}
[/source]

EOF (End Of Fun)
Özcan Acar

Uzaktan Eşli Programlama

Eşli programlama yapmak için illa iki programcının aynı mekanda olması gerekmiyor. Eclipse kullanıyorsanız Saros pluginini kurarak, uzak bir bilgisayardaki bir şahıs ile eşli programlama yapabilirsiniz.

Uzaktan eşli programlama için neler gerekli?

Eclipse >> Help >> Install new Software menüsünden http://dpp.sourceforge.net/update Saros update sayfasını kullanarak gerekli plugini kurabilirsiniz.

Kurulumun nasıl yapıldığı Saros kurulum sayfasında yer almaktadır.

Kurulum işlemi tamamlandıktan ve Eclipse kapatılıp, yeniden açıldıktan sonra yukarıda yer alan login paneli görünecektir. İki programcının beraber aynı proje üzerinde çalışabilmeleri için aynı sunucuya bağlantı kurmuş olmaları gerekmektedir. Saros sunucu olarak bir Jabber sunucusu kullanıyor. Yeni bir hesap oluşturduktan sonra sunucuya bağlanabilirsiniz.

Eşli programlama yapmak istediğiniz şahsın Saros hesabını edinmeniz ve aşağıda yer aldığı gibi Saros paneli üzerinden Buddy listenize eklemeniz gerekiyor.

Buddy ekleme işlemi tamamlandıktan sonra eğer Buddyniz online ise, herhangi bir Eclipse projesini karşı taraf ile paylaşıp, eşli programlamaya başlayabilirsiniz. Proje üzerinde yaptığınız her değişiklik anında karşı tarafa yansıyacaktır.

Ben bu altyapıyı genelde uzakta bir bilgisayar kullanan bir programcı ile beraber kata yapmak için kullanıyorum. Kata seansında iletişimi kolaylaştırmak için Skype kullanılabilir.

Benimle kata yapmak isterseniz koordinatlarım şöyle:

Saros hesabım: ozcanacar@saros-con.imp.fu-berlin.de
Skype hesabım: oezcanacar

Bana bir e-posta gönderin ve hangi katayı benimle birlikte eşli programlama yöntemi ile yapmak istediğinizi bildirin. Birlikte bir zaman dilimi bulmaya ve katayı yapmaya çalışalım.

EOF (End Of Fun)
Özcan Acar

Sorumluluk Sahibi Olmak

Yazılım yapmayı zorlaştıran her zaman kod birimleri arasındaki bağimlılıklar ve bu bağımlılıkların yönetimi olmuştur. Bu bağımlılıkları tamamen yok etmek yazılım sistemini anlamsız kılarken, kontrolden çıkmalarına göz yummak yazılım sisteminin ölüm fermanı olabilir. Yazılım mühendisi bunu bilir ve gerekli gördüğü yerlerde DIP, ISP ve SRP gibi tasarım prensiplerini kullanarak kodu dokur.

Yazılımcının kod yazarken devamlı uygulaması gerektiği bir tasarım prensibi varsa, bu da SRP (Single Responsibility Principle) tek sorumluluk prensibidir. Bu prensibe göre her kod biriminin sadece ve sadece bir sorumluluk alanı, yani yaptığı tek bir iş olmalıdır. Bu kod birimi bir paket (package), bir sınıf, metot ya da bir değişken olabilir. SRP uygulanmadığı taktirde yazılım sistemi kontrol edilemez, kırılgan bir bağımlılıklar yumağı haline gelebilir.

Genelde bir bakışta bir kod biriminin SRP uyumlu olup, olmadığını anlamak zor değildir. Eğer bir sınıf iki bin satırdan oluşuyorsa, bu sınıfın birden fazla işle meşgul olduğu söylenebilir, aksi taktirde bu kadar büyümesi mümkün olmazdı.

Büyük yazılım sistemlerini sınıf sınıf gezip, kim SRP uyumlu, kim değil diye kontrol etmek mümkün değil. Yazılımcı olarak daha ziyade anlık resmi görmemizi sağlayacak bir araç oluşturmamız lazım. Bu aracı geliştirmeden önce, sınıfların SRP uyumluluklarını ölçmek için bir yönteme ihtiyacımız var. Bu yöntem örneğin bir sınıfın versiyon kontrol sistemi bünyesinde ne kadar değişikliğe uğradığını ölçebilir. Eğer bir sınıf sıkça değişikliğe uğruyorsa, bu bu sınıfın birden fazla sorumluluk sahibi olduğu anlamına gelebilir. Gerçekten de binlerce satırdan oluşan kod birimlerinin versiyon kontrol sistemindeki geçmişleri incelendiğinde, çok sık değişikliğe ugradıklarını görmek mümkündür. Değişik sorumluk sahibi bir sınıf, her bir sorumluluk için değişikliğe maruz kalabileceğinden, bu gibi sınıfların sicilleri kabarıktır. O zaman bu sınıfları, kaç satır ihtiva ettiklerini ve kaç sefer değişikliğe uğradıklarını tespit edebilecek bir uygulama geliştirelim. Böyle bir uygulamanın kodu aşağıda yer almaktadır.

[source language=’java’]
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

public final class SvnHistory {

private static final String PROJECT_ROOT = “C:/xxx”;
private static Map STATS_MAP = new HashMap();
private static StringBuilder STATS = new StringBuilder();
private static final String SVN_EXE = “C:/Program Files/SlikSvn/bin/svn.exe”;
private static final String SVN_LOG_COMMAND = “log”;

public static void main(final String[] args) {
makeHistory(new File(PROJECT_ROOT));
makeHeader();
makeLines();
printStats();
}

private static void makeHeader() {
STATS.append(“Filename;revisionCount;loc”).append(“\n”);
}

private static void makeLines() {
for (final String key : STATS_MAP.keySet()) {
makeLine(key, STATS_MAP.get(key));
}
}

private static void printStats() {
System.out.println(STATS.toString());
}

private static void makeLine(final String key, final ClassStats s) {
STATS.append(key).append(“;”).append(s.revCount).append(“;”)
.append(s.loc).append(“\n”);
}

private static void makeHistory(final File root) {
for (final File file : root.listFiles()) {
if (isJavaFile(file)) {
history(file);
}
if (file.isDirectory()) {
makeHistory(file);
}
}
}

private static boolean isJavaFile(final File file) {
return file.getName().endsWith(“.java”);
}

private static void history(final File file) {
try {
final int revCounter = getRevisionCount(file);
STATS_MAP.put(file.getName(), ClassStats.make(file.getName(), revCounter,
getLinesOfCode(file)));
} catch (final Exception err) {
err.printStackTrace();
}
}

private static int getRevisionCount(final File file) throws IOException,
InterruptedException {
String line = null;
int revCounter = 0;
final Process p = Runtime.getRuntime().exec(
SVN_EXE + ” ” + SVN_LOG_COMMAND + ” ” + file.getAbsolutePath());
final BufferedReader bri = new BufferedReader(new InputStreamReader(
p.getInputStream()));
final BufferedReader bre = new BufferedReader(new InputStreamReader(
p.getErrorStream()));
while ((line = bri.readLine()) != null) {
if (line.startsWith(“r”))
revCounter++;
}
bri.close();
bre.close();
p.waitFor();
return revCounter;
}

private static int getLinesOfCode(final File file) {
int loc = 0;
try {
final FileInputStream fstream = new FileInputStream(file);
final DataInputStream in = new DataInputStream(fstream);
final BufferedReader br = new BufferedReader(new InputStreamReader(in));
while (br.readLine() != null) {
loc++;
}
in.close();
} catch (final Exception e) {
System.err.println(“Error: ” + e.getMessage());
}
return loc;
}

private static class ClassStats {
private String fileName;
private int revCount;
private int loc;

private ClassStats() {

}

public static ClassStats make(final String name, final int revCounter, final int loc) {
final ClassStats classStats = new ClassStats();
classStats.fileName = name;
classStats.revCount = revCounter;
classStats.loc = loc;
return classStats;
}
}
}
[/source]

Bu uygulama kodun Subversion tabanlı bir versiyon kontrol sisteminde tutulduğunu varsaymaktadır. PROJECT_ROOT değişkeni versiyon kontrol sisteminden edinilmiş projenin (working copy) ana dizinine işaret ediyor. Uygulama ana dizinde bulunan tüm dizinleri taradıktan sonra bulduğu her Java dosyası için versiyon kontrol sicilini kontrol ediyor ve dosya üzerinde yapılan değişiklik adedini tespit ediyor. Akabinde her Java dosyanın satır adedi tespit edildikten sonra, aşağıdaki şekilde bir ekran çıktısı oluşturuluyor:

[source language=’java’]
Filename;revisionCount;loc
AAA.java;13;55
BBB.java;21;325
CCCjava;3;292
DDD.java;2;367
EEE.java;6;384
FFF.java;1;156
GGG.java;1;77
HHH.java;11;47
III.java;10;81
JJJ.java;3;132
KKK.java;1;111
LLL.java;9;29
MMM.java;32;433
NNN.java;45;1301
OOO.java;3;88
PPP.java;11;47
QQQ.java;60;212
[/source]

Ekran çıktısı bir csv (comma seperated value) dosyası. Bu dosyayı örneğin Excel ile import edip, aşağıdaki şekilde bir diagram oluşturmak mümkün.

Diagram üzerindeki her nokta bir Java dosyasına işaret etmektedir. X ekseninde her dosyanın maruz kaldığı değişiklik adedi (revision count), Y ekseninde dosyanın sahip olduğu satır adedi yer almaktadır. Bu diagrama bakıldığında tek sorumluluk prensibine ters düşen kod birimleri hangileridir?

SRP uyumlu olmayan sınıflar için sağ üst alana bakmak yeterli. Bu sınıfları lokalize ettikten sonra tek bir iş yapacak hale getirmek biz yazılımcıların asli görevlerinden birisi. İzcilerden kendimize örnek alalım :)

EOF(End Of Fun)
Özcan Acar

Java’da Bilinmeyenler

Bu yazımda Java programclarının bilmediği, doğru olarak kabul edip kullandığı ya da yanlış bildiği konulardan bahsetmek istiyorum.

Hafıza Alanı Ayarları
Genelde JVM için hazıfa alanı -Xmx -Xms parametreleri ile oluşturulur. -X ile başlayan parametreler standart JVM parametreleri değildirler, yani bu parametreleri JVM implemente etmek zorunda değildir. Durum böyle olunca -Xmx1024m şeklindeki bir parametre, eğer JVM -Xmx parametresini implemente etmedi ise JVM için kullanılan hafıza alanının 1GB olarak ayarlanmasını sağlayamayacaktır. -Xmx ve -Xms yerine standart olan -mx ve -ms parametreleri kullanılmalıdır.

finally Bloğu
try/catch/finally yapısında metot son bulmadan önce finally bloğunda yer alan kodun mutlaka koşturulduğu düşünülür. Ama bu tam doğru değildir. İki durumda finally içinde yer alan kod koşturulmaz. Bu iki durum: Eğer try içinde System.exit(0); komutu kullanıldı ise (bu durumda program hemen son bulur) ya da bir Thread try içindeki kodları koştururken bilgisayarın elektriği kesilirse ;-)

Call By Reference
Java tamamen call by value mantığı ile çalışır. Java’da call by reference mantığı olduğu bir yanılgıdır. Primitif veri tipleri (int, long vs) nasıl metotlara parametre olarak gönderildiklerinde değerleri kopyalanıyorsa, nesne referansları da metot için oluşturulan stack’e kopyalanır. Referans bir göstergeştir (pointer). Bir metoda parametre olarak gönderildiğinde, çoğaltılarak aynı nesneye işaret eden ikinci bir göstergeç oluşturulur. İkinci göstergeç te aynı nesneye işaret ettiği için, metot içinde işaret ettiği nesne üzerinde işlem yaparak nesnenin durumunu değiştirebilir. Bu değişikliklere metot dışındaki göstergeç üzerinden de erişmek mümkündür, çünkü iki göstergeç aynı nesneye işaret etmektedir. Metot içinde oluşturulan ikinci göstergece null değeri atandığında, metot dışındaki göstergeç bu durumdan etkilenmez. Bu nesne referanslarının metotlara parametre olarak gönderildiklerinde kopyalandıkları yani call by value mantığı ile çalıştıkları anlamına gelmektedir.

Java’da sadece call by value mantığının kullanıldığının kanıtı Java spesifikasyonunda yer alan şu cümledir :

When the method or constructor is invoked (15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared Type, before execution of the body of the method or constructor. The Identifier that appears in the DeclaratorId may be used as a simple name in the body of the method or constructor to refer to the formal parameter.

String ve StringBuilder

“a”+”b” ile StringBuilder.append(“a”).append(“b”) arasında bytecode seviyesinde bir farklılık yoktur. “a”+”b” kullanıldığı taktirde bu bytecode seviyesinde StringBuilder.append(“a”).append(“b”) şekline dönüştürülür.

[source language=’java’]
public class Test
{

public static void main(String[] args)
{
StringBuilder temp = new StringBuilder();
temp.append(“a”);
temp.append(“b”);

String a =”a”;
String b = “b”;
System.out.println(a+b);
}

}
[/source]

[source language=’java’]
C:\Temp>javap -c Test
Compiled from “Test.java”
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object.”“:()V
4: return

public static void main(java.lang.String[]);
Code:
0: new #2; //class java/lang/StringBuilder
3: dup
4: invokespecial #3; //Method java/lang/StringBuilder.”“:()V
7: astore_1
8: aload_1
9: ldc #4; //String a
11: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: pop
15: aload_1
16: ldc #6; //String b
18: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: pop
22: ldc #4; //String a
24: astore_2
25: ldc #6; //String b
27: astore_3
28: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream;
31: new #2; //class java/lang/StringBuilder
34: dup
35: invokespecial #3; //Method java/lang/StringBuilder.”“:()V
38: aload_2
39: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
42: aload_3
43: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
46: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
49: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
52: return

}
[/source]

Bu yazım devam edecek!!! Sizde bildiklerinizi lütfen yorum olarak bu yazıya ekleyin.

Java’da Finalizer Metodu

Java_tr grubunda aşağıdaki yer alan soru soruldu. Bu soru ve benim bu konudaki düşüncelerim aşağıda yer almaktadır.

Java’da bulmaca sevenlere; Aşağıda ki kod parçası için çıktı ne olur,
“finalized” yazar mı? Constructor’ýnda exception fırlatmış bir nesnenin
finalize işleminden geçmesini bekler misiniz?

[source language=’java’]
public class FinalizerTest {

public FinalizerTest() {
System.out.println(“constructed”);
throw new RuntimeException();
}

protected void finalize() throws Throwable {
System.out.println(“finalized”);
}

public static void main(String[] args) {
try {
new FinalizerTest();
} catch (Exception e) {
System.out.println(“exception”);
}
System.gc();
}
}
[/source]

Benim verdiğim cevap şu şekildedir:


Java’da finalize() kullanılması doğru değildir, çünkü JVM’in finalize metodunu ne zaman koşturacağını kestirmek mümkün değildir. Buna güvenerek örneğin finalize() içinde kullanılan kaynakları serbest bırakmak yapılabilecek en büyük hatalardan birisidir. Pratikte finalize() metodu kesinlikle kullanmamalıdır. Finalizer kullanımı performansı düşürür. Finalizer ile nesnenin yok edilmesi daha uzun zaman alır. Finalize yerine bir terminasyon metodu kullanılmalıdır. gc() metodunun kullanımı da GC nin hemen devreye gireceği garantisini vermez.

Yukarıda yer alan programın konstruktöründe try catch ile hata yakalanarak, daha önce bahsettiğim terminasyon metodu (örneğin myExit()) koşturulmalıdır.


EOF ( End Of Fun)
Özcan Acar

Java’da Final Anahtar Kelimesi

Java’da final anahtar kelimesi aşağıda yer alan yapılar için kullanılabilir:

  • Final sınıf değişkenleri: Final olan bir sınıf değişkenine sadece bir kere değer ataması yapilabilir ve bu atama sadece sınıf konstrüktöründe gerçekleşebilir.
  • Final metot parametreleri: Final olarak tanımlanmış bir metot parametresine sadece bir kere değer atanabilir. Metot parametrelerinin tamamen final olarak tanımlamış olmalarında büyük fayda vardır. Bu şekilde parametrenin metot bünyesinde değişikliğe ugrama tehlikesi ortadan kaldırılmış olur.
  • Final metotlar: Final olan bir metot ne alt sınıflarca yeniden yüklenebilir (method overloading) ne de saklı (hidden) tutulabilir.
  • Final sınıflar: Final olan bir sınıf genişletilerek bir alt sınıf oluşturulamaz.

Final anahtar kelimesi Java programcılığında en çok göz ardı edilenlerdendir. En son siz ne zaman final anahtar kelimesini kullandınız?
Final anahtar kelimesi programın daha doğru, hatalara dayanıklı ve performanslı çalışmasını sağlar.

Final anahtar kelimesinin kullanımında yapılan en büyük hatalardan birisi final static olarak tanımlanmış olan bir sınıf değişkenine değistirilebilir bir değerin atanmasıdır. Örneğin aşağıdaki tanımlama yanlıştır, çünkü static değişken final olmasına rağmen, bu değişkene atanan değer, kullanıcı tarafından değiştirilebilir.

[source language=’java’]
public class App
{
public static final String[] LIST = {“1″,”2″,”3”};

public static void main(String[] args)
{
String[] list = App.LIST;
list[0] = “2”;
System.out.println(App.LIST[0]);
}
}
[/source]

Yukarda yer alan main() metodunda final olan LIST degiskeninin ihtiva ettiği array değiştirilmektedir. Bu sorunu ortadan kaldırmak için LIST değişkeni private yapılabilir ve bu değişkeni baz alarak içeriğinin değiştirilemeyeceği bir array değerini geri döndüren metot oluşturulabilir. Bunun bir örneği aşağıda yer almaktadır:

[source language=’java’]
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class App
{
private static final String[] LIST = {“1″,”2″,”3”};

public static final List getList()
{
return Collections.unmodifiableList(Arrays.asList(App.LIST));
}

public static void main(String[] args)
{
List list = App.getList();
list.remove(0);
System.out.println(list);
}
}
[/source]

Bu program koşturulduğunda alınacak hata şu şekildedır:


Exception in thread “main” java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableList.remove(Collections.java:1162)
at test.App.main(App.java:22)

Bu şekilde final olarak tanımladığımız LIST değişkenin ihtiva ettiği değerleri üzerinde oynama yapmak mümkün değildir. getList() metodu bünyesinde Collections.unmodifiableList() ile içeriğinin değiştirilmesi mümkün olmayan bir liste oluşturulmaktadır.


EOF ( End Of Fun)
Özcan Acar

Covariant Return Types

Java 1.5 ile kullanıma sunulan ve covariant return type ismini taşıyan bir yapı mevcuttur. Java 1.5 öncesi bir alt sınıf, bir üst sınıfın sahip olduğu metodu yeniden implemente etmek (method overriding) istediginde, bu metodun geri döndürdüğü veri tipinin, üst sınıftaki metot ile aynı olması gerekmekteydi. Bunun bir örneği aşağıda yer almaktadır.

[source language=’java’]
package test.controller;

import java.util.Map;

import test.criteria.Criteria;

public abstract class AbstractController implements Controller
{
public void executeFilter()
{
Map map = createCriteria().getCriteriaMap();

}

public abstract Criteria createCriteria();
}
[/source]

Soyut (abstract) olan AbstractController sınıfı bünyesinde, alt sınıflarda implemente edilmek üzere createCriteria() isminde bir metot tanımlıyoruz. AccountTableController sınıfı bu metodu implemente etmektedir.

[source language=’java’]
package test.controller;

import java.util.HashMap;
import java.util.Map;

import test.criteria.MyCriteria;
import test.model.Konto;

public class AccountTableController extends AbstractController
{
private Account model = new Account();

public Criteria createCriteria()
{
Map map = new HashMap();
map.put(“kontonr”, model.getKontonr());
map.put(“blz”, model.getBlz());
return new Criteria(map);
}
}
[/source]

Görüldüğü gibi AccountTableController.createCriteria() metodu Criteria tipinde bir degeri geri döndürmektedir. Bu metodun geri döndürdüğü değerin Java 1.5 öncesi Criteria tipinde olması gerekmektedir, çünkü üst sınıfta tanımlanmış olan bu metodun, üst sınıf bünyesinde geri döndürdüğü değer Criteria tipindedir.

Java 1.5 ile bu durum değişmiştir. Üst sınıfta yer alan bir metot alt sınıfta tekrar reimplement edilirse (method overriding), reimplemente edilen metodun geri döndürdüğü değerin tipi, üst sınıftaki metodun geri döndürdüğü sınıfın bir alt sınıfı olabilir. Bunun bir örneği aşağıda yer almaktadır.

[source language=’java’]
package test.controller;

import java.util.HashMap;
import java.util.Map;

import test.criteria.MyCriteria;
import test.model.Konto;

public class AccountTableController extends AbstractController
{
private Account model = new Account();

@Override
public MyCriteria createCriteria()
{
Map map = new HashMap();
map.put(“kontonr”, model.getKontonr());
map.put(“blz”, model.getBlz());
return new MyCriteria(map);
}
}
[/source]

MyCriteria, Criteria sınıfının bir alt sınıfıdır ve AccountTableController sınıfı implemente ettiği createCriteria() metodunun MyCriteria tipinde bir değeri geri döndürmesi sağlamıştır. Bu şekilde cast işlemi yapılmadan alt sınıfların daha zengin tipte verileri geri döndürebilmeleri amaçlanmaktadır.


EOF ( End Of Fun)
Özcan Acar

Java Enum ile Singleton Tasarım Şablonu

Daha önceki bir yazımda Singleton tasarım şablonunun ne olduğunu ve Java’da nasıl kullanıldığını yakından incelemiştik. Bu yazımda Java 1.5 den itibaren kullanıma sunulan Enum sınıfı ile singleton tasarım şablonunun nasıl kullanılabileceğine değinmek istiyorum.

Öncelikle küçük bir örnek üzerinde singleton bir nesnesinin nasıl oluşturulacağına bir göz atalım:

[source language=’java’]
package com.kurumsaljava.com.designpatterns.singleton;

public class MySingleton
{
private static final MySingleton instance = new MySingleton();

private MySingleton()
{
}

public void myMethod()
{

}
}
[/source]

MySingleton sınıfının sahip olduğu konstruktör private olduğu için bu sınıftan bir nesne oluşturmamız mümkün değildir. Bu sınıf bünyesinde ve MySingleton tipinde olan instance ismindeki sınıf değişkeni singleton nesnedir. Bu sınıf ilk kullanıldığında private olan konstruktör bir defaya mahsus olarak işlem göreceği için instance degişkeni bir singleton nesne haline gelecektir. Buradaki sorun, reflection yöntemleri kullanılarak private olan konstruktörün birden fazla singleton nesne oluşturmasını sağlayabilmektir. Bunun yanısıra eger MySingleton sınıfı Serializable interface sınıfını implemente ediyorsa, readResolve() metodunun da implemente edilmesi gerekmektedir, aksi taktirde deserializasyon esnasında yeni bir nesne oluşturulur ki, bu da singleton tasarım şablonunun mantığına ters düşer.

Java 1.5 den itibaren kullanıma sunulan Enum sınıfı ile singleton nesneler oluşturmak mümkündür. Bunun bir örneği aşağıda yer almaktadır.

[source language=’java’]
package com.kurumsaljava.com.designpatterns.singleton;

public enum MySingletonEnum
{
INSTANCE;

public void myMethod()
{

}
}
[/source]

MySingletionEnum.INSTANCE sadece ve sadece bir kere mevcut olan bir singleton değişkendir. Bunun yanısıra reflection metotları kullanılarak yeni bir singleton olmayan nesne oluşturulamaz. Ayrıca serializasyon işlemlerinde readResolve() kullanılmak zorunda kalmadan enum nesnesinin singletonluğu garanti edilmektedir. Tek elementli olan bir enum sınıfı singleton tasarım şablonunu implemente etmek i.in kullanılabilecek en iyi yöntemdir.

SCEA 5 (Sun Certified Enterprise Architect)

Bugün itibariyle SCEA 5 (Sun Certified Enterprise Architect) sertifikasını almış bulunuyorum. Bir seneye yayılan sertifikasyon sürecinde üç değişik sınav yaparak, bu sertifikayı edindim. Gerçekten kolay değildi, bir de JEE 5 alanında uzman olduğumu düşünürdüm ;-)

SCEA 5 sertifikası Sun tarafından sunulan Java sertifikasyon programının en en son kademesinde bulunan ve yazılım mimarisi ile uğraşan yazılımcıların edinebilecekleri bir sertifika. SCEA 5 sertifikasyon süreci üç sınavdan oluşuyor. İlk sınav JEE 5 teknolojisini kapsayan iki ya da daha fazla seçenekli sorulardan oluşuyor. İkinci sınav bir JEE 5 projesini kapsıyor. Sun, sertifika adayına üzerinde çalışmak üzere bir proje tayin ediyor. Sertifika adayı Sun tarafından belirlenen kriterler doğrultusunda JEE 5 teknolojisi ile örnek bir çözüm hazırlayarak, çözümünü Sun’a iletiyor. Son sınavda sertifika adayı geliştirdiği proje hakkında sorular cevaplayarak, çözümün kendisine ait olduğunu ispatlamak zorunda.

Sun Java sertifikasyon programi hakkında detayları bu link üzerinden edinebilrisiniz

Kaynak Kitaplar

  • Java Design Patterns
  • EJB 3 in Action
  • The J2EE Architects’s Handbook
  • Mastering Enterprise JavaBeans 3.0
  • Core J2EE Design Patterns
  • Sun Certified Enterprise Architecture for J2EE Technology Study Guide
  • KurumsalJava.com Tasarım Şablonları Bölümü
    • Sertifikasyon İÇERİĞİ

      SCEA 5 üç bölümden (sınav) oluşuyor. Bunlar

      • CX-310-052
      • CX-310-301A
      • CX-310-062

      CX-310-052

      SCEA sertikasyonunun ilk basamağını CX-310-052 sınavı oluşturuyor. Bu sınavın kapsadığı alan şu şekilde:

      Section 1: Application Design Concepts and Principles

      • Explain the main advantages of an object-oriented approach to system design including the effect of encapsulation, inheritance, and use of interfaces on architectural characteristics.
      • Describe how the principle of “separation of concerns” has been applied to the main system tiers of a Java Platform, Enterprise Edition application. Tiers include client (both GUI and web), web (web container), business (EJB container), integration, and resource tiers.
      • Describe how the principle of “separation of concerns” has been applied to the layers of a Java EE application. Layers include application, virtual platform (component APIs), application infrastructure (containers), enterprise services (operating system and virtualization), compute and storage, and the networking infrastructure layers.

      Section 2: Common Architectures

      • Explain the advantages and disadvantages of two-tier architectures when examined under the following topics: scalability, maintainability, reliability, availability, extensibility, performance, manageability, and security.
      • Explain the advantages and disadvantages of three-tier architectures when examined under the following topics: scalability, maintainability, reliability, availability, extensibility, performance, manageability, and security
      • Explain the advantages and disadvantages of multi-tier architectures when examined under the following topics: scalability, maintainability, reliability, availability, extensibility, performance, manageability, and security.
      • Explain the benefits and drawbacks of rich clients and browser-based clients as deployed in a typical Java EE application.
      • Explain appropriate and inappropriate uses for web services in the Java EE platform

      Section 3: Integration and Messaging

      • Explain possible approaches for communicating with an external system from a Java EE technology-based system given an outline description of those systems and outline the benefits and drawbacks of each approach.
      • Explain typical uses of web services and XML over HTTP as mechanisms to integrate distinct software components.
      • Explain how JCA and JMS are used to integrate distinct software components as part of an overall Java EE application.

      Section 4: Business Tier Technologies

      • Explain and contrast uses for entity beans, entity classes, stateful and stateless session beans, and message-driven beans, and understand the advantages and disadvantages of each type.
      • Explain and contrast the following persistence strategies: container-managed persistence (CMP) BMP, JDO, JPA, ORM and using DAOs (Data Access Objects) and direct JDBC technology-based persistence under the following headings: ease of development, performance, scalability, extensibility, and security.
      • Explain how Java EE supports the deployment of server-side components implemented as web services and the advantages and disadvantages of adopting such an approach.
      • Explain the benefits of the EJB 3 development model over previous EJB generations for ease of development including how the EJB container simplifies EJB development.

      Section 5: Web Tier Technologies

      • State the benefits and drawbacks of adopting a web framework in designing a Java EE application
      • Explain standard uses for JSP pages and servlets in a typical Java EE application.
      • Explain standard uses for JavaServer Faces components in a typical Java EE application.
      • Given a system requirements definition, explain and justify your rationale for choosing a web-centric or EJB-centric implementation to solve the requirements. Web-centric means that you are providing a solution that does not use EJB components. EJB-centric solution will require an application server that supports EJB components.

      Section 6: Applicability of Java EE Technology

      • Given a specified business problem, design a modular solution that solves the problem using Java EE.
      • Explain how the Java EE platform enables service oriented architecture (SOA) -based applications.
      • Explain how you would design a Java EE application to repeatedly measure critical non-functional requirements and outline a standard process with specific strategies to refactor that application to improve on the results of the measurements.

      Section 7: Patterns

      • From a list, select the most appropriate pattern for a given scenario. Patterns are limited to those documented in the book – Alur, Crupi and Malks (2003). Core J2EE Patterns: Best Practices and Design Strategies 2nd Edition and named using the names given in that book.
      • From a list, select the most appropriate pattern for a given scenario. Patterns are limited to those documented in the book – Gamma, Erich; Richard Helm, Ralph Johnson, and John Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software and are named using the names given in that book.
      • From a list, select the benefits and drawbacks of a pattern drawn from the book – Gamma, Erich; Richard Helm, Ralph Johnson, and John Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software.
      • From a list, select the benefits and drawbacks of a specified Core J2EE pattern drawn from the book – Alur, Crupi and Malks (2003). Core J2EE Patterns: Best Practices and Design Strategies 2nd Edition.

      Section 8: Security

      • Explain the client-side security model for the Java SE environment, including the Web Start and applet deployment modes.
      • Given an architectural system specification, select appropriate locations for implementation of specified security features, and select suitable technologies for implementation of those features
      • Identify and classify potential threats to a system and describe how a given architecture will address the threats.
      • Describe the commonly used declarative and programmatic methods used to secure applications built on the Java EE platform, for example use of deployment descriptors and JAAS.

      Sinav için hazırlıklarınızı tamamladıktan sonra Sun’a 200 dolar ödeyerek, bu sınava girebiliyorsunuz. Sınav yeri Prometric üzerinden belirlenebiliyor.

      CX-310-301A

      Birinci sınavı (CX-310-052) başarıyla tamamladıktan sonra sırada SCEA 5’in ikinci basamağı olan CX-310-301A yer almaktadır. Bu bir sınavdan ziyade JEE 5.0 teknolojisi ile geliştirilmiş bir projedir. Sertifika adayının JEE 5.0 teknoloji bütününü kullanarak verilen kriterler doğrultusunda projeyi tamamlaması ve Sun firmasına iletmesi gerekmektedir. Bana Sun tarafından Dreamcar projesi tayin edildi. Gizlilik prensipleri doğrultusunda burada ne yazik ki tüm detayları açıklamam mümkün değil. Lakin birkaç cümle ile gidişatın nasıl olduğunu sizlere aktarmak isterim.

      CX-310-301A çercevesinde Sun, sertifika adayının mevcut bir proje üzerinde mimari oluşturma ve açıklama yeteneklerini sınamaktadır. Sertifika adayının, proje gereksinimlerinin UML diagramları oluşturarak nasıl çözümlenebileceklerini açıklamaklası gerekmektedir. Sertifika adayı 6-10 arası değişen proje gereksinimini (requirement) Sequence Diagram, Class Diagram, Components Diagram ve Deployment Diagram kullanarak, oluşturduğu çözümü UML diagramları aracılığıyla görselleştirir. Sertifika adayının oluşturduğu çözümlerde hangi teknolojiyi ve tasarım şablonlarını kullandığını detaylandırması gerekmektedir. Dreamcar projesi için kullandığım tasarım şablonları şu şekilde idi:

      Presentation tier: MVC – Model View Controller

      MVC is used in most of the popular web frameworks (JSF, Struts…). In my implementation I used the domain objects as models, Java Server Pages as views and servlets as controller. View is only responsible for displaying data to the user. Navigation, validation and business logic access is done in controller classes.

      Presentation tier: View Helper

      Business domain objects are also used as view helpers when rendering JSPs.

      Presentation tier: Business Delegate

      In order to hide the complexity of accessing remote business logic (EJB) from clients I used a BusinessDelagate class.

      Presentation tier: Service Locator

      The BusinessDelegate class uses internally a ServiceLocator class to locate the remote business logic classes via JNDI calls. It caches remote interface instances to speed up the access to remote objects.

      Business tier: Session Facade

      The stateless session beans are used as session facade classes. The session facade centralizes and controls the access to business logic.

      Business tier: Business Object (BO)

      I used business object classes to encapsulate the business logic. The session beans uses the business object to delegate the business logic calls the business object instances. With business objects the business logic is encapsulated and reusable.

      Database tier: Data Access Object (DAO)

      I used DAO to isolate the database access for business tier. The business tier should not know any specific details about data storage. One can use other DAO implementations to change the data storage kind.

      Database tier: Domain Store

      I used the domain store design pattern to seperate persistance from my object model. JPA Entity manager is used to persist the busines domain objects.

      Sertifika adayından kod yazması beklenmemektedir. Çözümlerin UML diagramları aracılıği ile oluşturulmuş olması yeterlidir.

      CX-310-062

      Sertifikanın üçüncü basamağı CX-310-062‘dır. Bu sınavı yapabilmek için ikinci basamakta sertifika adayına verilen projenin tamamlanmış ve Sun’a iletilmiş olması gerekmektedir. Bu basamakta sertifika adayı, ikinci basamakta oluşturduğu çözüm hakkında kendisine sorulan soruları cevaplamak zorundadır. Bu şekilde sertifika adayının ikinci basamakta oluşturduğu çözümün kendisine ait olup, olmadıği kontrol edilmektedir.

      Sonuç

      Yazılım mimarisi ve Java ile uğraşan yazılımcıların gözdesi olan SCEA 5 sertifikası kolay elde edilebilecek bir sertifika değil. Bu sebepten dolayı da kıymetli ve itibarlı bir serfikadır. Sertifika adayının J2EE ve JEE 5 teknolojilerini yakından tanıması ve çalıştığı projelerde bu teknolojileri kullanmış olması, SCEA 5 sertifikasını edinmesini kolaylaştırmaktadır.

      SCEA 5 sertifikası diğer Java sertifikalarından bağımsız olarak alınabilmektedir. Bu sertifika için herhangi bir ön koşul bulunmamaktadır.


      Özcan Acar
      Java Champion, SCEA 5

Java’da interface ve soyut (abstract) sınıf kullanımı

Java dilinde interface adını taşıyan, tasarım şablonlarında ve modellemede kullanılan sınıflar tanımlamak mümkündür. Bir interface normal bir Java sınıfından farksız bir şekilde tanımlanır. Sınıf tanımlanırken class yerine interface terimi kullanılır.

Bu yazıyı PDF olarak edinebilirsiniz.

  Java'da Interface ve Abstract Sınıflar (273,4 KiB, 60.304 yükleme)