diff --git a/2020-08/spring-30-rest/pom.xml b/2020-08/spring-30-rest/pom.xml
new file mode 100644
index 00000000..07d68332
--- /dev/null
+++ b/2020-08/spring-30-rest/pom.xml
@@ -0,0 +1,18 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-30
+ 1.0
+
+ pom
+
+
+ rest-template
+ soap-client
+ soap-server
+
+
diff --git a/2020-08/spring-30-rest/rest-template/pom.xml b/2020-08/spring-30-rest/rest-template/pom.xml
new file mode 100644
index 00000000..10ada048
--- /dev/null
+++ b/2020-08/spring-30-rest/rest-template/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+ ru.otus
+ rest-template
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.6.RELEASE
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
+
+ org.springframework.retry
+ spring-retry
+
+
+
+ org.aspectj
+ aspectjweaver
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/Main.java b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..7d455040
--- /dev/null
+++ b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,31 @@
+package ru.otus.spring;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.ApplicationContext;
+import org.springframework.retry.annotation.EnableRetry;
+import ru.otus.spring.dto.Country;
+import ru.otus.spring.service.CountryService;
+
+import java.util.List;
+
+@EnableCaching
+@EnableRetry
+@SpringBootApplication
+public class Main {
+
+ private static final Logger log = LoggerFactory.getLogger(Main.class);
+
+ public static void main(String[] args) {
+ ApplicationContext ctx = SpringApplication.run(Main.class, args);
+ CountryService service = ctx.getBean(CountryService.class);
+
+ Country country = service.getCountry("col");
+
+ log.info(country.getName());
+
+ }
+}
diff --git a/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/config/CacheConfig.java b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/config/CacheConfig.java
new file mode 100644
index 00000000..a57c1386
--- /dev/null
+++ b/2020-08/spring-30-rest/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/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/dto/Country.java b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/dto/Country.java
new file mode 100644
index 00000000..cb4c8a03
--- /dev/null
+++ b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/dto/Country.java
@@ -0,0 +1,27 @@
+package ru.otus.spring.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Country {
+
+ private String name;
+
+ private String alpha3Code;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName( String name ) {
+ this.name = name;
+ }
+
+ public String getAlpha3Code() {
+ return alpha3Code;
+ }
+
+ public void setAlpha3Code( String alpha3Code ) {
+ this.alpha3Code = alpha3Code;
+ }
+}
diff --git a/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/service/CountryService.java b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/service/CountryService.java
new file mode 100644
index 00000000..f259945e
--- /dev/null
+++ b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/service/CountryService.java
@@ -0,0 +1,10 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.dto.Country;
+
+import java.util.List;
+
+public interface CountryService {
+
+ Country getCountry( String id );
+}
diff --git a/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/service/CountryServiceRest.java b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/service/CountryServiceRest.java
new file mode 100644
index 00000000..7808b80b
--- /dev/null
+++ b/2020-08/spring-30-rest/rest-template/src/main/java/ru/otus/spring/service/CountryServiceRest.java
@@ -0,0 +1,22 @@
+package ru.otus.spring.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+import ru.otus.spring.dto.Country;
+
+@Service
+public class CountryServiceRest implements CountryService {
+
+ private static final Logger log = LoggerFactory.getLogger( CountryServiceRest.class );
+
+ private RestOperations rest = new RestTemplate();
+
+ @Override
+ public Country getCountry( String id ) {
+ log.info( "Request" );
+ return rest.getForObject( "http://restcountries.eu/rest/v2/alpha/" + id, Country.class );
+ }
+}
diff --git a/2020-08/spring-30-rest/rest-template/src/main/resources/application.yml b/2020-08/spring-30-rest/rest-template/src/main/resources/application.yml
new file mode 100644
index 00000000..11e54c54
--- /dev/null
+++ b/2020-08/spring-30-rest/rest-template/src/main/resources/application.yml
@@ -0,0 +1,2 @@
+server:
+ port: 8081
diff --git a/2020-08/spring-30-rest/soap-client/pom.xml b/2020-08/spring-30-rest/soap-client/pom.xml
new file mode 100644
index 00000000..99def6dd
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-client/pom.xml
@@ -0,0 +1,110 @@
+
+
+ 4.0.0
+
+ ru.otus
+ soap-client
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.6.RELEASE
+
+
+
+
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.ws
+ spring-ws-core
+
+
+
+
+ javax.activation
+ activation
+ 1.1.1
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.0
+
+
+
+ com.sun.xml.bind
+ jaxb-core
+ 2.3.0
+
+
+
+ com.sun.xml.bind
+ jaxb-impl
+ 2.3.0
+
+
+
+ javax.xml.soap
+ javax.xml.soap-api
+ 1.4.0
+
+
+
+ com.sun.xml.messaging.saaj
+ saaj-impl
+ 1.5.1
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.codehaus.mojo
+ jaxb2-maven-plugin
+ 2.4
+
+
+ xjc
+
+ xjc
+
+
+
+
+ hello.wsdl
+ wsdl
+
+ src/main/resources/countries.wsdl
+
+
+
+
+ org.glassfish.jaxb
+ jaxb-xjc
+ 2.3.2
+
+
+ com.sun.activation
+ jakarta.activation
+ 1.2.1
+
+
+
+
+
+
+
diff --git a/2020-08/spring-30-rest/soap-client/src/main/java/hello/Application.java b/2020-08/spring-30-rest/soap-client/src/main/java/hello/Application.java
new file mode 100644
index 00000000..fbc91c04
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-client/src/main/java/hello/Application.java
@@ -0,0 +1,31 @@
+
+package hello;
+
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+import hello.wsdl.GetCountryResponse;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ @Bean
+ CommandLineRunner lookup(CountryClient quoteClient) {
+ return args -> {
+ String country = "Spain";
+
+ if (args.length > 0) {
+ country = args[0];
+ }
+ GetCountryResponse response = quoteClient.getCountry(country);
+ System.err.println(response.getCountry().getCurrency());
+ };
+ }
+
+}
diff --git a/2020-08/spring-30-rest/soap-client/src/main/java/hello/CountryClient.java b/2020-08/spring-30-rest/soap-client/src/main/java/hello/CountryClient.java
new file mode 100644
index 00000000..d24dd249
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-client/src/main/java/hello/CountryClient.java
@@ -0,0 +1,30 @@
+
+package hello;
+
+import hello.wsdl.GetCountryRequest;
+import hello.wsdl.GetCountryResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
+import org.springframework.ws.soap.client.core.SoapActionCallback;
+
+public class CountryClient extends WebServiceGatewaySupport {
+
+ private static final Logger log = LoggerFactory.getLogger(CountryClient.class);
+
+ 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/countries", request,
+ new SoapActionCallback(
+ "http://spring.io/guides/gs-producing-web-service/GetCountryRequest"));
+
+ return response;
+ }
+
+}
diff --git a/2020-08/spring-30-rest/soap-client/src/main/java/hello/CountryConfiguration.java b/2020-08/spring-30-rest/soap-client/src/main/java/hello/CountryConfiguration.java
new file mode 100644
index 00000000..01d7ac37
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-client/src/main/java/hello/CountryConfiguration.java
@@ -0,0 +1,29 @@
+
+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/2020-08/spring-30-rest/soap-client/src/main/resources/application.yml b/2020-08/spring-30-rest/soap-client/src/main/resources/application.yml
new file mode 100644
index 00000000..1aa84da6
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-client/src/main/resources/application.yml
@@ -0,0 +1,6 @@
+logging:
+ level:
+ hello: INFO
+ org:
+ springframework:
+ ws: DEBUG
diff --git a/2020-08/spring-30-rest/soap-client/src/main/resources/countries.wsdl b/2020-08/spring-30-rest/soap-client/src/main/resources/countries.wsdl
new file mode 100644
index 00000000..8b56dcca
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-client/src/main/resources/countries.wsdl
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2020-08/spring-30-rest/soap-server/pom.xml b/2020-08/spring-30-rest/soap-server/pom.xml
new file mode 100644
index 00000000..724a8234
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-server/pom.xml
@@ -0,0 +1,100 @@
+
+
+ 4.0.0
+
+ ru.otus
+ soap-server
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.6.RELEASE
+
+
+
+
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web-services
+
+
+ wsdl4j
+ wsdl4j
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ com.sun.xml.bind
+ jaxb-core
+ 2.3.0.1
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.1
+
+
+ com.sun.xml.bind
+ jaxb-impl
+ 2.3.1
+
+
+ org.javassist
+ javassist
+ 3.25.0-GA
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.codehaus.mojo
+ jaxb2-maven-plugin
+ 2.4
+
+
+ xjc
+
+ xjc
+
+
+
+
+
+ ${project.basedir}/src/main/resources/countries.xsd
+
+
+ io.spring.guides.gs_producing_web_service
+
+
+
+ org.glassfish.jaxb
+ jaxb-xjc
+ 2.3.2
+
+
+ com.sun.activation
+ jakarta.activation
+ 1.2.1
+
+
+
+
+
+
+
diff --git a/2020-08/spring-30-rest/soap-server/src/main/java/hello/Application.java b/2020-08/spring-30-rest/soap-server/src/main/java/hello/Application.java
new file mode 100644
index 00000000..33c3f3bf
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-server/src/main/java/hello/Application.java
@@ -0,0 +1,12 @@
+package hello;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/2020-08/spring-30-rest/soap-server/src/main/java/hello/CountryEndpoint.java b/2020-08/spring-30-rest/soap-server/src/main/java/hello/CountryEndpoint.java
new file mode 100644
index 00000000..60827d88
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-server/src/main/java/hello/CountryEndpoint.java
@@ -0,0 +1,31 @@
+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 io.spring.guides.gs_producing_web_service.GetCountryRequest;
+import io.spring.guides.gs_producing_web_service.GetCountryResponse;
+
+@Endpoint
+public class CountryEndpoint {
+ private static final String NAMESPACE_URI = "http://spring.io/guides/gs-producing-web-service";
+
+ private 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/2020-08/spring-30-rest/soap-server/src/main/java/hello/CountryRepository.java b/2020-08/spring-30-rest/soap-server/src/main/java/hello/CountryRepository.java
new file mode 100644
index 00000000..4888b3ee
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-server/src/main/java/hello/CountryRepository.java
@@ -0,0 +1,47 @@
+package hello;
+
+import javax.annotation.PostConstruct;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.spring.guides.gs_producing_web_service.Country;
+import io.spring.guides.gs_producing_web_service.Currency;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+
+@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/2020-08/spring-30-rest/soap-server/src/main/java/hello/WebServiceConfig.java b/2020-08/spring-30-rest/soap-server/src/main/java/hello/WebServiceConfig.java
new file mode 100644
index 00000000..5b21caf9
--- /dev/null
+++ b/2020-08/spring-30-rest/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.WsConfigurerAdapter;
+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 extends WsConfigurerAdapter {
+ @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://spring.io/guides/gs-producing-web-service");
+ wsdl11Definition.setSchema(countriesSchema);
+ return wsdl11Definition;
+ }
+
+ @Bean
+ public XsdSchema countriesSchema() {
+ return new SimpleXsdSchema(new ClassPathResource("countries.xsd"));
+ }
+}
diff --git a/2020-08/spring-30-rest/soap-server/src/main/resources/countries.xsd b/2020-08/spring-30-rest/soap-server/src/main/resources/countries.xsd
new file mode 100644
index 00000000..a893956d
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-server/src/main/resources/countries.xsd
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2020-08/spring-30-rest/soap-server/src/test/java/hello/ApplicationIntegrationTests.java b/2020-08/spring-30-rest/soap-server/src/test/java/hello/ApplicationIntegrationTests.java
new file mode 100644
index 00000000..927bbf52
--- /dev/null
+++ b/2020-08/spring-30-rest/soap-server/src/test/java/hello/ApplicationIntegrationTests.java
@@ -0,0 +1,57 @@
+/*
+ * 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 static org.assertj.core.api.Assertions.*;
+
+import io.spring.guides.gs_producing_web_service.GetCountryRequest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.oxm.jaxb.Jaxb2Marshaller;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.util.ClassUtils;
+import org.springframework.ws.client.core.WebServiceTemplate;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+public class ApplicationIntegrationTests {
+
+ private Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
+
+ @LocalServerPort
+ private int port = 0;
+
+ @Before
+ 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