RESTful Web Servisleri – Jersey

Hatırlarsanız önceki bölümlerde rest-db projesini tamamlamıştık. Artık DAO sınıflarımız ve veritabanı modellerimiz hazırdı. Şimdi ise artık Jersey ile RESTful web servislerimizi yazabiliriz.

Içindekiler

  1. Hazırlık
    1. Projenin Yaratılması
    2. Veritabanının Yaratılması
  2. JPA Modellerinin Oluşturulması
  3. Hibernate DAO Sınıflarının Yazılması
  4. Web Servisin Yazılması
  5. GAE Üzerine Web Servisin Yüklenmesi
  6. Web Servis İçin İstemcilerin Yazılması

4. RESTful Web Servislerinin Yazılması

Artık amacımız olan RESTful web servislerini yazmaya başlayabiliriz. Hatırlarsanız ilk başta yazmaya, bir şirkette projeye başlayacağınız şekilde başlamıştık. Tabi kide proje planı görev dağılımı gibi işleri dahil etmedik. Ayrıca tüm bu işleri tek başınızsa üstlenmenizde gerekmeyecektir. Daha doğrusu gerekmemelidir. En azından veritabanının ve projenin tasarımını bir başkasının yapması daha iyi olacaktır.

Projeye ilk başlarkende belirtmiştik, REST için JAX-RS API’sini kullanacağız. Açıklanan API’lar için farklı farklı uygulamalar bulmak mümkün, fakat ben proje içerisinde Jersey uygulamasını kullanacağım. Eğer siz şirkette çalışırken, projenizde hangi aracı kullanacağınıza karar vermeniz gerekirse, size tavsiyem öncelikle standartları inceleyiniz. JAX-RS (The Java API for RESTful Web Services) java tarafından oluşturulmuş JSR 311 içerisinde tanımlı olan bir REST API’dır. Benim projemde JAX-RS seçmemin temel nedi budur.

Tabi projenizde kullanacağınız aracı seçerken sadece API’n ne olduğunu seçmeniz sizin için yeterli olmayacaktır. Ayrıca uygulamayıda seçmeniz geremektedir. Benim Jersey’i seçmemin nedeni, Jersey’nin sadece JAX-RS API’ını destekliyor oluşudur. Böylelikle başka kütüphanelerle kafanızın karışmayacağını umuyorum. Ama şirketinizde hem SOAP hem REST web servisleri yazılacaksa karar vermeden önce Apache CXF uygulamasını incelemenizi tavsiye ederim. Özellikle CDI modülü olarak Spring kullanıyorsanız, kesinlikle Apache CXF işinizi çok kolaylaştıracaktır. Eğer değerlendirmek isterseniz aşağıda kullanılan JAX-RS uygulamalarının listesini bulabilirsiniz.

  • Jersey: Şu anda GlassFish grubu tarafından geliştiriliyor.
  • Apache CXF: Hem SOAP (JAX-WS) hem RESTful (JAX-RS) web servisleri geliştiriyorsanız ideal bir çözüm. CDI modülü olarak Spring ile çok rahat entegre olabiliyor.
  • RESTEasy: JBoss tarafından geliştiriliyor yani Red Hat topluluğunun bir üyesi. Eğer CDI modülü olarak Seam kullanıyorsanız özellikle tavsiye ediliyor. Seam ile entegrasyonun en iyi olduğu JAX-RS uygulaması olduğu söyleniyor. Hakkında güzel bir tutorial da mevcut.
  • Restlet: Bir başka JAX-RS API’ı. Jersey’den farklı yanı, nesneleri serileştirirken, JAXB yerine Xstream ve Jakson kullanıyor. Eğer tüm özelliklerini incelemek isterseniz buraya bir göz atabilirsiniz.

4.1 Hazırlık

Öncelikle ilk bölümde oluşturduğumuz rest-main projesini kullanacağız. Hatırlarsanız projemizi web uygulaması şeklinde oluşturmuştuk. Yakşalık olarak aşağıdaki dosya yapısına sahip olmanız gerekiyor. Emin olmak için Tomcat üzerinde bir çalıştırıp Hello World yazısını görmenizi tavsiye ederim.

Projemizi çalıştırdıktan sonra artık proje bağımlılıklarımızı eklemeye başlayabiliriz. Tabi burada ekleyeceğimiz bağımlılıklar kullanacağımızı ön gördüklerimiz, yeri geldiğinde yeni bağımlıklarımızı aşağıdaki pom.xml dosyasının üzerine ekleyeceğiz.

pom.xml
  Bağımlılıklarımızı da güncelledikten sonra artık restful web servislerinde ilk adımlarımızı atabiliriz. İlk adımlarımızı doğal olarak bir Hello World uygulaması üzerinden atacağız. Bu uygulama ile kullanılan annotation yapılarını ve URL formatını incelemiş olacağız.

4.2 Hello World

Öncelikle servis sınıflarımızı yazacağımız paketi oluşturalım. Bunun için src/main/java klasörünü oluşturmamız gerekiyor. Projeye sağ tıklayıp “New Source Folder” seçeneğini seçiyoruz. Açılan pencereye src/main/java yazıp klasörümüzü oluşturuyoruz. Klasörümüzü oluşturduktan sonra artık paketimizi oluşturabiliriz. Bunun için oluşturduğumuz kaynak klasörüne sağ tıklayıp yeni bir paket ekliyoruz. Ben paket adı olarka com.bahadirakin.services kullandım, siz istediğinizi kullanabilirsiniz. Fakat bu paket adını birazdan web.xml içerisinde kullanacağız.

web.xml

Web.xml dosyamızda, Jersey’nin JAX-RS web servisleri için tarayacağı paket dizini gösterdik. Ayrıca restful web servislerinin hangi noktadan başlayacağını belirttik. Bizim servislerimi rest ile başlayacak. Nasıl ki SOAP web servisleri WSDL dosyası üretiyorsa, Rest web servisleride WADL dosyaları üretmektedir. Bu sayede servisinizi açacağınız kullanıcıları haberdar edebilirsiniz. Bizim WADL dosyamız rest/application.wadl uzantısında bulunmaktadır.

Şimdi HelloWorldService sınıfımızı yazabiliriz.

HelloWorldService.java
Şimdi bu servis sınıfımızı inceleyelim ki nedir ne değildir öğrenelim.

  • Path: Bu annotation sayesinde, JAX-RS servisimiz, url içerisinde hangi isimle ile arama yapacağını anlıyor. Yani bizim “hello” olarak belirttiğimiz kısım servisimize bağlanmak için kullanacağımız url içerisinde, rest‘ten (ki bunu web.xml içerisinde tanımladık) sonra gelmelidir. Yani sınıf başında belirttiğimiz “path” annotation’ını, servis adı olarak düşünebilirsiniz. Eğer metod başında belirttiysek, servis içerisinde metoda özgü yol belirttiği gibi parametre olarakta gösterilebilmektedir.
  • PathParam: Metodlarımıza çeşitli şekillerde parametre aktarabiliyoruz. PathParam annotation’ı bu yollardan biri. Bunu kullanarak path üzerinden parametre alıyoruz. Nasıl kullandığımızı örnekte görebiliyorsunuz.
  • QueryParam: Eğer daha önce php ile kod yazdıyasınız bu en alışık olduğunuz parametre aktarma yöntemidir. Parametrelerinizi “&” ve “?” kullanarak url içerisinde belirtiyorsunuz. Gördüğünüz gibi PathParam’dan biraz farklı ama nerdeyse aynı kapıya çıkıyor.
  • FormParam: Örneğimizde yok ama genel olarak HTML Form’larından gönderdiğiniz veri olarak düşünebilirsiniz.
  • GET: Tahmin ettiğiniz üzere HTML GET isteği anlamına geliyor. Bunun haricinde DELETE, PUT, POST vs. gibi her türlü HTML isteğine uygun metodlar yazmanız mümkün. Ben proje içerisinde genel olarak GET kullanacağım ki kolay test edebilin. Fakat şirket içerisinde geliştireceğiniz projelerde en azından POST isteklerini kullanmanız gerekebilir. Örneğin eğer bir From’u bir JAX-RS web servisine bağlayacaksınız, POST methodunu ve FormParam’ı kullnabilirsiniz.
  • Produces: Bu annotation ile metodunuzun ne tür bir çıktı üreteceğini belirtiyorsunuz. HTML, XML, JSON ve PlainText üretilen çıktıların başında geliyor.  Uygulamanızın ne üreteceği ve nasıl üreteceği tamamen sizin kontrolünüzde. Fakat genel olarka XML için JAXB kullanılıyor. Bizde sonraki örneklerimizde JAXB kullanacağız.
  • Consumes: Metodumuz herzaman veri üretmeyecek. Yeri geldiğinde yine kendi belirlediğimiz sınıfları parametre olarak alabilmesi de gerekecek. Consumes metodu ne tür verilerin tüketileceğini belirtiyor. Yani Produces annotation’ının tersi gibi düşünebilirsiniz. Yine HTML, XML ya da JSON gibi veri tipleri üzerinden çalışıyor.

Şimdilik bu kadarlık ön bilginin yeteceği düşüncesindeyim. Projeyi basitleştirmek adına, JAX-RS projesinde sadece bu annotation’ların bazılarını kullanmaya özen göstereceğim. Fakat gerçek bir projede bu annotation’ların hepsini kullanmanız gerekcektir. Şimdi yazdığımız servisin metodlarını incelemeye başlayalım.

  • 1. Metod (saySimpleHello):  Bunu test etmek için kullanacağınız URL aşağıdakine benzer olmalıdır. http://localhost:8080/rest-main/rest/hello/ Bu url’in bize anlattığı, rest-main içerisinde, rest  yolunda JAX-RS web sevislerinin bulunduğu ve bu servislerinin içerisinde de hello diye bir sevisin olduğudur. Bunu herhangibir tarayıcıya yazdığınızda karşınıza PLAIN_TEXT formatında “Hello, World!” yazısı gelecektir.
  • 2. Metod (sayXmlHello): Bu metodu http://localhost:12000/rest-main/rest/hello/bhdrkn şeklinde ki bir URL ile test edebilirsiniz. Gördüğünüz üzere bu sefer methodumuzun bir yol parametresi (PathParam, bhdrkn) aldığını görüyoruz. Parametrelerinizi ve metodlarınızı belirlerken dikkatli olmalısınız, aksi halde metodlarınız karışabilir. En güzeli her metodunuza ayrı ayrı yollar vermenizdir. Bizde 3. metodumuzda bu tavsiyeye uyduk ve metodumuza ayrı bir yol uzantısı verdik.
  • 3.Metod (sayHelloAgain): Bu metodu http://localhost:12000/rest-main/rest/hello/query?to=bhdrkn şekilndeki bir URL ile çağırabilirsiniz. Gördüğünüz üzere bu metodumuz hello servisinin içerisindeki query metodunu çağırıyor. Sorgu parametresi olarakta to kullanılmış. Bu örnek biraz da PathParam ve QueryParam annotation’larının farkını göstermek için kullanılmıştır.

      Şimdi bu üç metodunda nasıl çalıştığını anladıktan sonra artık gerçek servislerimizi yazmaya geçebiliriz.

4.3 Gerçek Web Servisinin Yazılması

Gerçek web servisinin yazılmasına geçmeden önce web servisimiz de nelerin olması gerektiğine karar vermek gerekiyor. İlk yazıdan hatırlayacağınız üzere apartmanın otopark sistemini yönetmek üzere bir servis tasarlıyorduk. Minimum yapıda olması gereken yetenekler aşağıdaki gibidir.

  • Yeni kullanıcı ekleme: Sisteme herzaman yeni kullanıcı eklenebilmeli.
  • Eski kullanıcıları silme: Sistemden eski kullanıcıları silmek gerekecektir.
  • Kullanıcılara yeni araba ekleme
  • Kullanıcılardan araba silme
  • Kullanıcı Listesi: Apartmanın kullanıcı listesi.
  • Araba kayıtlı mı?: Plakası gönderilen aracın kayıtlı olup olmadığını döndüren metod.

      Şimdi bu yeteklerin çoğu şu an ki sistemde gerçeklenebilir. Yeni kullanıcı eklenebilir, eski kullanıcı silinebilir, yeni araç eklenip siline bilir ve kullanıcı listesi alınabilir. Fakat şuan ki yapıda plaka ile araç sorgusu yapılamıyor yani bunun eklenmesi gerekecek. Öteyandan eğer kullanıcı listesi döndüreceksek hangi formatta döndüreceğimize karar vermemiz gerekiyor. Yani XML, JSON, HTML ya da kendi belirlediğimiz başka bir yapıda döndürülecekse bunun için bir hazırlık yapmamız gerekiyor. Bu da demek oluyor ki modellerimiz üzerinde de bazı değişikliklere gideceğiz. Ben kolay olsun diye ve genel olarak kullanabilmesi açısından XML olarak işleyeceğim. XML dönüşümü içinde JAXB kullanacağım. Öyleyse şimdi tekrardan rest-db projesmize DAO sınıflarımızı ve modellerimizi güncelliyoruz.

4.3.1 DAO Sınıflarının güncellenmesi

Öncelikle basit olan değişikliği yapalım, yani plaka ile araç arama kısmını gerçekleştirelim. Bunun için öncelikle ICarDAO sınıfını ardından da CarDAO sınıfını güncelliyoruz. Ben sadece CarDAO sınıfına yaptığım güncellemeyi yapacağım, fakat siz ICarDAO sınıfını da güncellemeyi unutmayın.

CarDAO.java

Şimdi ise sınıflarımızı güncellemeliyiz.

4.3.2 Modellerin Güncellenmesi

Şimdi ise modellerimizi güncellememiz gerekiyor. Modellerimizi güncellemek için öncelikle, kullanacağımız XML kütüphanesini yani JAXB kütüphanesini projemize eklememiz gerekiyor. Bunun için aşağıdaki bağımlılığı rest-db projesinde ki pom.xml dosyasına eklemeniz yeterli olacaktır.

pom.xml

<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>

Şimdi ise modellerimizdeki değişiklikleri konuşalım. Bir kaç annotation ekleyip bir kaç annotation değerini değiştirip işimizi halledeceğiz.

  1. Öncelkile User sınıfımızı açalım. İlk eklememiz gereken XmlRootElement annotation’ı. Bu annotation’ı sınıfımızın başına ekliyoruz.
  2. Şimdi biz User listesini döndüreceğimiz için, XML oluşturulurken, kullanıcının araç listesine de erişilecek. Fakat kullanıcı araç listesi Lazy olarak çekiliyor. Fakat User listesinin alınması ve XML dönüşümü ayrı Thread’ler içerisinde olacağından, Lazy olarak bu veriyi çekemeyeceğiz. Bu sebepten OneToMany annotation’ı ile çekilen Car listesini, Lazy olarak değilde Eager olarak çekmeye başlıyoruz.
  3. Sırada Car sınıfımız var. Bu sınıfımızda da User sınıfında olduğu gibi en önce XmlRootElement annotation’ını ekliyoruz.
  4. Fakat bu bizim için yeterli değil. Aynı şekilde Car sınıfının içerisinde de User listesi bulunuyor. Eğer bu haliyle Xml oluşturmaya başlatırsak, önce User nesnesinden XML üretilmeye başlanacak. Sonra User’a ait arabaların listesinden XML üretilmeye başlanacak. Daha sonra her araba için User tekrardan yazılacak. Bu böyle döngü halinde gidecek. Fakat bu hataya neden olur ve bu tip döngüler JAXB tarafından yakalanır. Bunun önüne geçmek için her araba için kullanıcı bilgisinin XML olarak üretilmemesi gerekiyor. Bunu sağlamak içinde getUsers metodu üzerine XmlTransient  annotation’ını ekliyoruz.

Şimdi eklememiz gereken herşeyi ekledik. Sırada servis sınıfımızın yazılması kaldı.

4.3.3 Servisin Hazırlanması

Şimdi artık servisimizi tam manasıyla hazırlayabiliriz. Metodlarımız belirlendi, DAO sınıfılarımız ve modellerimiz güncellendi, bağımlılıklarımız da tamam. Şimdi yine rest-main projesinde com.bahadirakin.services paketinin altına bu sefer ParkingLotService isminde bir java dosyası oluşturuyoruz. İçeriği ise aşağıdaki gibi oluyor.

ParkingLotService.java

Evet servisimizide yukarıdaki gibi yazdık. Birazda neyin ne olduğunu incelemeye başlayalım. Öncelikle DELETE ve GET metodlarını kullandık. Kullanıcı ve araç silme işlemleri için DELETE tipinde HTTP Request’ler bekleniyor. Geri kalan tüm işlemleri ise GET ile yaptık. Yine silme işlemleri bir birine çok benzediğinden bunları ortak bir metod üzerinden uyguladık ki DRY ilkesine sağdık kalalım. Silme haricinde ki tüm metodlarınızı tarayıcınız üzerinden deneyebilirsiniz. Örnek bir kaç sorguyu aşağıda veriyorum.

Gibi listeyi sizde uzata bilirsiniz.

4.4 Sonuç

Evet, artık web servisimizide tamamladık. Burdan sonra GAE üzerinde web servisimizi nasıl koşturacağımız ve bu geliştirdiğimiz web servisimize özel istemcileri nasıl yazacağımız kaldı.

Takip edemeyenler ve projenin son halini merak edenler için projenin git deposu aşağıdaki gibidir.
Projeleri yaratırken sorun yaşadıysanız, proje yapısına Gir Repo’sundan bakabilirsiniz.

 

End Of Line

  • Pingback: RESTful Web Servisleri - Hibernate | Bahadır AKIN()

  • Çağrı

    Mükemmel bir çalışma.

  • Tuğba Yılmaz

    Merhaba, öncelikle çalışma için çok teşekkür ederim yaptığınız çalışmayı uygulamaya çalışıyorum ancak

    java.lang.ClassNotFoundException: com.sun.jersey.spi.container.servlet.ServletContainer şöyle bir hata alıyorum yardımcı olur musunuz?

    • bhdrkn

      Merhabalar

      ClassNotFoundException fırlatmasının sebebi com.sun.jersey.spi.container.servlet.ServletContainer sınıfının ClassPath’inizde bulamamasından kaynaklanıyor. Bu sınıf jersey-server kütüphanesinin içerisinde yer alıyor.

      4.1 Hazırlık Bölümün’de pom.xml dosyası içerisindeki bağımlılıklarınızı kontrol edebilir misiniz?

      Eğer pom.xml içerisindeki bağımlılıklarınız doğruysa, komut satırından pom.xml dosyanızın bulunduğu dizinde “mvn clean install” komutunu çalıştırmayı dener misniz?

      • Tuğba Yılmaz

        tekrar merhaba,

        dediğiniz şekilde baktım hatta projeyi yeniden oluşturdum olmadı yine 🙁
        com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes. hatam

        • Tuğba Yılmaz

          yuppi çalıştı 🙂

  • bhdrkn

    ClassNotFoundException fırlatmasının sebebi com.sun.jersey.spi.container.servlet.ServletContainer sınıfının ClassPath’inizde bulamamasından kaynaklanıyor. Bu sınıf jersey-server kütüphanesinin içerisinde yer alıyor.

    4.1 Hazırlık Bölümün’de pom.xml dosyası içerisindeki bağımlılıklarınızı kontrol edebilir misiniz?

    Eğer pom.xml içerisindeki bağımlılıklarınız doğruysa, komut satırından pom.xml dosyanızın bulunduğu dizinde “mvn clean install” komutunu çalıştırmayı dener misniz?

  • nazım

    Selam aleyküm kardeşim.
    Spring restful ile JAX-RS restful arasındaki fark nedir? Hangisi daha hızlıdır?

    • bhdrkn

      Merhabalar,

      Öncelikle Spring ile JAX-RS’i karşılaştırmak çok doğru değil. İkisi çok farklı problemlerin çözmektedirler.

      Yazımda çok net anlatamamışım ama, JAX-RS sadece bir API, bir java standardı. Bu API’ın farklı uygulamaları (implementasyonları) var. Yukarıdaki yazıda ben Jersey implementasyonunu kullanmışım. Fakat farklı implementasyonlarda mevcut, mesela Apache CXF’te bir JAX-RS implementasyonu.

      Spring ise bir CDI (Context and Dependency Injection) çözümü. Jersey ile Spring’i bir arada kullanabilirsiniz. Apache CXF ile de Spring’i bir arada kullanabilirsiniz. Spring gelişince aynı zamanda topluluğunda adı oluyor (Apache gibi mesela). Sonrasında bu topluluğun çıkardığı tüm frameworkler bu ön eki alıyor. Spring MVC ya da Spring Data gibi (aynı şekilde Apache Commons, Apache CXF gibi)…

      Spring MVC, bu topluluğun çıkardığı Restful web servisler üretmeye yönelik bir framework. Java’nın standard API’sini (JAX-RS) desteklemiyor. Her şeyi sıfırdan kendi geliştiriyor. Jersey’e göre farkı daha çok detayda. İkisini karşılaştırmaya kalkmayacağım, bunun için yorumdan çok daha fazlasına ihtiyacım olacaktır.

      Asıl sorunuza gelince yani, hız bakımından Spring MVC’imi daha iyidir Jersey’mi, açıkçası net bişey söylemek zor. Öncelikle elimde veriler olmadan böyle bir yorum yapmam doğru olmaz. Sonra, hız çok göreceli bir kavram. Hangi şartlar altında hız? Biri küçük verilerin işlenmesinde daha hızlı davranırken, diğeri büyük verilerde daha hızlı olabilir. Üstelik şartları değiştirerek istediğiniz sonucu da elde edebilirsiniz. Ondan size tavsiyem, ortalama nesne boyunuza göre ve sisteminizin alacağı tahmini yüke göre testlerinizi kendiniz yapın. Başkasının yaptığı testlere güvenmeyin.

      Tahminimi de söyleyeyim. Jersey Spring’e ihtiyaç duymuyorken, Spring MVC için Spring kullanmanız şart. Eğer Jersey’i Spring’siz kullanırsanız daha hızlı çalışacağını düşünüyorum. Fakat ikisinde de spring kullanacaksanız, çok büyük bir hız farkının olacağını sanmıyorum. Sonuçta her iki çözümde alt tarafta servlet mimarisini kullanıyor. Size sıkıntı yaratacak kadar büyük farklar yaşatmazlar.

      Jersey kullanacaksanız bu yazı dizisi zaten size bir başlangıç noktası sağlayacaktır.
      Eğer Spring MVC kullanmak isterseniz, bu yazı dizisine çok benzer başka bir yazı dizim daha var, http://www.bahadirakin.com/sprinmvc-ve-mongodb-4-springmvc-ile-rest-servisleri/ .

      Eğer aklınıza takılan başka bişey olursa sormaktan çekinmeyin lütfen.
      Teşekkürler

      • nazım

        Öncelikle bu kadar detaylı açıklama için teşekkür ederim kardeşim.
        Açıkcası aklımda tek çatı altında tek proje içerisinde mysql ile hibernate e bağlı olarak web service ve bunu kullanacak web site şeklinde. Devamında pilot uygulama olarak android platformu olacak iler ki zamanda ios inşallah. Şu an için araştırma yapıp veri topluyorum. Projenin temelini düşünerekten tabi kalitesi ve geleceği için de.

        Teşekkürler.