JBOSS Security

JBOSS SERVER GÜVENLİK AYARLARI

Günümüzde yazılım uygulamalarını local ortamdan canlı ortama geçirirken uygulama sunucularının standart ayarlarını değiştirmeden taşıdığımızda, uygulama dış tehditlere karşı açık kalır, yazımızda bu tehditlere karşı nasıl korunabiliriz, hangi güvenlik ayarlarını yapmalıyız, temel başlıklar altında inceleyeceğiz.

1.Securing jmx-console

Jmx –console web arayüzünden uygulamayı yönetme aracıdır. Güvenlik ayarları yapılmadıgında tehdit oluştaracak konular; yeni uygulamalar yüklenebilir, mevcut uygulama silinebilir. Bu yüzden bu web arayüzünü şifreyle korumak önemlidir. Güvenlik için önemli bir adımdır.

Yapılması gerekenler dosya dizinleri ve değiştirilmesi gereken dosyalarla aşağıda belirtilmiştir.

Dosya Dizin:/apps/jboss-4.2.3.GA/server/jboos_home /conf/props

Dosya:jmx-console-roles.properties (modified)

Açıklama: kullanıcı ve rol bilgilerini çeren dosyadır, dosyadaki default değerler ve update edlimiş değerler aşağıda belirtilmiştir.

Default:

admin=JBossAdmin,HttpInvoker

Modified: xxAdmin diye belirttiğimiz rol ismini kendiniz belirleyiniz.

xxAdmin=JBossAdmin,HttpInvoker

Dosya Dizin://apps/jboss-4.2.3.GA/server/it4em/deploy/jmx-console.war/WEB-INF

Dosya: jboss-web.xml (modified)

Açıklama : Login içinde böyle bir configirasyon vardır, User ve rolleri nereden alacagını gösterir.

<security-domain>java:/jaas/jmx-console</security-domain>

// Yukardaki ifade default commentlenmiş gelir ve commenti açılmalıdır.

Dosya: web.xml (modified, GET and POST methods comment out)

<!-<http-method>GET</http-method>->

<!-<http-method>POST</http-method>->

//Yukardaki ifade commentlenir, Son zamanlarda açık kaynak kod uygulama sunucularında belirlenen http metodlarını kullanarak yapılan atakları engellemek adına yapılmıştır. Güvenlik bütün http metodlarına uygulanmıştır.

Jmx- console güvenliği ile ilgili ayrıntılı bilgiye ulaşmak için aşağıdaki linklerin okunması tavsiye edilir.

http://java.dzone.com/articles/jboss-jmx-console

http://threatpost.com/en_us/blogs/jboss-worm-exploiting-old-bug-infect-unpatched-servers-102111

2.Securing web-console

Web-console istatistiklerin görülebildiği araçtır. Uygulama için tehlike arz etmez fakat içerdiği bilgiler şirket için önem arz edebilir.

Dosya Dizin:/apps/jboss-4.2.3.GA/server/it4em/conf/props

Dosya:web-console-roles.properties // Bu dosya yeni yaratılır ve içeriği aşagıdaki gibi olmalıdır

xxAdmin=JBossAdmin

Dosya:web-console-users.properties // Bu dosya da yeni eklenecektir. Admin şifrenizi kendiniz belirleyeceksiniz.

xxAdmin =sifre

Dosya Dizin:/apps/jboss-4.2.3.GA/server/it4em/deploy/management/console-mgr.sar/web-console.war/WEB-INF

Dosya: jboss-web.xml (modified)

<security-domain>java:/jaas/web-console</security-domain>

//Yukardaki ifade comment açılmalıdır.

Dosya: web.xml (modified)

<security-constraint>

<web-resource-collection>

<web-resource-name>HtmlAdaptor</web-resource-name>

<description>An example security config that only allows users with the

role JBossAdmin to access web console

</description>

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

<http-method>GET</http-method>

<http-method>POST</http-method>

</web-resource-collection>

<auth-constraint>

<role-name>JBossAdmin</role-name>

</auth-constraint>

</security-constraint>

//Yukarda belirtilen ifadenin Commenti açılmalıdır.

3.Securing jbossws

Dosya Dizin:/apps/jboss-4.2.3.GA/server/it4em/conf/props

Dosya:jbossws-roles.properties (modified)

kermit=friend

xxAdmin=friend

Dosya: jbossws-users.properties (modified)

xxAdmin =şifre

kermit=thefrog

Dosya Dizin: /apps/jboss-4.2.3.GA/server/it4em/deploy/jbossws.sar/jbossws-management.war/WEB-INF

Dosya: jboss-web.xml (modified)

<security-constraint>

<web-resource-collection>

<web-resource-name>ContextServlet</web-resource-name>

<description>An example security config that only allows users with the role ‘friend’ to access the JBossWS console web application

</description>

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

<http-method>GET</http-method>

<http-method>POST</http-method>

</web-resource-collection>

<auth-constraint>

<role-name>friend</role-name>

</auth-constraint>

</security-constraint>

// Yukardaki ifadenin commenti açılmalıdır.

Dosya: web.xml (modified),

GET and POST methods commentlenir.

4.Securing invokers

Dosya Dizin:/apps/jboss-4.2.3.GA/server/it4em/deploy/http-invoker.sar/invoker.war/WEB-INF

Dosya:jboss-web.xml

<http-method>GET</http-method>

<http-method>POST</http-method>

//Yukarıdaki ifade commentlendi.

Dosya: web.xml

<jboss-web>

<security-domain>java:/jaas/jmx-console</security-domain>

</jboss-web>

//Yukarıdaki ifade commentlendi.

Konu hakkında ayrıntılı bilgiye ulaşmak için aşağıdaki linkin okunması tavsiye edilir.

http://docs.jboss.org/jbossas/docs/Server_Configuration_Guide/4/html/How_to_Secure_the_JBoss_Server-The_HTTP_Invokers.html

5.Securing status servlet

Dosya Dizin: /apps/jboss-4.2.3.GA/server/it4em/deploy/jboss-web.deployer/ROOT.war/WEB-INF

Dosya: web.xml (modified)

<security-constraint>

<web-resource-collection>

<web-resource-name>Status Servlet</web-resource-name>

<description>An example security config that only allows users with the role JBossAdmin to access status servlet

</description>

<url-pattern>/status</url-pattern>

<http-method>GET</http-method>

<http-method>POST</http-method>

</web-resource-collection>

<auth-constraint>

<role-name>TomcatStatus</role-name>

</auth-constraint>

</security-constraint>

<login-config>

<auth-method>BASIC</auth-method>

<realm-name>tomcat-status</realm-name>

</login-config>

<security-role>

<role-name>TomcatStatus</role-name>

</security-role>

Dosya:jboss-web.xml // Bu dosya yeni eklenmiştir ve aşağıdaki ifadeyle doldurulmalıdır.

<jboss-web>

<security-domain>java:/jaas/tomcat-status</security-domain>

</jboss-web>

Dosya Dizin:/apps/jboss-4.2.3.GA/server/it4em/conf/props

Dosya: tomcat-status-users.properties // Bu dosya yukarda belirtilmiş olan dizinde yaratılmalıdır. İçeriği aşagıda belirttiğimiz gibi olmalıdır.

# A sample users.properties file for use with the UsersRolesLoginModule

xxAdmin=şifre

Dosya: tomcat-status-roles.properties // Bu dosya belirtilen dizinde yaratılmalıdır.

# A sample roles.properties file for use with the UsersRolesLoginModule

xxAdmin=TomcatStatus

Dosya Dizin:/apps/jboss-4.2.3.GA/server/it4em/conf/

Dosya:login-config.xml (modified)

<application-policy name = “web-console”>

<authentication>

<login-module code=”org.jboss.security.auth.spi.UsersRolesLoginModule”

flag = “required”>

<module-option name=”usersProperties”>props/web-console-users.properties</module-option>

<module-option name=”rolesProperties”>props/web-console-roles.properties</module-option>

</login-module>

</authentication>

</application-policy>

<application-policy name = “tomcat-status”>

<authentication>

<login-module code=”org.jboss.security.auth.spi.UsersRolesLoginModule”

flag = “required”>

<module-option name=”usersProperties”>props/tomcat-status- users.properties</module-option>

<module-option name=”rolesProperties”>props/tomcat-status-roles.properties</module-option>

</login-module>

</authentication>

</application-policy>

Aşagıdaki link bu konu hakkında ayrıntılı bilgiye ulaşmak için okunması tavsiye edilir.

http://davidghedini.blogspot.com/2011/03/install-jboss-51-on-centos.html

GWT ile AJAX Çağrısı Ve Çoklu Dil Desteği

GWT

Bu blog yazımda kısaca GWT’nin nasıl bir teknoloji olduğunu, basit bir uygulamanın nasıl geliştirilebileceğini ve GWT ile uygulama geliştirmenin alternatifleri ile karşılaştırıldığında sunduğu avantaj ve dezavantajlara değineceğim.

GWT Google tarafından hazırlanmış resmi sayfasında tanımlandığı şekli ile bir tarayıcı üzerinde o taraycının kendine özgü davranışlarını bilmeye gerek kalmadan JavaScript ve Ajax uygulamaları geliştirmeye yarayan bir yazılım aracıdır. Bunu daha Türkçe hale getirirsek GWT JavaScript bilmenize gerek kalmadan Ajax çağırılarını kolaylıkla yapabileceğiniz her tarayıcıda çalışan web uygulaması yazmanıza yardım eden bir araçtır.

GWT’nin JavaScript’in sağladığı tüm istemci tarafı programlama işlerini yapma konusundaki sunduğu alternatif ise Java ile istemci tarafı kodun yazılıp daha sonra JavaScript’ e derlenmesi şeklinde. Kısacası siz yalnızca Java bildiğiniz sürece JavaScript’ e de Ajax çağrılarını JavaScript kullanarak yapamanıza da gerek yok, bu işleri Java’ya bırakın biz kodunuzu derler her tarayıcıda çalışır hale getiririz diyor. Şimdi işin teorik kısmını bir süreliğine bir kenara bırakıp, GWT kullanarak basit bir çok dil deseteği ve olan web uygulaması geliştirelim.

Eclipse İçin GWT Kurulumu

Teorik olarak GWT için hiçbir özel IDE’ye ihtiyaç olmadığı söylensede dünyadaki GWT kullanıcıları belkide tamamı geliştirme platformu olarak Eclipse’i ve Google tarafından Eclipse için hazırlanmış olan Goggle pluginini kullanmakta. Internette GWT geliştirmesi ile ilgili bulunabilecek hemen hemen tüm örneklerde Eclipse üzerinde geliştirilmiş diyebiliriz. Aşağıdaki link yardımıyla Eclipse için gerekli pluginler indirilebilinir:

http://code.google.com/eclipse/docs/download.html

Eclipse ile ilgili plugin kurulumunun detayları bu sayfada bulunabilir:

http://code.google.com/webtoolkit/gettingstarted.html#download

Eclipse’te Windov -> Preferences altında aşağıdaki gibi Google eklentisi görülebiliyorsa kurulum başarıyla tamamlanamıştır.

GWT Uygulaması

  • İlk olarak Ecclipse’teki araç çubuğunda New Web Application Project düğmesine basilır.

Proje adı olarak ; GWTTutorials

Package adı olarak; tr.com.gturan.gwtTrials.client seçilir. (Use Google Web Toolkit ve Use default SDK (GWT) seçeneklerinin seçili olduğundan emin olun.)

  • Bu işlemin sonucunda src package’ nın altında aşağıdaki package’ ların yaratılmış olduğunu görürsünüz;

tr.com.gturan.gwtTrials

tr.com.gturan.gwtTrials.client

tr.com.gturan.gwtTrials.server

tr.com.gturan.gwtTrials.shared

Bu pacakage’ larden client ile biten JavaScript dosyalarına çevirilerek istemci tarafında kullanılacak olan dosyaları içerecektir. Server ile biten package altında ise sunucu tarafında çalışacak olan kodlar bulunacaktır. tr.com.gturan.gwtTrials package’ı altında ise TrialG.gwt.xml isimli konfigürasyon dosyası bulunmaktadır.

tr.com.gturan.gwtTrials

tr.com.gturan.gwtTrials.client

tr.com.gturan.gwtTrials.server

tr.com.gturan.gwtTrials.shared

Bu pacakage’ larden client ile biten JavaScript dosyalarına çevirilerek istemci tarafında kullanılacak olan dosyaları içerecektir. Server ile biten package altında ise sunucu tarafında çalışacak olan kodlar bulunacaktır. tr.com.gturan.gwtTrials package’ ı altında ise TrialG.gwt.xml isimli konfigürasyon dosyası bulunmaktadır.

  • aşağıdaki dosyalar yaratılmış olur ;

TrialG.gwt.xml
GWT modul tanımı

TrialG.html
giriş sayfası

TrialG.css
stil dosyası

web.xml
Java web uygulama tanımlayıcısı

TrialG.java
GWT entry point dosyası

GreetingService.java, GreetingServiceAsync.java, GreetingServiceImpl.java
GWT örnek RPC(Remote Procedure Call) dosyaları

gwt-servlet.jar
GWT server runtime kütüphanesi

  • Yarattığımız projenin üzerine Mouse ile gelip sağa tıklayıp “Run as web Application” dediğimizde aşağıdaki ekranı görmeliyiz:
  • TrialG.gwt.xml dosyası açıldığında aşağıdaki kod görülecektir:

<?xml version=”1.0″ encoding=”UTF-8″?>

<module rename-to=’trialg’>

<!- Inherit the core Web Toolkit stuff. ->

<inherits name=’com.google.gwt.user.User’/>

<!- Inherit the default GWT style sheet. You can change ->

<!- the theme of your GWT application by uncommenting ->

<!- any one of the following lines. ->

<inherits name=’com.google.gwt.user.theme.clean.Clean’/>

<!- <inherits name=’com.google.gwt.user.theme.standard.Standard’/> ->

<!- <inherits name=’com.google.gwt.user.theme.chrome.Chrome’/> ->

<!- <inherits name=’com.google.gwt.user.theme.dark.Dark’/> ->

<!- Other module inherits ->

<!- Specify the app entry point class. ->

<entry-point class=’tr.com.gturan.gwtTrials.client.TrialG’/>

<!- Specify the paths for translatable code ->

<source path=’client’/>

<source path=’shared’/>

</module>

  • <entry-point class=’tr.com.gturan.gwtTrials.client.TrialG’/> satırına bakarsak burada uygulamanın giriş noktası (entry Point) dosyası tanımlanır. Yani ilk html sayfası yüklendiğinde derlenerek JavaScript’e dönüştürülecek ve web sayfasına dahil edilecek olan dosyayı burada ilan ederiz. Bizim bu örnekteki giriş noktası dosyamız TrialG.java’dır.
  • Giriş sayfasını TrialG.html’i açtığımızda head tagleri arasında aşağıdaki satırları görebiliriz:
  • …<head><meta http-equiv=”content-type” content=”text/html; charset=UTF-8″><!- -><!- Consider inliningCSS to reduce the number of requested files -><!- -><link type=”text/css” rel=”stylesheet” href=”TrialG.css”><!- -><!- Any title is fine -><!- -><title>Web Application Starter Project</title><!- -><!- This script loads your compiled module. -><!- If you add any GWT meta tags, they must -><!- be added before this line. -><!- -><script type=”text/javascript” language=”javascript” src=”trialg/trialg.nocache.js”></script></head>…
  • Burada stil dosyası olarak uygulama tarafından yaratılan TrialG.css kullanılmaktadır. Sayfa ile ilgili görsel değişiklikleri bu dosya üzerinden yapabiliriz.<script type=”text/javascript” language=”javascript” src=”trialg/trialg.nocache.js”></script>Satırına bakılacak olursa burada src = “trialg/trialg.nocache.js” kısmındaki ilk trialg ibaresi modül adıdır ve bizim TrialG.gwt.xml dosyasındaki;<module rename-to=’trialg’>
  • Değeri ile eşleşmelidir. ‘/’ işaretinden sonra gelen trialg.nocache.js ibaresi TrialG.java dosyasının (yani entry point olarak ilan ettiğimiz TrialG.java dosyasının) javascripte derlenmesi Ile oluşur. Burada trialg.nocache.js’nin adının java dosyamızın adıyla aynı olduğuna dikkat edelim.
  • Şimdi aşağıda görülen bir çok web uygulamasında gerekli olabilecek ile göre ilçe getirilmesi uygulamasını gwt yardımıyla yapmaya çalışacağız ve buna bir dil desteği ekleyeceğiz.
  • Uygulamayı geliştirmeye başlamak için Giriş noktası doyamızı TrilaG.java’yı açalım, aşağıdaki satırları ekleyelim.

.

public class TrialG implements EntryPoint{

private VerticalPanel mainPanel = new VerticalPanel();

private HorizontalPanel provincePanel = new HorizontalPanel();

private HorizontalPanel countyPanel = new HorizontalPanel();

private Label provinceLabel = new Label();

private ListBox provinceListBox = new ListBox();

private Label countyLabel = new Label();

private ListBox countyListBox = new ListBox();

….

Burada bazı package’ların importu istenecektir, ide nin yardımıyla bu importları yapalım. Kodumuz aşağıdaki gibi olacaktır:

package tr.com.gturan.gwtTrials.client;

import com.google.gwt.core.client.EntryPoint;

import com.google.gwt.core.client.GWT;

import com.google.gwt.user.client.ui.HorizontalPanel;

import com.google.gwt.user.client.ui.Label;

import com.google.gwt.user.client.ui.RootPanel;

import com.google.gwt.user.client.ui.VerticalPanel;

import com.google.gwt.user.client.ui.ListBox;

public class TrialG implements EntryPoint{

private VerticalPanel mainPanel = new VerticalPanel();

private HorizontalPanel provincePanel = new HorizontalPanel();

private HorizontalPanel countyPanel = new HorizontalPanel();

private Label provinceLabel = new Label();

private ListBox provinceListBox = new ListBox();

private Label countyLabel = new Label();

private ListBox countyListBox = new ListBox();

Yaptığımız şey sayfamızın içerisinde bulunmasını istediğimiz bileşenleri tanımlamaktır. Burada mainPanel adında ana bir panelimiz, il ve ilçe alanlarını tutacağımız sırasıyla provincePanel ve countyPanel adında alt panellerimiz ve bu alt panlleri içinde yer alacak provinceLabel, provinceListBox , countyLabel, countyListBox olarak tanımlanmış label ve listbox alanlarımız olacaktır.

  • public void onModuleLoad() olarak tanımlanmış metod dosya yüklenirken çalışacak olan metodtur ve yapılmasını istediğimiz işleri bu metodun altında tanımlarız. Aşağıdaki satırları onModuleLoad() metoduna ekleyelim:

public void onModuleLoad() {

RootPanel.get(“appTitle”).add(new Label(“İl / İlçeler Uygulaması”));

// Assemble Province panel.

provinceLabel.setText(“İl”);

provinceLabel.addStyleName(“stdLabel”);

provinceListBox.setWidth(“100px”);

provincePanel.add(provinceLabel);

provincePanel.add(provinceListBox);

provincePanel.addStyleName(“provincePanel”);

// Assemble County panel.

countyLabel.setText(“İlçe”);

countyLabel.addStyleName(“stdLabel”);

countyListBox.setWidth(“100px”);

countyPanel.add(countyLabel);

countyPanel.add(countyListBox);

countyPanel.addStyleName(“countyPanel”);

// Assemble Main panel.

mainPanel.add(provincePanel);

mainPanel.add(countyPanel);
RootPanel.get(“provinceCounty”).add(mainPanel);
}
Burada bir sayfa başlığı, İl ve İlçe adlı iki label, bunlara karşılık gelecek iki listbox ve bu bileşenlerin içinde bulunacağı panel tanımlandığı gibi bu bileşenlerle ilgili bazı stil tanımları yapacağımız ve main panelinin sayfada “provinceCounty” county adında bir div altında olacağını belirtmiş olduk. Şimdi bu ilan ettiğimiz parametrelerin geçerli olması için once TrialG.css daha sonra TrialG.html’e bazı eklemeler yapacağız.

  • TrialG.css’i açalım ve aşağıdaki stil tanımlarını dosyaya ekleyelim;

/** Add css rules here for your application. */

.provincePanel{

padding: 2px;

margin: 5px 0px 0px 10px;

}

.countyPanel{

padding: 2px;

margin: 5px 0px 0px 10px;

}

.stdLabel{

width:100px;

height:20px;

line-height: 20px;

font-size: 0.5 em;

font-weight: bold;

}

  • TrialG.html’i açalım ve aşağıdaki satırları body tagleri arasına ekleyelim;

<body>

<h1 id=“appTitle”></h1>

<div id=“provinceCounty”></div>

Burada herhangi bir div tanımlamadanda Trial.G javada yarattığımız bileşenleri sayfaya yani body’nin altına ekleyebilirdik. Yalnız bu udrumda TrialG.java’da yazığımız ;

RootPanel.get(“appTitle”).add(new Label(“İl / İlçeler Uygulaması”));
RootPanel.get(“provinceCounty”).add(mainPanel);

Satırları yerine;

RootPanel.get(“”).add(new Label(“İl / İlçeler Uygulaması”));
RootPanel.get(“”).add(mainPanel);

yazmalıydık.

TrialG.html’ in son hali şu şekildedir:

<!doctype html>

<!- The DOCTYPE declaration above will set the ->

<!- browser’s rendering engine into ->

<!- “Standards Mode”. Replacing this declaration ->

<!- with a “Quirks Mode” doctype is not supported. ->

<html>

<head>

<meta http-equiv=“content-type” content=“text/html; charset=UTF-8″>

<meta name=“gwt:property” content=“locale=tr”>

<!- ->

<!- Consider inlining CSS to reduce the number of requested files ->

<!- ->

<link type=“text/css” rel=“stylesheet” href=“TrialG.css”>

<!- ->

<!- Any title is fine ->

<!- ->

<title>Web Application Starter Project</title>

<!- ->

<!- This script loads your compiled module. ->

<!- If you add any GWT meta tags, they must ->

<!- be added before this line. ->

<!- ->

<script type=“text/javascript” language=“javascript” src=“trialg/trialg.nocache.js”></script>

</head>

<!- ->

<!- The body can have arbitrary html, or ->

<!- you can leave the body empty if you want ->

<!- to create a completely dynamic UI. ->

<!- ->

<body>

<h1 id=“appTitle”></h1>

<div id=“provinceCounty”></div>

<!- OPTIONAL: include this if you want history support ->

http://em

<!- RECOMMENDED if your web app will not function without JavaScript enabled ->

<noscript>

<div style=“width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif”>

Your web browser must have JavaScript enabled

in order for this application to display correctly.

</div>

</noscript>

</body>

</html>

TrialG.java’ nın son hali şu şekildedir:

package tr.com.gturan.gwtTrials.client;

import com.google.gwt.core.client.EntryPoint;

import com.google.gwt.user.client.Window;

import com.google.gwt.user.client.ui.HorizontalPanel;

import com.google.gwt.user.client.ui.Label;

import com.google.gwt.user.client.ui.ListBox;

import com.google.gwt.user.client.ui.RootPanel;

import com.google.gwt.user.client.ui.VerticalPanel;

/**

* Entry point classes define <code>onModuleLoad()</code>.

*/

public class TrialG implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel();

private HorizontalPanel provincePanel = new HorizontalPanel();

private HorizontalPanel countyPanel = new HorizontalPanel();

private Label provinceLabel = new Label();

private ListBox provinceListBox = new ListBox();

private Label countyLabel = new Label();

private ListBox countyListBox = new ListBox();

/**

* This is the entry point method.

*/

public void onModuleLoad() {

Window.setTitle(“İl / İlçe Uygulaması”);

RootPanel.get(“appTitle”).add(new Label(“İl / İlçe Uygulaması”));

// Assemble Province panel.

provinceLabel.setText(“İl :”);

provinceLabel.addStyleName(“stdLabel”);

provinceListBox.setWidth(“100px”);

provincePanel.add(provinceLabel);

provincePanel.add(provinceListBox);

provincePanel.addStyleName(“provincePanel”);

// Assemble County panel.

countyLabel.setText(“İlçe :”);

countyLabel.addStyleName(“stdLabel”);

countyListBox.setWidth(“100px”);

countyPanel.add(countyLabel);

countyPanel.add(countyListBox);

countyPanel.addStyleName(“countyPanel”);

// Assemble Main panel.

mainPanel.add(provincePanel);

mainPanel.add(countyPanel);

RootPanel.get(“provinceCounty”).add(mainPanel);

}

}

  • Sayfanın görsel kısmını bu şekilde tamamlamış olduk. Şimdi sayfanın iş mantığını oluşturmak için sunucu tarafına bize illeri ve girilen ile gore ilçeleri veren metodlar yerleştireceğiz ve sayfa yüklenirken istemci tarafından bu il listesini doldurup, daha sonra ildeki değişime gore sunucudan ilçe listesi isteyen Asenkron çağrı yapan kodu yazacağız. Bunun için ilk olarak list box’larımızı dolduracak il ve ilçe objeleri içln bir data modeli yaratıyoruz.
  • Src altında tr.com.gturan.gwtTrials.client.model adlı bir pacakage yaratılır ve bu pacakage altında aşağıdaki İl ve İlçe class’ları yaratılır:

package tr.com.gturan.gwtTrials.client.model;

import java.io.Serializable;

public class County implements Serializable {

private int id;

private String name;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

package tr.com.gturan.gwtTrials.client.model;

import java.io.Serializable;

import java.util.List;

public class Province implements Serializable {

private int id;

private String plateCode;

private String name;

private List<County> counties;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getPlateCode() {

return plateCode;

}

public void setPlateCode(String plateCode) {

this.plateCode = plateCode;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public List<County> getCounties() {

return counties;

}

public void setCounties(List<County> counties) {

this.counties = counties;

}

}

(Bu model classlarının Serializable olduğuna dikkat edin, GWT RPC yardımıyla network üzerinde iletilen nesneler serializable olmalıdır)

  • Daha sonra client package’ı altına içinde iş yapmasını istediğimiz metodları , yani illeri alan ve girlen bir ile gore ilçeleri alan metodları tanımlayacağımız bir interface yaratıyoruz;
  • tr.com.gturan.gwtTrials.client seçilir. Eclipse menu barından File > New > Interface seçilir. TrialGService adlı bir interface aşağıdaki şekilde yaratılır;

package tr.com.gturan.gwtTrials.client;

import java.util.List;

import tr.com.gturan.gwtTrials.client.model.County;

import tr.com.gturan.gwtTrials.client.model.Province;

import com.google.gwt.user.client.rpc.RemoteService;

import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath(“provinceCounties”)

public interface TrialGService extends RemoteService {

List<Province> getProvinces();

List<County> getCountiesByProvince(String provinceId);

}

@RemoteServiceRelativePath annotationa dikkat edin, bu sunucu tarafında çalışacak olan servletin url’i ile aynı olmalıdır ve web.xml’de de tanımlanacaktır.

  • Bu tanımlamaya yaptıktan sonra Eclipse bizden TrialGServiceAsync sınıfını tanımlamamızı isteyecektir. TrialGService sınıfı üzerindeki hata mesajına tıklayarak bunu otomatik olarak yaprız. Bu sınıfın kodu aşağıdaki şekilde olacaktır:

TrialGServiceAsync;

package tr.com.gturan.gwtTrials.client;

import java.util.List;

import tr.com.gturan.gwtTrials.client.model.County;

import tr.com.gturan.gwtTrials.client.model.Province;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface TrialGServiceAsync {

void getProvinces(AsyncCallback<List<Province>> callback);

void getCountiesByProvince(String provinceId,

AsyncCallback<List<County>> callback);

}

  • Şimdi bu metodların sunucu tarafındaki implemantasyonunu yazmalaıyız. Bildiğimiz gibi asenkon çağrılar istemciden yapılır ve sunucuda bulunan kodların çalışıp döndükleri sonuca göre istemcinin görüntülediği html in güncellenmesi prensibine dayanır. Bunun için ilk olarak tr.com.gturan.gwtTrials.server package’ı yaratılır.
  • Bu pacakage altında TrialGServerImpl sınıfını yaratalım ve aşağıdaki şekilde kodunu ekleyelim:

package tr.com.gturan.gwtTrials.server;

import java.util.List;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import tr.com.gturan.gwtTrials.client.TrialGService;

import tr.com.gturan.gwtTrials.client.model.County;

import tr.com.gturan.gwtTrials.client.model.Province;

public class TrialGServiceImpl extends RemoteServiceServlet implements TrialGService{

public List<Province> getProvinces(){

return null;

}

public List<County> getCountiesByProvince(String provinceId){

return null;

}

}

  • Bu metodun TrialGService interfaceını implement ederek asenkron çağırıda kullanılacak metodları yaratmaya zorlandığına dikkate ediniz. İl ve İlçeler için very kaynağı olarak bir Dao sınıfını tr.com.gturan.gwtTrials.server package’I alrtında yaratıyoruz.

TrialGDao :

package tr.com.gturan.gwtTrials.server;

import java.util.ArrayList;

import java.util.List;

import tr.com.gturan.gwtTrials.client.TrialGService;

import tr.com.gturan.gwtTrials.client.model.County;

import tr.com.gturan.gwtTrials.client.model.Province;

public class TrialGDao {

public static List<Province> getProvinces(){

List<Province> provinces = new ArrayList<Province>();

Province province1 = new Province();

province1.setId(1);

province1.setPlateCode(“01″);

province1.setName(“Adana”);

Province province2 = new Province();

province2.setId(6);

province2.setPlateCode(“06″);

province2.setName(“Ankara”);

provinces.add(province1);

provinces.add(province2);

return provinces;

}

public static List<County> getCountiesByProvince(String provinceId){

List<County> counties = new ArrayList<County>();

County county1 = new County();

county1.setId(1);

county1.setName(“Merkez”);

County county2 = new County();

county2.setId(2);

county2.setName(“Ceyhan”);

County county3 = new County();

county3.setId(3);

county3.setName(“Çankaya”);

County county4 = new County();

county4.setId(4);

county4.setName(“Yenimahalle”);

if(provinceId.equals(“01″)){

counties.add(county1);

counties.add(county2);

} else if(provinceId.equals(“06″)){

counties.add(county3);

counties.add(county4);

}

return counties;

}

}

  • TrialGService kodu aşağıdaki şekilde tamamlanır:

package tr.com.gturan.gwtTrials.server;

import java.util.ArrayList;

import java.util.List;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import tr.com.gturan.gwtTrials.client.TrialGService;

import tr.com.gturan.gwtTrials.client.model.County;

import tr.com.gturan.gwtTrials.client.model.Province;

public class TrialGServiceImpl extends RemoteServiceServlet implements TrialGService{

public List<Province> getProvinces(){

List<Province> provinces = new ArrayList<Province>();

try{

provinces = TrialGDao.getProvinces();

} catch(Exception e){

}

return provinces;

}

public List<County> getCountiesByProvince(String provinceId){

List<County> counties = new ArrayList<County>();

try{

counties = TrialGDao.getCountiesByProvince(provinceId);

} catch(Exception e){

}

return counties;

}

}

  • Şu anda sunucu tarafında bize il ve ilçe sağlayacak kodumuz ve bunu istemci tarafından çağırabilmek için kullanacağımız arayüz(interface) metodlarımız hazırdır. Şimdi bu arayüz metodldarı ile istemci tarafından RPC çağrısı yapacağız. TrilG.java aşağıdaki hale getirilir:

package tr.com.gturan.gwtTutorials.client;

import java.util.List;

import tr.com.gturan.gwtTrials.client.model.County;

import tr.com.gturan.gwtTrials.client.model.Province;

import tr.com.gturan.gwtTrials.client.TrialG2Service;

import tr.com.gturan.gwtTrials.client.TrialG2ServiceAsync;

import com.google.gwt.core.client.EntryPoint;

import com.google.gwt.core.client.GWT;

import com.google.gwt.event.dom.client.ChangeEvent;

import com.google.gwt.event.dom.client.ChangeHandler;

import com.google.gwt.user.client.Window;

import com.google.gwt.user.client.rpc.AsyncCallback;

import com.google.gwt.user.client.ui.HorizontalPanel;

import com.google.gwt.user.client.ui.Label;

import com.google.gwt.user.client.ui.ListBox;

import com.google.gwt.user.client.ui.RootPanel;

import com.google.gwt.user.client.ui.VerticalPanel;

/**

* Entry point classes define <code>onModuleLoad()</code>.

*/

public class TrialG2 implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel();

private HorizontalPanel provincePanel = new HorizontalPanel();

private HorizontalPanel countyPanel = new HorizontalPanel();

private Label provinceLabel = new Label();

private ListBox provinceListBox = new ListBox();

private Label countyLabel = new Label();

private ListBox countyListBox = new ListBox();

private TrialG2ServiceAsync trialGSvc = GWT.create(TrialG2Service.class);

/**

* This is the entry point method.

*/

public void onModuleLoad() {

Window.setTitle(“İl / İlçe Uygulaması”);

RootPanel.get(“appTitle”).add(new Label(“İl / İlçe Uygulaması”));

// Assemble Province panel.

provinceLabel.setText(“İl :”);

provinceLabel.addStyleName(“stdLabel”);

provinceListBox.setWidth(“100px”);

provincePanel.add(provinceLabel);

provincePanel.add(provinceListBox);

provincePanel.addStyleName(“provincePanel”);

// Assemble County panel.

countyLabel.setText(“İlçe :”);

countyLabel.addStyleName(“stdLabel”);

countyListBox.setWidth(“100px”);

countyPanel.add(countyLabel);

countyPanel.add(countyListBox);

countyPanel.addStyleName(“countyPanel”);

// Assemble Main panel.

mainPanel.add(provincePanel);

mainPanel.add(countyPanel);

RootPanel.get(“provinceCounty”).add(mainPanel);

// Set up the callback object.

AsyncCallback<List<Province>> callback = new AsyncCallback<List<Province>>() {

public void onFailure(Throwable caught) {

// If there is a mistake, display an error message.

String details = caught.getMessage();

Window.alert(details);

}

public void onSuccess(List<Province> result) {

fillCombo(result);

}

};

countyListBox.addItem(“- Seçiniz -”, “0″);

// Listen for onchange events on the Province list box.

provinceListBox.addChangeHandler(new ChangeHandler() {

@Override

public void onChange(ChangeEvent event) {

// Set up the callback object.

AsyncCallback<List<County>> callback = new AsyncCallback<List<County>>() {

public void onFailure(Throwable caught) {

// If there is a mistake, display an error message.

String details = caught.getMessage();

Window.alert(details);

}

public void onSuccess(List<County> result) {

fillComboCounties(result);

}

};

trialGSvc.getCountiesByProvince(provinceListBox.getValue(provinceListBox.getSelectedIndex()), callback);

}

});

trialGSvc.getProvinces(callback);

}

private void fillCombo(List<Province> result) {

provinceListBox.addItem(“- Seçiniz -”, “0″);

for(Province province : result){

provinceListBox.addItem(province.getName(),province.getPlateCode));

}

}

private void fillComboCounties(List<County> result) {

countyListBox.clear();

countyListBox.addItem(“- Seçiniz -”, “0″);

for(County county : result){

countyListBox.addItem(county.getName(),”"+county.getId());

}

}

  • Burada ilk olarak ;

private TrialG2ServiceAsync trialGSvc = GWT.create(TrialG2Service.class)

  • Tanımlamasını yaparak il ve ilçe listesi alan metodları çağırabileceğimiz arayüzü tanımlarız.
  • Kodun aşağaıdaki kısmı asenkron çağrımızın baaşrılı aolma ve olmama koşullarını belirler. Başarılı olursa İl listesi combosu dolacak akis halde hata mesajı gösterilecektir.

AsyncCallback<List<Province>> callback = new AsyncCallback<List<Province>>() {

public void onFailure(Throwable caught) {

// If there is a mistake, display an error message.

String details = caught.getMessage();

Window.alert(details);

}

public void onSuccess(List<Province> result) {

fillCombo(result);

}

};

  • Aşağıdaki satır ile sayfa yüklemnirken il dolduran metod çağırısını yaparız.

trialGSvc.getProvinces(callback);

  • İlçe listesi dolduran combo box için aşağıdaki kod parçası kullanılır:

countyListBox.addItem(“- Seçiniz -”, “0″);

// Listen for onchange events on the Province list box.

provinceListBox.addChangeHandler(new ChangeHandler() {

@Override

public void onChange(ChangeEvent event) {

// Set up the callback object.

AsyncCallback<List<County>> callback = new AsyncCallback<List<County>>() {

public void onFailure(Throwable caught) {

// If there is a mistake, display an error message.

String details = caught.getMessage();

Window.alert(details);

}

public void onSuccess(List<County> result) {

fillComboCounties(result);

}

};

trialGSvc.getCountiesByProvince(provinceListBox.getValue(provinceListBox.getSelectedIndex()), callback);

}

});

  • Burada ;

provinceListBox.addChangeHandler(new ChangeHandler() {…

satırı ile ilçe combo boxının değişmesi durumunda bir iş yapılması gerektiğini belirtiriz. Yine il combo boxında olduğu gibi askenkron çağrının başarılı olma ve olmama koşullarında yapılacak işleri belirtir ve ;

trialGSvc.getCountiesByProvince(provinceListBox.getValue(provinceListBox.getSelectedIndex()), callback);

satırı ile seçilen ile gore ilçe listesini çekeriz.

  • Şimdi yaptığımız uygulamaya çoklu dil desteği vereceğiz.;

provinceListBox.addChangeHandler(new ChangeHandler() {…

satırı ile ilçe combo boxının değişmesi durumunda bir iş yapılması gerektiğini belirtiriz. Yine il combo boxında olduğu gibi askenkron çağrının başarılı olma ve olmama koşullarında yapılacak işleri belirtir ve ;

trialGSvc.getCountiesByProvince(provinceListBox.getValue(provinceListBox.getSelectedIndex()), callback);

satırı ile seçilen ile gore ilçe listesini çekeriz.

  • Dil desteği vermek isteğimiz sabitler için tr.com.gturan.gwtTrials.client package altında TrialGConstants classını yaratırız:

package tr.com.gturan.gwtTrials.client;

import com.google.gwt.i18n.client.Constants;

public interface TrialGConstants extends Constants{

@DefaultStringValue(“Province”)

String province();

@DefaultStringValue(“County”)

String county();

@DefaultStringValue(“Continue”)

String next();

@DefaultStringValue(” - Choose - “)

String choose();

@DefaultStringValue(“Province / County Application”)

String provinceCounty();

}

  • TrialG.gwt.xml dosyasını açıp <module> tagleri arasına şu satırı ekleyelim:

<extend-property name=“locale” values=“tr”/>

  • TrialG.html dosyasını açıp <head> tagleri arasına şu satırı ekleyelim:

<meta http-equiv=”content-type” content=”text/html; charset=UTF-8″>

  • tr.com.gturan.gwtTrials.client package’ı altına dil desteği sağlamakta kullanacağımız .properties dosyasını yaratalım. Bu dosyada TrialGConstants.java dosyasında default İngilizce değerlerini verdiğimiz sabitlerin Türkçe karşılıklarını tanımlayacağız.
  • Dosyamizn adı TrialGConstants_tr.properties olacaktır. Burada dosya adı ‘_’ işaretine kadar java dosyasının aynısı olacak iken, ‘_’ işaretinden sonra destek vereceğimiz dile gore bir eklenti alacaktır. Örneğin; Almanca için ‘de’, Fransızca için ‘fr’ gibi.
  • TrialGConstants_tr.properties:

province = İl

county = İlçe

provinceCounty = İl / İlçeler Uygulaması

choose = - Seçiniz -

  • Sayfamızda dil ayarına gore değişmesini istediğimiz değerleri sabitler içerisine yerleştirmiş olduk, şimdi bu sabitleri yazmış olduğumuz kodun içerisine koyacağız, TrialG.java’mız aşağıdaki şekilde olacaktır:

package tr.com.gturan.gwtTrials.client;

import java.util.List;

import tr.com.gturan.gwtTrials.client.model.County;

import tr.com.gturan.gwtTrials.client.model.Province;

import com.google.gwt.core.client.EntryPoint;

import com.google.gwt.core.client.GWT;

import com.google.gwt.event.dom.client.ChangeEvent;

import com.google.gwt.event.dom.client.ChangeHandler;

import com.google.gwt.user.client.Window;

import com.google.gwt.user.client.rpc.AsyncCallback;

import com.google.gwt.user.client.ui.HorizontalPanel;

import com.google.gwt.user.client.ui.Label;

import com.google.gwt.user.client.ui.RootPanel;

import com.google.gwt.user.client.ui.VerticalPanel;

import com.google.gwt.user.client.ui.ListBox;

public class TrialG implements EntryPoint{

private VerticalPanel mainPanel = new VerticalPanel();

private HorizontalPanel provincePanel = new HorizontalPanel();

private HorizontalPanel countyPanel = new HorizontalPanel();

private Label provinceLabel = new Label();

private ListBox provinceListBox = new ListBox();

private Label countyLabel = new Label();

private ListBox countyListBox = new ListBox();

private TrialGServiceAsync trialGSvc = GWT.create(TrialGService.class);

private TrialGConstants constants = GWT.create(TrialGConstants.class);

private TrialGMessages messages = GWT.create(TrialGMessages.class);

@Override

public void onModuleLoad() {

Window.setTitle(constants.provinceCounty());

RootPanel.get(“appTitle”).add(new Label(constants.provinceCounty()));

// Assemble Province panel.

provinceLabel.setText(constants.province());

provinceLabel.addStyleName(“stdLabel”);

provinceListBox.setWidth(“100px”);

provincePanel.add(provinceLabel);

provincePanel.add(provinceListBox);

provincePanel.addStyleName(“provincePanel”);

// Assemble County panel.

countyLabel.setText(constants.county());

countyLabel.addStyleName(“stdLabel”);

countyListBox.setWidth(“100px”);

countyPanel.add(countyLabel);

countyPanel.add(countyListBox);

countyPanel.addStyleName(“countyPanel”);

// Assemble Main panel.

mainPanel.add(provincePanel);

mainPanel.add(countyPanel);

// Set up the callback object.

AsyncCallback<List<Province>> callback = new AsyncCallback<List<Province>>() {

public void onFailure(Throwable caught) {

// If the stock code is in the list of delisted codes, display an error message.

String details = caught.getMessage();

Window.alert(details);

}

public void onSuccess(List<Province> result) {

fillCombo(result);

}

};

countyListBox.addItem(constants.choose(), “0″);

// Listen for onchange events on the Province list box.

provinceListBox.addChangeHandler(new ChangeHandler() {

@Override

public void onChange(ChangeEvent event) {

// Set up the callback object.

AsyncCallback<List<County>> callback = new AsyncCallback<List<County>>() {

public void onFailure(Throwable caught) {

// If the stock code is in the list of delisted codes, display an error message.

String details = caught.getMessage();

Window.alert(details);

}

public void onSuccess(List<County> result) {

fillComboCounties(result);

}

};

trialGSvc.getCountiesByProvince(provinceListBox.getValue(provinceListBox.getSelectedIndex()), callback);

}

});

trialGSvc.getProvinces(callback);

RootPanel.get(“provinceCounty”).add(mainPanel);

}

private void fillCombo(List<Province> result) {

provinceListBox.addItem(constants.choose(), “0″);

for(Province province : result){

provinceListBox.addItem(province.getName(),province.getPlateCode());

}

}

private void fillComboCounties(List<County> result) {

countyListBox.clear();

countyListBox.addItem(constants.choose(), “0″);

for(County county : result){

countyListBox.addItem(county.getName(), “”+county.getId());

}

}

}

  • Burada :

private TrialGConstants constants = GWT.create(TrialGConstants.class);

satırı ile TrialGConstants.class dosyasına erişmemizi sağlayacak sabiti elde ediyoruz. Daha sonra;

provinceLabel.setText(constants.province());

gibi satırlarla properties dosyasından yada java dosyası içindeki default değerinden sabitin değerini vermiş oluyoruz.

  • Sayfamızın hangi dilde görünmesi gerektiği ayaroını yine TrialG.html dosyası içinden yapacağız, yine <head> taglaeri arasına aşağıdaki satırı ekleyelim:

<meta name=“gwt:property” content=“locale=tr”>

  • Eğer bu satırı aşağıdaki hale getirirsek uygulamamız İngilizce olarak çalışacaktır

<meta name=“gwt:property” content=“locale=en”>

  • TrialG.html dosyasının son hali aşağıdaki gibidir:

<!doctype html>

<!- The DOCTYPE declaration above will set the ->

<!- browser’s rendering engine into ->

<!- “Standards Mode”. Replacing this declaration ->

<!- with a “Quirks Mode” doctype is not supported. ->

<html>

<head>

<meta http-equiv=“content-type” content=“text/html; charset=UTF-8″>

<meta name=“gwt:property” content=“locale=en”>

<!- ->

<!- Consider inlining CSS to reduce the number of requested files ->

<!- ->

<link type=“text/css” rel=“stylesheet” href=“TrialG.css”>

<!- ->

<!- Any title is fine ->

<!- ->

<title>Web Application Starter Project</title>

<!- ->

<!- This script loads your compiled module. ->

<!- If you add any GWT meta tags, they must ->

<!- be added before this line. ->

<!- ->

<script type=“text/javascript” language=“javascript” src=“trialg/trialg.nocache.js”></script>

</head>

<!- ->

<!- The body can have arbitrary html, or ->

<!- you can leave the body empty if you want ->

<!- to create a completely dynamic UI. ->

<!- ->

<body>

<h1 id=“appTitle”></h1>

<div id=“provinceCounty”></div>

<!- OPTIONAL: include this if you want history support ->

http://em

<!- RECOMMENDED if your web app will not function without JavaScript enabled ->

<noscript>

<div style=“width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif”>

Your web browser must have JavaScript enabled

in order for this application to display correctly.

</div>

</noscript>

</body>

</html>

  • TrialG.gwt.cml dosyasının son hali aşağıdaki gibidir:

<?xml version=“1.0″ encoding=“UTF-8″?>

<module rename-to=‘trialg’>

<!- Inherit the core Web Toolkit stuff. ->

<inherits name=‘com.google.gwt.user.User’/>

<!- Inherit the default GWT style sheet. You can change ->

<!- the theme of your GWT application by uncommenting ->

<!- any one of the following lines. ->

<inherits name=‘com.google.gwt.user.theme.clean.Clean’/>

<!- <inherits name=’com.google.gwt.user.theme.standard.Standard’/> ->

<!- <inherits name=’com.google.gwt.user.theme.chrome.Chrome’/> ->

<!- <inherits name=’com.google.gwt.user.theme.dark.Dark’/> ->

<!- Other module inherits ->

<!- Specify the app entry point class. ->

<entry-point class=‘tr.com.gturan.gwtTrials.client.TrialG’/>

<extend-property name=“locale” values=“tr”/>

<!- Specify the paths for translatable code ->

<source path=‘client’/>

<source path=‘shared’/>

</module>

SONUÇ

Bir asenkron çağrı yapan ve çoklu dil desteğine sahip bir GWT uygulamasının nasıl yapılacağını göstermeye çalıştım. Şimdi gelelim hayati soruya; “GWT öğrenmilmeye ve bir proje içinde kullanmaya değecek bir framework mu ?”

Bu konuda internette gwt ile ilgili yorumda bulunula, artıların ve eksilerin karşılaştırıldığı çok sayıda blog yazılmış. Bunların içinde okuduklarımdan benim duygularıma tercüman olanı şu:

http://www.cforcoding.com/2009/10/lost-in-translation-or-why-gwt-isnt.html

Bloğun başlığı İngilizce’deki “Lost in Tarnslation” deyiminden alınmış, bu deyimin Türkçe karşılığını vermek gerekirse bir dilde bir anlam ifade eden bir kavramın tam olarak tercüme edilememsi veya tercüme edildiğinde gerçekten kastetmeye çalışılan şeyin tam olarak ifade edilememesi.

GWT’nin tam olarak yapmaya çalıştığı şeyde Javascriptçe’ ye tercümanlık yapmak, bunun içinde sizden Javaca konuşmanızı istyiyor ve çeviriyi kendisi yapıyor. Burada yazılımcı şöyle bir soru işareti ile baş başa kalıyor; bir proglama dilinde yazılmış bir kod bir diğerine yüzde yüz olarak ve çevirilen bir programlama dilinin hiç bir gücünü, niteliğini kaybetmeden çevirilebilir mi? Yıllarımı gwtye vermesemde ben bu konuda oldukça şüpheciyim.

GWT’nin irdelenmesi gereken felsefesi ise kısaca şöyle; JavaScript çözülmesi ve öğrenilmekten kaçınılması gereken bir bugtır.

Oysa 2011 yılının yazılım trendlerine ve en çok ses getiren projelerine baktığımızda CofeeScript’i ve artık neredeyse bir kütüphane olmaktan çıkıp kendi bir dil haline gelen JQuery’yi, sunucu tarafındada javaScript’le kodlama yapılmasına olanak veren node.js’yi görüyoruz. (http://www.infoworld.com/d/application-development/11-programming-trends-watch-179761?page=0,1)

Yurtdışında yapılan programlama dili / ücret eksenli araştırmalarda Javascript’in gittikçe daha fazla aranan ve ödenen bir nitelik olduğu ortaya çıkıyor. Bu durumda GWT’nin önerisene kulak versek, javaScript öğrenmekten nereye kadar kaçabiliriz sorusu benim kafam fena halde takılıyor.

GWT kodlaması sırasında benim hissetiğim bir diğer şey sanki bir awt, swing uygulaması yazdığımdı. Bir çok blogtada bu benzerliğe dikkat çekilmiş. Swing 90’ların sonundan iki binlerin ortalarına kadar java ile uğraşanların aşina olduğu ama masa üstü yazılımın neredeyse bitmesi ve yerini güçlü web frameworklere bırakmasıyla artık kimselerin pek girmediği bir konu. GWT sanki hala bu mimariyi yaşatmaya çalışıyor, bu eğer hayatınızın bir bölümünde swing kodladıysanız gwt’yi kolay kodlamanız için bir avantaj ama bir çok yeni nesil kodcu gibi yalnızca java web frameworkleriyle uğraştıysanız öğrenmesi zaman alabilecek ve biraz ters gelebilecek bir kodlama biçimi olarak karşımıza çıkıyor.

Aynı zamanda GWT HTML, JavScript konusunda benim gibi hatırı sayılır mesai harcıyan insanlara bilgilerinizi çöpe atsanızda olur JavScript ’i ben hallediyorum diyor ki bu bir çok web geliştiricisi çokta kabul edilebilir bir önerme değil.

GWT ile çok fazla değinilen bir diğer konu ise uzun compile süreleri ki bu JavScript ’ i JavScript yazarak yazan bir yazılımcının gündemine dahi gelebilecek bir konu değil. Yalnızca .js dosyasını kaydetmek ve browserda ctrl + F5’e basmak javaScript dilinde compile anlamına geliyor. Aynı zamanda GWT ile yazılım geliştirmek için şöyle bir konuyudu göz önünde bulundurmak gerekiyor;

https://developers.google.com/web-toolkit/doc/latest/DevGuideOptimizing

Optimizasyon şüphesiz javaScript ile yazılım geliştirikende göz önünde bulundaracağımız bir konu fakat GWT’Nin bu konudaki parametreleri ve beklediği iş gücü çok daha fazla.

GWT’nin göz ardı edilemeyecek bir avantajı cross browser desteği, aslında ben bu cross browser sorununu gerçek sorunun kibarlaştırılmış bir ifadesi olarak görüyorum Sorun Internet Explorer ve diğer mantığa dayalı browserlar arasındaki uyuşmazlık. Aynı anda IE’nin 7, 8 ve 9 versiyonları ile Mozilla ve Chrome’da çalışması gerekli css, javaScript kodu yazmak zorunda kalan biri olaral IE’nin her yeni versiyonunda diğer browserlara daha benzediğini ve sorunların azaldığını gördüm. Ümidimiz bir iki versiyon sonra cross browser compatibility sorunu tamamen ortadan kalkmasada en azından web geliştircilerinin şimdi olduğu gibi zamanının yarısını değil ufak bir miktarını alan bir sorun haline dönüşmesi, bu durumdada GWT en önemli argümanı olan cross browser destekli framework olma özelliğini kaybetmiş olacak.

Benim GWT ile ilgili gerçek anlamda çekincelerimden birisi kaynak konusundaki kısırlığı. Arkasında Google gibi bir devde olsa bir frameworkun gelişmesi ve kolay kullanılır hale gelmesi için mutlaka bir kitlesinin olması ve o ürünle ilgili bir şeylerin yazılması gerekli. Ben gwt öğreneceğim, bununla ilgili bir kitap bakayım diyerek amazon.com’a giren birisi iki sayfalık bir arama listesi ve bu listedeki çoğu kitabın bundan üç, dört yıl önce basılmış olduğu gerçeğiyle karşılaşıyor ki üç dört yılda yazılım dünyasında diğer teknolojiler otuz kırk yılda olabilecek gelişmelerin olabileceğini bir düşünmek lazım. Oysa JavScript veya popular eklentilerinden birisiyle çalışmak isteyenin karşısına onlarca yeni tarihli basılmış kaynak çıkmakta.

Buda daha once linkini verdiğim William Shields’in bloğundaki önermeyi desteklemekte; script yazılımı yazılımın geleceği olma yönünde. Yazılımın evrimine baktığımızda tamamen derlenen (fully-compiled) olan C/C++ ‘nın yerini bir VM üzerinde derlendikten sonra tekrar derlenen yani semi-compiled diller olan Java ve .NET’e bıraktığını görüyoruz. JavaScript dünyasındaki gelişmelerde evrimin devam edeceği ve bu platformlarında yerini script dillerine bırakabileceğini veyazılımın geleceği script dillerinde diyenleri çokta haksız çıkarmıyor.

Madem bu kadar alıntı yaptım yazımıda William Shields’in bitirdiği gibi bitireyim:

“GWT herkesin e-mail kullanmayı başladığı bir dünyada, telgrafı tekrar nasıl verimli ve hızlı kullanılır hale getirmenin yolunu arayan bir frameworke benziyor.”

Java’da Eşzamanlılık

Bilgisayar kullanıcıları doğal olarak kullandıkları bilgisayarın aynı anda birden fazla iş yaptığını düşünürler. İnternette geziniyorken veya klavye tuşlarına basıldığında arka planda çalan şarkının kesintiye uğraması pekçok kişi tarafından rahatsız edici bulunacaktır. Bu durum sadece farklı programların aynı anda çalışabilmesi için değil tek başına bir programın aynı anda birden fazla iş yapabilmesi için de geçerlidir. Tek çekirdekli bir işlemci fetch(Memory den bir sonraki komutun alınması) - decode(Komutun çözümlenmesi) - execute(Komutun çalıştırılması) döngüsünde -pipelining ile tek bir çevrimde fetch, decode ve execute işlemleri aynı anda yapılabilir- bir işlemci zamanında(CPU Cycle) tek bir komut çalıştırabilir(execute cycle). Eğer son çalıştırılan komut neticesinde uzun sürecek bir işlem yapılıyorsa(Sabit diske yazma, ağ bağlantısı kurma gibi..) boşta kalan işlemci başka programlara ait komutları çalıştırabilir, veya işlemci zamanı tüm processler arasında adil olarak dağıtabilir. Bu şekilde kullanıcı aynı anda birden fazla program kullanabilir. Son yıllarda kişisel bilgisayarlarda kullanılmaya başlanan çok çekirdekli işlemciler ise aynı anda birden fazla komut işleme olanağı verir ve bu şekilde bir iş birden fazla ayrı parçaya(task) bölünebilir. Java her ne kadar threadlerin eş zamanlı programlamlanmasına olanak vereceği iddaasıyla yola çıkmış olsa da, versiyon 5 e kadar eşzamanlılık desteği oldukça zayıf kalmıştır.

Processler ve threadler eşzamanlı programlamanın temel iki birimini oluşturur. Bir program sıralı komutlardan oluşur ve bu komutları çalıştıran öğeye process denir. Thread’ler processler gibi komutlara çalışacak ortamı sağlarlar ancak processlerden farklı olarak processlerin içerisinde yer alırlar, aynı memory alanını paylaşırlar ve bir processin ihtiyacı olan kaynağın çok daha azı ile oluşturulabilirler. Java’da eşzamanlılık ortak kaynakları kullanan threadlerin bu ortaklıktan kaynaklanabilecek hataları engellemeye yönelik senkronizasyon işini kapsar. Uygun senkronizasyon koda dahil edilmez ise threadler birbirlerinin bir değişken üzerinde yaptığı değişiklikleri kaybedebilirler veya tüm bunların dışında bir thread belli bir işe başlamadan önce başka bir threadin bir işi bitirmesine ihtiyaç duyabilir. Tüm bu senkronizasyon işlerinin yapılması için Java dili içerisinde ayrılmış keywordler bulunur. Bunun yanında java.util.concurrent paketi içerisinde senkronizasyon problemlerine çözümler sağlayan APIler bulunmaktadır.

Bir programın thread-safe(Threadlerin eşzamanlılığından kaynaklanabilecek sorunların olmadığı program) olduğunu söyleyebilmek için aşağıdaki özelliklerden birine sahip olması gerekmektedir:

  1. Uygulama içerisinde threadler oluşturulmuyorsa ya da diğer bir deyişle process içinde sadece bir thread bulunuyorsa herhangi bir senkronizasyon planı yapmaya gerek yoktur. Diğer yandan, kullanılan 3rd party frameworklerin thread yaratıyor olabilme olasılığı göz önünde bulundurulmalıdır.
  2. Bir sınıfın anlık durumunu(state) sınıf değişkenleri(class variables) belirler. Threadler tarafından bu durum bilgisi paylaşılmaktadır ve senkronizasyon da durum belirten değişkenlere erişimi koordine eder. Yazılan bir sınıfta hiç sınıf değişkeni yok ise(yani durum belirtmiyorsa) o sınıfın doğal olarak thread-safe olduğu söylenebilir.
  3. Sınıf değişkeninin değeri nesnenin yaratılması aşamasında(constructor içinde) atanır ve yaşam süresi boyunca hiç değiştirilmezse bu değişkenle ilgili senkronizasyon endişesi taşımaya gerek kalmaz.

Durum değişkenleri değiştirilemeyen sınıflar üretmek için(immutable classes):

  1. Tüm alanları final ve private tanımlanmalı, alanları değiştiren “setter” methodları bulunmamalı ve direk olarak bu alanlar diğer sınıflara kullandırılmamalı. Direk olarak dönülmesi gereken bir alan var ise bu alanın kopyası(defensive copy) oluşturulmalı ve bu kopyaya ait referans dönülmelidir.
  2. Başka sınıfların bu sınıfa ait methodları override etmesi engellenmeli(final sınıf veya private constructor ile yapılabilir)
  3. Construction sırasında this referansı başka nesnelere verilmemeli.
  4. Değişebilen sınıflara ait(mutable classes) referanslar bulundurulmamalı, gerekiyorsa kopyaları oluşturulmalıdır.
  1. Değişen durum değişkenlerine direk olarak ulaşım engellenmeli, bu değişkenler ulaşımı koordine etmekten sorumlu bir sınıf içerisinde barındırılmalıdır.(encapsulation) Durum değişkenlerinin tamamı thread-safe sınıflar tarafından yönetilirse yazılan programın thread-safe olduğu söylenebilir.

Gerekli senkronizasyonun bulunmadığı bir program çalışıyormuş gibi görünebilir; ancak bu noktada programın doğru sonuç üretebilmesi için zamanlama önemlidir. Bunun nedeni; programlama dilinde tek bir satırlık kodun aslında birden fazla komut içermesi ve bu işlemin atomik olmamasıdır. Birden fazla işlem tek bir işlem gibi çalıştırıldığında atomiklik sağlanır. Aşağıdaki kod parçacığında unsafeCount++ tek bir işlem gibi gözüksede üç ayrı parçadan oluşmaktadır. Bunlar sırasıyla Fetch(O anki değer okunur), Increment(Değer 1 arttırılır) ve Write(Yeni değer yazılır.) şeklindedir. Burada ortaya çıkabilecek sorun aşağıda gösterilmiştir.

Başlangıçta 8 olan değeri her iki threadde 8 olarak okuyor ve sonuçta 9 olarak yazıyor. Neticede 1 artımlık değer kaybolmuş oluyor. Bu durumdan kaçınmak için işlemin atomik hale getirilmesi gerekir. Aşağıdaki kod parçacığında increment metodu Java dilinin içerisinde yer alan synhronized keywordü ile atomik hale getirilmiştir. Synchronized keywordü içerisinde kalan blok aynı anda iki thread tarafından çalıştırılamaz. Bir thread synhronized bloğuna girdiğinde bu bloğa gelen diğer threadler bloğu çalıştıran thread bloktan çıkana kadar bekler.(intrinsic lock)

public class AtomicServlet implements Servlet{

private long unsafeCount = 0;

private long lockingCount = 0;

private final AtomicLong nonblockingCount = new AtomicLong(0);

public synchronized void increment(){lockingCount++;}//intrinsic lock

public void service(ServletRequest req, ServletResponse resp) {

unsafeCount++; // Bu işlem atomik değil!

increment(); //İşlem süresince diğer threadler bekler

unblockingCount.incrementAndGet(); //atomiktir ve diğer threadler beklemez

}

}

Kod içerisinde en verimli kullanım AtomicLong sınıfıdır. Çünkü AtomicLong sınıfı increment() metodundan farklı olarak diğer threadleri bekletmez. Bu modern işlemcilerin sahip olduğu bir özelliğin Java 5 tarafından implementasyonu ile sağlanmaktadır. Lock mekanizması kullanılacak ise bloğun içinde çalışan kodun uzun sürecek(I/O, network işlemleri gibi..) bir işlem barındırmamasına dikkat edilmelidir; çünkü bu durum diğer threadlerin gereksiz yere beklemelerine ve sonuç olarak programın performansının düşük olmasına veya deadlocklara sebep olabilir. Deadlock durumunu engellemek için intrinsic lock yerine Lock sınıfları(java.util.concurrent.locks paketi içerisinde) kullanılabilir. Lock sınıfları intrinsic locktan farklı olarak lock istendiği anda veya belirtilen belli bir süre içerisinde hazır değilse lock isteğinden vazgeçilebilir. Ancak intrinsic lockta bloktan çıkınca lock kaldırılıyorken Lock objeleri kullanıldığında bu iş kod içerisinde yapılmalıdır. (try-finally bloğu ile)

try { //Lock nesnesinin kullanımı synchronized(lock) {//intrinsic lock

locked = myLock.tryLock(); /*Diğer threadler locku kullanamaz

Bloktan çıkana kadar diğer

} finally { *threadleri bekletir*/

if(locked) { }//lock kaldırılır

myLock.unlock();

}

}

Beklemeye neden olabilecek metodlar(blocking methods) InterruptedException fırlatırlar. Normal süreç içerisinde bir threadin çalışması başka threadler tarafından engellenemez; ancak bir thread başka bir threadin yaptığı işi bırakmasını isteyebilir. Örneğin Thread sınıfına ait sleep ve wait methodları blocking method olarak nitelendirilirler ve ait oldukları Thread interrupt edildiğinde InterruptedException fırlatırlar. Yazdığınız beklemeye neden olabilecek bir method uzun sürebilecek bir işlem üzerinde çalışıyor ise belli aralıklarla Threadin isInterrupted metodu ile interrupt edilip edilmediği kontrol edilmelidir.

public void interrupt1() throws InterruptedException{//Blocking method

try {

/*If you call the deprecated method stop, it wont immediately stop, it will quit after sleep method executes */

Thread.sleep(5000); /*It is a blocking method and throws InterruptedExce**/

} catch (InterruptedException e) {

throw e;

}

}

public void interrupt2() throws InterruptedException{//Blocking method

while(notCompleted ){

workOnSomeIOTask();

if(Thread.currentThread().isInterrupted()){

throw new InterruptedException(“Stop!”);

}

}

}

public void howToInterruptThread(){

Thread t = getThread();

t.interrupt();

}

Verinin threadler arasında paylaşılması ile ilgili diğer bir problem bir değişkendeki değişikliğin tüm threadler tarafından görülememesi ile ilgili yaşanmaktadır(visibility). Bu performansı arttırmak için bazı değerlerin önbellekte tutulmasından kaynaklanır. Sonuç olarak bir değişkeni sadece bir thread değiştiriyor ve diğer tüm threadler bu değişkeni okuyor olsa dahi bu paylaşılan değişken için senkronizasyon gereklidir. Tüm threadlerin değişkenin son değerini gördüğünden emin olmak için değişkene yazma ve okuma aynı lock üzerinden gerçekleştirilmelidir veya değişken volatile keywordü ile tanımlanabilir. Volatile JVMe ilgili değişkenin paylaşıldığını ve değişikliklerin tüm threadler tarafından görülebilmesi gerektiğini bildirir. Ancak volatile keywordü atomikliği sağlamaz.

Java 5 versiyonu threadlerin zamanlamasını kontrol etmeyi sağlayan bazı sınıfları bulundurmaktadır.(synchronizers) Bir synchronizer sınıfı kendisini çağıran threadin çalışmasına devam edip etmesi ile ilgili durum bilgisini içerir.

Semaphore sınıfları sınırlı sayıda olan kaynağa erişimi kontrol etmekte kullanılır. Semaphore sınıfı mevcut kaynak sayısını bilir(counter) ve bir thread kaynak erişimi için izin istediğinde bu değeri 1 azaltır, thread kaynağı bıraktığında 1 arttırır. Sayı 0 olduğunda kullanımda olan bir kaynak bırakılana kadar kaynak isteyen threadler bekletilir.

public void semaphore() throws InterruptedException{ //May be interrupted

Semaphore sem = new Semaphore(1);

sem.acquire();

/** Since we have 1 permit no other thread can execute doSomething

* method until current thread releases the permit*/

doSomethind();

sem.release();

}

Latchler bitiş durumuna ulaşana kadar await methodunu çağıran threadleri bekletir. Örneğin; CountDownLatch içerisinde barındırdığı counter 0 a ulaştığında bitiş durumuna ulaşır ve beklettiği threadleri bırakır.

public void latch(int nThreads) throws InterruptedException {

final CountDownLatch start = new CountDownLatch(1);

final CountDownLatch end = new CountDownLatch(nThreads);

for (int i = 0; i < nThreads; i++) {

Thread t = new Thread() {

public void run() {

try {

start.await(); // waits until all the threads are started

doSomethind();

} catch (InterruptedException e) {

} finally {

end.countDown();

}

}

};

t.start();

}

start.countDown(); //All the created threads start execution

end.await(); //Main thread waits until all the created threads finish their task

}

Barrier olarak adlandırılan sınıflar belli sayıda threadin aynı noktaya ulaşmasını bekler. Bitiş durumuna ulaşıldığında tüm threadler aynı noktadan çalışmaya başlar. Bir thread bekleme sürecinde interrupt edilirse beklemede olan diğer threadler BrokenBarrierException fırlatır.

public void barrier(int nThreads) throws InterruptedException{

final CyclicBarrier bar = new CyclicBarrier(nThreads);

for (int i = 0; i < nThreads; i++) {

Thread t = new Thread() {

public void run() {

try {

bar.await(); //Waits until nThread calls await method

//And all the threads will call doSomething method at

//the same time!

doSomethind();

} catch (InterruptedException e) {

return;

} catch (BrokenBarrierException e) {

//Called if a thread is interrupted

return;

}

}

};

t.start();

}

}

Eşzamanlı programlarda yaşanabilecek temel sorunlar paylaşılan dataya ulaşımdan kaynaklanan performans problemleri ve deadlocklar ile gerekli senkronizasyonun bulunmamasından kaynaklanan yanlış sonuç üretimidir. Ayrıca çok fazla thread üretimi -işlemci threadler arasında sürekli geçiş yapmaya ihtiyaç duyacağından dolayı- performansın düşmesine neden olabilir. Bu durumdan kaçınmak için Thread pool kullanılabilir. Bir thread pool içerisinde her zaman çalışan belirli sayıda thread vardır. Aşağıdaki kodda thread pool 10 adet thread içerebilir. Bu nedenle 500 thread aynı anda yaratılmaz. Böylece sistemin kaldırabileceği yük sınırının aşılmadığı garanti altına alınır.

ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 500; i++) {
Runnable worker = new SomeRunnable();
executor.execute(worker);
}

Thread-safe program tasarlamak tamamen değişen durum değişkenleri ile alakalı olduğundan mümkün olduğu kadar az değişebilir durum değişkeni(mutable state variable) tanımlanması programın thread-safe tasarımını daha kolay hale getirecektir. Değişmeyen durum değişkenleri her zaman final keywordü ile tanımlanmalıdır. Böylece kod içerisinde değişkenin değiştirilmediğinden emin olunur ve kodu okuyan programcılar için dökümantasyon sağlar. Değişebilir durum değişkeni tanımlamak gerektiğinde bu değişkenler direk ulaşıma açık olmamalı ve bu değişkenlere ulaşımın koordinasyonu bir sınıfın sorumluluğunda olmalıdır. Diğer bir deyişle değişen durum değişkenleri bir sınıf içerisinde encapsulate edilmelidir.

Güven

Scrum lightweight, kolayca anlaşılabilen ve uygulanması güç bir framework olarak tanımlanabilir. Bazı kuralları olabildiğince esnek, bazıları katıdır, değiştirilemez. Kendine özgü bir dil geliştirmiştir. Toplantılar yapılır. Toplantı notları bir araya getirilip üzerinde tartışılır. Şeffaflık bir şekilde sağlanmaya çalışılır. Scrum takımları oturma şekline kadar tarif edilir. Roller biçilmiştir. Flat bir hiyerarşi, ama sınırları çizilmiştir. Kendi içinde bir disiplin, her gün mutlaka görüşen takım arkadaşları dirsek teması scrum ı oluşturur.

Bütün bunların üstünde bir özellik vardır, asıl üstünde durulması gereken: Güven… Scrum ın sağladığı yararların gösterildiği diyagramda birsürü kutucuk vardı. Hepsinin kökünde güven vardı. Hoca, o kutucuğu göstererek “Eğer güven yoksa diğer hepsi çöker!” dedi. Belki bütün bunların hepsi, aslında takımın içinde güveni tesis etmek için yapılıyor diyebiliriz.