Ciele
- Upraviť závislosti projektu gamestudio pre umožnenie ďalšieho postupu v rámci cvičení.
- Vytvoriť triedu pre spustenie serverovej časti.
- Implementovať služby sprístupňujúce servisné komponenty klientovi prostredníctvom REST rozhrania.
- Vytvoriť služby klienta využívajúceho vytvorené REST rozhranie.
- Spustiť serverovú a klientskú časť projektu.
Úvod
Na dnešnom cvičení vykonáte v projekte úpravy, aby Gamestudio fungovalo ako distribuovaná klient-server aplikácia.
Aplikácia servera bude poskytovať funkcionalitu pre doplnkové služby Score, Rating a Comment a bude komunikovať s databázou. Klientska aplikácia bude spúšťať hru a pre získanie a ukladanie údajov služieb Score, Comment a Rating bude posielať dopyty na server prostredníctvom REST.
Postup
Krok 1
Úloha 1.1
V konfiguračnom súbore pom.xml
pre nástroj Maven doplňte novú závislosť, ktorá umožní prácu s webovými službami v rámci Spring projektu.
Medzi existujúce závislosti projektu (dependencies
) doplňte nasledovné:
<dependencies>
... //predtým pridané závislosti ponechať nezmenené
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Poznámka
Pri pridaní závislosti je potrebné aktívne pripojenie na internet, aby sa stiahli potrebné knižnice. V prostredí sa vám môže vpravo dolu objaviť notifikácia Maven pre potvrdenie aktualizácie závislostí projektu. Túto aktualizáciu je možné vyvolať aj manuálne cez kontextové menu (po kliknutí pravým tlačidlom myši) Maven -> Reload project alebo cez panel Maven a tlačidlo Reload All Maven Projects.
Úloha 1.2
Upravte metódu main()
v triede SpringClient
, aby sa pri spustení klientskej časti projektu nespúšťal zároveň aj webový server, keďže ten sa bude spúšťať v inej časti.
Pôvodnú inicializáciu aplikácie v metóde main()
, ktorá vyzerala takto:
public static void main(String[] args) {
SpringApplication.run(SpringClient.class, args);
}
upravíme týmto spôsobom:
public static void main(String[] args) {
new SpringApplicationBuilder(SpringClient.class).web(WebApplicationType.NONE).run(args);
}
Krok 2
V tomto kroku implementujete kód pre spúšťanie serverovej časti aplikácie.
Úloha 2.1
V balíku sk.tuke.gamestudio.server
pridajte hlavnú triedu pre spúšťanie serverovej časti aplikácie, s názvom GameStudioServer
.
Implementácia triedy GameStudioServer
zatiaľ bude obsahovať metódu main()
, ktorá je veľmi podobná metóde v triede SpringClient
pred úpravami z predošlého kroku:
package sk.tuke.gamestudio.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Configuration;
@SpringBootApplication
@Configuration
@EntityScan("sk.tuke.gamestudio.entity")
public class GameStudioServer {
public static void main(String[] args) {
SpringApplication.run(GameStudioServer.class, args);
}
}
Poznámka
Anotácia @EntityScan
umožňuje definovať umiestnenie balíka s entitnými triedami, keďže je tento balík umiestnený na inej úrovni projektovej štruktúry, než na ktorej je umiestnená trieda GameStudioServer
. Podstatné to bude pre ďalšie časti implementácie servera.
Úloha 2.2
Otestujte funkčnosť serverovej časti spustením triedy GameStudioServer
.
Po pridaní novej závislosti do projektu (Krok 1) sa stiahla a nastavila aj knižnica pre webový server Tomcat. Tento server zvyčajne beží na porte 8080.
Pri správnom spustení servera sa v termináli objaví na konci správa obsahujúca Started GameStudioServer in ... seconds
. Pri otvorení webového prehliadača na adrese localhost:8080
zatiaľ dostanete ako odpoveď kód 404
, keďže na danej adrese ešte nie je dostupný žiaden obsah. Server bol ale schopný poslať túto odpoveď na požiadavku, čo znamená, že je spustený.
Upozornenie
Pokiaľ vám v systéme už beží iná služba na tom istom porte, môže nastať konflikt a spustenie aplikácie môže skončiť chybou. V takom prípade je riešením dočasné zastavenie služby, ktorá využíva port 8080, alebo zmena portu pre server Tomcat.
Po tejto úlohe bude vaša aplikácia rozdelená na klientsku a serverovú časť. Pre zjednodušenie je ale implementácia oboch častí v jednom spoločnom projekte.
Krok 3
V tomto kroku implementujete REST službu, ktorá sprístupní servisný komponent cez webový protokol HTTP. Implementácia je pomerne priamočiara. Pre službu Score budú k dispozícii 2 metódy: jedna na získanie najlepších skóre (GET), druhá na uloženie skóre (POST).
Úloha 3.1
V balíku sk.tuke.gamestudio.server
vytvorte balík webservice
, ktorý bude obsahovať implementáciu webových služieb. V balíku vytvorte a implementujte triedy jednotlivých služieb.
Implementácia metód REST služby bude jednoduchá, keďže sa v nej len využijú metódy injektovanej implementácie služby ScoreService
. O všetko ostatné sa postará Spring.
Trieda pre implementáciu služby Score
bude pomenovaná ScoreServiceRest
a bude vyzerať nasledovne:
package sk.tuke.gamestudio.server.webservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import sk.tuke.gamestudio.entity.Score;
import sk.tuke.gamestudio.service.ScoreService;
import java.util.List;
@RestController
@RequestMapping("/api/score")
public class ScoreServiceRest {
@Autowired
private ScoreService scoreService;
@GetMapping("/{game}")
public List<Score> getTopScores(@PathVariable String game) {
return scoreService.getTopScores(game);
}
@PostMapping
public void addScore(@RequestBody Score score) {
scoreService.addScore(score);
}
}
Poznámka
V rámci implementácie služieb pre web nebudeme sprístupňovať metódu pre reset()
. Tá preto nebude súčasťou implementovanej triedy.
V uvedenom príklade sú dôležité anotácie, ktoré REST službu definujú:
@RestController
- označuje, že komponent je REST služba,@RequestMapping("/api/score")
- URL adresa služby na serveri, na ktorej bude služba sprístupnená,@PostMapping
- metóda označená anotáciou bude reprezentovať REST metódu typu POST,@GetMapping
- metóda označená anotáciou bude reprezentovať REST metódu typu GET,@PathVariable
- označuje parameter metódy, ktorého hodnota bude naplnená z časti URL, v našom prípade to je názov hry, napr. z URL/api/score/mines
bude hodnota parametramines
,@RequestBody
- označuje parameter metódy, ktorý bude naplnený z obsahu dopytu.
Úloha 3.2
Doplňte implementáciu triedy GameStudioServer
o komponenty služieb, ktoré budú využívané vo webových službách z predošlého kroku.
Príklad pre službu ScoreService
:
package sk.tuke.gamestudio.server;
import org.springframework.boot.SpringApplication;
// ... dalsie importy
@SpringBootApplication
@Configuration
@EntityScan("sk.tuke.gamestudio.entity")
public class GameStudioServer {
public static void main(String[] args) {
SpringApplication.run(GameStudioServer.class, args);
}
@Bean
public ScoreService scoreService() {
return new ScoreServiceJPA();
}
}
Krok 4
Server môžete pred ďalším postupom otestovať. Spustenie sa vykonáva spustením hlavnej triedy servera GameStudioServer
. Server sa spustí na porte 8080.
Upozornenie
Na porte 8080 nesmie v tom čase bežať iná služba, inak sa server nespustí.
Úloha 4.1
Spustite server a vyskúšajte vytvorenú REST službu.
Pripravte si HTTP dopyty pre vyskúšanie služieb.
Napríklad, GET
vyskúšajte prostredníctvom prehliadača na URL adrese http://localhost:8080/api/score/mines
.
Poznámka
Pre testovanie REST služieb si môžete vytvoriť súbor s koncovkou .http
, do ktorého zapíšete jednotlivé dopyty a môžete ich tak spúšťať priamo vo vývojovom prostredí IntelliJ IDEA.
Úloha 4.2
Podľa vzoru ScoreServiceRest.java
vytvorte implementácie REST služieb pre zvyšné dva servisné komponenty: CommentServiceRest.java
a RatingServiceRest.java
.
Úloha 4.3
Prostredníctvom HTTP požiadaviek otestujte fungovanie zvyšných služieb.
Krok 5
V tomto kroku vytvoríme službu REST klienta na strane klientskej časti aplikácie GameStudio s využitím už vytvoreného REST webového rozhrania. Príklad bude opísaný na službe Score
.
Úloha 5.1
V balíku sk.tuke.gamestudio.service
vytvorte novú triedu ScoreServiceRestClient
reprezentujúcu klienta vytvoreného REST rozhrania. Trieda bude implementovať rozhranie ScoreService
.
Pre použitie REST metód využijeme objekt typu RestTemplate
, ktorý môže byť injektovaný zvonku, pri využití anotácie @Autowired
.
package sk.tuke.gamestudio.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;
import sk.tuke.gamestudio.entity.Score;
import java.util.Arrays;
import java.util.List;
public class ScoreServiceRestClient implements ScoreService {
private final String url = "http://localhost:8080/api/score";
@Autowired
private RestTemplate restTemplate;
//private RestTemplate restTemplate = new RestTemplate();
@Override
public void addScore(Score score) {
restTemplate.postForEntity(url, score, Score.class);
}
@Override
public List<Score> getTopScores(String gameName) {
return Arrays.asList(restTemplate.getForEntity(url + "/" + gameName, Score[].class).getBody());
}
@Override
public void reset() {
throw new UnsupportedOperationException("Not supported via web service");
}
}
Pri injektovaní objektu RestTemplate
zvonku bude potrebné ešte v triede SpringClient
vytvoriť patričnú metódu, označenú anotáciou @Bean
, ktorá vráti objekt RestTemplate
.
Krok 6
V tomto kroku využijeme vytvorených REST klientov v klientskej časti aplikácie GameStudio.
Úloha 6.1
Zmeňte definície servisných komponentov v triede SpringClient
na REST.
Príklad pre ScoreService
bude vyzerať nasledovne:
@Bean
public ScoreService scoreService() {
//return new ScoreServiceJPA();
return new ScoreServiceRestClient();
}
Obdobným spôsobom upravte aj ostatné servisné komponenty.
Úloha 6.2
Pridajte k triede SpringClient anotáciu @ComponentScan
s parametrom excludeFilters
pre zabránenie konfliktov s definíciami komponentov, ktoré sú v balíku serverovej časti aplikácie.
Keďže máme pre zjednodušenie klientsku aj serverovú aplikáciu v spoločnom projekte, potrebujeme zabrániť konfliktom pri skenovaní dostupných komponentov.
Anotáciu použite nasledovne:
...
@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX,
pattern = "sk.tuke.gamestudio.server.*"))
public class SpringClient {
...
}
Úloha 6.3
Overte funkčnosť klientskej aplikácie.
Upozornenie
Keďže už je klientska a serverová časť oddelená, musíte mať vždy pred spustením klienta spustený aj server.
Spustite klienta aplikácie spustením jeho hlavnej triedy SpringClient
a skúste si zahrať hru. Pri využití servisných služieb klient pošle HTTP požiadavku na server, ten pri spracovaní požiadavky bude komunikovať s databázou, a pošle odpoveď prípadne požadované údaje naspäť klientovi.
Krok 7
Úloha 7.1
Nahrajte vašu implementáciu do vášho repozitára na GitLab-e. Projekt môžete ďalej priebežne aktualizovať. Pred ďalším cvičením sa uistite, že máte v repozitári vaše aktuálne súbory. Zároveň si pripravte otázky, ktoré by ste na cvičení chceli vyriešiť.
Krok 8
Bonus: A teraz si za odmenu môžete dať virtuálny koláčik (prezentácia What makes a delicious game).
Zdroje
- HTTP klient v IntelliJ IDEA Ultimate
- Aplikácia Postman: https://www.postman.com/.
- Prezentácia What makes a delicious game.