JaxRS 2.0 Rest Client, Spring MVC, Hibernate ve JavaFX 2
Merhaba arkadaşlar;
Bugün sizlerle, RESTful sitiline uygun çalışan, sunucu ve istemci taraflı teknolojiler barındıran bir örnek uygulama paylaşmak istiyorum. Uygulama içeriğine geçmeden hemen önce, uygulamanın sunucu ve istemci tarafında koşan teknolojilerden bahsetmek istemekteyim.
Sunucu tarafı
- Spring MVC 3.1 (Restful sunucu servisi için)
- Hibernate 4 (ORM ile verileri kalıcı hale getirmek için)
İstemci tarafı
- Java FX 2 (Görsel kullanıcı arayüzü için)
- JaxRS 2 – Rest Client (Restful sunucusuna yapılacak HTTP isteklerinin yönetimi için)
Kullanılan teknolojileri tanıttıktan sonra, ilk aşamada sunucu tarafından işe koyulalım derim. Sunucu tarafındaki uygulama bir Maven uygulaması olduğu için, istenilen IDE ortamında çalışabilir haldedir. Sunucu uygulama içersindeki bileşenleri hızlıca tanımak için aşağıdaki resme göz atabiliriz.
1) Kitap.java
@Entity @XmlRootElement public class Kitap implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String kitapAdi; private Double kitapFiyati; public Kitap(){} public Kitap(String kitapAdi, Double kitapFiyati) { this.kitapAdi = kitapAdi; this.kitapFiyati = kitapFiyati; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getKitapAdi() { return kitapAdi; } public void setKitapAdi(String kitapAdi) { this.kitapAdi = kitapAdi; } public Double getKitapFiyati() { return kitapFiyati; } public void setKitapFiyati(Double kitapFiyati) { this.kitapFiyati = kitapFiyati; } }
Kitap entity nesnesi (@Entity ile tanımlanır), veritabanına kalıcı hale getirilecek, okunacak, düzenlenecek ve silinecek bir klasik Java nesnesidir. JAXB standardına göre bir Java nesnesinden bir XML nesnesine dönüşüm gerekliyse (tam tersi de geçerli Java <> XML), en basit haliyle dönüşüme ugrayacak sınıf @XmlRootElement notasyonuyla sarmalanmalıdır.
2) KitapWrapper.java
@XmlRootElement public class KitapWrapper implements Serializable { private List kitaplar; public List getKitaplar() { return kitaplar; } public void setKitaplar(List kitaplar) { this.kitaplar = kitaplar; } public KitapWrapper(List kitaplar) { this.kitaplar = kitaplar; } public KitapWrapper() { } }
KitapWrapper sınıfı, birden fazla kitap nesnesini sarmalayan bir sınıf vazifesi görür. Tek amacı, List arayüzü türündeki nesnelerin kolaylıkla XML ya da JSON formatına dönüşümüne olanak sağlamasıdır.
3) MvcInitializer.java
public class MvcInitializer implements WebApplicationInitializer { public void onStartup(ServletContext servletContext) throws ServletException { // Spring web context olustur AnnotationConfigWebApplicationContext mvcContext= new AnnotationConfigWebApplicationContext(); // Konfigürasyon dosyasinin sınıf tipi mvcContext.register(WebConfig.class); ServletRegistration.Dynamic register= // Mvc DispatcherServlet, web context ile birlikte uygulamaya ekleniyor. servletContext.addServlet("distpatcher", new DispatcherServlet(mvcContext)); register.addMapping("/*"); // Tüm http istekleri register.setLoadOnStartup(1); // Ilk sirada } }
Sunucu uygulamaya dönük resme bakıldığında, proje dizininde web.xml aktarım tanımlayıcısının yer almadığı görülür. Peki, Spring MVC Conxtext, web.xml tanılayıcısı olmadan nasıl ayağa kalkacak?
Bunun cevabı Servlet 3.0 standardına dayanmakta. Servlet 3.0 standardıyla birlikte artık, web.xml aktarım tanımlayıcısına olan bağımlılık tamamen ortadan kalkmış bulunuyor.
Servlet 3.0 konteyner ilk çalismaya başladığı anda, eğer uygulamanın CLASSPATH’inde ServletContainerInitializer arayüzünü uygulayan bir sınıf varsa, otomatik olarak o sınıfın onStartup(..) metodu işletiliyor, ve bu metod içersinde web.xml içerisinde yapılabilecek, Servlet, Filter, Listener tanımlama ve benzeri işlemler gerçeklestirilebiliniyor.
Spring Framework içinde bu işleri kolaylaştırmak için SpringServletContainerInitializer sınıfı tarafından yönetilen WebApplicationInitializer arayüzü Spring konteyneri daha kolay yapılandırmak açısından bizlere sunuluyor.
Spring Framework 3.1 versiyonuyla beraber, artık Spring nesnelerinin tanımlandığı, birbirine bağlandığı, konfigürasyonların yapıldığı XML bazlı yapılandırma dosyaları da, kullanim açısından zaruri olmaktan çikti. Çünkü 3.1 versiyonuyla beraber artık tamamen XML bağımsız Spring Context konfigürasyonu yapılabilir haldedir. Bende bu noktada web.xml ve spring-beans.xml (isim değişebilir) bağımsız yapılandırmayı ayrıca göstermek adına bu yöntemi tercih ettim.
Yukarıda yapılan işlemler kısaca, Spring web context’in oluşturulması ve WebConfig isimli Spring context yapılandırma sınıfının tanımlanmasını ve Spring MVC’ nin ayrılmaz parçası DispatcherServlet’in tanımlanmasını içeriyor. Bu sınıf için web.xml aktarım tanılayıcısınin eşleniği diyebiliriz.
4) WebConfig.java
@Configuration // Bu konfigürasyon sınıfıdir. @EnableTransactionManagement // <tx:annotation-driven/> gibi @EnableWebMvc // <mvc:component-scan .. /> gibi @ComponentScan(basePackages = "com.usta") // <context:component-scan ../> gibi public class WebConfig extends WebMvcConfigurerAdapter { @Bean // Enjekte edilebilir Transaction yönetici public HibernateTransactionManager createTransactionManager(){ HibernateTransactionManager transactionManager= new HibernateTransactionManager(); transactionManager.setSessionFactory( createSessionFactoryBean().getObject()); return transactionManager; } @Bean // Enjekte edilebilir Hibernate Session üreteci public LocalSessionFactoryBean createSessionFactoryBean(){ LocalSessionFactoryBean localSessionFactoryBean= new LocalSessionFactoryBean(); Properties properties=new Properties(); properties.setProperty( "hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect"); properties.setProperty("hibernate.hbm2ddl.auto","create-drop"); properties.setProperty("hibernate.show_sql","true"); localSessionFactoryBean.setHibernateProperties(properties); localSessionFactoryBean.setAnnotatedClasses(new Class<?>[]{Kitap.class}); localSessionFactoryBean.setDataSource(createDataSource()); return localSessionFactoryBean; } @Bean // Enjekte edilebilir veri kaynağı public BasicDataSource createDataSource(){ BasicDataSource dataSource=new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/soap"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } }
Günümüzde XML bazlı yapılandırıcılara göre Notasyon bazlı yapılandırma işlemlerine bir meyil olduğu görülmektedir. Açıkçası hangi yöntem daha iyi veya kötü, bunun cevabı yok. Geliştiricinin takdirine bağlı olarak bu seçimin yapılması gerekmektedir.
Yukarıda yer alan WebConfig.java konfigürasyon sınıfına bakıldığında, XML tabanlı Spring konfigürasyon dosyalarinin eşleniği vaziyette olduğu görülebilir.
Örnegin:
<bean id=”..” class=”com. apache….BasicDataSource” >…</bean> tanımlamasi yerine, BasicDataSource türünden bir nesne döndüren yordam tanımlamak ve bu metoda @Bean notasyonunu uygulamak aynı işi görmektedir.
5) YayineviKontrolor.java
@Transactional(readOnly = true) @Repository // Spring DAO @Controller // Spring MVC Kontrolor @RequestMapping(value = "/yayinevi/*") public class YayineviKontrolor { @Autowired // Session üretici fabrika private SessionFactory sessionFactory; // O anki Session nesnesini döndürür public Session getSession(){ return sessionFactory.getCurrentSession(); } @RequestMapping(value = "/kitap", method = RequestMethod.POST, produces = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE }, consumes = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE }) @Transactional(readOnly = false) public @ResponseBody Long kitapEkle(@RequestBody Kitap kitap) { return (Long)getSession().save(kitap); } @RequestMapping(value = "/kitap/{id}", method = RequestMethod.DELETE) @Transactional(readOnly = false) public void kitapSil(@PathVariable Long id) { getSession().delete(getSession().get(Kitap.class,id)); } @RequestMapping(value = "/kitaplar", produces = {MediaType.APPLICATION_XML_VALUE},method = RequestMethod.GET) @ResponseStatus( HttpStatus.OK ) public @ResponseBody KitapWrapper tumKitaplar() { List kitaplar= (List)getSession().createQuery("SELECT k FROM Kitap k").list(); return new KitapWrapper(kitaplar) ; } @RequestMapping(value = "/kitap", method = RequestMethod.PUT, consumes = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE }) @Transactional(readOnly = false) public void kitapDuzenle(@RequestBody Kitap kitap) { getSession().saveOrUpdate(kitap); } }
YayineviKontrolor sınıfı, her bir metodu Transaction destekli (@Transactional sayesinde), aynı zamanda Spring MVC’ye ait bir @Controller sınıfı olarak görev alan bir DAO(Data Access Object) nesnesidir.
@RequestMapping notasyonu, JAX-RS standardında yer alan @Path, @GET, @POST, @PUT, @Produces, @Consumes gibi notasyonların bileşimi gibi görev alan, URL -> Kaynak (Resource) erişimini yönlendiren faydalı bir Spring MVC notasyonudur.
@RequestBody ve @ResponseBody notasyonları ise, metoda gönderilen ve yordamdan çiktilanan veri hüzmelerinin, Spring MVC tarafından anlamli veriler olarak çiktilanmasina, gerekiyorsa tip dönüşümlerinin (Java <> XML veya Java <> JSON vs. gibi) arka planda yapılmasına olanak tanır. @PathVariable notasyonu ise, URL bileşenlerinden değişken ayıklamakta ve bu değeri metod parametresi içindeki değişkene aktarmaktadır.
Veri tabanına kalıcı hale getirme, güncelleme, silme, okuma gibi işlemler ise, Hibernate Session nesnesinin çesitli yordamları (saveOrUpdate, delete gibi. ) ile sağlanmaktadır. Sunucu uygulamanın genel mimarisi açıklandıktan sonra, İstemci tarafındaki bileşenlerin detaylarına geçebiliriz.
RESTful web servislerine, HTTP isteklerini yönetebilen herhangi bir araç, programlama dili, veya yazılımla erişilebilir. Java programlama dilinde RESTful web servislerini yazmaya olanak saglayan standart JAX-RS 1.1’ dir. Fakat JAX-RS 1.1 versiyonunda, HTTP isteklerini kolaylıkla yönetebilecek bir istemci aracı bulunmuyordu. Bu sebeple Java EE 7 içinde yer alacak JAX-RS 2 standardında, RESTful client adında yeni bir kütüphane eklenmiş olacak. Şu anki çalışmalarla, bu REST client kütüphanesi erişilebilir ve kullanılabilir haldedir.
http://repo1.maven.org/maven2/org/glassfish/jersey/bundles/jax-rs-ri/ adresinden, JAX-RS 2.0 referans uygulayıcı kütüphaneye erişebilir ve kullanabilirsiniz.
JAX-RS 2.0 ile bir RESTful istemcisi olusturmak için Client client= ClientFactory.newClient(); çagrısıyla bir Client nesnesi elde edilebilir. Bu nesne üzerinden, RESTful sitiline uygun olarak HTTP istekleri yapılabilir, HTTP isteği üzerinde veri(REST Entity) postalanabilir, dönen yanıttan HTTP header, body gibi bilgiler elde edilebilir. Bu kütüphane, Builder tasarım deseni sayesinde gerçekten kullanımı çok rahat ve kolay olmuş.
Şimdi isterseniz, sunucu tarafında bulunan Spring MVC RESTful servisine yapılacak metod erişimlerinin nasıl gerçeklestirileceğini, JAX-RS 2.0 standardının uygulayıcı kütüphanesiyle test edelim.
1) HTTP DELETE – http://localhost:8080/rest/yayinevi/kitap/{ID}
Response response= client. target(“http://localhost:8080/rest/”). // ROOT_URL path("yayinevi"). // yayinevi kaynağı path("kitap"). // kitap kaynağı path(String.valueOf(ID). // ID parametresi request(). // HTTP isteği delete(); // HTTP DELETE
2) HTTP POST – http://localhost:8080/rest/yayinevi/kitap
Kitap kitap=new Kitap(); kitap.setKitapAdi(kitapAdi.getText()); kitap.setKitapFiyati(Double.parseDouble(kitapFiyati.getText())); // XML tipine dönüstürülecek REST Entity nesnesi Entity kitapEntity=Entity.entity(kitap,MediaType.APPLICATION_XML) ; Response response= client.target(REST_ROOT_URL). // ROOT_URL path("yayinevi"). // yayinevi kaynağı path("kitap"). // kitap kaynağı request(). // HTTP isteği post(kitapEntity); // REST Entity nesnesi sunucuya gönderiliyor.
3) HTTP GET – http://localhost:8080/rest/yayinevi/kitap
Response response= client.target(REST_ROOT_URL). path("yayinevi"). path("kitaplar"). request(MediaType.APPLICATION_JSON). // Veriyi JSON olarak talep eder get(); // HTTP GET isteği yapılıyor // Sunucudan gelen REST entity nesnesi elde ediliyor. KitapWrapper kitapWrapper= response.readEntity(KitapWrapper.class);
4) HTTP PUT – http://localhost:8080/rest/yayinevi/kitap
Entity kitapEntity=Entity.entity(kitap,MediaType.APPLICATION_XML) ; Response response= client.target(REST_ROOT_URL). path("yayinevi"). path("kitap"). request(). put(kitapEntity); // REST Entity nesnesi sunucuya gönderiliyor.
Yukarıdaki örneklerle birlikte, basit bir komut dizini uygulamasıyla, Spring MVC RESTful servisine erişebilir ve kullanabilirsiniz. Fakat insanoğlu her zaman görselliği olan uygulamaları daha fazla zihninde yaşatabilir ve anlamlandırabilir. Bu düşünceyi desteklemek adına, proje kodlarında sizlerle basit bir Java FX uygulamasını da paylaşmak istiyorum. JavaFX uygulamasının görsel bileşenleri, JavaFX Scene Builder aracıyla birlikte FXML standardında oluşturuldu. Sizlerde Scene Builder aracıyla birlikte proje içersindeki FXML bileşen dosyasını açarak detayları daha açık görebilirsiniz.
Java FX ile daha önce ilgilenmediyseniz Ümit Vardar hocamızın, kodedu.com webiner kaydını izleyebilirsiniz. JavaFX Scene Builder için ise bu yazıyı okumanızı öneririm. Aynı şekilde RESTful mimarisine yabancıysanız şu iki video kaydını izleyebilirsiniz. Birinci bölüm – İkinci bölüm
Sunucu kaynak kod İstemci kaynak kod
Tekrar görüşmek dileğiyle..