From 1e8923dbb12235c68bd2fe995209677dbacbdeda Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Thu, 29 Jan 2026 19:24:55 +0300 Subject: [PATCH] 2025-09 spring-32 http-client --- 2025-09/spring-32-http-client/pom.xml | 36 ++++++++ .../rest-template/pom.xml | 85 +++++++++++++++++++ .../src/main/java/ru/otus/spring/Main.java | 21 +++++ .../ru/otus/spring/config/CacheConfig.java | 15 ++++ .../spring/config/ClientConfiguration.java | 12 +++ .../otus/spring/config/ClientProperties.java | 13 +++ .../main/java/ru/otus/spring/dto/Country.java | 19 +++++ .../otus/spring/service/CountryService.java | 12 +++ .../spring/service/FeignCountryService.java | 25 ++++++ .../otus/spring/service/MainServiceImpl.java | 29 +++++++ .../spring/service/RestCountryService.java | 56 ++++++++++++ .../service/TemplateCountryService.java | 48 +++++++++++ .../spring/service/WebCountryService.java | 66 ++++++++++++++ .../src/main/resources/application.yml | 19 +++++ .../spring-32-http-client/soap-client/pom.xml | 67 +++++++++++++++ .../src/main/java/hello/ClientApp.java | 39 +++++++++ .../src/main/java/hello/CountryClient.java | 24 ++++++ .../main/java/hello/CountryConfiguration.java | 28 ++++++ .../src/main/resources/application.yml | 6 ++ .../src/main/resources/countries.wsdl | 76 +++++++++++++++++ .../spring-32-http-client/soap-server/pom.xml | 67 +++++++++++++++ .../src/main/java/hello/CountryEndpoint.java | 30 +++++++ .../main/java/hello/CountryRepository.java | 47 ++++++++++ .../src/main/java/hello/ServerApp.java | 12 +++ .../src/main/java/hello/WebServiceConfig.java | 40 +++++++++ .../src/main/resources/application.yml | 3 + .../src/main/resources/countries.xsd | 36 ++++++++ .../hello/ApplicationIntegrationTests.java | 53 ++++++++++++ 28 files changed, 984 insertions(+) create mode 100644 2025-09/spring-32-http-client/pom.xml create mode 100644 2025-09/spring-32-http-client/rest-template/pom.xml create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/Main.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/CacheConfig.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/ClientConfiguration.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/ClientProperties.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/dto/Country.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/CountryService.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/FeignCountryService.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/MainServiceImpl.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/RestCountryService.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/TemplateCountryService.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/WebCountryService.java create mode 100644 2025-09/spring-32-http-client/rest-template/src/main/resources/application.yml create mode 100644 2025-09/spring-32-http-client/soap-client/pom.xml create mode 100644 2025-09/spring-32-http-client/soap-client/src/main/java/hello/ClientApp.java create mode 100644 2025-09/spring-32-http-client/soap-client/src/main/java/hello/CountryClient.java create mode 100644 2025-09/spring-32-http-client/soap-client/src/main/java/hello/CountryConfiguration.java create mode 100644 2025-09/spring-32-http-client/soap-client/src/main/resources/application.yml create mode 100644 2025-09/spring-32-http-client/soap-client/src/main/resources/countries.wsdl create mode 100644 2025-09/spring-32-http-client/soap-server/pom.xml create mode 100644 2025-09/spring-32-http-client/soap-server/src/main/java/hello/CountryEndpoint.java create mode 100644 2025-09/spring-32-http-client/soap-server/src/main/java/hello/CountryRepository.java create mode 100644 2025-09/spring-32-http-client/soap-server/src/main/java/hello/ServerApp.java create mode 100644 2025-09/spring-32-http-client/soap-server/src/main/java/hello/WebServiceConfig.java create mode 100644 2025-09/spring-32-http-client/soap-server/src/main/resources/application.yml create mode 100644 2025-09/spring-32-http-client/soap-server/src/main/resources/countries.xsd create mode 100644 2025-09/spring-32-http-client/soap-server/src/test/java/hello/ApplicationIntegrationTests.java diff --git a/2025-09/spring-32-http-client/pom.xml b/2025-09/spring-32-http-client/pom.xml new file mode 100644 index 00000000..51c37b83 --- /dev/null +++ b/2025-09/spring-32-http-client/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + ru.otus + spring-32-http-client + 1.0 + + pom + + + org.springframework.boot + spring-boot-starter-parent + 3.5.2 + + + + + rest-template + soap-client + soap-server + + + + 17 + 17 + UTF-8 + UTF-8 + 3.1.2 + 10.9.1 + https://raw.githubusercontent.com/OtusTeam/Spring/master/checkstyle.xml + + + diff --git a/2025-09/spring-32-http-client/rest-template/pom.xml b/2025-09/spring-32-http-client/rest-template/pom.xml new file mode 100644 index 00000000..311b446e --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + rest-template + 1.0-SNAPSHOT + + + ru.otus + spring-32-http-client + 1.0 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.httpcomponents.client5 + httpclient5 + + + + org.springframework.boot + spring-boot-starter-cache + + + + + org.springframework.retry + spring-retry + + + org.aspectj + aspectjweaver + + + + org.springframework + spring-webflux + + + io.projectreactor.netty + reactor-netty + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.projectlombok + lombok + provided + + + + + + org.springframework.cloud + spring-cloud-dependencies + 2025.0.0 + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/Main.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/Main.java new file mode 100644 index 00000000..c51fb9cd --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/Main.java @@ -0,0 +1,21 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.retry.annotation.EnableRetry; +import ru.otus.spring.config.ClientProperties; + +@EnableCaching +@EnableRetry +@EnableFeignClients +@EnableConfigurationProperties(ClientProperties.class) +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + SpringApplication.run(Main.class, args); + } +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/CacheConfig.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/CacheConfig.java new file mode 100644 index 00000000..cd39f893 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/CacheConfig.java @@ -0,0 +1,15 @@ +package ru.otus.spring.config; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CacheConfig { + + @Bean + public CacheManager cacheManager() { + return new ConcurrentMapCacheManager("countries"); + } +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/ClientConfiguration.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/ClientConfiguration.java new file mode 100644 index 00000000..d7b6a412 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/ClientConfiguration.java @@ -0,0 +1,12 @@ +package ru.otus.spring.config; + +import feign.RequestInterceptor; +import org.springframework.context.annotation.Bean; + +public class ClientConfiguration { + + @Bean + public RequestInterceptor requestInterceptor(ClientProperties properties) { + return requestTemplate -> requestTemplate.query("access_key", properties.getKey()); + } +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/ClientProperties.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/ClientProperties.java new file mode 100644 index 00000000..a683ff88 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/config/ClientProperties.java @@ -0,0 +1,13 @@ +package ru.otus.spring.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Getter +@Setter +@ConfigurationProperties(prefix = "client") +public class ClientProperties { + private String url; + private String key; +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/dto/Country.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/dto/Country.java new file mode 100644 index 00000000..5d0f66e4 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/dto/Country.java @@ -0,0 +1,19 @@ +package ru.otus.spring.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@NoArgsConstructor +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class Country { + private String name; + // private String capital; +// private String region; + private String alpha3Code; +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/CountryService.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/CountryService.java new file mode 100644 index 00000000..dbaa7878 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/CountryService.java @@ -0,0 +1,12 @@ +package ru.otus.spring.service; + +import ru.otus.spring.dto.Country; + +import java.util.List; + +public interface CountryService { + + Country findByCode(String id); + + List getAll(); +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/FeignCountryService.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/FeignCountryService.java new file mode 100644 index 00000000..2a87c1e7 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/FeignCountryService.java @@ -0,0 +1,25 @@ +package ru.otus.spring.service; + +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import ru.otus.spring.config.ClientConfiguration; +import ru.otus.spring.dto.Country; + +import java.util.List; + +@FeignClient(value = "countrylayer", configuration = ClientConfiguration.class) +@Cacheable("countries") +@Retryable(retryFor = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 2000)) +public interface FeignCountryService { + @RequestMapping(method = RequestMethod.GET, value = "/alpha/{code}", produces = "application/json") + Country findByCode(@PathVariable("code") String code); + + @RequestMapping(method = RequestMethod.GET, value = "/all", produces = "application/json") + List getAll(); + +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/MainServiceImpl.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/MainServiceImpl.java new file mode 100644 index 00000000..9ae99afb --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/MainServiceImpl.java @@ -0,0 +1,29 @@ +package ru.otus.spring.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Service; +import ru.otus.spring.dto.Country; + +@Slf4j +@RequiredArgsConstructor +@Service +public class MainServiceImpl implements CommandLineRunner { + // private final TemplateCountryService countryService; +// private final WebCountryService countryService; +// private final RestCountryService countryService; + private final FeignCountryService countryService; + + @Override + public void run(String... args) { +// List countries = countryService.getAll(); +// log.info("Countries: {}", countries); + Country country = countryService.findByCode("col"); + log.info("Find {}", country); +// country = countryService.findByCode("col"); +// log.info("Find {}", country); +// country = countryService.findByCode("rus"); +// log.info("Find {}", country); + } +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/RestCountryService.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/RestCountryService.java new file mode 100644 index 00000000..c4792623 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/RestCountryService.java @@ -0,0 +1,56 @@ +package ru.otus.spring.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.MediaType; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; +import ru.otus.spring.config.ClientProperties; +import ru.otus.spring.dto.Country; + +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class RestCountryService implements CountryService { + final ClientProperties properties; + + final RestClient restClient; + + public RestCountryService(ClientProperties properties) { + this.properties = properties; + this.restClient = RestClient.builder() + .requestFactory(new HttpComponentsClientHttpRequestFactory()) +// .messageConverters(converters -> converters.add(new MyCustomMessageConverter())) + .baseUrl(properties.getUrl()) + .defaultUriVariables(Map.of("key", properties.getKey())) +// .defaultHeader("Header", "") +// .requestInterceptor(myCustomInterceptor) +// .requestInitializer(myCustomInitializer) + .build(); + } + + @Override + public Country findByCode(String id) { + log.info("Rest client Request findByCode"); + return restClient.get() + .uri(uriBuilder -> uriBuilder.path("/alpha/{id}") + .queryParam("access_key", properties.getKey()) + .build(Map.of("id", id))) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .body(Country.class); + } + + @Override + public List getAll() { + log.info("Web client Request findByCode"); + return restClient.get() + .uri("/all?access_key={key}") + .retrieve() + .body(new ParameterizedTypeReference<>() { + }); + } +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/TemplateCountryService.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/TemplateCountryService.java new file mode 100644 index 00000000..34b0a482 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/TemplateCountryService.java @@ -0,0 +1,48 @@ +package ru.otus.spring.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpMethod; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; +import ru.otus.spring.config.ClientProperties; +import ru.otus.spring.dto.Country; + +import java.net.URI; +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@Service +public class TemplateCountryService implements CountryService { + final ClientProperties properties; + + private final RestOperations rest = new RestTemplate(); + + @Override + public Country findByCode(String id) { + log.info("RestTemplate Request findByCode"); + return rest.getForObject("http://api.countrylayer.com/v2/alpha/" + id + "?access_key=" + properties.getKey(), + Country.class); + } + + @Override + public List getAll() { + log.info("RestTemplate Request getAll"); + URI uri = UriComponentsBuilder.fromHttpUrl(properties.getUrl()) + .path("/all") + .queryParam("access_key", properties.getKey()) + .build().toUri(); + RequestEntity> requestEntity = new RequestEntity<>(HttpMethod.GET, + uri); +// List countries = rest.getForObject(uri, List.class); + ResponseEntity> exchange = rest.exchange(requestEntity, new ParameterizedTypeReference<>() { + }); + return exchange.getBody(); + } +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/WebCountryService.java b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/WebCountryService.java new file mode 100644 index 00000000..891a74fd --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/java/ru/otus/spring/service/WebCountryService.java @@ -0,0 +1,66 @@ +package ru.otus.spring.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; +import ru.otus.spring.config.ClientProperties; +import ru.otus.spring.dto.Country; + +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class WebCountryService implements CountryService { + final ClientProperties properties; + + final WebClient webClient; + + public WebCountryService(ClientProperties properties) { + this.properties = properties; + this.webClient = WebClient.builder() + .baseUrl(properties.getUrl()) + .defaultUriVariables(Map.of("key", properties.getKey())) + .filter(errorHandler()) + .build(); + } + + @Override + public Country findByCode(String id) { + log.info("Web client Request findByCode"); + return webClient.get() + .uri("/alpha/{id}?access_key={key}", Map.of("id", id)) + .retrieve() + .bodyToMono(Country.class) + .block(); + } + + @Override + public List getAll() { + log.info("Web client Request findByCode"); + return webClient.get() + .uri("/all?access_key={key}") + .retrieve() + .bodyToMono(new ParameterizedTypeReference>() { + }) + .block(); + } + + private static ExchangeFilterFunction errorHandler() { + return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { + if (clientResponse.statusCode().is5xxServerError()) { + return clientResponse.bodyToMono(String.class) + .flatMap(errorBody -> Mono.error(new RuntimeException(errorBody))); + } else if (clientResponse.statusCode().is4xxClientError()) { + return clientResponse.bodyToMono(String.class) + .flatMap(errorBody -> Mono.error(new RuntimeException(errorBody))); + } else { + return Mono.just(clientResponse); + } + }); + } + +} diff --git a/2025-09/spring-32-http-client/rest-template/src/main/resources/application.yml b/2025-09/spring-32-http-client/rest-template/src/main/resources/application.yml new file mode 100644 index 00000000..4ac51230 --- /dev/null +++ b/2025-09/spring-32-http-client/rest-template/src/main/resources/application.yml @@ -0,0 +1,19 @@ +spring.main.web-application-type: none + +spring: + cloud: + openfeign: + client: + config: + countrylayer: + url: http://api.countrylayer.com/v2 + +client: + url: http://api.countrylayer.com/v2 + key: [ !!!Your key!!! ] + +logging: + level: + org: + springframework: + web: debug diff --git a/2025-09/spring-32-http-client/soap-client/pom.xml b/2025-09/spring-32-http-client/soap-client/pom.xml new file mode 100644 index 00000000..dc7feaa7 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-client/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + soap-client + 1.0 + + + ru.otus + spring-32-http-client + 1.0 + + + + + org.springframework.boot + spring-boot-starter-web-services + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.projectlombok + lombok + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + com.sun.xml.ws + jaxws-maven-plugin + 3.0.0 + + + + wsimport + + + + + hello.wsdl + + src/main/resources/countries.wsdl + + + true + + + + + + diff --git a/2025-09/spring-32-http-client/soap-client/src/main/java/hello/ClientApp.java b/2025-09/spring-32-http-client/soap-client/src/main/java/hello/ClientApp.java new file mode 100644 index 00000000..185dff2d --- /dev/null +++ b/2025-09/spring-32-http-client/soap-client/src/main/java/hello/ClientApp.java @@ -0,0 +1,39 @@ +package hello; + +import hello.wsdl.Country; +import hello.wsdl.GetCountryResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@Slf4j +@SpringBootApplication +public class ClientApp { + + public static void main(String[] args) { + SpringApplication.run(ClientApp.class, args); + } + + @Bean + CommandLineRunner lookup(CountryClient quoteClient) { + return args -> { + String country = "Spain"; + + if (args.length > 0) { + country = args[0]; + } + GetCountryResponse response = quoteClient.getCountry(country); + if (response.getCountry() != null) { + Country countryDetails = response.getCountry(); + log.warn("GetCountry {} : Country[name={}, population={}, capital={}, currency={}]", + country, countryDetails.getName(), countryDetails.getPopulation(), + countryDetails.getCapital(), countryDetails.getCurrency()); + } else { + log.warn("GetCountry {} : no country found in response", country); + } + }; + } + +} diff --git a/2025-09/spring-32-http-client/soap-client/src/main/java/hello/CountryClient.java b/2025-09/spring-32-http-client/soap-client/src/main/java/hello/CountryClient.java new file mode 100644 index 00000000..eec56348 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-client/src/main/java/hello/CountryClient.java @@ -0,0 +1,24 @@ +package hello; + +import hello.wsdl.GetCountryRequest; +import hello.wsdl.GetCountryResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ws.client.core.support.WebServiceGatewaySupport; + +@Slf4j +public class CountryClient extends WebServiceGatewaySupport { + + public GetCountryResponse getCountry(String country) { + + GetCountryRequest request = new GetCountryRequest(); + request.setName(country); + + log.info("Requesting location for {}", country); + + GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate() + .marshalSendAndReceive("http://localhost:8080/ws", request); + + return response; + } + +} diff --git a/2025-09/spring-32-http-client/soap-client/src/main/java/hello/CountryConfiguration.java b/2025-09/spring-32-http-client/soap-client/src/main/java/hello/CountryConfiguration.java new file mode 100644 index 00000000..edd3a4a4 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-client/src/main/java/hello/CountryConfiguration.java @@ -0,0 +1,28 @@ +package hello; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; + +@Configuration +public class CountryConfiguration { + + @Bean + public Jaxb2Marshaller marshaller() { + Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); + // this package must match the package in the specified in + // pom.xml + marshaller.setContextPath("hello.wsdl"); + return marshaller; + } + + @Bean + public CountryClient countryClient(Jaxb2Marshaller marshaller) { + CountryClient client = new CountryClient(); + client.setDefaultUri("http://localhost:8080/ws"); + client.setMarshaller(marshaller); + client.setUnmarshaller(marshaller); + return client; + } + +} diff --git a/2025-09/spring-32-http-client/soap-client/src/main/resources/application.yml b/2025-09/spring-32-http-client/soap-client/src/main/resources/application.yml new file mode 100644 index 00000000..1aa84da6 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-client/src/main/resources/application.yml @@ -0,0 +1,6 @@ +logging: + level: + hello: INFO + org: + springframework: + ws: DEBUG diff --git a/2025-09/spring-32-http-client/soap-client/src/main/resources/countries.wsdl b/2025-09/spring-32-http-client/soap-client/src/main/resources/countries.wsdl new file mode 100644 index 00000000..061e18f5 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-client/src/main/resources/countries.wsdl @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/2025-09/spring-32-http-client/soap-server/pom.xml b/2025-09/spring-32-http-client/soap-server/pom.xml new file mode 100644 index 00000000..7b02d5b8 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-server/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + soap-server + 1.0 + + + ru.otus + spring-32-http-client + 1.0 + + + + + + org.springframework.boot + spring-boot-starter-web-services + + + wsdl4j + wsdl4j + + + org.springframework.boot + spring-boot-starter-test + test + + + org.projectlombok + lombok + provided + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.codehaus.mojo + jaxb2-maven-plugin + 3.1.0 + + + xjc + + xjc + + + + + + ${project.basedir}/src/main/resources/countries.xsd + + + ru.otus.hello.web_service + + + + + + diff --git a/2025-09/spring-32-http-client/soap-server/src/main/java/hello/CountryEndpoint.java b/2025-09/spring-32-http-client/soap-server/src/main/java/hello/CountryEndpoint.java new file mode 100644 index 00000000..b0c6f9f7 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-server/src/main/java/hello/CountryEndpoint.java @@ -0,0 +1,30 @@ +package hello; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ws.server.endpoint.annotation.Endpoint; +import org.springframework.ws.server.endpoint.annotation.PayloadRoot; +import org.springframework.ws.server.endpoint.annotation.RequestPayload; +import org.springframework.ws.server.endpoint.annotation.ResponsePayload; +import ru.otus.hello.web_service.GetCountryRequest; +import ru.otus.hello.web_service.GetCountryResponse; + +@Endpoint +public class CountryEndpoint { + private static final String NAMESPACE_URI = "http://otus.ru/hello/web-service"; + + private final CountryRepository countryRepository; + + @Autowired + public CountryEndpoint(CountryRepository countryRepository) { + this.countryRepository = countryRepository; + } + + @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest") + @ResponsePayload + public GetCountryResponse getCountry(@RequestPayload GetCountryRequest request) { + GetCountryResponse response = new GetCountryResponse(); + response.setCountry(countryRepository.findCountry(request.getName())); + + return response; + } +} diff --git a/2025-09/spring-32-http-client/soap-server/src/main/java/hello/CountryRepository.java b/2025-09/spring-32-http-client/soap-server/src/main/java/hello/CountryRepository.java new file mode 100644 index 00000000..be6d81dc --- /dev/null +++ b/2025-09/spring-32-http-client/soap-server/src/main/java/hello/CountryRepository.java @@ -0,0 +1,47 @@ +package hello; + +import jakarta.annotation.PostConstruct; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import ru.otus.hello.web_service.Country; +import ru.otus.hello.web_service.Currency; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class CountryRepository { + private static final Map countries = new HashMap<>(); + + @PostConstruct + public void initData() { + Country spain = new Country(); + spain.setName("Spain"); + spain.setCapital("Madrid"); + spain.setCurrency(Currency.EUR); + spain.setPopulation(46704314); + + countries.put(spain.getName(), spain); + + Country poland = new Country(); + poland.setName("Poland"); + poland.setCapital("Warsaw"); + poland.setCurrency(Currency.PLN); + poland.setPopulation(38186860); + + countries.put(poland.getName(), poland); + + Country uk = new Country(); + uk.setName("United Kingdom"); + uk.setCapital("London"); + uk.setCurrency(Currency.GBP); + uk.setPopulation(63705000); + + countries.put(uk.getName(), uk); + } + + public Country findCountry(String name) { + Assert.notNull(name, "The country's name must not be null"); + return countries.get(name); + } +} diff --git a/2025-09/spring-32-http-client/soap-server/src/main/java/hello/ServerApp.java b/2025-09/spring-32-http-client/soap-server/src/main/java/hello/ServerApp.java new file mode 100644 index 00000000..489836fb --- /dev/null +++ b/2025-09/spring-32-http-client/soap-server/src/main/java/hello/ServerApp.java @@ -0,0 +1,12 @@ +package hello; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ServerApp { + + public static void main(String[] args) { + SpringApplication.run(ServerApp.class, args); + } +} diff --git a/2025-09/spring-32-http-client/soap-server/src/main/java/hello/WebServiceConfig.java b/2025-09/spring-32-http-client/soap-server/src/main/java/hello/WebServiceConfig.java new file mode 100644 index 00000000..79455f80 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-server/src/main/java/hello/WebServiceConfig.java @@ -0,0 +1,40 @@ +package hello; + +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.ws.config.annotation.EnableWs; +import org.springframework.ws.config.annotation.WsConfigurer; +import org.springframework.ws.transport.http.MessageDispatcherServlet; +import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition; +import org.springframework.xml.xsd.SimpleXsdSchema; +import org.springframework.xml.xsd.XsdSchema; + +@EnableWs +@Configuration +public class WebServiceConfig implements WsConfigurer { + @Bean + public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) { + MessageDispatcherServlet servlet = new MessageDispatcherServlet(); + servlet.setApplicationContext(applicationContext); + servlet.setTransformWsdlLocations(true); + return new ServletRegistrationBean<>(servlet, "/ws/*"); + } + + @Bean(name = "countries") + public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) { + DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); + wsdl11Definition.setPortTypeName("CountriesPort"); + wsdl11Definition.setLocationUri("/ws"); + wsdl11Definition.setTargetNamespace("http://otus.ru/hello/web-service"); + wsdl11Definition.setSchema(countriesSchema); + return wsdl11Definition; + } + + @Bean + public XsdSchema countriesSchema() { + return new SimpleXsdSchema(new ClassPathResource("countries.xsd")); + } +} diff --git a/2025-09/spring-32-http-client/soap-server/src/main/resources/application.yml b/2025-09/spring-32-http-client/soap-server/src/main/resources/application.yml new file mode 100644 index 00000000..4b4d02dc --- /dev/null +++ b/2025-09/spring-32-http-client/soap-server/src/main/resources/application.yml @@ -0,0 +1,3 @@ +logging: + level: + root: debug \ No newline at end of file diff --git a/2025-09/spring-32-http-client/soap-server/src/main/resources/countries.xsd b/2025-09/spring-32-http-client/soap-server/src/main/resources/countries.xsd new file mode 100644 index 00000000..806a4136 --- /dev/null +++ b/2025-09/spring-32-http-client/soap-server/src/main/resources/countries.xsd @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/2025-09/spring-32-http-client/soap-server/src/test/java/hello/ApplicationIntegrationTests.java b/2025-09/spring-32-http-client/soap-server/src/test/java/hello/ApplicationIntegrationTests.java new file mode 100644 index 00000000..0bc377ea --- /dev/null +++ b/2025-09/spring-32-http-client/soap-server/src/test/java/hello/ApplicationIntegrationTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hello; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.util.ClassUtils; +import org.springframework.ws.client.core.WebServiceTemplate; +import ru.otus.hello.web_service.GetCountryRequest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class ApplicationIntegrationTests { + + private final Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); + + @LocalServerPort + private int port = 0; + + @BeforeEach + public void init() throws Exception { + marshaller.setPackagesToScan(ClassUtils.getPackageName(GetCountryRequest.class)); + marshaller.afterPropertiesSet(); + } + + @Test + public void testSendAndReceive() { + WebServiceTemplate ws = new WebServiceTemplate(marshaller); + GetCountryRequest request = new GetCountryRequest(); + request.setName("Spain"); + + assertThat(ws.marshalSendAndReceive("http://localhost:" + port + "/ws", request)).isNotNull(); + } +} \ No newline at end of file