Sansür, bir toplumun kendine olan güvensizliğini yansıtır ve otoriter rejimlerin belirgin bir özelliğidir.

--Potter Stewart

23.01.2008

Spring ve POJO-tabanlı Uzak Erişim (Remoting) Servisi

1. Giriş

Günümüzde geliştirilen uygulamaların tek başına hizmet üretmeleri uygulamalardan talep edilen gereksinimlerin karşılanması için bazen yeterli olmamaktadır. İstenilen işlevi kendi bünyesindeki servislerle karşılayamayan istemci uygulama, söz konusu işlevi barındıran sunucu nitelikli başka bir uygulamanın servisi ile iletişim kurma gerekliliği duymaktadır. İstemci-sunucu uygulamalar arasında gerçekleştirilen bu iletişim uzak erişim (remoting) adıyla anılır.

Java dünyasında kullanılabilecek birkaç uzak erişim teknolojileri bulunmaktadır:

  • Remote Method Invocation (RMI)

  • Caucho’nun Hessian ve Burlap yöntemleri

  • Spring’in kendi HTTP çağrısı

  • SOAP ve JAX-RPC kullanan Web servisleri

Bu kılavuz belge kapsamında RMI ve Spring HTTPInvoker teknolojilerinden bahsedilecektir.


2. Spring Uzak Erişime Genel Bakış

Spring, adı geçen uzak erişim teknolojilerinin kullanımını oldukça sadeleştirmektedir. Seçilen uzak erişim modelini Spring’in sağladığı ortak bir yapı üzerinden yönetmek kolaylaşmaktadır. Bu ortak yapı sayesinde farklı bir modele geçiş yapmak zahmetsiz hale getirilmiştir.

Tüm modellerde, uzak erişimi sağlanacak servis, yönetilebilir(managed) bean olarak tanımlanır. İstemci bean, proxy aracılığıyla çağrısını yapar. Proxy, uzak servis ile istemci adına iletişime girer. Bağlantı detaylarının sağlanması, uzak çağrının yapılmasını ve dönen sonucun istemciye iletilmesini proxy ele alır. Ayrıca proxy iletişim sırasında oluşan hataları istemcinin isteğe bağlı ele alabilmesi için unchecked hata olarak tekrar fırlatır.

Sunucu tarafında herhangi bir yönetilebilir bean olarak tanımlananan servisi uzaktan erişime açabilirsiniz. Dolayısıyla herhangi bir servis başka bir sınıftan türemeden ya da başka bir arayüzün gerçekleştimini yapmak zorunda olmadan uzak erişim özelliği kazandırılabilir. Servisin, uzak servis olma bilgisi servis koduna bağımlı değildir. Uzak erişim ayarları konfigürasyon kütükleri ile ele alınır.


3. RMI ile Uzak Erişim

RMI, Java geliştiricileri için JDK 1.1’den beri güçlü bir uzak erişim seçeneği sunmaktadır. RMI ile uzak erişim sağlamak biraz zahmetli olmasına karşın Spring hem istemci hem sunucu tarafında zahmeti ortadan kaldırmaktadır. İstemci tarafında proxy factory bean ile sunucu erişimini sağlayacak proxy’i yaratırken sunucu tarafında remote exporter ile uzak erişim sağlanacak Spring yönetilebilir bean servisini adapter örüntüsü yardımıyla RMI servisine dönüştürerek erişime açmaktadır.


3.1. RMI - İstemci Tarafı

Öğrenci Ders kayıt sisteminin ders kaydını öğrencinin harcını yatırmış olması koşuluyla gerçekleştireceğini ve harcın yatırılıp yatırılmadığını da bankanın servisini kullanarak ele alacağı bir örnek düşünelim. Böyle bir senaryoda standard RMI kullanarak uzak servis referansına şu şekilde factory method yazarak erişebilirdik:


private String harcControlUrl = "rmi:/banka/HarcControlService";

public HarcControlService lookupHarcControlService()

throws RemoteException, NotBoundException, MalformedURLException {

HarcControlService harcControlService = (HarcControlService)

Naming.lookup(harcControlUrl);

return harcControlService;

}


Bu yöntem ile fırlatılan checked hatalar ele alınmalı. Ayrıca RMI servisini kullanacak her servis lookupHarcControlService yöntemini çağırmalı. Aynı zamanda istemci kodu, çağırılacak servisin RMI niteliği taşıdığı bilgisinden ve servisin RMI yer bilgisinden de haberdar olmak zorunda. Spring Remoting ile dependency Injection yöntemiyle uzak servisi kullanmak isteyen bir istemciye, servisin uzak veya yerel bilgisinden bağımsız sorumluluk ataması yapılabilir.

Spring’in RmiProxyFactoryBean factory bean’i RMI servisine çağrı yapan proxy’i yaratmakta kullanılabilir. Herhangi bir bean tanımı nasıl yapılıyorsa Spring config kütüğünde gerekli tanımı şu şekilde yapabilirsiniz.


<bean id="harcControlService"

class="org.springframework.remoting.rmi.RmiProxyFactoryBean">


<property name="serviceUrl" value="rmi://${bankahost}/HarcControlService" />

<property name="serviceInterface" value="com.banka.universite.HarcControlService" />

</bean>


Bu tanımda serviceUrl niteliği uzak servisin bulunduğu makine ismini ve servis ismini barındırır. serviceInterface niteliği ise uzak servisin, yerel uygulamada bulunan interface tanımını belirtir. Tanımladığımız bu bean’i uzak servisi kullanacak olan yerel bean’nimizde bağımlılık aktarımı yaparak kullanabilirsiniz:


<bean id="ogrenciKayitService" class="com.yerelUygulama.service.OgrenciKayitServiceImpl">

<property name="harcControlService">

<ref bean="harcControlService"/>

</property>

</bean>

---

import com.banka.universite.HarcControlService


public class OgrenciKayitServiceImpl implements OgrenciKayitService{

private HarcControlService harcControlService;

...

}


Bağımlılık aktarımı sayesinde arka planda çalışan harcControlService gerçekleştiriminde, istemcinin haberi olmadan değişikliğe gitmek mümkün olmaktadır. Buraya kadar yapılan işlemler istemci tarafındaydı. Şimdi de sunucu tarafı işlemlerine bakalım.


3.2. RMI – Sunucu Tarafı

Standard RMI kullanarak uzak servis yazmak için şu adımları izlenir:

  1. java.rmi.RemoteException fırlatan service gerçekleştirimi yazılır.

  2. java.rmi.Remote arayüzünü extend eden service arayüzü yazılır.

  3. İstemci stub ve sunucu skeleton sınıflarını yaratmak için RMI derleyici (rmic) çalıştırılır.

  4. Servisleri barındıracak RMI registry çalıştırılır.

  5. Servisler RMI registry’e bağlanır.

Spring tüm bu adımları sadeleştirmektedir. Geliştirici sadece servisi standard Spring bean olarak hazırlar. RMI’a özel sınıf yazmak zorunda değildir. Dolayısıyla java.rmi.Remote arayüzünden kaynaklı hiçbir yöntemi gerçekleştirmek ya da hataları ele almak zorunda değildir. Servise sadece iş mantığını gerçekleştirmek kalır.


package com.banka.universite;

public interface HarcControlService {

boolean controlOgrenciHarc(int ogrenciNo);

}



package com.banka.universite;

public class HarcControlServiceImpl implements HarcControlService {

public HarcControlImpl() {}

public boolean controlOgrenciHarc(int ogrenciNo) {

boolean harcDurum;

return harcDurum;

}

}


HarcControlImpl sınıfımızı diğer servis sınıflarımız gibi konfigürasyon kütüğüne tanımlıyoruz:


<bean id="harcControlService"

class="com.banka.universite.HarcControlServiceImpl">

</bean>


Gördüğümüz gibi buraya kadar yapılan işlemlerde RMI ile ilgili hiçbir şey yapılmadı. Dolayısıyla servisimiz kendisine uzaktan ya da yerel erişim yapılacağı bilgisini üzerinde barındırmıyor. Diğer servisler gibi geliştirimi yapılan basit bir POJO. Bu servise uzak erişimi sağlamak için stub ve skeleton sınıflarını oluşturacak rmic kullanmak ve RMI registry’e eklemek yerine tüm işleri soyutlandıran Spring’in RmiServiceExporter’ını kullanacağız.

RmiServiceExporter Spring yönetilebilir bean’leri RMI servisleri olarak dışarı açar. Bunu da ilgili bean’i adapter örüntüsü ile sağlar. Adapter sınıfı servisin RMI registry’e bağlanması ve gelen istemleri sarmaladığı servise aktarmakla sorumludur. Çalışan bir RMI registry varsa servisi ona bağlar, eğer yoksa yeni bir RMI registry başlatır.

Servisi uzak erişime açmak için yapılması gereken ayarlar oldukça basittir:


<bean class="org.springframework.remoting.rmi.RmiServiceExporter">

<property name="service" ref="harcControlService"/>

<property name="serviceName" value="HarcControlService"/>

<property name="serviceInterface" value="com.banka.univeriste.HarcControlService"/>

</bean>



4. Spring HTTP Invoker ile Uzak Erişim

RMI ile uzak erişimde eğer arada güvenlik duvarı var ise iletişim sağlanamaz. Güvenlik duvarını geçmek için Spring, nesnelerin Http ile gönderilebilmesini olanaklı kılan ve RMI gibi Java Serialization mekanizmasını kullanan HTTP Invoker modelini geliştirmiştir. HTTP Invoker tabanlı uzak erişim sistemi geliştirimi, RMI-Spring’e çok benzemektedir.


4.1. HTTP Invoker - İstemci

RMI servisine ulaşmak için RmiProxyFactoryBean kullandığımız gibi HTTP invoker servisine ulaşmak için de HttpInvokerProxyFactoryBean kullanıyoruz. Yapılması gereken bean tanımı da RMI’dan pek farklı değil:


<bean id="harcControlService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">

<property name="serviceUrl">

<value>

http://${serverName}/${contextPath}/harcControlService.remote

</value>

</property>

<property name="serviceInterface">

<value>com.banka.universite.HarcControlService</value>

</property>

</bean>


Bu işlemin ardından istemde bulunacak yerel servisimize harcControlService bean’i bağımlılık aktarımı yöntemiyle bağlayabiliriz.



4.2. HTTP Invoker – Sunucu

Uzak erişim için kullanacağımız servisi RMI’da olduğu gibi standard bir bean gibi geliştirip bean tanımını yapıyoruz. Ardından servisimizi uzak erişime açmak için RMI’da olduğu gibi HttpInvokerServiceExporter ile sarmalıyoruz. HttpInvokerServiceExporter bir Spring MVC controller’ıdır. Sunucuya istemciden gelen istem DispatcherServlet aracılıyla HttpInvokerServiceExporter’a aktarılır. Oradan da ilgili servise ulaşılır:


HttpInvokerServiceExporter’ın Spring MVC controller olması sebebiyle projemizin DispatcherServlet-servlet.config.xml ayar kütüğünde tanımlanması gerekir:


<bean name="/harcControlService.remote"

class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">

<property name="service">

<ref bean="harcControlService"/>

</property>

<property name="serviceInterface">

<value>com.banka.universite.HarcControlService</value>

</property>

</bean>


HttpInvokerServiceExporter Controller arayüzünden değil de HttpRequestHandler arayüzünden gerçekleştirilmiştir. DispatcherServlet için varsayılan tanımları ezecek herhangi bir "HandlerAdapter" bean tanımı yapılmışsa HttpInvokerServiceExporter'ın DispatcherServlet ile iletişim kurabilmesi için aşağıdaki adapter tanımı da eklenmelidir:

<bean

class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>

Yukarıdaki örneğe göre projemizin web.xml tanımında DispatcherServlet'i tetikleyecek tanımın yapılması da gerekir:

<servlet-mapping>

<servlet-name>DispatcherServlet</servlet-name>

<url-pattern>*.remote</url-pattern>

</servlet-mapping>

Spring HTTP Invoker modeli, HTTP iletişimini kullanması ve Java Serialization mekanizmasını desteklemesi ve yazılan uzak servislerin sıradan Spring yönetilebilir bean olarak geliştirilmesi sebebiyle uzak erişimde kolaylıkla kullanılabilecek bir seçenektir. Spring HTTP Invoker modelinin tek kısıtı, istemci ve sunucu uygulamaların Spring tabanlı uygulamalar olması gerekliliğidir. Yukarda anlatılan yöntemler sonucunda istemci uygulama uzak servis yöntemini sanki kendi yerelindeymiş gibi çağırabilir.



Hiç yorum yok:

Yorum Gönder