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());
}
}
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);
}
});
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());
}
}
}
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.”