Pazar, Ağustos 14, 2011

RESTful Web Servis Client–Android

Geçen haftaki yazımda Jersey kullanarak JSON üreten oldukça basit bir RESTful Web servis hazırlamıştık. Bu hafta bu web servisi kullanacak Android uygulamasını yazacağız.

Kısaca hatırlamak gerekirse geçen haftaki web servisimiz çıktı olarak aşağıdaki sınıfın JSON karşılığını üretiyordu.

RSOutputDTO.java
package com.blogspot.emrahkocaman.restfulws.json;
public class RSOutputDTO {
	
	private String userName;
	private int userAge;
	
	public void setUserName(String userName) {
		this.userName = userName;
	}
	
	public String getUserName() {
		return userName;
	}
	
	public void setUserAge(int userAge) {
		this.userAge = userAge;
	}
	
	public int getUserAge() {
		return userAge;
	}
	
}

Sadece userName ve userAge alanlarına sahip olan sınıfımızın örnek JSON karşılığı ise aşağıdaki satırdan ibaret.


{"userName":"Kamil","userAge":30}


Projemizin tek amacı bu web servisi çağırmak, dönen JSON string’ini parse ederek ekranda göstermekten ibaret olacak.


İşe önce ADT plugin’i kurulmuş bir Eclipse’te Android projesi oluşturarak başlıyoruz.


File –> New –> Android Project menüsüni kullanarak bir Android projesi oluşturuyoruz.





Daha sonra ADT plugin’i yardımı ile web servisten dönen bilgilerin gösterileceği basit bir arayüz hazırlıyoruz.



main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout android:id="@+id/relativeLayout1" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_marginTop="10dip">
<EditText android:layout_toRightOf="@+id/textView1" android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/txtUserName">
<requestFocus></requestFocus>
</EditText>
<EditText android:layout_height="wrap_content" android:layout_below="@+id/txtUserName" android:layout_width="wrap_content" android:layout_alignLeft="@+id/txtUserName" android:layout_alignRight="@+id/txtUserName" android:id="@+id/txtUserAge"></EditText>
<Button android:text="WS Çağır" android:id="@+id/btnCall" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_below="@+id/txtUserAge" android:layout_alignLeft="@+id/txtUserAge" android:layout_alignRight="@+id/txtUserAge"></Button>
<TextView android:text="Kullanıcı Adı" android:paddingRight="10dip" android:layout_height="wrap_content" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_alignParentLeft="true" android:paddingTop="10dip" android:paddingBottom="10dip"></TextView>
<TextView android:text="Kullanıcı Yaşı" android:layout_height="wrap_content" android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_below="@+id/textView1" android:layout_alignLeft="@+id/textView1" android:layout_alignRight="@+id/textView1" android:paddingTop="10dip"></TextView>
</RelativeLayout>
</LinearLayout>

Arayüzü hazırladığımıza göre web servis çağırmak için hazırız. RESTful web servislerin HTTP protokolü ile (GET, PUT, POST vb…) çalıştığından bahsetmiştik. Yani RESTful bir web servis çağırmak için ihtiyacımız olan tek şey bir http istemcisi ve bu istemciyi Android kendi bünyesinde barındırıyor (org.apache.http.impl.client.DefaultHttpClient) bu sayede 3.parti bir kütüphaneye gerek kalmadan HTTP istekleri yapmamız mümkün.

Bundan sonrasını kod üzerinde anlatarak devam edeyim.

package com.blogspot.emrahkocaman;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class RestClientActivity extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
	
super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	Button btnCallWS = (Button) findViewById(R.id.btnCall);
	btnCallWS.setOnClickListener(new OnClickListener() {
			@Override
	public void onClick(View arg0) {
//HTTP protokolüne ulaşmak için kullanılan sınıf
	 DefaultHttpClient httpclient = new DefaultHttpClient();
	 //Hazırladığımız web srvis GET isteklerine cevap veriyordu
	 HttpGet httpget = new HttpGet(
	 "http://192.168.37.1:8080/restfulwssample/rest/examples/getUserInfo");
	//IP olarak bilgisayarınızın local ip'sini vermeniz gerekiyor. 
//Aksi halde android uygulaması kendi 
	//localhost'una ulaşmaya çalışacaktır.
	HttpResponse response;
	try {
	//İsteği gönder
	  response = httpclient.execute(httpget);
	  StatusLine requestSuccess = response.getStatusLine();
	//İstek başarılı
	if (requestSuccess.getStatusCode() == 200) {
	  HttpEntity entity = response.getEntity();
	  if (entity != null) {
	   InputStream instream = entity.getContent();
	   //Stream olarak dönen cevabı anlaşılır bir hale getirmemiz gerekiyor
String requestResult = convertStreamToString(instream);
try {
	   //Android kendi içerisinde JSON stack'ini barındırıyor.
//Gelen string'i JSON objesine dönüştürüyoruz
	   JSONObject jsonResult = new JSONObject(requestResult);
	   Toast.makeText(RestClientActivity.this, jsonResult.toString(), Toast.LENGTH_LONG).show();
} catch (JSONException e) {
	    Toast.makeText(RestClientActivity.this, "JSON Exception", Toast.LENGTH_SHORT).show();
	   }
instream.close();
	}
	}
	} catch (Exception ex) {
	Log.e("REST_ERR", ex.getMessage());
	Toast.makeText(RestClientActivity.this, "Houston we have a problem", Toast.LENGTH_SHORT).show();
			
	}
	}
});
}
	private static String convertStreamToString(InputStream is) {
		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		StringBuilder sb = new StringBuilder();
		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}
}

Uygulamayı çalıştırıp “WS Çağır” butonuna bastığımızda bize ulaşan JSON nesnesini görmeliyiz.



Bize ulaşan JSON nesnesini parse ederek içinde “Kullanıcı Adı” ve “Kullanıcı Yaşı” bilgilerini çıkarmamız gerekiyor. Android içerisinde bulunan JSON stack’i bu işlemi yapmak mümkün.

userName.setText(jsonResult.getString("userName"));
userAge.setText(jsonResult.getString("userAge"));

Örneğimizdeki response’da sadece iki alan olduğu için zorlanmadık ama gerçek hayatta web servisler bu kadar basit değil tabiki. Çok fazla alan içeren JSON nesnelerini bu şekilde parse etmek hem hataya açık hemde çok zahmetli bir hal alacaktır. Bu sebeple JSON nesnelerini bizim içip parse edip istediğimiz nesnelere dönüştürecek kütüphaneler kullanabiliriz. Bu için tavsiyem Jackson ve Gson kütüphaneleri. 

Bunları arasındaki seçim konusunda ise tavsiyem, öncelik hız ise Jackson, öncelik uygulama boyutu ise (mobil uygulamaların boyutları büyük olduğunda fazlaca eleştiriliyorlar) GSON kullanmanız.

Şimdi örneğimize Gson’uda ekleyerek projeyi tamamlayalım.

Öncelik Gson kütüphanesi http://code.google.com/p/google-gson/ adresinden indiriyor ve projemizin classpath’ine ekliyoruz..

Hatırlayacağımız üzere dönen JSON nesnesi server tarafında RSOutputDTO.java sınıfına karşılık geliyor. Aynı sınıfı Android projemizede kopyalarak ilerliyoruz.


Gson sınıfı yardımıyla dönen JSON nesnesini RSOutputDTO nesnesine dönüştürerek örneği tamamlıyoruz.

Gson gson = new GsonBuilder().create();
RSOutputDTO result = gson.fromJson(jsonResult.toString(), RSOutputDTO.class);
userName.setText(result.getUserName());
userAge.setText(Integer.toString(result.getUserAge()));

Herkese kolay gelsin.

Eclipse projesi : http://db.tt/NoHiJ30

Pazar, Ağustos 07, 2011

Hızlı Bir Başlangıç - RESTful Web Servisler (JAX-RS)

Önce biraz teori;
Kısaca REST,  tüm bilgilerin kendilerine has birer adrese yani URI (Uniform Resource Idetifier) ‘ye sahip olmasını öngören mimari yaklaşımdır. (Web üzerindeki linkleri buna örnek olarak verebiliriz).
RESTful Web servisler ise REST mimarisini temel alarak geliştirilen web servislerdir yani SOAP yaklaşımı ile web’den uzaklaşan web servislerin tekrar web ile buluşmasıdır. RESTful tasarlanan web servisler web üzerindeki linklere dönüşmüşlerdir ve client ile server arasındaki iletişim stateless bir iletişim protokolü (genellikle HTTP) üzerinden sağlanır.
RESTful Web servislerin Java dünyasındaki standartları JAX-RS spesifikasyonu ile çizilmiştir.
Bu kadar teoriden sonra RESTful Web servislerin nasıl çalıştığını bir örnek üzerinden inceleyelim.
Not: Örnek için geliştirme ortamı olarak Eclipse (mümkünse Maven plugin’i kurulmuş) ile devam edeceğim. Maven kullanmak istemeyenler gerekli kütüphaneleri kendileri eklemek durumunda.

1) Proje oluşturulması

Öncelikle işe bir maven web projesi oluşturarak başlıyoruz. Projemizin adı “restfulwssample” olsun.
Bunun için File –> New –> Others –> Maven Project yolunu izliyoruz. Bu aşamada Maven kullanmak istemiyorsanız yada daha sonra aktif hale getirmek isterseniz “Dynamic Web Project” seçeneği ile ilerleyebilirsiniz.




Sonraki ekranda “Maven Project” seçeneği ile devam ediyoruz.




Sonrasında “Next” tuşlarıyla ilerleyip archetype seçim ekranı geldiğinde “maven-archetype-webapp” seçeneği ile ilerliyoruz.




Sonraki ekrandaki bilgiler tamamen size kalmış, bu bilgileride doldurduktan sonra “Finish” butonu ile web projemizin taslağını oluşturmuş oluyoruz.




Projemizin aşağıdaki gibi görünmesi gerekiyor.




Projemiz hazır. RESTful web servisler hazırlamamız için önmüzdeki tek engel RESTful Web servis için kullanılacak kütüphaneyi bulmamız. Ben bu örnekte Jersey kütüphanesi kullanacağım. JAX-RS spesifikasyonu için referans implementasyon olmasının yanı sıra production ortamlarında da kullanılabilecek olgunluğa sahip olduğu için Jersey’i tercih ettim. Projemizde Jersey kullanabilmek için pom.xml’e aşağıdaki satırları eklememiz yeterli olacaktır.

<repositories> 
<repository>
<id>maven2-repository.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2/</url>
<layout>default</layout>
</repository> 
<repository>
<id>maven-repository.java.net</id>
<name>Java.net Maven 1 Repository (legacy)</name>
<url>http://download.java.net/maven/1</url>
<layout>legacy</layout>
</repository>
<repositories>
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.6</version>
</dependency>
<dependencies>

Not: Jersey kullanarak text/html, xml, json gibi farklı yöntemler kullanarak haberleşen web servisler tasarlanması mümkün. Ben örnekte JSON kullanacağım için onunla ilgili dependency’i ekledim. Detalı bilgi için http://jersey.java.net/nonav/documentation/latest/user-guide.html adresine göz atmanızda fayda var.


2) Biraz kod görelim


Örneğimiz için iki sınıf kullacağız. Bunlardan biri RESTful web servisi barındıran sınıf (RSWebServices.java) diğeri ise bu servisin çıktısını temsil eden veri transfer sınıfı (RSOutputDTO.java)

Sınıfları aşağıdaki paket yapısında oluşturalım.





RSOutputDTO.java
package com.blogspot.emrahkocaman.restfulws.json;
public class RSOutputDTO {
private String userName;
private int userAge;
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
public int getUserAge() {
return userAge;
}
}

Bu sınıf web servisimizin üreteceği çıktıyı temsil eden basit bir java sınıfı namı diğer POJO.


RSWebServices.java
package com.blogspot.emrahkocaman.restfulws.ws;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.blogspot.emrahkocaman.restfulws.json.RSOutputDTO;
@Path("/examples")
public class RSWebServices {
@GET
@Path("/getUserInfo")
@Produces(MediaType.APPLICATION_JSON)
public RSOutputDTO getUserInfo() {
RSOutputDTO out = new RSOutputDTO();
out.setUserAge(30);
out.setUserName("Kamil");
return out;
}
}

Web servisimiz bu sınıftan ibaret. SOAP mimarisindekinin aksine xml konfigürasyonları ve xsd’ler ile boğuşmak zorunda değiliz. Doğrudan hedefe yapılan bir atış.

@Path: Sınıf tanımının üzerindeki annotation, web servise ulaşacağımız kök tanımı. Method üzerindeki ise methoda ulaşacağımız tanım. Yani web servis adresimiz http://server:port/examples/getUserInfo gibi bir adres olacak.

@GET: RESTful web servisler HTTP protokülü üzerinden haberleşirler sonuç olarak HTTP operasyonlarına karşılık gelecek annotationlar ile kullanılırlar (@GET, @PUT, @POST, @DELETE and @HEAD gibi). Bizim servisimizin GET isteklerine cevap vereceğini işaretliyoruz.

@Produces: RESTful web servisin çıktısının hangi türde olacağı bu annotation ile belirtilir. Biz örneğimizde JSON kullanıyoruz.


3) Son rötüşlar


RESTful web servisimizi dünyaya açmanın zamanı geldi. Öncelikle Jersey servletini ayağa kaldırmamız gerekiyor.

Bunun için web.xml’e aşağıdaki satırları ekyelim.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>restfulwssample</display-name>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.blogspot.emrahkocaman.restfulws.ws</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>

Servlet’in aldığı parametrelerden com.sun.jersey.config.property.packages parametresi hangi paketin altındaki sınıfların web servis olarak açılacağını belirtiyor.

com.sun.jersey.api.json.POJOMappingFeature parametresini “true” olarak set ederek ise POJO’larımız jackson kütüphanesi kullanılarak Jersey tarafından otomatik olarak JSON’a dönüştürülüyor.

Web projemizi sunucumuza deploy ettikten sonra,

http://localhost:8080/restfulwssample/rest/examples/getUserInfo adresini browser’a girdiğimiz zaman restful web servisimize ulaşmış olacağız. Bize dönen JSON nesnesini bir text editör ile açtığımızda içeriğinin aşağıdaki gibi olması gerekiyor.

{"userName":"Kamil","userAge":30}

Bundan sonra RESTful ile yapabilecekleriniz size kalmış. Ama bence web servis yazmak hiç bu kadar kolay olmamıştı.

Bir sonraki yazımda hazırladığımız RESTful web servis için bir Android istemci yazıyor olacağız.

Herkese iyi çalışmalar.

Eclipse Projesi : http://db.tt/PCS4UrX

Pazar, Ocak 16, 2011

GlassFish Maven Repository

EJB 3.1 ile uğraşırken implementasyon olarak glassfish'i seçeyim dedim. (Bu aralar referans implementasyonlara sardım).

Tam ilgili dependency'yi eklemiştim ki tahmin ettiğim şey yine başıma geldi ilgili artifact merkezi maven repository'sinde yine yoktu. Bana da java.net maven repository'sini listeye eklemek düştü.

Not : java.net repository'si her ne kadar öğrenme sürecinde yada ev kullanımı için yeterli olsada profesyonel hayatta kullanmak uygun değil çünkü her an kullandığınız artifact kaldırılabilir. Bunun için "Nexus" veya muadili bir repository yöneticisi ile bağımlı olduğunuz kütüphanleri cache'lemeniz gerekiyor.

GlassFish EJB 3.1 Artifact
<dependency>
  <groupId>org.glassfish</groupId>
  <artifactId>javax.ejb</artifactId>
  <version>3.1-b37</version>
</dependency>

Java.Net GlassFish Maven Repository
<repository>
  <id>glassfish</id>
  <name>Glassfish Maven Repo</name>
  <url>http://download.java.net/maven/glassfish</url>
</repository>

Pazar, Ocak 09, 2011

EclipseLink Maven Repository

Küçük Bir Not:

JPA 2.0'ın referans implementasyonu olan EclipseLink 2.0 henüz merkezi maven repository'sinde bulunmuyor bu yüzden projenizde kullanmak istediğinizde "Artifact"in bulunamadığına dair bir hata ile karşılaşıyorsunuz.

Aslında bu sadece EclipseLink'le ilgili bir problem değil, maven'in merkezi kütüphanesinin oldukça geç güncellendiği bir gerçek.

Çözümü ise aşağıdaki şekilde EclipseLink repository'sini pom'unuza eklemekten geçiyor ve sonrasında EclipseLink 2.0 artifactlerini ekleyebilirsiniz.

EclipseLink Maven Repository
<repositories>
  <repository>
     <id>EclipseLink Repo</id>
     <url>http://www.eclipse.org/downloads/download.php?r=1&nf=1&file=/rt/eclipselink/maven.repo</url>
  </repository>
</repositories>

EclipseLink JPA 2 API Artifact
<dependency>
   <groupId>org.eclipse.persistence</groupId>
   <artifactId>javax.persistence</artifactId>
   <version>2.0.0</version>
</dependency>

EclipseLink JPA 2 Implementation
<dependency>
  <groupId>org.eclipse.persistence</groupId>
  <artifactId>eclipselink</artifactId>
  <version>2.0.0</version>
</dependency>