Yeni Sınıf Oluşturma (Extract Class)

Bir sınıfın yüzlerce ya da binlerce satırdan oluşmasının ana sebeplerinden birisi bu sınıfa birden fazla sorumluluğun yüklenmiş olmasıdır. Single Repsonsiblity (SRP) prensibinden de bildigimiz gibi her sınıfın sadece ve sadece bir sorumluluk alanı olmalıdır yani sınıf sadece bir iş yapmalıdır ve bu işlemi iyi yapmalıdır. Aşağıda yer alan Order sınıfı SRP ile uyumlu değildir. Yeni Sınıf Oluşturma (Extract Class) refactoring metodunu kullanarak bu sınıfı SRP’ye uygun hale getirebiliriz.

[source language=’java’]
package com.kurumsaljava.refactoring.extractClass;

public class Order {

private double price;

// siparis verilen ürün
private String productName;

// siparis veren müsteri bilgileri
private String customerName;
private String customerFirstnam;
private String customerBirthdate;

// müsteri adresi
private String customerStreet;
private String customerHouseNumber;
private String customerZip;
private String customerTown;
private String customerCountry;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerFirstnam() {
return customerFirstnam;
}
public void setCustomerFirstnam(String customerFirstnam) {
this.customerFirstnam = customerFirstnam;
}
public String getCustomerBirthdate() {
return customerBirthdate;
}
public void setCustomerBirthdate(String customerBirthdate) {
this.customerBirthdate = customerBirthdate;
}
public String getCustomerStreet() {
return customerStreet;
}
public void setCustomerStreet(String customerStreet) {
this.customerStreet = customerStreet;
}
public String getCustomerHouseNumber() {
return customerHouseNumber;
}
public void setCustomerHouseNumber(String customerHouseNumber) {
this.customerHouseNumber = customerHouseNumber;
}
public String getCustomerZip() {
return customerZip;
}
public void setCustomerZip(String customerZip) {
this.customerZip = customerZip;
}
public String getCustomerTown() {
return customerTown;
}
public void setCustomerTown(String customerTown) {
this.customerTown = customerTown;
}
public String getCustomerCountry() {
return customerCountry;
}
public void setCustomerCountry(String customerCountry) {
this.customerCountry = customerCountry;
}
}
[/source]

Yeni Sınıf Oluşturma (Extract Class) refactoring metodunu uygulayarak ürün, müşteri ve müşteri adresi bilgilerini kendi sınıflarına taşıyabiliriz. Bu işlemin ardından Order sınıfı aşağıdaki yapıya sahip olacaktır:

[source language=’java’]

package com.kurumsaljava.refactoring.extractClass;

public class OrderTemp {

private Product product = new Product();

private Customer customer = new Customer();

private CustomerAddress address = new CustomerAddress();

public double getPrice() {
return product.getPrice();
}
public void setPrice(double price) {
this.product.setPrice(price);
}
public String getProductName() {
return product.getProductName();
}
public void setProductName(String productName) {
this.product.setProductName(productName);
}
public String getCustomerName() {
return customer.getCustomerName();
}
public void setCustomerName(String customerName) {
this.customer.setCustomerName(customerName);
}
public String getCustomerFirstnam() {
return customer.getCustomerFirstnam();
}
public void setCustomerFirstnam(String customerFirstnam) {
this.customer.setCustomerFirstnam(customerFirstnam);
}
public String getCustomerBirthdate() {
return customer.getCustomerBirthdate();
}
public void setCustomerBirthdate(String customerBirthdate) {
this.customer.setCustomerBirthdate(customerBirthdate);
}
public String getCustomerStreet() {
return address.getCustomerStreet();
}
public void setCustomerStreet(String customerStreet) {
this.address.setCustomerStreet(customerStreet);
}
public String getCustomerHouseNumber() {
return address.getCustomerHouseNumber();
}
public void setCustomerHouseNumber(String customerHouseNumber) {
this.address.setCustomerHouseNumber(customerHouseNumber);
}
public String getCustomerZip() {
return address.getCustomerZip();
}
public void setCustomerZip(String customerZip) {
this.address.setCustomerZip(customerZip);
}
public String getCustomerTown() {
return address.getCustomerTown();
}
public void setCustomerTown(String customerTown) {
this.address.setCustomerTown(customerTown);
}
public String getCustomerCountry() {
return address.getCustomerCountry();
}
public void setCustomerCountry(String customerCountry) {
this.address.setCustomerCountry(customerCountry);
}
}
[/source]

Değişik sorumluluk alanlarını ifade eden sınıf değişkenleri yerine sorumluluk alanını kapsayan sınıf tipinde değişkenler kullandık. Böylece her bir sorumluluk alanını yeni bir sınıf ile izole etmiş olduk. Order bünyesinde yer alan metotlar delegasyon usulü ile bu yeni degişkenleri kullanabilirler. Eğer bunu istemiyorsak Order bünyesinde bulunan eski sınıf değişkenleri ile ilişkili tüm metotları kaldırarak, Order sınıfını kullanan diğer sınıfların oluşturdugumuz yeni sınıfları kullanmalarını sağlayabiliriz.


EOF (End Of Fun)
Özcan Acar

Common Closure Principle (CCP) – Ortak Kapama Prensibi

Yazılım sistemi müşteri gereksinimleri doğrultusunda zaman içinde değişikliğe uğrar. Meydana gelen değişiklerin sistemde bulunan birçok paketi etkilemesi, sistemin bakılabilirliğini negatif etkiler. CCP’ye göre yapılan değişikliklerin sistemin büyük bir bölümünü etkilemesini önlemek için, aynı sebepten dolayı değişikliğe uğrayabilecek sınıfların aynı paket içinde yer alması gerekir. CCP daha önce incelediğimiz, sınıflar için uygulanan Single Responsibility (SRP) prensibinin paketler için uygulanan halidir. Her paketin değişmek için sadece bir sebebi olmalıdır. CCP uygulandığı taktirde sistemin bakılabilirliği artırılır ve test ve yeni sürüm için harcanan zaman ve emek azaltılır.

Bu yazıyı PDF olarak edinebilirsiniz.

Metodu Metot Nesnesine Dönüştürme (Replace Method with Method Object)

Metodu Metot Nesnesine Dönüştürme (Replace Method with Method Object) refactoring metodu aşağıdaki durumlarda kullanılabilir:

  • Çok sayıda lokal değişken Yeni Metot Oluşturma (Extract Method) refactoring metodunun kullanımını engelliyor.
  • Uzun bir metot bünyesinde tanımlanan lokal değişkenlerin kapsama alanı (scope) geniş yani lokal değişkenler metodun sonuna kadar kullanılıyor.
  • Metot SRP tasarım prensibi ile uyumlu degil yani refactor etmek istediğimiz metot, bünyesinde bulunduğu sınıfa yeni bir sorumluluk yüklüyor.

Metodu Metot Nesnesine Dönüştürme (Replace Method with Method Object) refactoring metodu ile yeni bir sınıf oluşturulur ve uzun metod bu sınıfa taşınır. Metot bünyesinde tanımlanmış olan lokal değişkenler sınıf değişkenleri olarak değiştirilir. Metodun yer aldığı eski sınıf bünyesinde yeni oluşturulan sınıftan bir nesne oluşturularak, yeni sınıf bünyesindeki metoda delege edilir.

Aşağıda yer alan örnekte calculatePrice metodu Order sınıfına yeni bir sorumluluk yüklediği için Metodu Metot Nesnesine Dönüştürme (Replace Method with Method Object) refactoring metodunu kullanarak PriceCalculator isminde yeni bir sınıf oluşturuyor ve calculatePrice metodunu bu sınıfa taşıyoruz.

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

public class Order {

private double price;
private String locale;

public static Double calculatePrice(final Order order) {
Double result = new Double(0);
final String locale = order.getLocale();
final double price = order.getPrice();
double taxRate = 0;
if (locale != null && locale.equals(“de_DE”)) {
taxRate = 19.00;
final Double tax = Double.valueOf((price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
} else if (locale != null && locale.equals(“tr_TR”)) {
taxRate = 18.00;
final Double tax = Double.valueOf((price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}

public double getPrice() {
return price;
}

public String getLocale() {
return locale;
}
}
[/source]

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

public class PriceCalculator {

public Double calculatePrice(final Order order) {
Double result = new Double(0);
final String locale = order.getLocale();
final double price = order.getPrice();
double taxRate = 0;
if (locale != null && locale.equals(“de_DE”)) {
taxRate = 19.00;
final Double tax = Double.valueOf((price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
} else if (locale != null && locale.equals(“tr_TR”)) {
taxRate = 18.00;
final Double tax = Double.valueOf((price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}
}
[/source]

Bu işlemin ardında Order sınıfını aşağıdaki şekilde değiştiriyoruz.

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

public class Order {

private double price;
private String locale;

public static Double calculatePrice(final Order order) {
return new PriceCalculator().calculatePrice(order);
}

public double getPrice() {
return price;
}

public String getLocale() {
return locale;
}
}
[/source]

Bu değişikliğin ardından Order sınıfında bulunan fiyat hesaplama metodunu PriceCalculator sınıfına taşımış olduk. Order sınıfı fiyat hesaplama işlemi için yeni oluşturduğumuz PriceCalculator sınıfını kullanır hale geldi.


EOF (End Of Fun)
Özcan Acar

Geçici Değişkenlerin Sorguya Dönüştürülmesi (Replace Temp with Query)

Bir metot bünyesinde bir işlemin sonucu geçici bir değişkende saklanabilir. Aşağıda yer alan kod örneğinde basePrice geçici bir değişkendir ve ihtiva ettiği değer fiyat * adet şeklinde hesaplanmaktadır. Replace Temp with Query refactoring metodunu kullanılarak geçici değişkenin değerini elde etmek için kullanılan yapı yeni bir metot bünyesinde taşınır. Geçici değişkenin kullanıldığı diğer alanlar metot ismiyle değiştirilir. Bu şekilde yapılan işlemin başka metotlar bünyesinde kullanılması sağlanır.

[source language=”java”]
public double calculatePrice(final double price, final int itemCount)
{
double basePrice = price * itemCount;
double tax = ( basePrice / 100 ) * 18.00;
double totalPrice = basePrice + tax;
return totalPrice;
}
[/source]

Refactoring işleminin ardından oluşan yeni yapı aşağıdaki şekildedir.

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

public class ReplaceTempwithQueryRefactored {

private double price;
private int itemCount;

public ReplaceTempwithQueryRefactored(final double pPrice,
final int pItemCount) {
this.price = pPrice;
this.itemCount = pItemCount;
}

public double calculatePrice() {
double tax = (getBasePrice() / 100) * 18.00;
return getBasePrice() + tax;
}

private double getBasePrice() {
return price * itemCount;
}
}
[/source]

getBasePrice isminde, başka bir metot bünyesinde tekrar kullanılabilir yapıda yeni bir metot oluşturmus olduk. getBasePrice sorgu (query) yaptığımız bir metotdur. Belli bir değeri elde etmek için bu metodu sorgulamaktayız. basePrice isimli geçici değişkeni ortadan kaldırmak için getBasePrice metodunu kullandık yani geçici değişkeni bir sorguya dönüştürmüş olduk. Bu sebepten dolayı Replace Temp with Query‘yi Geçici Değişkenlerin Sorguya Dönüştürülmesi olarak tercüme ettim.

calculatePrice metodunu tekrar gözden geçirdiğimiz zaman, katma değer verginin hesaplandığı görmekteyiz. Eğer daha önce basePrice geçici değişkenini bir sorguya dönüştürmemiş olsaydık, katma değer vergisinin hesaplandığı bölümü refactor etmemiz kolay olmayacaktı. Ama bu şekilde katma değer vergisinin hesaplandığı kod kısmını aşağıdaki şekilde refactor edebiliriz.

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

public class ReplaceTempwithQueryRefactored {

private static final double TAX_RATE = 18.00;
private double price;
private int itemCount;

public ReplaceTempwithQueryRefactored(final double pPrice,
final int pItemCount) {
this.price = pPrice;
this.itemCount = pItemCount;
}

public double calculatePrice() {
double tax = calculateTax();
return getBasePrice() + tax;
}

private double calculateTax() {
return (getBasePrice() / 100) * TAX_RATE;
}

private double getBasePrice() {
return price * itemCount;
}
}
[/source]


EOF (End Of File)
Özcan Acar

Parametrele Değer Atamasının Kaldırılması (Remove Assignments to Parameters)

Metot imzasında yer alan parametrelere metot bünyesinde değer atanmamalıdır. Bu gibi atamalar istenmeyen sonuçlar doğurabilir. Remove Assignments to Parameters refactoring metodu kullanılarak bu tür parametre değer atamaları geçici degişkenlere yapılmalıdır.

[source language=”java”]
private void execute(int value) {
if (value == 10)
value = 1;
doSometthink(value);
}
[/source]

Yukarda yer alan metot Remove Assignments to Parameters uygulandığı taktirde aşağıdaki yapıya sahip olacaktır. Metot bünyesinde result isminde yeni geçici bir değişken tanımlanmış ve böylece parametrenin taşıdığı değer metot gövdesinde korunmuştur.

[source language=”java”]
private void execute(int value) {
int result = 0;
if (value == 10)
result = 1;
doSometthink(result);
}
[/source]

final kelimesi kullanılarak metot bünyesinde parametrelere değer atanması otomatik olarak engellenebilir.

[source language=”java”]
private void execute(final int value) {
if (value == 10)
value = 1;
doSometthink(value);
}
[/source]

Yukarda yer alan value=1 şeklindeki atama, value parametresi final olduğu için aşağıda yer alan hataya sebep olacaktır:

The final local variable value cannot be assigned. It must be blank and not using a compound assignment


EOF (End Of Fun)
Özcan Acar

Yeni Metot Oluşturma (Extract Method)

Uzun metotları daha okunabilir ve kompak hale getirmek için Extract Method refactoring metodunu kullanabiliriz.

[source language=”java”]
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}
[/source]

calculatePrice metodu bünyesinde ülkeye bağlı olarak katma deger vergisi oranı (taxRate) tespit edilmektedir. Bu işlemi aşağıdaki şekilde başka bir metot bünyesine alarak, calculatePrice metodunu küçültebiliriz.

[source language=”java”]
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
taxRate = getTaxRateBasedOnCountry(locale);
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}

private double getTaxRateBasedOnCountry(String locale) {
double taxRate = 0.0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
return taxRate;
}
[/source]

Bu refactoring metodunu kullanırken seçtiğimiz metot isimlerinin, işlemin nasıl yapıldığını değil, hangi işlemin yapıldığını yansıtmasına dikkat etmemiz gerekmektedir. Bu kodun okunmasını kolaylaştıracaktır. Uzunda olsa yapılan işlemleri tanımlayan metot isimleri kullanmaktan kaçınmamamız gerekmektedir.

Yeni oluşturulan metodun küçük tutulmasında fayda vardır. Bu, yeni metodun başka bir metot bünyesinde tekrar kullanım şansını artıracaktır.

Eğer uzun bir metodun bu şekilde parçalarına bölünmesi okunurluluğu ve kodun anlaşılma seviyesini artırıyorsa Extract Method refactoring metodu kullanılmalıdır.

Bu refactoring işleminin ardından bile calculatePrice metodunu daha da kompak hale getirme potansiyeli vardır. calculatePrice metodunu aşağıdaki şekilde tekrar refactor etmeye ne dersiniz?

[source language=”java”]
private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}

private Double getCalculatedPriceWithTax(double price, double taxRate) {
Double result;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}

private double getTaxRateBasedOnCountry(String locale) {
double taxRate = 0.0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
return taxRate;
}
[/source]

Şimdi calculatePrice metodunun eski ve yeni halini bir kıyaslayalım. Hangi metot size ilk bakışta ne yaptığını daha kolay anlatıyor?

Eski hali:
[source language=”java”]
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}

[/source]

Yeni hali:
[source language=”java”]
private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}
[/source]

calculatePrice metodunda gördüğümüz gibi oluşturdugumuz metotlar 4-5, en fazla 6-7 satırı geçmemelidir. Bu kurala uyduğumuz taktirde aşağıda yer aldığı gibi, okunması, bakımı ve geliştirilmesi çok kolay sınıflar oluşur.

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

public class TaxCalculator {
private static final String LOCALE_DE = “de_DE”;
private static final String LOCALE_TR = “tr_TR”;
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;
private static final Double NULL_VALUE_DOUBLE_OBJECT = new Double(0);

public Double calculate(String locale, double price) {
return calculateBasedOnLocale(locale, price);
}

private Double calculateBasedOnLocale(String locale, double price) {
if (isCountryGermany(locale)) {
return calculatePrice(price, locale);
} else if (isCountryTurkishRepublic(locale)) {
return calculatePrice(price, locale);
} else
return NULL_VALUE_DOUBLE_OBJECT;
}

private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}

private Double getCalculatedPriceWithTax(double price, double taxRate) {
Double tax = Double.valueOf( (price / 100) * taxRate);
return Double.valueOf(tax.doubleValue() + price);
}

private double getTaxRateBasedOnCountry(String locale) {
if (isCountryGermany(locale)) {
return TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
return TAX_RAT_TURKISH_REPUBLIC;
} else
return NULL_VALUE_DOUBLE_OBJECT.doubleValue();
}

private boolean isCountryGermany(String locale) {
return locale != null && locale.equals(LOCALE_DE);
}

private boolean isCountryTurkishRepublic(String locale) {
return locale != null && locale.equals(LOCALE_TR);
}
}
[/source]

Refactoring için çıkış noktamız aşağıdaki sınıftı. Umarım Extract Method refactoring metodunun ne kadar kıymetli bir araç olduğunu gösterebilmişimdir :)

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

public class TaxCalculatorOld {

public Double calculate(String locale, double price) {
Double result = new Double(0);
double taxRate = 0;
if (locale != null && locale.equals(“de_DE”)) {
taxRate = 19.00;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
} else if (locale != null && locale.equals(“tr_TR”)) {
taxRate = 18.00;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}
}
[/source]

Koşulları Parçalarına Ayırma (Reverse Conditional)

TaxCalculator sınıfı, uygulamanın kullanıldığı ülkeye bağlı olarak katma değer vergisini ihtiva eden fiyatı hesaplamak için kullanılmaktadır.

[source language=’java’]
package com.kurumsaljava.refactoring.reverseconditional;

public class TaxCalculator{
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;

public Double calculate(String locale, double price){
Double result = new Double(0.0);

if(locale != null && locale.equals(“de_DE”)){
Double tax = Double.valueOf( (price / 100 ) * TAX_RAT_GERMANY);
result = Double.valueOf(tax.doubleValue() + price);
}
else if(locale != null && locale.equals(“tr_TR”)){
Double tax = Double.valueOf( (price / 100) * TAX_RAT_TURKISH_REPUBLIC);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}
}

[/source]

calculate gibi metotlarda karşılaştığımız en büyük sorunlardan birisi if/else blokları içinde mantıksal operatörler kullanılarak bir takım koşulların tanımlanması ve bu koşullara bağımlı olarak bazı işlemlerin yapılmasıdır. Bu tür bir programlama tarzı ne yazık ki uzun ve anlaşılması zor metotların oluşmasına sebep olmaktadır. calculate metoduna baktığımızda if/else bünyesinde ne olup bittiğini belki anlamaktayız, lakın neden bunun yapıldığını anlamak her zaman mümkün olmamaktadır.

Bahsettiğim sorunları ortadan kaldırmak için Reverse Conditional refactoring metodunu kullanabiliriz.

[source language=’java’]
package com.kurumsaljava.refactoring.reverseconditional;

public class TaxCalculator {
private static final String LOCALE_DE = “de_DE”;
private static final String LOCALE_TR = “tr_TR”;
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;

public Double calculate(String locale, double price) {
Double result = new Double(0.0);

if (isCountryGermany(locale)) {
result = calculatePrice(price, locale);
} else if (isCountryTurkishRepublic(locale)) {
result = calculatePrice(price, locale);
}
return result;
}

private boolean isCountryTurkishRepublic(String locale) {
return locale != null && locale.equals(LOCALE_TR);
}

private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;

if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}

Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}

private boolean isCountryGermany(String locale) {
return locale != null && locale.equals(LOCALE_DE);
}
}
[/source]

Yeni implmentasyonu gözden geçirdiğimizde calculate metodunda olup bitenlerin ne anlama geldiğini daha iyi algılayabilmekteyiz. Reverse Conditional refactoring metodunu kullanarak if/else koşullarını parçalara böldük ve yeni metotlar oluşturduk. Kullandığımız metot isimleri yapılan işlemi algılamamızı daha kolaylaştırmakta ve bu şekilde kodun okunulurluk derecesi artmaktadır.


EOF (End Of Fun)
Özcan Acar

IDL Compiler

Bu aralar bir Corba projesinde çalışıyorum. Corba teknolojisinde interface tanımlama dili olarak IDL kullanılıyor. Eğer bir Corba servisine erişimi sağlamak için size bir IDL verildi ise, bu IDL´i kullanarak client sınıflarını oluşturabilirsiniz. Bu işlem için aşağidaki sınıfı oluşturdum.

Bu implementasyon Sun JDK bünyesinde bulunan com.sun.tools.corba.se.idl.toJavaPortable.Compile ve IBM JDK bünyesinde bulunan com.ibm.idl.toJavaPortable.Compile sınıflarını kullanarak client sınıflarını oluşturuyor. Bu iki sınıf JDK lib dizini altındaki tools.jar içinde yer alıyor. Sınıfın bulunabilmesi için -Djava.home= parametresinin tanımlanmasi gerekiyor. JDK bin dizininde yer alan idlj komutu ile aynı işlemi gerçekleştirebilirsiniz.

[sourcecode language=’java’]
/**
*
* @author F520947 Oezcan Acar
*
*/
public interface IdlToJavaGeneratorService
{
void generate( String workingDir, String idlFile, String outputDirectroy );
}

[/sourcecode]

[sourcecode language=’java’]
import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

import org.codehaus.plexus.util.StringOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.csg.cs.testing.common.CommonConstants;
import com.csg.cs.testing.generator.GeneratorException;
import com.csg.cs.testing.project.ProjectDirectoryUtils;
import com.csg.cs.testing.service.logger.ILogger;

/**
*
* @author F520947 Oezcan Acar
*
*/
@Component
final class DefaultIdlToJavaGeneratorServiceImpl implements IdlToJavaGeneratorService
{

@Autowired
private ILogger logger;

@Autowired
private IIdlParserService parser;

@Override
public void generate( final String inputDirectory, final String idlFile, final String outputDirectory)
{

File idl = new File( ProjectDirectoryUtils.getWorkingDirRoot() + “/” + idlFile );
List< String > includes = parser.getIdlIncludes( idl );

List args = new ArrayList< String >();

args.add( “-emitAll” );
args.add( “-fclient” );
args.add( “-pkgPrefix” );

String interfaceName = parser.getRealInterface( idl );
args.add( interfaceName );
args.add( CommonConstants.SERVICE_PACKAGE );

for (String include : includes)
{
args.add( “-pkgPrefix” );
args.add( include );
args.add( CommonConstants.SERVICE_PACKAGE );
}

List< String > baseInterfaces = parser.getBaseInterfaces( idl );
for (String base : baseInterfaces)
{
args.add( “-pkgPrefix” );
args.add( base );
args.add( CommonConstants.SERVICE_PACKAGE );
}

args.add( “-pkgPrefix” );
args.add( “CS_Inquiry_0_0” );
args.add( CommonConstants.SERVICE_PACKAGE );

args.add( “-pkgPrefix” );
args.add( “CS_Inquiry_0_0Operations” );
args.add( CommonConstants.SERVICE_PACKAGE );

args.add( “-i” );
args.add( inputDirectory );
args.add( “-td” );
args.add( outputDirectory );
args.add( idlFile );

Class compilerClass = getCompilerClass();
invokeCompiler( compilerClass, args );
}

private Class getCompilerClass()
{
ClassLoader cl = this.getClass().getClassLoader();
Class idljCompiler;
try
{
idljCompiler = Class.forName(getIDLCompilerClass());
}
catch (ClassNotFoundException e)
{
try
{
File javaHome = new File(System.getProperty(“java.home”));
logger.log(“java.home=” + javaHome);
logger.log(“tools.har=” + javaHome+”/lib/tools.jar”);

File toolsJar = new File(javaHome, “/lib/tools.jar”);
URL toolsJarUrl = toolsJar.toURL();
URLClassLoader urlLoader = new URLClassLoader(new URL[]
{ toolsJarUrl }, cl);

// Unfortunately the idlj compiler reads messages using the
// system class path.
// Therefore this really nasty hack is required.
System.setProperty(“java.class.path”,System.getProperty(“java.class.path”)+ System.getProperty(“path.separator”)+ toolsJar.getAbsolutePath());
if (System.getProperty(“java.vm.name”).indexOf(“HotSpot”) != -1)
{
urlLoader.loadClass(“com.sun.tools.corba.se.idl.som.cff.FileLocator”);
}
idljCompiler = urlLoader.loadClass(getIDLCompilerClass());
}
catch (Exception notUsed)
{
throw new GeneratorException(notUsed);
}
}
return idljCompiler;
}

private String getIDLCompilerClass()
{
String vendor = System.getProperty( “java.vm.vendor” );

if ( vendor.indexOf( “IBM” ) != -1 )
{
return “com.ibm.idl.toJavaPortable.Compile”;
}
return “com.sun.tools.corba.se.idl.toJavaPortable.Compile”;
}

private void invokeCompiler( final Class compilerClass, final List args)
{
logger.log(“Current dir : ” + System.getProperty(“user.dir”));
Method compilerMainMethod;
String arguments[];

args.add(0, “-verbose”);
arguments = (String[]) args.toArray(new String[args.size()]);
String command = compilerClass.getName();
for (int i = 0; i < arguments.length; i++) { command += " " + arguments[i]; } logger.log(command); try { compilerMainMethod = compilerClass.getMethod("main", new Class[] { String[].class }); } catch (NoSuchMethodException e1) { throw new GeneratorException("Error: Compiler had no main method"); } int exitCode = 0; // Backup std channels PrintStream stdErr = System.err; PrintStream stdOut = System.out; // Local channels StringOutputStream err = new StringOutputStream(); StringOutputStream out = new StringOutputStream(); System.setErr(new PrintStream(err)); System.setOut(new PrintStream(out)); try { Object retVal = (Object) compilerMainMethod.invoke(compilerClass,new Object[]{ arguments }); if (retVal != null &amp;amp;amp;&amp;amp;amp; retVal instanceof Integer) exitCode = ((Integer) retVal).intValue(); } catch (InvocationTargetException e) { throw new GeneratorException(e); } catch (Throwable e) { e.printStackTrace(); throw new GeneratorException("IDL compilation failed"); } finally { if (!"".equals(out.toString())) logger.log(out.toString()); if (!"".equals(err.toString())) logger.log(err.toString()); // Restore std channels System.setErr(stdErr); System.setOut(stdOut); } } } [/sourcecode]
EOF ( End Of Fun )
Özcan Acar

Tek Kelimeyle Harika!

Bugün Amazon.com’dan sipariş ettiğim Kindle DX geldi. Tek klime ile harika. Daha önce uzunca bir zaman Irex Iliad kullandım. Ama Kindle DX onu arattırmayacak sanırım :)

Teknik Özellikleri

Boyutları
264 x 180 mm x 9,7 mm

Ekran
9,7 Inch (24,6 cm)
824 x 1200 Pixel
150 ppi

Hafıza
4GB

CPU
ARM 11
532 MHz

Bateri
3,7 V
1530 mAh

Desteklediği Formatlar
Kindle (.azw)
Text (.txt)
HTML (.html)
Mobipocket (.mobi, .prc)
Adobe PDF (.pdf)

Kindle DX’in en ilginç özelliklerinden birisi, bünyesinde bir EDGE/UMTS modem barındırması. Amazon’dan alınan kitaplar anında mobile network üzerinden Kindle DX’in hafızasına ışınlanıyor.

Benim asıl ilgimi çeken, kullanılan E-Ink teknolojisi ve Kindle DX’in PDF formatındaki dosyaları okuyabiliyor olması. Doğal olarak ilk aldığım tepki neden IPad almadığım oldu. IPad bir LCD ekranına sahip ve baterisi 10 saat kadar dayanabiliyor. LCD ekranda PDF dosyaları okumanın gözler için ne kadar yorucu olduğunu düşünebilirsiniz. Buna karşın Kindle DX E-Ink teknolojisiyle çalışan bir ekrana sahip. Böyle bir ekranda bir PDF dosyasını okumak, bir kitabı okumaktan farksız ve bir o kadar da zevkli.

Subclipse Şifresi

Elipse altında Subversion pluginini kullanıyorsanız, bir Subversion repositorisine bağlanmak için kullandığınız şifreyi nasıl değiştirebileceğinizi merak etmiş olabilirsiniz. Eclipse altında bu şifreyi değiştirmek mümkün değil, çünkü plugin şifreyi %APPDATA%\Subversion\auth (Linux altında ~/.subversion/auth) altında saklıyor. Bu dizini sildiğiniz taktirde, tekrar şifreyi girme panelini görebilirsiniz.

Yazılım Maketleri

Dün kızıma lego parçalarından oluşan bir set aldım. Bu sabah beraber lego parçalarından kaleler yaparken birşeyin farkına vardım. Yazılım mühendisleri olarak çok soyut şeylerle ugraşıyoruz. Artık soyutluk seviyesi öyle bir hal almış ki, geçenlerde kendimi CPU içide yer alan registerlerin Assembler kullanılarak programlanmasından bahseden bir programcı hakkında “bu kadar low level işlerle uğraşılır mı ya” gibisinden düşünürken yakaladım. Her defasında soyutluk çıtasını bir kademe daha yukarıya çekmeye alışmış ben, somut olan ve ele alınıp, bir mikroskop altında görülebilecek olan CPU registerlerine ne kadar yabancılaşmışım! Bu verebileceğim örneklerden sadece bir tanesi.

İşler soyutlaşdıkça tüm resmi algılamak ve sistemleri modellemek belki daha da kolaylaşıyor, lakin temelde olup bitenleri anlamadığımız sürece, dünyanın en büyük finans krizine sebep olan bankacılardan bir farkımız kalmıyor. Onlarda ne yazik ki temelde çürük kredilerden oluşan yeni finans ürünleri oluşturarak, bilgisiz insanlara pazarladılar. Bu ürünleri kullanarak yeni finans ürünleri soyutladılar. Bu işlem, kimsenin içinde ne olduğunu anlamadığı finans ürünleri oluşana kadar devam etti. Sonuç malum!

Bunların lego ile ne alakası var diye düşünebilirsiniz. Yeni yetişen yazılımcıların OOP (Object Oriented Programming), modüler yazılım sistemleri, komponent tabanlı yazılım, kodun tekrar kullanımı (code reuse) gibi kavramları teorik ve pratik olarak kavramaları çok güç olabilir, çünkü bunlar ilk etapta elle tutulur, gözle görülür olmayan soyut kavramlar. Bu kavramları elle tutulur, gözle görülür hale getirebilseydik nasıl olurdu? Bunun için lego parçalarından faydalanabiliriz.

Siz daha önce bir yazılım sisteminin maketini elinizde tuttunuz mu? Bizler yazılım mimarilerini yazılım mimarlarının kağıtlar üzerinde çizdikleri kutucuklardan tanıyoruz. Bu iki boyutlu resimler örneğin kodun tekrar kullanımını bize ne kadar ifade edebiliyor?

Nasıl mimarlar tasarladıkları evlerin küçük maketlerini yapıyorlarsa, biz yazılım mühendisleri de legoları kullanarak oluşturmak istediğimiz yazılım sistemlerinin maketlerini oluşturabiliriz. Örneğin bir lego parçası tekrar kullanılabilir bir modülü simgeliyor olabilir. Yazılımcı bu lego parçasını eline alıp, sistemin değişik bölümlerinde tekrar kullanabilir. Bunu yaptığı esnada kodun tekrar kullanımının ne anlama geldiğini daha iyi anlayabilir, kodun tekrar kullamının getirdiği avantajları daha iyi görebilir. İnsan elinde tuttuğu üç boyutlu bir nesneyi daha kolay algılama eğilimi gösteriyor. Ben de dün kızımla beraber oynarken bunun farkına vardım. Lego parçalarını bir an için tekrar kullanılabilir yazılım modülleri olarak hayal ettim. Aynı renkte ve boyuttaki lego parçalarını değişik kalelerin (yazılım sistemi olarak düşünün) yapımında kullandım. Lego parçaları birer modül ve modülleri bir araya getirerek, değişik sistemler oluşturmak mümkün. Her defasında temel bir lego modülünü icat etmek zorunda kalmadan tekrar tekrar yeni bir sistemin inşasında kullanabiliyorum. İşte oluşturduğumuz yazılım sistemleri de bu prensibe göre inşa edilmeli. Yazılımda işin sırrı tekrar kullanılabilir modüllerde yatıyor.

Kanımca genç bir yazılımcıya bahsettiğim kavramları somutlaştırıp, eline alabileceği üç boyutlu nesneler olarak sunduğumuz taktirde, bu kavramların algılanması daha da somutlaşıyor. Belki yazılım maketleri oluşturmak yazılım mimarilerinin esnekliğini artırabilir. Ne dersiniz? Bu konu hakkında biraz kafa yormamız lazım!


EOF (End Of Fun)
Özcan Acar

Corebanking Next Generation

Yaklaşık 10 aylık bir çalışmanın sonunda 1 şubat 2011 tarihinde İşbankası Corebanking projesindeki görevimi tamamladım. Corebanking projesi, İsbankası’nın 2 sene önce başlatmış olduğu, Cobol ile geliştirilen Mainframe sistemlerinden açık sistemlere (Java, J2EE) geçişi öngören bir proje. Projenin nihayi amacı uzun vadede bankanın alt yapısını tamamen açık sistemlere taşımak ve Mainframe sistemlerini devre dışı bırakmak.

Danışman, teknik ekip lideri ve programcı olarak çalışmış olduğum bu 10 aylık zaman diliminde, Corebanking projesindeki faaliyet alanlarım şu şekilde oldu:

  • Test güdümlü yazılımın proje genelinde kullanımı
  • Continuous Integration ve Continuous Deployment gibi çevik metotların projeye entegrasyonu
  • Yazılım sürecinin iterasyon bazlı organizasyonu (Extreme Programming)
  • Build işlemlerinin otomatize edilmesi (Ant,Maven)
  • Lokal yazılım geliştirme işlemi için Websphere uygulama sunucusunun devre dışı bırakılması ve uygulama sunucusu kullanmak zorunda kalmadan test güdümlü yazılım yapılabilmesi
  • Lokal testler için gerekli referans verilerinin cachelenmesi ve uygulama sunucusunun start-up zamanının optimize edilmesi
  • Veri tabanı üzerindeki yükü azaltmak için proje genelinde bir caching mekanizmasinin implementasyonu (hazelcast)
  • Maven kullanılarak tüm Corebanking projesinin modüler bir yapıya geçirilmesi
  • Nexus kurulumu ve otomatik dependency yönetimi
  • Performans analizi ve logmalası için AOP (Aspect Oriented Programming) kullanımı
  • Performans testlerini yapılması ve performans testlerinde ortaya çıkan problemlerin lokalizasyonu ve eliminasyonu
  • Ekibin çevik süreç, çevik metotlar ve tasarım prensipleri konusunda eğitimi
  • Code ve architecture review
  • Açık sistemler ile Mainframe arasındaki komunikasyonu sağlamak için REST tabanlı bir mimarinin oluşturulması (JBoss EasyREST)

İlk çalışma alanlarımdan birisi build ve deployment işlemlerini otomatize etmek oldu. Bunun için gerekli Ant skriptleri oluşturdum ve build işlemlerinin merkezi bir yerden tetiklenmesini sağladım.

Bir proje bünyesinde kodun sürekli entegre edilmesi (Continuous Integration) önemli ve gerekli bir işlemdir. Bu şekilde entegrasyon problemlerini zamanında lokalize etmek mümkündür. Bu amaçla Corebanking projesi için Cruise Control kullanarak bir sürekli entegrasyon serveri oluşturdum. Bu server programcıların yaptığı her commit sonunda otomatik olarak tüm kodu Clear Case’den alarak derliyor ve mevcut testleri kosturuyor. Altta yer alan resimlerde de görüldüğü gibi bir build monitörü kullanarak, projelerin entegrasyon işlemlerinin ne durumda olduğunu takip etmemiz mümkün. Herhangi bir kırılma olması durumunda gerekli programcılar email aracılığı ile uyarılıyor. Bunun yanısıra entegrasyon monitörü herkesin gözü önünde olduğundan, entegrasyon sürecinin hangi safhada olduğunu görmek her zaman mümkün.

Diğer bir faaliyet alanım performans testleri esnasında performans ölçümünde kullanılabilecek metriklerin oluşturulması idi. Bu amaçla AspectJ (Java’da Aspect Oriented Programming yapmak için kullanılan bir framework) kullanarak perfomans aspektleri oluşturdum. Bu aspektleri kullanılan teknolojiye göre (Webservice, Rest, JDBC) metot bazında işlem tamamlama süresini ölçecek şekilde programladım. Tüm proje bu aspektler kullanılarak derlendikten sonra, kod bazında bir değişiklik yapmak zorunda kalmadan, işlem tamamlama sürelerini ölçmek mümkün oldu.

[sourcecode language=’java’]
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect(“perthis(execution(public * com.kurumsaljava.core.restapi.*Resource.*(..)))”)
public class RestPerformanceMonitor extends AbstractPerformanceMonitor
{
@Before(“execution(public * com.kurumsaljava.restapi.*Resource.*(..))”)
public void before()
{
startMonitor();
}

@After(“execution(public * com.kurumsaljava.restapi.*Resource.*(..))”)
public void after()
{
stopMonitor(“[performance:rest]”);
}
}
[/sourcecode]

Çevik süreçleri kullanarak iterasyon bazlı proje yönetimi yapabilmek için kullanıcı hikayelerinin (user story) oluşturulması gerekiyor. Corebanking bünyesindeki bir projenin yazılım sürecini hikaye kartları (story card) kullanarak yapılandırdık. Hikaye kartları üzerinde kullanıcı hikayeleri yer aldı. Aşağıdaki resimlerde görüldüğü gibi çalışma alanımızın bir duvarını hikaye kartlarını barındıracak biçimde şekillendirdik.

Son dört aylık çalışmamın temelini, Corebanking projesinin daha modüler ve geliştirilebilir bir alt yapıya geçirilmesi işlemi oluşturdu. Corebanking uygulamaları, oluşturduğum ve Corebanking Next Generation (CBNG) ismini verdiğim bu yeni alt yapı ile şimdi İşbankasi’nın production sistemlerinde deploy ediliyor.

Neden CBNG’ye gerek duyulduğunu sizlere kısaca aktarmak istiyorum. Başlangıçta tek bir uygulama olarak yola çıkan Corebanking projesi, zaman içinde büyüyerek, bünyesinde üç ve daha fazla uygulama barındırmaya başlamış. Prensipte birbirlerinden bağımsız olan bu projeler ne yazık ki aynı kod köklerine sahip olduklarından, birbirlerinden bağımsız olarak deploy edilmeleri mümkün olmuyordu. Yaşadığımız en büyük sorun, hazır olmayan ve test edilmemiş kodun, Corebanking bünyesindeki herhangi bir uygulamanın test ya da production sistemlerine çıkmayı istemesi durumunda, bu projeyle beraber gitmek zorunda olmasıydı, çünkü tüm Corebanking uygulamaları bir EAR dosyası olarak deploy ediliyordu. Bir başka sorun ise, proje yöneticilerinin sürekli kendi test ya da production çıkışlarını diğer projelerle koordine etmek zorunda olmaları idi. Bu şartlar altında yeni Corebanking uygulamalarının geliştirilmesi, yani binanın üzerinde yeni katlar çıkılması imkansız bir hal alıyordu.

Buradan yola çıkarak mimari ekip / teknik lider toplantılari bünyesinde Corebanking Next Generation alt yapısına geçiş fikri oluştu ve kısa bir zaman sonra bu yeni alt yapıyı oluşturmak için çalışmalara başladım.

Prensip olarak yeni alt yapının “Programming in the large” modeline göre tasarlanması gerekiyordu. Bu modele göre proje bünyesindeki birçok alt proje, ufak ekipler tarafından, birbirlerini etkilemeden, versiyonlanmış modüler ve komponentler kullanılarak geliştirilebilir hale geliyor.

Yeni alt yapı büyük bir gökdelen inşa etmek yerine, bir site oluşturarak, bu site içerisinde 2-3 katlı villalar kurulmasına izin veriyor. Her bir villayı bir proje olarak düşünürsek, ekipler birbirlerinden bağımsız olarak kendi villalarını oluşturabiliyorlar.

Kulağa hoş gelen bu model, ne yazik ki 2 senelik bir kod tabanına sahip bir projeden yola çıkıldığında uygulaması çok zor bir hal alabilir. Karşılaştığım en büyük problemlerden birisi, her proje tarafından kullanılan ortak sınıfların lokalize edilmesi ve her proje tarafından kullanılabilir bir modül haline getirilmesi oldu. Bunun yanısıra sınıflar arasında sirküler bağımlılıkların (circular dependency) olması, ortak kullanılabilir modüllerin ortaya çıkmasını zorlayan bir durumdu. Yer yer DRY (Do not repear yourself) prensibine uyulmadığı için aynı özellikleri taşıyan sınıfların birden fazla lokasyonda bulunması ve konsolide edilmesi karşılaştığım diğer bir sorundu.

İşe mevcut tüm projeleri Maven projesi olacak şekilde yeniden yapılandırarak başladım. Daha sonra Corebanking bünyesindeki en ufak çaplı projeyi seçerek, bu projeyi yeni modele uygun şekilde yeniden yapılandırdım. Bu esnada diğer projelerinde kullanabileceği ortak modüller şekillenmeye başladı. Bu şekilde adım adım ilerleyerek ilk Corebanking uygulamasını CBNG konform hale getirdim. Bu işlemin ardından projenin tüm testlerini (1000 üzerinde acceptance testleri) koşturarak, yeni yapıyı test ettik. Testler yüzde yüz çalışır hale gelinceye kadar yeni alt yapıyı rekonfigüre ettim. Bu işlemler tamamlandıktan sonra yeni alt yapıyı programcı ekibe teslim ettim. Bu şekilde yeni alt yapı sahada yayılmaya başladı. Bu noktadan itibaren iki Corebanking uygulaması, eski ve yeni dünya olarak paralel yaşamaya başladı. Tüm projeyi durdurup, 1-2 ay yeni alt yapıyı oluşturmak için ayıramayacağımız için bu şekilde bir seçim yapmak zorunda kaldık.

Kısa bir zaman sonra ilk CBNG uygulaması bir EAR dosyası olarak production sistemlerinde deploy edildi. Akabinde diğer bir Corebanking uygulamasını aynı şemayi takip ederek eski alt yapıdan yeni alt yapıya uyarladım ve sahada kullanılır hale gelmesini sağladım. Bu sürecin sonunda Corebanking tamemen transform edilmiş ve CBNG olarak hayatını sürdürmeye devam ediyor olacak. Bu gerçekleşene kadar eski ve yeni alt yapı iki değişik EAR olarak yanyana yaşamaya devam edecek.

Lokal repository olarak Nexus serverini kurdum. Bu server bünyesinde ortak kullanılan tüm modüller versiyonlanmış JAR dosyalar olarak yer aldı. Nexus ve Maven bağımlılıkların otomatik yönetimini ve kod birimlerinin ortak ve yeniden kullanımını sağlayan alt yapı komponentleri. Bu şekilde herhangi bir CBNG projesi ihtiyaç duydugu bir modülü Nexus‘dan temin edebilir. Aynı şekilde bu proje diğer projelere sunmak istediği modülleri versiyonlanmış bir Jar dosyası olarak Nexus bünyesine katabilir.

Benim için şüphesiz en heyecan verici faaliyet alanı CBNG oldu. Geliştirme plarformu olarak kullandığımız RAD 7 (Eclipse 3.2) mümkün olan tüm engelleri ortaya koyarak, yaptığım işin hergün heyecan verici kalmasını sağladı :) En büyük sorunlardan birisi RAD‘ın eski bir Eclipse versiyonu olduğu için M2Eclipse pluginini aktüel versiyonunda kullanamıyor olmamdı. Eski ve birçok bugı olan bir M2Eclipse plugin versiyonu ile çalışmanın ne kadar ve heyecan verici olabileceğini düsünebilirsiniz :) Oluşan birçok problem için birçok workaround bulmak zorunda kaldım. Bu kadar sorunla karşılaşacağımı önceden biliyor olsaydım, acaba CBNG‘yi hayata geçirmeye kalkarmıydım? Beni tanıyanlar bu sorunu cevabını biliyor :)

Corebanking Next Generation (CBNG) hakkında yazabileceğim sayfalar dolusu anılarım var. Lakin banka bünyesinde olup bitenleri çok detaylı bir şekilde anlatmak doğru olmaz. Danışman ya da banka çalısanı olarak dikkat etmemiz gereken en önemli konulardan birisi, gizlilik prensibine uymaktır.

Corebanking projesi bünyesinde birçok yetenekli programcıyla çalışma fırsatı buldum. Bilgi alışverişine açık bir ortamda çalışmak gerçekten çok zevk vericiydi. Bu programcı arkadaşlara bu vesile ile buradan kucak dolusu selam ve sevgilerimi gönderiyorum.

EOF ( End of Fun)

Özcan Acar

Maven2 ve OutOfMemory

Danışman olarak çalıştığım bir projenin modüllerini Maven2 kullanacak şekilde yeniden yapılandırdım. Birçok JAR ve WAR dosyası bir araya gelerek büyük bir EAR dosyası oluşturdu. JAR ve WAR’ları oluştururken bir sorun yaşamadım, lakin EAR dosyasını alırken durum farkli idi. EAR projesi için maven clean install komutunu kullandığımda aşağıdaki hata oluşuyordu:

[sourcecode language=”java”]
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2882)
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:515)
at java.lang.StringBuffer.append(StringBuffer.java:306)
at java.io.StringWriter.write(StringWriter.java:77)
at hidden.org.codehaus.plexus.util.IOUtil.copy(IOUtil.java:214)
at hidden.org.codehaus.plexus.util.IOUtil.toString(IOUtil.java:416)
at hidden.org.codehaus.plexus.util.IOUtil.toString(IOUtil.java:405)
at org.apache.maven.project.DefaultMavenProjectBuilder.readModel(DefaultMavenProjectBuilder.java:1595)
at org.apache.maven.project.DefaultMavenProjectBuilder.readModel(DefaultMavenProjectBuilder.java:1571)
at org.apache.maven.project.DefaultMavenProjectBuilder.findModelFromRepository(DefaultMavenProjectBuilder.java:562)
at org.apache.maven.project.DefaultMavenProjectBuilder.buildFromRepository(DefaultMavenProjectBuilder.java:251)
at org.apache.maven.project.artifact.MavenMetadataSource.retrieveRelocatedProject(MavenMetadataSource.java:163)
at org.apache.maven.project.artifact.MavenMetadataSource.retrieveRelocatedArtifact(MavenMetadataSource.java:94)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:387)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:435)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:435)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.collect(DefaultArtifactCollector.java:74)
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolveTransitively(DefaultArtifactResolver.java:316)
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolveTransitively(DefaultArtifactResolver.java:304)
at org.apache.maven.plugin.DefaultPluginManager.resolveTransitiveDependencies(DefaultPluginManager.java:1499)
at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:442)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
[INFO] ————————————————————————
[INFO] Total time: 3 seconds
[INFO] Finished at: Tue Oct 26 11:40:45 EEST 2010
[INFO] Final Memory: 9M/9M
[INFO] ————————————————————————
C:\CCVIEWS\itcs_oacar_UCM_YUKSELIS\CBVOB01\CorebankingAppBuilder>
[/sourcecode]

EAR dosyası birçok JAR ve WAR’dan oluştuğu için doğal olarak büyük bir hacme sahip. Default Maven2 ayarları bu boyuttaki bir EAR dosyasını oluşturmak için yeterli değil.

Bu sorunu aşmak için Maven’in daha fazla hafıza alanını kullanabilmesi gerekiyor. Bunu sağlamak için Windows işletim sistem ile çalışıyorsanız, console altında

set MAVEN_OPTS=-Xmx512m

ya da Linux/Unix altında

export MAVEN_OPTS=-Xmx512m

komutunu girmeniz yeterli. Bir sonraki mvn komutu tanımladığınız şekilde yeni hafıza alanı ile çalşacaktır.