Skip to content

14. [TD]: [metier] katmanının web'de yayınlanması

Anahtar kelimeler: çok katmanlı mimari, Spring, bağımlılık enjeksiyonu, web hizmeti / jSON, istemci / sunucu.

Şimdi TD uygulamasının mevcut mimarisine geri dönelim:

Bu mimariyi aşağıdaki gibi geliştireceğiz:

Böylece iş katmanındaki [IMetier] arayüzünü web üzerinde sunabileceğiz. Bunun için 13.5. paragrafında açıklanan metodolojiyi uygulayacağız.

14.1. Support

  

Bu bölümdeki projeler [support / chap-14] klasöründe bulunabilir.

14.2. [métier] katmanının Eclipse projesi

  

14.2.1. Maven Yapılandırması

[métier] katmanındaki proje, aşağıdaki [pom.xml] dosyası ile yapılandırılmış bir Maven projesidir:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-metier-dao-spring-data</artifactId>
    <version>0.1.0</version>

    <!-- bağımlılıklar -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.7.RELEASE</version>
    </parent>
    <dependencies>
        <!-- katman [DAO] -->
        <dependency>
            <groupId>istia.st.elections</groupId>
            <artifactId>elections-dao-spring-data-01</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot Testi -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <!-- her şey için UTF-8 kullan -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </build>
</project>
  • 18-22. satırlar: 12. paragrafta oluşturulan [DAO] katmanına olan bağımlılık;
  • 23-34. satırlar: testler için gerekli bağımlılıklar;

14.2.2. Spring Yapılandırması

  

[métier] katmanının projesi, aşağıdaki [MetierConfig] dosyası ile yapılandırılmış bir Spring projesidir:


package elections.metier.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

import elections.dao.config.DaoConfig;

@Import({ DaoConfig.class })
@ComponentScan({ "elections.metier.service" })
public class MetierConfig {
}
  • Burada, sınıfı bir Spring yapılandırma sınıfı haline getiren [@Configuration] notasyonunu kullanmıyoruz. [@Import, @ComponentScan] anotasyonlarının varlığı, sınıfı otomatik olarak bir yapılandırma sınıfı haline getirir;
  • 8. satır: [DAO] katmanının yapılandırma dosyası içe aktarılır. Böylece, bu dosyada tanımlanan tüm bean’lere erişim sağlanır;
  • 9. satır: Diğer Spring bean'leri [elections.metier.service] klasöründe aranmalıdır;

14.2.3. [métier] katmanının uygulanması

  

[métier] katmanının uygulaması, 8.5. paragrafta tanımlanan uygulamadır.

14.2.4. [métier] katmanının testi

  

Test sınıfı, 8.6. paragrafta açıklanan sınıftır.


Yapılması gerekenler: [métier] katman projesini uygulayın ve birim testini geçirin. Katman arşivini yerel Maven deposunda oluşturun (run as/ Maven / install).


14.3. [web] katmanının Eclipse projesi

Web katmanı, Spring tabanlı MVC katmanıdır:

Eclipse projesi aşağıdaki yapıya sahiptir:

  • [Boot.java], web hizmetini başlatan sınıftır;
  • [WebConfig.java], web hizmetinin yapılandırma sınıfıdır;
  • [Response.java], web hizmetinin çeşitli URL'leri tarafından verilen yanıttır;
  • [ElectionsController], web hizmetinin uygulama sınıfıdır;

14.4. Maven Yapılandırması

Proje, aşağıdaki [pom.xml] dosyası ile yapılandırılmış bir Maven projesidir:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-webjson-metier-dao-spring-data</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>elections-webjson-metier-dao-spring-data</name>
    <description>couche métier exposée comme un service web / jSON</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.7.RELEASE</version>
    </parent>

    <dependencies>
        <!-- iş mantığı katmanı -->
        <dependency>
            <groupId>istia.st.elections</groupId>
            <artifactId>elections-metier-dao-spring-data</artifactId>
            <version>0.1.0</version>
        </dependency>
        <!-- MVC katmanı -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </build>

</project>
  • 19-23. satırlar: [métier] katman arşivine olan bağımlılık. Bu, 14. paragrafta oluşturduğumuz bağımlılıktır;
  • 25-28. satırlar: Spring uygulaması MVC için bağımlılık;

14.5. Spring Yapılandırması

 

[WebConfig] sınıfı, web hizmetini yapılandırır:


package elections.webjson.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import com.fasterxml.jackson.databind.ObjectMapper;

import elections.metier.config.MetierConfig;

@EnableWebMvc
@Import({ MetierConfig.class })
@ComponentScan({ "elections.webjson.service" })
public class WebConfig {
    // -------------------------------- [web] katmanı yapılandırması
    @Autowired
    private ApplicationContext context;

    @Bean
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet servlet = new DispatcherServlet((WebApplicationContext) context);
        return servlet;
    }

    @Bean
    public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
        return new ServletRegistrationBean(dispatcherServlet, "/*");
    }

    @Bean
    public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
        return new TomcatEmbeddedServletContainerFactory("", 8080);
    }
    // eşleştirici jSON
    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public ObjectMapper jsonMapper() {
        return new ObjectMapper();
    }

}
  • Bu yapılandırmanın anlamı 13.5.3.1. paragrafında açıklanmıştır. Burada sadece yeni özellikleri açıklayacağız:
  • 22. satır: Tüm bean'lerinden yararlanmak için [métier] katmanının yapılandırma dosyası içe aktarılır;
  • 23. satır: [elections.webjson.server.service] klasöründe başka bean'lerin de bulunacağı belirtilir;

14.6. Web hizmetinin başlatma sınıfı

 

[Boot] sınıfı, web hizmetini şu şekilde başlatır:


package elections.webjson.boot;

import org.springframework.boot.SpringApplication;

import elections.webjson.config.WebConfig;

public class Boot {

    public static void main(String[] args) {
        SpringApplication.run(WebConfig.class, args);
    }
}
  • 10. satır: [SpringApplication.run] statik yöntemi, [WebConfig] yapılandırma dosyasını kullanacaktır. [@EnableAutoConfiguration] ek açıklaması nedeniyle, Spring Boot Tomcat sunucusunu başlatacak ve web hizmetini bu sunucuya dağıtacaktır;

14.7. Web hizmetinden gelen URL yanıtı

 

Web hizmetinin tüm URL / jSON istekleri aynı türde yanıt gönderir:


package elections.webjson.service;

import java.util.List;

public class Response<T> {

    // ----------------- özellikler
    // işlem durumu
    private int status;
    // olası hata mesajları
    private List<String> messages;
    // yanıt gövdesi
    private T body;

    // yapıcılar
    public Response() {

    }

    public Response(int status, List<String> messages, T body) {
        this.status = status;
        this.messages = messages;
        this.body = body;
    }

    // getter ve setter'lar
...
}

Bu sınıf, 13.5.5.3. paragrafında tanıtılmış ve incelenmiştir.

14.8. / jSON web hizmetinin uygulaması

 

/ jSON web hizmeti, aşağıdaki [ElectionsController] sınıfı tarafından uygulanmaktadır:


package elections.webjson.service;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ElectionsException;
import elections.metier.service.IElectionsMetier;

@Controller
public class ElectionsController {

    // Spring bağımlılıkları
    @Autowired
    private ObjectMapper jsonMapper;

    @Autowired
    private IElectionsMetier metier;

    @RequestMapping(value = "/getElectionsConfig", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
    @ResponseBody
    public String getElectionsConfig() throws JsonProcessingException {
        // yanıt
        Response<ElectionsConfig> response;
        try {
            response = new Response<>(0, null,
                    new ElectionsConfig(metier.getNbSiegesAPourvoir(), metier.getSeuilElectoral()));
        } catch (ElectionsException e1) {
            response = new Response<>(e1.getCode(), e1.getErreurs(), null);
        } catch (RuntimeException e2) {
            response = new Response<>(1000, getErreursForException(e2), null);
        }
        // yanıt
        return jsonMapper.writeValueAsString(response);
    }

    @RequestMapping(value = "/getListesElectorales", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
    @ResponseBody
    public String getListesElectorales() throws JsonProcessingException {
        throw new UnsupportedOperationException("Not supported yet");
    }

    @RequestMapping(value = "/setListesElectorales", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8", produces = "application/json; charset=UTF-8")
    @ResponseBody
    public String setListesElectorales(HttpServletRequest request) throws JsonProcessingException {
        throw new UnsupportedOperationException("Not supported yet");
    }

    @RequestMapping(value = "/calculerSieges", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8", produces = "application/json; charset=UTF-8")
    @ResponseBody
    public String calculerSieges(HttpServletRequest request) throws JsonProcessingException {
        throw new UnsupportedOperationException("Not supported yet");
    }

    // özel yöntemler -----------------------------
    // bir RuntimeException'in hata mesajları listesi
    private List<String> getErreursForException(Exception e) {
        // istisnanın hata mesajları listesi alınır
        Throwable cause = e;
        List<String> erreurs = new ArrayList<>();
        while (cause != null) {
            // mesajı yalnızca !=null ve boş değilse alınır
            String message = cause.getMessage();
            if (message != null) {
                message = message.trim();
                if (message.length() != 0) {
                    erreurs.add(message);
                }
            }
            // sonraki neden
            cause = cause.getCause();
        }
        return erreurs;
    }

}

Yapılması gereken iş: 13.5.5 numaralı paragrafta yapılanlara uygun olarak, [ElectionsController] sınıfının kodunu tamamlayın.


Notlar:

  • Burada jSON filtreleri yoktur, çünkü [CONF] ve [LISTES] tabloları birbirlerine yabancı anahtar ilişkisi ile bağlı değildir; bu da web hizmetinin kodunu önemli ölçüde hafifletir;
  • Gerekli olan çeşitli Spring anotasyonlarını unutmayın;
  • URL'e ilişkili yöntemlerin adları verilecektir;
  • [setListeElectorales] yöntemi, bir [POST] işlemiyle çağrılır. Gönderilen değer, veritabanına kaydedilmesi gereken [sieges, voix, elimine] özniteliklerine sahip yarışan listelerin dizisidir (ListeElectorale[] türü). Bu yöntem, hata olmaması durumunda [Response<Void>] türünde bir değer ve [status=0] alanını döndürür; aksi takdirde başka bir değer döndürür;
  • [calculerSieges] yöntemi, bir [POST] işlemiyle çağrılır. Gönderilen değer, ListeElectorale[] türündeki yarışan listeler dizisi ve bunların [nom, voix] öznitelikleridir. Bu yöntem, gövdesi olarak [sieges, elimine] alanları başlatılmış seçim listelerini içeren [Response<ListeElectorale[]>] türünde bir değer döndürür;

14.9. Tests

Web hizmetini başlattıktan sonra, [Advanced Rest Client] yardımcı programı ile web hizmetinin düzgün çalıştığından emin olmak için aşağıdaki testleri gerçekleştireceksiniz:

 

Önceki isteğe verilen jSON yanıtı şöyledir: [1]:

1
2

[2]'te, yanıtı panoya kopyalayın, ardından bunu herhangi bir metin düzenleyicisine yapıştırın [3]:

[body] alanındaki değeri ayırın ve örneğin listelerdeki oy sayısını değiştirin. Aşağıdaki [4] örneğinde, tüm listelerdeki oy sayısı 100 olarak değiştirilmiştir:

jSON dizesinin [ et se termine par ] ile başladığını kontrol edin. Bu karakterler, jSON tablosunu sınırlamak için kullanılır. [5] alanına, yukarıdaki jSON dizesini yapıştırın. Bu, bir sonraki URL için gönderilecek değer olacaktır. Bunun için HTTP [POST] [7] yöntemini seçmeniz gerekir.

  • [6]'te, URL ve [setListesElectorales]'i talep edin. Bu URL, bir POST ile talep edilir. Gönderilen değer, sonuçlarının veritabanına kaydedilmesi gereken yarışma listelerinin jSON tablosudur;

Aşağıdaki sonuç elde edilir:

 

[status=0] alanı, herhangi bir hata olmadığını gösterir. Bunu doğrulamak için yarışma listelerini yeniden isteyin ve listelerde yaptığınız değişikliklerin dikkate alındığını kontrol edin:

Listelerin kazandığı koltuk sayısını hesaplamak için [POST] komutunu tekrar çalıştırıyoruz:

  • [1]'te: koltuk hesaplamasına ilişkin URL;
  • [2]'e: [POST] komutunu çalıştırıyoruz;
  • [3]'e: yarışan listeler. [voix] alanına TD'in değerleri verilir, tüm [sieges] değerleri 0 olarak ayarlanır, tüm [elimine] alanları false olarak ayarlanır;

Elde edilen sonuç şöyledir: