From c7978bf0e5505732c7425559518ad9b7fb923c70 Mon Sep 17 00:00:00 2001 From: Max Zhilin Date: Thu, 11 Nov 2021 09:35:20 +0300 Subject: [PATCH 01/48] Update README.md Link to next page --- core-java-modules/core-java-io-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-java-modules/core-java-io-3/README.md b/core-java-modules/core-java-io-3/README.md index d0ac5387a8..312797cb94 100644 --- a/core-java-modules/core-java-io-3/README.md +++ b/core-java-modules/core-java-io-3/README.md @@ -14,4 +14,4 @@ This module contains articles about core Java input and output (IO) - [Find the Last Modified File in a Directory with Java](https://www.baeldung.com/java-last-modified-file) - [Get a Filename Without the Extension in Java](https://www.baeldung.com/java-filename-without-extension) - [Writing byte[] to a File in Java](https://www.baeldung.com/java-write-byte-array-file) -- [[<-- Prev]](/core-java-modules/core-java-io-2) +- [[<-- Prev]](/core-java-modules/core-java-io-2)[[More -->]](/core-java-modules/core-java-io-4) From f741713458df29630a75ac9ed079321ff02dcd5c Mon Sep 17 00:00:00 2001 From: janmadle <85927377+janmadle@users.noreply.github.com> Date: Fri, 12 Nov 2021 09:16:13 +0100 Subject: [PATCH 02/48] fixed missing parameter Fixed not working example - Added missing zipParameters to .addFile() function --- .../src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java index d0947afa2e..7639a2e9df 100644 --- a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java +++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java @@ -21,7 +21,7 @@ public class ZipSingleFile { if (!fileToAdd.exists()) { fileToAdd.createNewFile(); } - zipFile.addFile(fileToAdd); + zipFile.addFile(fileToAdd, zipParameters); zipFile.close(); } } From 0f1a1b5cf2478076dba070c7e601bf83b6deb461 Mon Sep 17 00:00:00 2001 From: Max Rydahl Andersen Date: Sun, 14 Nov 2021 11:47:06 +0100 Subject: [PATCH 03/48] initial jbang example --- jbang/hello.java | 11 +++++++++++ jbang/hellocli.java | 27 +++++++++++++++++++++++++++ jbang/index.html | 18 ++++++++++++++++++ jbang/jbang-catalog.json | 15 +++++++++++++++ jbang/jbangquarkus.java | 21 +++++++++++++++++++++ 5 files changed, 92 insertions(+) create mode 100755 jbang/hello.java create mode 100755 jbang/hellocli.java create mode 100644 jbang/index.html create mode 100644 jbang/jbang-catalog.json create mode 100755 jbang/jbangquarkus.java diff --git a/jbang/hello.java b/jbang/hello.java new file mode 100755 index 0000000000..4a457b153b --- /dev/null +++ b/jbang/hello.java @@ -0,0 +1,11 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +// //DEPS + +import static java.lang.System.*; + +public class hello { + + public static void main(String... args) { + out.println("Hello World"); + } +} diff --git a/jbang/hellocli.java b/jbang/hellocli.java new file mode 100755 index 0000000000..8722cb27b8 --- /dev/null +++ b/jbang/hellocli.java @@ -0,0 +1,27 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS info.picocli:picocli:4.5.0 + +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Parameters; + +import java.util.concurrent.Callable; + +@Command(name = "hellocli", mixinStandardHelpOptions = true, version = "hellocli 0.1", + description = "hellocli made with jbang") +class hellocli implements Callable { + + @Parameters(index = "0", description = "The greeting to print", defaultValue = "World!") + private String greeting; + + public static void main(String... args) { + int exitCode = new CommandLine(new hellocli()).execute(args); + System.exit(exitCode); + } + + @Override + public Integer call() throws Exception { // your business logic goes here... + System.out.println("Hello " + greeting); + return 0; + } +} diff --git a/jbang/index.html b/jbang/index.html new file mode 100644 index 0000000000..bd34d026a3 --- /dev/null +++ b/jbang/index.html @@ -0,0 +1,18 @@ + + + + + + + JBang meets Quarkus + + + + + Go Say Hello! +

+ Powered by: + +

+ + \ No newline at end of file diff --git a/jbang/jbang-catalog.json b/jbang/jbang-catalog.json new file mode 100644 index 0000000000..2e30f0eb4a --- /dev/null +++ b/jbang/jbang-catalog.json @@ -0,0 +1,15 @@ +{ + "catalogs": {}, + "aliases": { + "hello": { + "script-ref": "hello.java" + }, + "hellocli": { + "script-ref": "hellocli.java" + }, + "jbangquarkus": { + "script-ref": "jbangquarkus.java" + } + }, + "templates": {} +} \ No newline at end of file diff --git a/jbang/jbangquarkus.java b/jbang/jbangquarkus.java new file mode 100755 index 0000000000..9e3cb5f69a --- /dev/null +++ b/jbang/jbangquarkus.java @@ -0,0 +1,21 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +// Update the Quarkus version to what you want here or run jbang with +// `-Dquarkus.version=` to override it. +//DEPS io.quarkus:quarkus-bom:${quarkus.version:2.4.0.Final}@pom +//DEPS io.quarkus:quarkus-resteasy +//JAVAC_OPTIONS -parameters + +//FILES META-INF/resources/index.html=index.html + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path("/hello") +@ApplicationScoped +public class jbangquarkus { + @GET + public String sayHello() { + return "Hello from Quarkus with jbang.dev"; + } +} From 8fd0ab58b984b9a5538d2d67bc94592c87431a57 Mon Sep 17 00:00:00 2001 From: sharifi Date: Sun, 5 Dec 2021 13:57:38 +0330 Subject: [PATCH 04/48] bael-4197: add application-context --- .../src/main/resources/application-context.xml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 spring-boot-modules/spring-boot-testing/src/main/resources/application-context.xml diff --git a/spring-boot-modules/spring-boot-testing/src/main/resources/application-context.xml b/spring-boot-modules/spring-boot-testing/src/main/resources/application-context.xml new file mode 100644 index 0000000000..a436b39a2f --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/main/resources/application-context.xml @@ -0,0 +1,9 @@ + + + + + From 63843c008c055e7d9da3efef2d5773e5316f3722 Mon Sep 17 00:00:00 2001 From: sharifi Date: Sun, 5 Dec 2021 13:58:13 +0330 Subject: [PATCH 05/48] bael-4197: add main source --- .../XmlBeanApplication.java | 15 +++++++++ .../domain/Employee.java | 32 +++++++++++++++++++ .../service/EmployeeService.java | 8 +++++ .../service/EmployeeServiceImpl.java | 11 +++++++ .../service/EmployeeServiceTestImpl.java | 11 +++++++ 5 files changed, 77 insertions(+) create mode 100644 spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/XmlBeanApplication.java create mode 100644 spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/domain/Employee.java create mode 100644 spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeService.java create mode 100644 spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeServiceImpl.java create mode 100644 spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeServiceTestImpl.java diff --git a/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/XmlBeanApplication.java b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/XmlBeanApplication.java new file mode 100644 index 0000000000..4e2af67ab0 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/XmlBeanApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.xmlapplicationcontext; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ImportResource; + +@SpringBootApplication +@ImportResource({"classpath*:application-context.xml"}) +public class XmlBeanApplication { + + public static void main(String[] args) { + SpringApplication.run(XmlBeanApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/domain/Employee.java b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/domain/Employee.java new file mode 100644 index 0000000000..f81277f027 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/domain/Employee.java @@ -0,0 +1,32 @@ +package com.baeldung.xmlapplicationcontext.domain; + +public class Employee { + + private String name; + private String role; + + public Employee() { + + } + + public Employee(String name, String role) { + this.name = name; + this.role = role; + } + + public String getName() { + return this.name; + } + + public String getRole() { + return this.role; + } + + public void setName(String name) { + this.name = name; + } + + public void setRole(String role) { + this.role = role; + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeService.java b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeService.java new file mode 100644 index 0000000000..867e9b8c9f --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeService.java @@ -0,0 +1,8 @@ +package com.baeldung.xmlapplicationcontext.service; + +import com.baeldung.xmlapplicationcontext.domain.Employee; + +public interface EmployeeService { + + Employee getEmployee(); +} diff --git a/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeServiceImpl.java b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeServiceImpl.java new file mode 100644 index 0000000000..b541c62dcd --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeServiceImpl.java @@ -0,0 +1,11 @@ +package com.baeldung.xmlapplicationcontext.service; + +import com.baeldung.xmlapplicationcontext.domain.Employee; + +public class EmployeeServiceImpl implements EmployeeService { + + @Override + public Employee getEmployee() { + return new Employee("Baeldung", "Admin"); + } +} diff --git a/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeServiceTestImpl.java b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeServiceTestImpl.java new file mode 100644 index 0000000000..7cebaa399b --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/service/EmployeeServiceTestImpl.java @@ -0,0 +1,11 @@ +package com.baeldung.xmlapplicationcontext.service; + +import com.baeldung.xmlapplicationcontext.domain.Employee; + +public class EmployeeServiceTestImpl implements EmployeeService { + + @Override + public Employee getEmployee() { + return new Employee("Baeldung-Test", "Admin"); + } +} From 232bd3080b506b2cfd846f64c2b4b953d08b4bf2 Mon Sep 17 00:00:00 2001 From: sharifi Date: Sun, 5 Dec 2021 13:58:28 +0330 Subject: [PATCH 06/48] bael-4197: add test context --- .../src/test/resources/test-context.xml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 spring-boot-modules/spring-boot-testing/src/test/resources/test-context.xml diff --git a/spring-boot-modules/spring-boot-testing/src/test/resources/test-context.xml b/spring-boot-modules/spring-boot-testing/src/test/resources/test-context.xml new file mode 100644 index 0000000000..664b528b06 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/test/resources/test-context.xml @@ -0,0 +1,9 @@ + + + + + From 58b01b624a25cb858a46a32d2b857aaf9c4af270 Mon Sep 17 00:00:00 2001 From: sharifi Date: Sun, 5 Dec 2021 13:58:51 +0330 Subject: [PATCH 07/48] bael-4197: add test class --- ...loyeeServiceAppContextIntegrationTest.java | 21 ++++++++++++++++ ...oyeeServiceTestContextIntegrationTest.java | 25 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java create mode 100644 spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceTestContextIntegrationTest.java diff --git a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java new file mode 100644 index 0000000000..7a63abf1a3 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java @@ -0,0 +1,21 @@ +package com.baeldung.xmlapplicationcontext; + +import com.baeldung.xmlapplicationcontext.service.EmployeeService; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = XmlBeanApplication.class) +public class EmployeeServiceAppContextIntegrationTest { + + @Autowired + private EmployeeService service; + + @Test + public void whenContextLoads_thenServiceISNotNull() { + assertThat(service).isNotNull(); + } + +} diff --git a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceTestContextIntegrationTest.java b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceTestContextIntegrationTest.java new file mode 100644 index 0000000000..9880c7c567 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceTestContextIntegrationTest.java @@ -0,0 +1,25 @@ +package com.baeldung.xmlapplicationcontext; + +import com.baeldung.xmlapplicationcontext.service.EmployeeService; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@ContextConfiguration(locations = "/test-context.xml") +public class EmployeeServiceTestContextIntegrationTest { + + @Autowired + @Qualifier("employeeServiceTestImpl") + private EmployeeService serviceTest; + + @Test + public void whenTestContextLoads_thenServiceTestISNotNull() { + assertThat(serviceTest).isNotNull(); + } + +} From 7f17253b07adc3852967f8ada1c49f51d680a66f Mon Sep 17 00:00:00 2001 From: sharifi Date: Sun, 5 Dec 2021 14:28:10 +0330 Subject: [PATCH 08/48] bael-4197: fix bug --- .../EmployeeServiceAppContextIntegrationTest.java | 4 ++++ .../EmployeeServiceTestContextIntegrationTest.java | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java index 7a63abf1a3..0ab157744a 100644 --- a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java +++ b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java @@ -2,11 +2,15 @@ package com.baeldung.xmlapplicationcontext; import com.baeldung.xmlapplicationcontext.service.EmployeeService; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) @SpringBootTest(classes = XmlBeanApplication.class) public class EmployeeServiceAppContextIntegrationTest { diff --git a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceTestContextIntegrationTest.java b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceTestContextIntegrationTest.java index 9880c7c567..0182d5dbb1 100644 --- a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceTestContextIntegrationTest.java +++ b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceTestContextIntegrationTest.java @@ -2,14 +2,17 @@ package com.baeldung.xmlapplicationcontext; import com.baeldung.xmlapplicationcontext.service.EmployeeService; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; -@SpringBootTest +@RunWith(SpringRunner.class) +@SpringBootTest(classes = XmlBeanApplication.class) @ContextConfiguration(locations = "/test-context.xml") public class EmployeeServiceTestContextIntegrationTest { From 0cb91257011f91f287a26f25437c94ef1e77b6c9 Mon Sep 17 00:00:00 2001 From: chaos2418 <> Date: Wed, 15 Dec 2021 09:22:21 +0530 Subject: [PATCH 09/48] JAVA-3060: adding new spring-reactive module for the ebook. --- pom.xml | 2 + spring-reactive/README.md | 17 + spring-reactive/pom.xml | 66 ++++ .../reactive/concurrency/Application.java | 17 + .../reactive/concurrency/Controller.java | 127 +++++++ .../baeldung/reactive/concurrency/Person.java | 27 ++ .../concurrency/PersonRepository.java | 6 + .../ConsumerDebuggingApplication.java | 33 ++ .../consumer/chronjobs/ChronJobs.java | 152 ++++++++ .../ReactiveConfigsToggleRestController.java | 22 ++ .../debugging/consumer/model/Foo.java | 27 ++ .../debugging/consumer/model/FooDto.java | 16 + .../consumer/service/FooNameHelper.java | 44 +++ .../consumer/service/FooQuantityHelper.java | 30 ++ .../consumer/service/FooReporter.java | 24 ++ .../consumer/service/FooService.java | 118 +++++++ .../server/ServerDebuggingApplication.java | 29 ++ .../server/handlers/ServerHandler.java | 45 +++ .../reactive/debugging/server/model/Foo.java | 13 + .../server/routers/ServerRouter.java | 21 ++ .../ErrorHandlingApplication.java | 24 ++ .../errorhandling/GlobalErrorAttributes.java | 52 +++ .../GlobalErrorWebExceptionHandler.java | 48 +++ .../errorhandling/NameRequiredException.java | 11 + .../errorhandling/handlers/Handler1.java | 28 ++ .../errorhandling/handlers/Handler2.java | 37 ++ .../errorhandling/handlers/Handler3.java | 33 ++ .../errorhandling/handlers/Handler4.java | 29 ++ .../errorhandling/handlers/Handler5.java | 21 ++ .../errorhandling/routers/Router1.java | 21 ++ .../errorhandling/routers/Router2.java | 21 ++ .../errorhandling/routers/Router3.java | 21 ++ .../errorhandling/routers/Router4.java | 21 ++ .../errorhandling/routers/Router5.java | 21 ++ .../reactive/security/GreetController.java | 37 ++ .../reactive/security/GreetService.java | 15 + .../reactive/security/SecurityConfig.java | 55 +++ .../security/SpringSecurity5Application.java | 34 ++ .../com/baeldung/reactive/webclient/Foo.java | 24 ++ .../baeldung/reactive/webclient/Tweet.java | 13 + .../TweetsSlowServiceController.java | 20 ++ .../webclient/WebClientApplication.java | 14 + .../webclient/WebClientController.java | 41 +++ .../reactive/webclient/WebController.java | 60 ++++ .../SpringWebClientRequestsApp.java | 12 + .../baeldung/reactive/webflux/Employee.java | 17 + .../reactive/webflux/EmployeeRepository.java | 59 ++++ .../annotation/EmployeeController.java | 39 +++ .../annotation/EmployeeSpringApplication.java | 16 + .../webflux/annotation/EmployeeWebClient.java | 32 ++ .../annotation/EmployeeWebSecurityConfig.java | 45 +++ .../functional/EmployeeFunctionalConfig.java | 74 ++++ .../EmployeeSpringFunctionalApplication.java | 13 + .../ConsumerFooServiceIntegrationTest.java | 62 ++++ .../consumer/ConsumerFooServiceLiveTest.java | 53 +++ .../consumer/utils/ListAppender.java | 25 ++ .../introduction/ReactorIntegrationTest.java | 123 +++++++ .../security/SecurityIntegrationTest.java | 37 ++ .../reactive/webclient/SpringContextTest.java | 12 + .../webclient/WebClientIntegrationTest.java | 325 ++++++++++++++++++ .../WebControllerIntegrationTest.java | 51 +++ .../WebTestClientIntegrationTest.java | 98 ++++++ .../WebClientRequestsUnitTest.java | 176 ++++++++++ .../EmployeeControllerIntegrationTest.java | 74 ++++ ...ployeeSpringFunctionalIntegrationTest.java | 92 +++++ .../src/test/resources/logback-test.xml | 12 + 66 files changed, 2984 insertions(+) create mode 100644 spring-reactive/README.md create mode 100644 spring-reactive/pom.xml create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Application.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Controller.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Person.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/concurrency/PersonRepository.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/ConsumerDebuggingApplication.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/chronjobs/ChronJobs.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/controllers/ReactiveConfigsToggleRestController.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/model/Foo.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/model/FooDto.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooNameHelper.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooQuantityHelper.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooReporter.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooService.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/ServerDebuggingApplication.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/handlers/ServerHandler.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/model/Foo.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/routers/ServerRouter.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/ErrorHandlingApplication.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/security/GreetController.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/security/GreetService.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/security/SecurityConfig.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/security/SpringSecurity5Application.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webclient/Foo.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webclient/Tweet.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webclient/TweetsSlowServiceController.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebClientApplication.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebClientController.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebController.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webclientrequests/SpringWebClientRequestsApp.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeController.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeSpringApplication.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeWebClient.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeWebSecurityConfig.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webflux/functional/EmployeeFunctionalConfig.java create mode 100644 spring-reactive/src/main/java/com/baeldung/reactive/webflux/functional/EmployeeSpringFunctionalApplication.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/ConsumerFooServiceIntegrationTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/ConsumerFooServiceLiveTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/utils/ListAppender.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/introduction/ReactorIntegrationTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/security/SecurityIntegrationTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/webclient/SpringContextTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebClientIntegrationTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebControllerIntegrationTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebTestClientIntegrationTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/webclientrequests/WebClientRequestsUnitTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/webflux/annotation/EmployeeControllerIntegrationTest.java create mode 100644 spring-reactive/src/test/java/com/baeldung/reactive/webflux/functional/EmployeeSpringFunctionalIntegrationTest.java create mode 100644 spring-reactive/src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index ad002650d7..3770c05b49 100644 --- a/pom.xml +++ b/pom.xml @@ -619,6 +619,7 @@ spring-5-reactive-security spring-5-webflux spring-5-webflux-2 + spring-reactive spring-activiti spring-akka @@ -1089,6 +1090,7 @@ spring-5-reactive-oauth spring-5-reactive-security spring-5-webflux + spring-reactive spring-activiti spring-akka diff --git a/spring-reactive/README.md b/spring-reactive/README.md new file mode 100644 index 0000000000..6e887fe2f8 --- /dev/null +++ b/spring-reactive/README.md @@ -0,0 +1,17 @@ +## Spring Reactive + +This module contains articles about Spring Reactor. + +## Relevant articles: + +- [Introduction to Project Reactor Bus](https://www.baeldung.com/reactor-bus) +- [Intro To Reactor Core](https://www.baeldung.com/reactor-core) +- [Debugging Reactive Streams in Java](https://www.baeldung.com/spring-debugging-reactive-streams) +- [Guide to Spring 5 WebFlux](https://www.baeldung.com/spring-webflux) +- [Introduction to the Functional Web Framework in Spring 5](https://www.baeldung.com/spring-5-functional-web) +- [Spring 5 WebClient](https://www.baeldung.com/spring-5-webclient) +- [Spring WebClient vs. RestTemplate](https://www.baeldung.com/spring-webclient-resttemplate) +- [https://www.baeldung.com/webflux-webclient-parameters](https://www.baeldung.com/webflux-webclient-parameters) +- [Handling Errors in Spring WebFlux](https://www.baeldung.com/spring-webflux-errors) +- [Spring Security 5 for Reactive Applications](https://www.baeldung.com/spring-security-5-reactive) +- [https://www.baeldung.com/spring-webflux-concurrency](https://www.baeldung.com/spring-webflux-concurrency) \ No newline at end of file diff --git a/spring-reactive/pom.xml b/spring-reactive/pom.xml new file mode 100644 index 0000000000..a4d375ea15 --- /dev/null +++ b/spring-reactive/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + spring-reactive + + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-security + + + io.reactivex.rxjava2 + rxjava + ${rxjava.version} + + + io.projectreactor.kafka + reactor-kafka + ${reactor-kafka.version} + + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + + + org.springframework.security + spring-security-test + test + + + io.projectreactor + reactor-test + ${reactor.version} + test + + + org.projectlombok + lombok + + + + + 3.4.12 + 1.2.2.RELEASE + 2.2.19 + + + \ No newline at end of file diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Application.java b/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Application.java new file mode 100644 index 0000000000..f34d930ef0 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Application.java @@ -0,0 +1,17 @@ +package com.baeldung.reactive.concurrency; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** +* Please note we assume Mongo and Kafka are running in the local machine and on default configuration. +* Additionally, if you want to experiment with Tomcat/Jetty instead of Netty, just uncomment the lines in pom.xml and rebuild. +*/ +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Controller.java b/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Controller.java new file mode 100644 index 0000000000..70928d4dca --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Controller.java @@ -0,0 +1,127 @@ +package com.baeldung.reactive.concurrency; + +import io.reactivex.Observable; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.IntegerDeserializer; +import org.apache.kafka.common.serialization.IntegerSerializer; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; +import reactor.kafka.receiver.KafkaReceiver; +import reactor.kafka.receiver.ReceiverOptions; +import reactor.kafka.receiver.ReceiverRecord; +import reactor.kafka.sender.KafkaSender; +import reactor.kafka.sender.SenderOptions; +import reactor.kafka.sender.SenderRecord; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/") +public class Controller { + + @Autowired + private PersonRepository personRepository; + + private Scheduler scheduler = Schedulers.newBoundedElastic(5, 10, "MyThreadGroup"); + + private Logger logger = LoggerFactory.getLogger(Controller.class); + + @GetMapping("/threads/webflux") + public Flux getThreadsWebflux() { + return Flux.fromIterable(getThreads()); + } + + @GetMapping("/threads/webclient") + public Flux getThreadsWebClient() { + WebClient.create("http://localhost:8080/index") + .get() + .retrieve() + .bodyToMono(String.class) + .subscribeOn(scheduler) + .publishOn(scheduler) + .doOnNext(s -> logger.info("Response: {}", s)) + .subscribe(); + return Flux.fromIterable(getThreads()); + } + + @GetMapping("/threads/rxjava") + public Observable getIndexRxJava() { + Observable.fromIterable(Arrays.asList("Hello", "World")) + .map(s -> s.toUpperCase()) + .observeOn(io.reactivex.schedulers.Schedulers.trampoline()) + .doOnNext(s -> logger.info("String: {}", s)) + .subscribe(); + return Observable.fromIterable(getThreads()); + } + + @GetMapping("/threads/mongodb") + public Flux getIndexMongo() { + personRepository.findAll() + .doOnNext(p -> logger.info("Person: {}", p)) + .subscribe(); + return Flux.fromIterable(getThreads()); + } + + @GetMapping("/threads/reactor-kafka") + public Flux getIndexKafka() { + Map producerProps = new HashMap<>(); + producerProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + producerProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class); + producerProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + SenderOptions senderOptions = SenderOptions.create(producerProps); + KafkaSender sender = KafkaSender.create(senderOptions); + Flux> outboundFlux = Flux.range(1, 10) + .map(i -> SenderRecord.create(new ProducerRecord<>("reactive-test", i, "Message_" + i), i)); + sender.send(outboundFlux) + .subscribe(); + + Map consumerProps = new HashMap<>(); + consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "my-consumer"); + consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); + consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, IntegerDeserializer.class); + consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + ReceiverOptions receiverOptions = ReceiverOptions.create(consumerProps); + receiverOptions.subscription(Collections.singleton("reactive-test")); + KafkaReceiver receiver = KafkaReceiver.create(receiverOptions); + Flux> inboundFlux = receiver.receive(); + inboundFlux.subscribe(r -> { + logger.info("Received message: {}", r.value()); + r.receiverOffset() + .acknowledge(); + }); + return Flux.fromIterable(getThreads()); + } + + @GetMapping("/index") + public Mono getIndex() { + return Mono.just("Hello world!"); + } + + private List getThreads() { + return Thread.getAllStackTraces() + .keySet() + .stream() + .map(t -> String.format("%-20s \t %s \t %d \t %s\n", t.getName(), t.getState(), t.getPriority(), t.isDaemon() ? "Daemon" : "Normal")) + .collect(Collectors.toList()); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Person.java b/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Person.java new file mode 100644 index 0000000000..10029330af --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/Person.java @@ -0,0 +1,27 @@ +package com.baeldung.reactive.concurrency; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document +public class Person { + @Id + String id; + + public Person(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String toString() { + return "Person{" + "id='" + id + '\'' + '}'; + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/PersonRepository.java b/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/PersonRepository.java new file mode 100644 index 0000000000..221ea3d74d --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/concurrency/PersonRepository.java @@ -0,0 +1,6 @@ +package com.baeldung.reactive.concurrency; + +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; + +public interface PersonRepository extends ReactiveMongoRepository { +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/ConsumerDebuggingApplication.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/ConsumerDebuggingApplication.java new file mode 100644 index 0000000000..fa10383c95 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/ConsumerDebuggingApplication.java @@ -0,0 +1,33 @@ +package com.baeldung.reactive.debugging.consumer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; +import reactor.core.publisher.Hooks; + +import java.util.Collections; + +@SpringBootApplication(exclude = MongoReactiveAutoConfiguration.class) +@EnableScheduling +public class ConsumerDebuggingApplication { + + public static void main(String[] args) { + Hooks.onOperatorDebug(); + SpringApplication app = new SpringApplication(ConsumerDebuggingApplication.class); + app.setDefaultProperties(Collections.singletonMap("server.port", "8082")); + app.run(args); + } + + @Bean + public SecurityWebFilterChain debuggingConsumerSpringSecurityFilterChain(ServerHttpSecurity http) { + http.authorizeExchange() + .anyExchange() + .permitAll(); + http.csrf().disable(); + return http.build(); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/chronjobs/ChronJobs.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/chronjobs/ChronJobs.java new file mode 100644 index 0000000000..b58648ff9d --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/chronjobs/ChronJobs.java @@ -0,0 +1,152 @@ +package com.baeldung.reactive.debugging.consumer.chronjobs; + +import com.baeldung.reactive.debugging.consumer.model.Foo; +import com.baeldung.reactive.debugging.consumer.model.FooDto; +import com.baeldung.reactive.debugging.consumer.service.FooService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; + +import java.time.Duration; +import java.util.concurrent.ThreadLocalRandom; + +@Component +public class ChronJobs { + + private static Logger logger = LoggerFactory.getLogger(ChronJobs.class); + private WebClient client = WebClient.create("http://localhost:8081"); + + @Autowired + private FooService service; + + @Scheduled(fixedRate = 10000) + public void consumeInfiniteFlux() { + Flux fluxFoo = client.get() + .uri("/functional-reactive/periodic-foo") + .accept(MediaType.TEXT_EVENT_STREAM) + .retrieve() + .bodyToFlux(FooDto.class) + .delayElements(Duration.ofMillis(100)) + .map(dto -> { + logger.debug("process 1 with dto id {} name{}", dto.getId(), dto.getName()); + return new Foo(dto); + }); + Integer random = ThreadLocalRandom.current() + .nextInt(0, 3); + switch (random) { + case 0: + logger.info("process 1 with approach 1"); + service.processFoo(fluxFoo); + break; + case 1: + logger.info("process 1 with approach 1 EH"); + service.processUsingApproachOneWithErrorHandling(fluxFoo); + break; + default: + logger.info("process 1 with approach 2"); + service.processFooInAnotherScenario(fluxFoo); + break; + + } + } + + @Scheduled(fixedRate = 20000) + public void consumeFiniteFlux2() { + Flux fluxFoo = client.get() + .uri("/functional-reactive/periodic-foo-2") + .accept(MediaType.TEXT_EVENT_STREAM) + .retrieve() + .bodyToFlux(FooDto.class) + .delayElements(Duration.ofMillis(100)) + .map(dto -> { + logger.debug("process 2 with dto id {} name{}", dto.getId(), dto.getName()); + return new Foo(dto); + }); + Integer random = ThreadLocalRandom.current() + .nextInt(0, 3); + switch (random) { + case 0: + logger.info("process 2 with approach 1"); + service.processFoo(fluxFoo); + break; + case 1: + logger.info("process 2 with approach 1 EH"); + service.processUsingApproachOneWithErrorHandling(fluxFoo); + break; + default: + logger.info("process 2 with approach 2"); + service.processFooInAnotherScenario(fluxFoo); + break; + + } + } + + @Scheduled(fixedRate = 20000) + public void consumeFiniteFlux3() { + Flux fluxFoo = client.get() + .uri("/functional-reactive/periodic-foo-2") + .accept(MediaType.TEXT_EVENT_STREAM) + .retrieve() + .bodyToFlux(FooDto.class) + .delayElements(Duration.ofMillis(100)) + .map(dto -> { + logger.debug("process 3 with dto id {} name{}", dto.getId(), dto.getName()); + return new Foo(dto); + }); + logger.info("process 3 with approach 3"); + service.processUsingApproachThree(fluxFoo); + } + + @Scheduled(fixedRate = 20000) + public void consumeFiniteFluxWithCheckpoint4() { + Flux fluxFoo = client.get() + .uri("/functional-reactive/periodic-foo-2") + .accept(MediaType.TEXT_EVENT_STREAM) + .retrieve() + .bodyToFlux(FooDto.class) + .delayElements(Duration.ofMillis(100)) + .map(dto -> { + logger.debug("process 4 with dto id {} name{}", dto.getId(), dto.getName()); + return new Foo(dto); + }); + logger.info("process 4 with approach 4"); + service.processUsingApproachFourWithCheckpoint(fluxFoo); + } + + @Scheduled(fixedRate = 20000) + public void consumeFiniteFluxWitParallelScheduler() { + Flux fluxFoo = client.get() + .uri("/functional-reactive/periodic-foo-2") + .accept(MediaType.TEXT_EVENT_STREAM) + .retrieve() + .bodyToFlux(FooDto.class) + .delayElements(Duration.ofMillis(100)) + .map(dto -> { + logger.debug("process 5-parallel with dto id {} name{}", dto.getId(), dto.getName()); + return new Foo(dto); + }); + logger.info("process 5-parallel with approach 5-parallel"); + service.processUsingApproachFivePublishingToDifferentParallelThreads(fluxFoo); + } + + @Scheduled(fixedRate = 20000) + public void consumeFiniteFluxWithSingleSchedulers() { + Flux fluxFoo = client.get() + .uri("/functional-reactive/periodic-foo-2") + .accept(MediaType.TEXT_EVENT_STREAM) + .retrieve() + .bodyToFlux(FooDto.class) + .delayElements(Duration.ofMillis(100)) + .map(dto -> { + logger.debug("process 5-single with dto id {} name{}", dto.getId(), dto.getName()); + return new Foo(dto); + }); + logger.info("process 5-single with approach 5-single"); + service.processUsingApproachFivePublishingToDifferentSingleThreads(fluxFoo); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/controllers/ReactiveConfigsToggleRestController.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/controllers/ReactiveConfigsToggleRestController.java new file mode 100644 index 0000000000..df13113a82 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/controllers/ReactiveConfigsToggleRestController.java @@ -0,0 +1,22 @@ +package com.baeldung.reactive.debugging.consumer.controllers; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Hooks; + +@RestController +public class ReactiveConfigsToggleRestController { + + @GetMapping("/debug-hook-on") + public String setReactiveDebugOn() { + Hooks.onOperatorDebug(); + return "DEBUG HOOK ON"; + } + + @GetMapping("/debug-hook-off") + public String setReactiveDebugOff() { + Hooks.resetOnOperatorDebug(); + return "DEBUG HOOK OFF"; + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/model/Foo.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/model/Foo.java new file mode 100644 index 0000000000..d20e2c9ba0 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/model/Foo.java @@ -0,0 +1,27 @@ +package com.baeldung.reactive.debugging.consumer.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.concurrent.ThreadLocalRandom; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class Foo { + + private Integer id; + private String formattedName; + private Integer quantity; + + public Foo(FooDto dto) { + this.id = (ThreadLocalRandom.current() + .nextInt(0, 100) == 0) ? null : dto.getId(); + this.formattedName = dto.getName(); + this.quantity = ThreadLocalRandom.current() + .nextInt(0, 10); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/model/FooDto.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/model/FooDto.java new file mode 100644 index 0000000000..bf6f614e18 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/model/FooDto.java @@ -0,0 +1,16 @@ +package com.baeldung.reactive.debugging.consumer.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class FooDto { + + private Integer id; + private String name; +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooNameHelper.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooNameHelper.java new file mode 100644 index 0000000000..cdd9ca31a6 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooNameHelper.java @@ -0,0 +1,44 @@ +package com.baeldung.reactive.debugging.consumer.service; + +import com.baeldung.reactive.debugging.consumer.model.Foo; +import reactor.core.publisher.Flux; + +import java.util.concurrent.ThreadLocalRandom; + +public class FooNameHelper { + + public static Flux concatAndSubstringFooName(Flux flux) { + flux = concatFooName(flux); + flux = substringFooName(flux); + return flux; + } + + public static Flux concatFooName(Flux flux) { + flux = flux.map(foo -> { + String processedName = null; + Integer random = ThreadLocalRandom.current() + .nextInt(0, 80); + processedName = (random != 0) ? foo.getFormattedName() : foo.getFormattedName() + "-bael"; + foo.setFormattedName(processedName); + return foo; + }); + return flux; + } + + public static Flux substringFooName(Flux flux) { + return flux.map(foo -> { + String processedName; + Integer random = ThreadLocalRandom.current() + .nextInt(0, 100); + + processedName = (random == 0) ? foo.getFormattedName() + .substring(10, 15) + : foo.getFormattedName() + .substring(0, 5); + + foo.setFormattedName(processedName); + return foo; + }); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooQuantityHelper.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooQuantityHelper.java new file mode 100644 index 0000000000..f4600b41b9 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooQuantityHelper.java @@ -0,0 +1,30 @@ +package com.baeldung.reactive.debugging.consumer.service; + +import com.baeldung.reactive.debugging.consumer.model.Foo; +import reactor.core.publisher.Flux; + +import java.util.concurrent.ThreadLocalRandom; + +public class FooQuantityHelper { + + public static Flux processFooReducingQuantity(Flux flux) { + flux = flux.map(foo -> { + Integer result; + Integer random = ThreadLocalRandom.current() + .nextInt(0, 90); + result = (random == 0) ? result = 0 : foo.getQuantity() + 2; + foo.setQuantity(result); + return foo; + }); + return divideFooQuantity(flux); + } + + public static Flux divideFooQuantity(Flux flux) { + return flux.map(foo -> { + Integer result = Math.round(5 / foo.getQuantity()); + foo.setQuantity(result); + return foo; + }); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooReporter.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooReporter.java new file mode 100644 index 0000000000..1a8f9bc783 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooReporter.java @@ -0,0 +1,24 @@ +package com.baeldung.reactive.debugging.consumer.service; + +import com.baeldung.reactive.debugging.consumer.model.Foo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Flux; + +public class FooReporter { + + private static Logger logger = LoggerFactory.getLogger(FooReporter.class); + + public static Flux reportResult(Flux input, String approach) { + return input.map(foo -> { + if (foo.getId() == null) + throw new IllegalArgumentException("Null id is not valid!"); + logger.info("Reporting for approach {}: Foo with id '{}' name '{}' and quantity '{}'", approach, foo.getId(), foo.getFormattedName(), foo.getQuantity()); + return foo; + }); + } + + public static Flux reportResult(Flux input) { + return reportResult(input, "default"); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooService.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooService.java new file mode 100644 index 0000000000..bafaa3cfa0 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/consumer/service/FooService.java @@ -0,0 +1,118 @@ +package com.baeldung.reactive.debugging.consumer.service; + +import com.baeldung.reactive.debugging.consumer.model.Foo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.scheduler.Schedulers; + +import static com.baeldung.reactive.debugging.consumer.service.FooNameHelper.concatAndSubstringFooName; +import static com.baeldung.reactive.debugging.consumer.service.FooNameHelper.substringFooName; +import static com.baeldung.reactive.debugging.consumer.service.FooQuantityHelper.divideFooQuantity; +import static com.baeldung.reactive.debugging.consumer.service.FooQuantityHelper.processFooReducingQuantity; +import static com.baeldung.reactive.debugging.consumer.service.FooReporter.reportResult; + +@Component +public class FooService { + + private static Logger logger = LoggerFactory.getLogger(FooService.class); + + public void processFoo(Flux flux) { + flux = FooNameHelper.concatFooName(flux); + flux = FooNameHelper.substringFooName(flux); + flux = flux.log(); + flux = FooReporter.reportResult(flux); + flux = flux.doOnError(error -> { + logger.error("The following error happened on processFoo method!", error); + }); + flux.subscribe(); + } + + public void processFooInAnotherScenario(Flux flux) { + flux = FooNameHelper.substringFooName(flux); + flux = FooQuantityHelper.divideFooQuantity(flux); + flux.subscribe(); + } + + public void processUsingApproachOneWithErrorHandling(Flux flux) { + logger.info("starting approach one w error handling!"); + flux = concatAndSubstringFooName(flux); + flux = concatAndSubstringFooName(flux); + flux = substringFooName(flux); + flux = processFooReducingQuantity(flux); + flux = processFooReducingQuantity(flux); + flux = processFooReducingQuantity(flux); + flux = reportResult(flux, "ONE w/ EH"); + flux = flux.doOnError(error -> { + logger.error("Approach 1 with Error Handling failed!", error); + }); + flux.subscribe(); + } + + public void processUsingApproachThree(Flux flux) { + logger.info("starting approach three!"); + flux = concatAndSubstringFooName(flux); + flux = reportResult(flux, "THREE"); + flux = flux.doOnError(error -> { + logger.error("Approach 3 failed!", error); + }); + flux.subscribe(); + } + + public void processUsingApproachFourWithCheckpoint(Flux flux) { + logger.info("starting approach four!"); + flux = concatAndSubstringFooName(flux); + flux = flux.checkpoint("CHECKPOINT 1"); + flux = concatAndSubstringFooName(flux); + flux = divideFooQuantity(flux); + flux = flux.checkpoint("CHECKPOINT 2", true); + flux = reportResult(flux, "FOUR"); + flux = concatAndSubstringFooName(flux).doOnError(error -> { + logger.error("Approach 4 failed!", error); + }); + flux.subscribe(); + } + + public void processUsingApproachFourWithInitialCheckpoint(Flux flux) { + logger.info("starting approach four!"); + flux = concatAndSubstringFooName(flux); + flux = flux.checkpoint("CHECKPOINT 1", true); + flux = concatAndSubstringFooName(flux); + flux = divideFooQuantity(flux); + flux = reportResult(flux, "FOUR"); + flux = flux.doOnError(error -> { + logger.error("Approach 4-2 failed!", error); + }); + flux.subscribe(); + } + + public void processUsingApproachFivePublishingToDifferentParallelThreads(Flux flux) { + logger.info("starting approach five-parallel!"); + flux = concatAndSubstringFooName(flux).publishOn(Schedulers.newParallel("five-parallel-foo")) + .log(); + flux = concatAndSubstringFooName(flux); + flux = divideFooQuantity(flux); + flux = reportResult(flux, "FIVE-PARALLEL").publishOn(Schedulers.newSingle("five-parallel-bar")); + flux = concatAndSubstringFooName(flux).doOnError(error -> { + logger.error("Approach 5-parallel failed!", error); + }); + flux.subscribeOn(Schedulers.newParallel("five-parallel-starter")) + .subscribe(); + } + + public void processUsingApproachFivePublishingToDifferentSingleThreads(Flux flux) { + logger.info("starting approach five-single!"); + flux = flux.log() + .subscribeOn(Schedulers.newSingle("five-single-starter")); + flux = concatAndSubstringFooName(flux).publishOn(Schedulers.newSingle("five-single-foo")); + flux = concatAndSubstringFooName(flux); + flux = divideFooQuantity(flux); + flux = reportResult(flux, "FIVE-SINGLE").publishOn(Schedulers.newSingle("five-single-bar")); + flux = concatAndSubstringFooName(flux).doOnError(error -> { + logger.error("Approach 5-single failed!", error); + }); + flux.subscribe(); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/ServerDebuggingApplication.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/ServerDebuggingApplication.java new file mode 100644 index 0000000000..c9838705b5 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/ServerDebuggingApplication.java @@ -0,0 +1,29 @@ +package com.baeldung.reactive.debugging.server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.web.reactive.config.EnableWebFlux; + +import java.util.Collections; + +@EnableWebFlux +@SpringBootApplication +public class ServerDebuggingApplication { + + public static void main(String[] args) { + SpringApplication app = new SpringApplication(ServerDebuggingApplication.class); + app.setDefaultProperties(Collections.singletonMap("server.port", "8081")); + app.run(args); + } + + @Bean + public SecurityWebFilterChain debuggingServerSpringSecurityFilterChain(ServerHttpSecurity http) { + http.authorizeExchange() + .anyExchange() + .permitAll(); + return http.build(); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/handlers/ServerHandler.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/handlers/ServerHandler.java new file mode 100644 index 0000000000..15f9a4b786 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/handlers/ServerHandler.java @@ -0,0 +1,45 @@ +package com.baeldung.reactive.debugging.server.handlers; + +import com.baeldung.reactive.debugging.server.model.Foo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.Duration; +import java.util.concurrent.ThreadLocalRandom; + +@Component +public class ServerHandler { + + private static Logger logger = LoggerFactory.getLogger(ServerHandler.class); + + public Mono useHandler(final ServerRequest request) { + // there are chances that something goes wrong here... + return ServerResponse.ok() + .contentType(MediaType.TEXT_EVENT_STREAM) + .body(Flux.interval(Duration.ofSeconds(1)) + .map(sequence -> { + logger.info("retrieving Foo. Sequence: {}", sequence); + if (ThreadLocalRandom.current() + .nextInt(0, 50) == 1) { + throw new RuntimeException("There was an error retrieving the Foo!"); + } + return new Foo(sequence, "name" + sequence); + + }), Foo.class); + } + + public Mono useHandlerFinite(final ServerRequest request) { + return ServerResponse.ok() + .contentType(MediaType.TEXT_EVENT_STREAM) + .body(Flux.range(0, 50) + .map(sequence -> { + return new Foo(new Long(sequence), "theFooNameNumber" + sequence); + }), Foo.class); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/model/Foo.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/model/Foo.java new file mode 100644 index 0000000000..2c419a23f8 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/model/Foo.java @@ -0,0 +1,13 @@ +package com.baeldung.reactive.debugging.server.model; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Foo { + + private Long id; + private String name; + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/routers/ServerRouter.java b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/routers/ServerRouter.java new file mode 100644 index 0000000000..5db2ab92b6 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/debugging/server/routers/ServerRouter.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.debugging.server.routers; + +import com.baeldung.reactive.debugging.server.handlers.ServerHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Configuration +public class ServerRouter { + + @Bean + public RouterFunction responseRoute(@Autowired ServerHandler handler) { + return RouterFunctions.route(RequestPredicates.GET("/functional-reactive/periodic-foo"), handler::useHandler) + .andRoute(RequestPredicates.GET("/functional-reactive/periodic-foo-2"), handler::useHandlerFinite); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/ErrorHandlingApplication.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/ErrorHandlingApplication.java new file mode 100644 index 0000000000..a22f04a8d2 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/ErrorHandlingApplication.java @@ -0,0 +1,24 @@ +package com.baeldung.reactive.errorhandling; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; + +@SpringBootApplication +public class ErrorHandlingApplication { + + public static void main(String[] args) { + SpringApplication.run(ErrorHandlingApplication.class, args); + } + + @Bean + public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { + http.authorizeExchange() + .anyExchange() + .permitAll(); + http.csrf().disable(); + return http.build(); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java new file mode 100644 index 0000000000..0a96a8593c --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java @@ -0,0 +1,52 @@ +package com.baeldung.reactive.errorhandling; + +import org.springframework.boot.web.error.ErrorAttributeOptions; +import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; + +import java.util.Map; + +@Component +public class GlobalErrorAttributes extends DefaultErrorAttributes{ + + private HttpStatus status = HttpStatus.BAD_REQUEST; + private String message = "please provide a name"; + + @Override + public Map getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) { + Map map = super.getErrorAttributes(request, options); + map.put("status", getStatus()); + map.put("message", getMessage()); + return map; + } + + /** + * @return the status + */ + public HttpStatus getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(HttpStatus status) { + this.status = status; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message the message to set + */ + public void setMessage(String message) { + this.message = message; + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java new file mode 100644 index 0000000000..24583308cd --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java @@ -0,0 +1,48 @@ +package com.baeldung.reactive.errorhandling; + +import org.springframework.boot.autoconfigure.web.WebProperties; +import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler; +import org.springframework.boot.web.error.ErrorAttributeOptions; +import org.springframework.boot.web.reactive.error.ErrorAttributes; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.codec.ServerCodecConfigurer; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +import java.util.Map; + +@Component +@Order(-2) +public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler { + + public GlobalErrorWebExceptionHandler(GlobalErrorAttributes g, ApplicationContext applicationContext, + ServerCodecConfigurer serverCodecConfigurer) { + super(g, new WebProperties.Resources(), applicationContext); + super.setMessageWriters(serverCodecConfigurer.getWriters()); + super.setMessageReaders(serverCodecConfigurer.getReaders()); + } + + @Override + protected RouterFunction getRoutingFunction(final ErrorAttributes errorAttributes) { + return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse); + } + + private Mono renderErrorResponse(final ServerRequest request) { + + final Map errorPropertiesMap = getErrorAttributes(request, ErrorAttributeOptions.defaults()); + + return ServerResponse.status(HttpStatus.BAD_REQUEST) + .contentType(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue(errorPropertiesMap)); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java new file mode 100644 index 0000000000..bdc7771b80 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java @@ -0,0 +1,11 @@ +package com.baeldung.reactive.errorhandling; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +public class NameRequiredException extends ResponseStatusException { + + public NameRequiredException(HttpStatus status, String message, Throwable e) { + super(status, message, e); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java new file mode 100644 index 0000000000..32f2f1c3a2 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java @@ -0,0 +1,28 @@ +package com.baeldung.reactive.errorhandling.handlers; + +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler1 { + + public Mono handleRequest1(ServerRequest request) { + return sayHello(request).onErrorReturn("Hello, Stranger") + .flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .bodyValue(s)); + } + + private Mono sayHello(ServerRequest request) { + try { + return Mono.just("Hello, " + request.queryParam("name") + .get()); + } catch (Exception e) { + return Mono.error(e); + } + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java new file mode 100644 index 0000000000..093120c92b --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java @@ -0,0 +1,37 @@ +package com.baeldung.reactive.errorhandling.handlers; + +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler2 { + +public Mono handleRequest2(ServerRequest request) { + return + sayHello(request) + .flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .bodyValue(s)) + .onErrorResume(e -> sayHelloFallback() + .flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .bodyValue(s))); +} + + private Mono sayHello(ServerRequest request) { + try { + return Mono.just("Hello, " + request.queryParam("name") + .get()); + } catch (Exception e) { + return Mono.error(e); + } + } + + private Mono sayHelloFallback() { + return Mono.just("Hello, Stranger"); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java new file mode 100644 index 0000000000..44842e0539 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java @@ -0,0 +1,33 @@ +package com.baeldung.reactive.errorhandling.handlers; + +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler3 { + + public Mono handleRequest3(ServerRequest request) { + return + sayHello(request) + .flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .bodyValue(s)) + .onErrorResume(e -> (Mono.just("Hi, I looked around for your name but found: " + + e.getMessage())).flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .bodyValue(s))); + } + + private Mono sayHello(ServerRequest request) { + try { + return Mono.just("Hello, " + request.queryParam("name") + .get()); + } catch (Exception e) { + return Mono.error(e); + } + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java new file mode 100644 index 0000000000..2d391a42a7 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java @@ -0,0 +1,29 @@ +package com.baeldung.reactive.errorhandling.handlers; + +import com.baeldung.reactive.errorhandling.NameRequiredException; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler4 { + +public Mono handleRequest4(ServerRequest request) { + return ServerResponse.ok() + .body(sayHello(request) + .onErrorResume(e -> + Mono.error(new NameRequiredException( + HttpStatus.BAD_REQUEST, "please provide a name", e))), String.class); +} + + private Mono sayHello(ServerRequest request) { + try { + return Mono.just("Hello, " + request.queryParam("name").get()); + } catch (Exception e) { + return Mono.error(e); + } + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java new file mode 100644 index 0000000000..a466982865 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.errorhandling.handlers; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler5 { + + public Mono handleRequest5(ServerRequest request) { + return ServerResponse.ok() + .body(sayHello(request), String.class); + + } + + private Mono sayHello(ServerRequest request) { + return Mono.just("Hello, " + request.queryParam("name").get()); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java new file mode 100644 index 0000000000..caf779b456 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler1; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router1 { + + @Bean + public RouterFunction routeRequest1(Handler1 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint1") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest1); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java new file mode 100644 index 0000000000..b965257c30 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler2; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router2 { + + @Bean + public RouterFunction routeRequest2(Handler2 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint2") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest2); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java new file mode 100644 index 0000000000..b8f7f983cc --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler3; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router3 { + + @Bean + public RouterFunction routeRequest3(Handler3 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint3") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest3); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java new file mode 100644 index 0000000000..03c65fec67 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler4; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router4 { + + @Bean + public RouterFunction routeRequest4(Handler4 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint4") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest4); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java new file mode 100644 index 0000000000..c68e04659f --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler5; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router5 { + + @Bean + public RouterFunction routeRequest5(Handler5 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint5") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest5); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/security/GreetController.java b/spring-reactive/src/main/java/com/baeldung/reactive/security/GreetController.java new file mode 100644 index 0000000000..99b79d88ea --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/security/GreetController.java @@ -0,0 +1,37 @@ +package com.baeldung.reactive.security; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +import java.security.Principal; + +@RestController +public class GreetController { + + private GreetService greetService; + + public GreetController(GreetService greetService) { + this.greetService = greetService; + } + + @GetMapping("/") + public Mono greet(Mono principal) { + return principal + .map(Principal::getName) + .map(name -> String.format("Hello, %s", name)); + } + + @GetMapping("/admin") + public Mono greetAdmin(Mono principal) { + return principal + .map(Principal::getName) + .map(name -> String.format("Admin access: %s", name)); + } + + @GetMapping("/greetService") + public Mono greetService() { + return greetService.greet(); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/security/GreetService.java b/spring-reactive/src/main/java/com/baeldung/reactive/security/GreetService.java new file mode 100644 index 0000000000..93df64bced --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/security/GreetService.java @@ -0,0 +1,15 @@ +package com.baeldung.reactive.security; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Service +public class GreetService { + + @PreAuthorize("hasRole('ADMIN')") + public Mono greet() { + return Mono.just("Hello from service!"); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/security/SecurityConfig.java b/spring-reactive/src/main/java/com/baeldung/reactive/security/SecurityConfig.java new file mode 100644 index 0000000000..bb2f2d50e1 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/security/SecurityConfig.java @@ -0,0 +1,55 @@ +package com.baeldung.reactive.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.server.SecurityWebFilterChain; + +@EnableWebFluxSecurity +@EnableReactiveMethodSecurity +public class SecurityConfig { + + @Bean + public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) { + return http.authorizeExchange() + .pathMatchers("/admin") + .hasAuthority("ROLE_ADMIN") + .anyExchange() + .authenticated() + .and() + .formLogin() + .and() + .csrf() + .disable() + .build(); + } + + @Bean + public MapReactiveUserDetailsService userDetailsService() { + UserDetails user = User + .withUsername("user") + .password(passwordEncoder().encode("password")) + .roles("USER") + .build(); + + UserDetails admin = User + .withUsername("admin") + .password(passwordEncoder().encode("password")) + .roles("ADMIN") + .build(); + + return new MapReactiveUserDetailsService(user, admin); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/security/SpringSecurity5Application.java b/spring-reactive/src/main/java/com/baeldung/reactive/security/SpringSecurity5Application.java new file mode 100644 index 0000000000..bc0895a38b --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/security/SpringSecurity5Application.java @@ -0,0 +1,34 @@ +package com.baeldung.reactive.security; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; +import org.springframework.web.reactive.config.EnableWebFlux; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import reactor.netty.DisposableServer; +import reactor.netty.http.server.HttpServer; + +@ComponentScan(basePackages = {"com.baeldung.reactive.security"}) +@EnableWebFlux +public class SpringSecurity5Application { + + public static void main(String[] args) { + try (AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(SpringSecurity5Application.class)) { + context.getBean(DisposableServer.class).onDispose().block(); + } + } + + @Bean + public DisposableServer disposableServer(ApplicationContext context) { + HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context) + .build(); + ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler); + HttpServer httpServer = HttpServer.create().host("localhost").port(8083); + return httpServer.handle(adapter).bindNow(); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webclient/Foo.java b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/Foo.java new file mode 100644 index 0000000000..a58d672686 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/Foo.java @@ -0,0 +1,24 @@ +package com.baeldung.reactive.webclient; + +public class Foo { + + private String name; + + public Foo() { + super(); + } + + public Foo(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webclient/Tweet.java b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/Tweet.java new file mode 100644 index 0000000000..4ef1aecabb --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/Tweet.java @@ -0,0 +1,13 @@ +package com.baeldung.reactive.webclient; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Tweet { + private String text; + private String username; +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webclient/TweetsSlowServiceController.java b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/TweetsSlowServiceController.java new file mode 100644 index 0000000000..42ff603b87 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/TweetsSlowServiceController.java @@ -0,0 +1,20 @@ +package com.baeldung.reactive.webclient; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; + +@RestController +public class TweetsSlowServiceController { + + @GetMapping("/slow-service-tweets") + private List getAllTweets() throws Exception { + Thread.sleep(2000L); // delay + return Arrays.asList( + new Tweet("RestTemplate rules", "@user1"), + new Tweet("WebClient is better", "@user2"), + new Tweet("OK, both are useful", "@user1")); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebClientApplication.java b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebClientApplication.java new file mode 100644 index 0000000000..d2a5b6a024 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebClientApplication.java @@ -0,0 +1,14 @@ +package com.baeldung.reactive.webclient; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; + +@SpringBootApplication(exclude = { ReactiveSecurityAutoConfiguration.class }) +public class WebClientApplication { + + public static void main(String[] args) { + SpringApplication.run(WebClientApplication.class, args); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebClientController.java b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebClientController.java new file mode 100644 index 0000000000..0a83b1a1b7 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebClientController.java @@ -0,0 +1,41 @@ +package com.baeldung.reactive.webclient; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +import java.util.HashMap; +import java.util.Map; + +@RestController +public class WebClientController { + + @ResponseStatus(HttpStatus.OK) + @GetMapping("/resource") + public Map getResource() { + Map response = new HashMap<>(); + response.put("field", "value"); + return response; + } + + @PostMapping("/resource") + public Mono postStringResource(@RequestBody Mono bodyString) { + return bodyString.map(body -> "processed-" + body); + } + + @PostMapping("/resource-foo") + public Mono postFooResource(@RequestBody Mono bodyFoo) { + return bodyFoo.map(foo -> "processedFoo-" + foo.getName()); + } + + @PostMapping(value = "/resource-multipart", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public String handleFormUpload(@RequestPart("key1") String value1, @RequestPart("key2") String value2) { + return "processed-" + value1 + '-' + value2; + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebController.java b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebController.java new file mode 100644 index 0000000000..30b3ccc959 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webclient/WebController.java @@ -0,0 +1,60 @@ +package com.baeldung.reactive.webclient; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; + +import java.util.List; + +@Slf4j +@RestController +public class WebController { + + private static final int DEFAULT_PORT = 8080; + + @Setter + private int serverPort = DEFAULT_PORT; + + @GetMapping("/tweets-blocking") + public List getTweetsBlocking() { + log.info("Starting BLOCKING Controller!"); + final String uri = getSlowServiceUri(); + + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity> response = restTemplate.exchange( + uri, HttpMethod.GET, null, + new ParameterizedTypeReference>(){}); + + List result = response.getBody(); + result.forEach(tweet -> log.info(tweet.toString())); + log.info("Exiting BLOCKING Controller!"); + return result; + } + + @GetMapping(value = "/tweets-non-blocking", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux getTweetsNonBlocking() { + log.info("Starting NON-BLOCKING Controller!"); + Flux tweetFlux = WebClient.create() + .get() + .uri(getSlowServiceUri()) + .retrieve() + .bodyToFlux(Tweet.class); + + tweetFlux.subscribe(tweet -> log.info(tweet.toString())); + log.info("Exiting NON-BLOCKING Controller!"); + return tweetFlux; + } + + private String getSlowServiceUri() { + return "http://localhost:" + serverPort + "/slow-service-tweets"; + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webclientrequests/SpringWebClientRequestsApp.java b/spring-reactive/src/main/java/com/baeldung/reactive/webclientrequests/SpringWebClientRequestsApp.java new file mode 100644 index 0000000000..d43fe43a77 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webclientrequests/SpringWebClientRequestsApp.java @@ -0,0 +1,12 @@ +package com.baeldung.reactive.webclientrequests; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringWebClientRequestsApp { + + public static void main(String[] args) { + SpringApplication.run(SpringWebClientRequestsApp.class, args); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java new file mode 100644 index 0000000000..d41a4f2791 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java @@ -0,0 +1,17 @@ +package com.baeldung.reactive.webflux; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Employee { + + private String id; + private String name; + + // standard getters and setters + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java new file mode 100644 index 0000000000..8a57f5c510 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java @@ -0,0 +1,59 @@ +package com.baeldung.reactive.webflux; + +import com.baeldung.reactive.webflux.Employee; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.HashMap; +import java.util.Map; + +@Repository +public class EmployeeRepository { + + static Map employeeData; + + static Map employeeAccessData; + + static { + employeeData = new HashMap<>(); + employeeData.put("1", new Employee("1", "Employee 1")); + employeeData.put("2", new Employee("2", "Employee 2")); + employeeData.put("3", new Employee("3", "Employee 3")); + employeeData.put("4", new Employee("4", "Employee 4")); + employeeData.put("5", new Employee("5", "Employee 5")); + employeeData.put("6", new Employee("6", "Employee 6")); + employeeData.put("7", new Employee("7", "Employee 7")); + employeeData.put("8", new Employee("8", "Employee 8")); + employeeData.put("9", new Employee("9", "Employee 9")); + employeeData.put("10", new Employee("10", "Employee 10")); + + employeeAccessData = new HashMap<>(); + employeeAccessData.put("1", "Employee 1 Access Key"); + employeeAccessData.put("2", "Employee 2 Access Key"); + employeeAccessData.put("3", "Employee 3 Access Key"); + employeeAccessData.put("4", "Employee 4 Access Key"); + employeeAccessData.put("5", "Employee 5 Access Key"); + employeeAccessData.put("6", "Employee 6 Access Key"); + employeeAccessData.put("7", "Employee 7 Access Key"); + employeeAccessData.put("8", "Employee 8 Access Key"); + employeeAccessData.put("9", "Employee 9 Access Key"); + employeeAccessData.put("10", "Employee 10 Access Key"); + } + + public Mono findEmployeeById(String id) { + return Mono.just(employeeData.get(id)); + } + + public Flux findAllEmployees() { + return Flux.fromIterable(employeeData.values()); + } + + public Mono updateEmployee(Employee employee) { + Employee existingEmployee = employeeData.get(employee.getId()); + if (existingEmployee != null) { + existingEmployee.setName(employee.getName()); + } + return Mono.just(existingEmployee); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeController.java b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeController.java new file mode 100644 index 0000000000..23aacfdd95 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeController.java @@ -0,0 +1,39 @@ +package com.baeldung.reactive.webflux.annotation; + +import com.baeldung.reactive.webflux.Employee; +import com.baeldung.reactive.webflux.EmployeeRepository; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping("/employees") +public class EmployeeController { + + private final EmployeeRepository employeeRepository; + + public EmployeeController(EmployeeRepository employeeRepository) { + this.employeeRepository = employeeRepository; + } + + @GetMapping("/{id}") + private Mono getEmployeeById(@PathVariable String id) { + return employeeRepository.findEmployeeById(id); + } + + @GetMapping + private Flux getAllEmployees() { + return employeeRepository.findAllEmployees(); + } + + @PostMapping("/update") + private Mono updateEmployee(@RequestBody Employee employee) { + return employeeRepository.updateEmployee(employee); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeSpringApplication.java b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeSpringApplication.java new file mode 100644 index 0000000000..1a2ada96e9 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeSpringApplication.java @@ -0,0 +1,16 @@ +package com.baeldung.reactive.webflux.annotation; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class EmployeeSpringApplication { + + public static void main(String[] args) { + SpringApplication.run(EmployeeSpringApplication.class, args); + + EmployeeWebClient employeeWebClient = new EmployeeWebClient(); + employeeWebClient.consume(); + } + +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeWebClient.java b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeWebClient.java new file mode 100644 index 0000000000..611a261a1b --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeWebClient.java @@ -0,0 +1,32 @@ +package com.baeldung.reactive.webflux.annotation; + +import com.baeldung.reactive.webflux.Employee; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class EmployeeWebClient { + + private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeWebClient.class); + + WebClient client = WebClient.create("http://localhost:8080"); + + public void consume() { + + Mono employeeMono = client.get() + .uri("/employees/{id}", "1") + .retrieve() + .bodyToMono(Employee.class); + + employeeMono.subscribe(employee -> LOGGER.debug("Employee: {}", employee)); + + Flux employeeFlux = client.get() + .uri("/employees") + .retrieve() + .bodyToFlux(Employee.class); + + employeeFlux.subscribe(employee -> LOGGER.debug("Employee: {}", employee)); + } +} \ No newline at end of file diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeWebSecurityConfig.java b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeWebSecurityConfig.java new file mode 100644 index 0000000000..8dfa455ce3 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/annotation/EmployeeWebSecurityConfig.java @@ -0,0 +1,45 @@ +package com.baeldung.reactive.webflux.annotation; + +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.server.SecurityWebFilterChain; + +@EnableWebFluxSecurity +public class EmployeeWebSecurityConfig { + + @Bean + public MapReactiveUserDetailsService userDetailsService() { + UserDetails user = User + .withUsername("admin") + .password(passwordEncoder().encode("password")) + .roles("ADMIN") + .build(); + return new MapReactiveUserDetailsService(user); + } + + @Bean + public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http.csrf() + .disable() + .authorizeExchange() + .pathMatchers(HttpMethod.POST, "/employees/update") + .hasRole("ADMIN") + .pathMatchers("/**") + .permitAll() + .and() + .httpBasic(); + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webflux/functional/EmployeeFunctionalConfig.java b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/functional/EmployeeFunctionalConfig.java new file mode 100644 index 0000000000..f97d40e4e7 --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/functional/EmployeeFunctionalConfig.java @@ -0,0 +1,74 @@ +package com.baeldung.reactive.webflux.functional; + +import com.baeldung.reactive.webflux.Employee; +import com.baeldung.reactive.webflux.EmployeeRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerResponse; + +import static org.springframework.web.reactive.function.BodyExtractors.toMono; +import static org.springframework.web.reactive.function.server.RequestPredicates.GET; +import static org.springframework.web.reactive.function.server.RequestPredicates.POST; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +@Configuration +public class EmployeeFunctionalConfig { + + @Bean + EmployeeRepository employeeRepository() { + return new EmployeeRepository(); + } + + @Bean + RouterFunction getAllEmployeesRoute() { + return route(GET("/employees"), + req -> ok().body( + employeeRepository().findAllEmployees(), Employee.class)); + } + + @Bean + RouterFunction getEmployeeByIdRoute() { + return route(GET("/employees/{id}"), + req -> ok().body( + employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class)); + } + + @Bean + RouterFunction updateEmployeeRoute() { + return route(POST("/employees/update"), + req -> req.body(toMono(Employee.class)) + .doOnNext(employeeRepository()::updateEmployee) + .then(ok().build())); + } + + @Bean + RouterFunction composedRoutes() { + return + route(GET("/employees"), + req -> ok().body( + employeeRepository().findAllEmployees(), Employee.class)) + + .and(route(GET("/employees/{id}"), + req -> ok().body( + employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class))) + + .and(route(POST("/employees/update"), + req -> req.body(toMono(Employee.class)) + .doOnNext(employeeRepository()::updateEmployee) + .then(ok().build()))); + } + + @Bean + public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http.csrf() + .disable() + .authorizeExchange() + .anyExchange() + .permitAll(); + return http.build(); + } +} diff --git a/spring-reactive/src/main/java/com/baeldung/reactive/webflux/functional/EmployeeSpringFunctionalApplication.java b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/functional/EmployeeSpringFunctionalApplication.java new file mode 100644 index 0000000000..f710cf44eb --- /dev/null +++ b/spring-reactive/src/main/java/com/baeldung/reactive/webflux/functional/EmployeeSpringFunctionalApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.reactive.webflux.functional; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class EmployeeSpringFunctionalApplication { + + public static void main(String[] args) { + SpringApplication.run(EmployeeSpringFunctionalApplication.class, args); + } + +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/ConsumerFooServiceIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/ConsumerFooServiceIntegrationTest.java new file mode 100644 index 0000000000..5cb1a69fbd --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/ConsumerFooServiceIntegrationTest.java @@ -0,0 +1,62 @@ +package com.baeldung.reactive.debugging.consumer; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import com.baeldung.reactive.debugging.consumer.model.Foo; +import com.baeldung.reactive.debugging.consumer.service.FooService; +import com.baeldung.reactive.debugging.consumer.utils.ListAppender; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Hooks; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ConsumerFooServiceIntegrationTest { + + FooService service = new FooService(); + + @BeforeEach + public void clearLogList() { + Hooks.onOperatorDebug(); + ListAppender.clearEventList(); + } + + @Test + public void givenFooWithNullId_whenProcessFoo_thenLogsWithDebugTrace() { + Foo one = new Foo(1, "nameverylong", 8); + Foo two = new Foo(null, "nameverylong", 4); + Flux flux = Flux.just(one, two); + + service.processFoo(flux); + + Collection allLoggedEntries = ListAppender.getEvents() + .stream() + .map(ILoggingEvent::getFormattedMessage) + .collect(Collectors.toList()); + + Collection allSuppressedEntries = ListAppender.getEvents() + .stream() + .map(ILoggingEvent::getThrowableProxy) + .flatMap(t -> { + return Optional.ofNullable(t) + .map(IThrowableProxy::getSuppressed) + .map(Arrays::stream) + .orElse(Stream.empty()); + }) + .map(IThrowableProxy::getClassName) + .collect(Collectors.toList()); + assertThat(allLoggedEntries).anyMatch(entry -> entry.contains("The following error happened on processFoo method!")) + .anyMatch(entry -> entry.contains("| onSubscribe")) + .anyMatch(entry -> entry.contains("| cancel()")); + + assertThat(allSuppressedEntries) + .anyMatch(entry -> entry.contains("reactor.core.publisher.FluxOnAssembly$OnAssemblyException")); + } +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/ConsumerFooServiceLiveTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/ConsumerFooServiceLiveTest.java new file mode 100644 index 0000000000..c3ef67f534 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/ConsumerFooServiceLiveTest.java @@ -0,0 +1,53 @@ +package com.baeldung.reactive.debugging.consumer; + +import com.baeldung.reactive.debugging.consumer.service.FooService; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec; + +/** + * In order to run this live test, start the following classes: + * - com.baeldung.debugging.server.ServerDebuggingApplication + * - com.baeldung.debugging.consumer.ConsumerDebuggingApplication + */ +public class ConsumerFooServiceLiveTest { + + FooService service = new FooService(); + + private static final String BASE_URL = "http://localhost:8082"; + private static final String DEBUG_HOOK_ON = BASE_URL + "/debug-hook-on"; + private static final String DEBUG_HOOK_OFF = BASE_URL + "/debug-hook-off"; + + private static WebTestClient client; + + @BeforeAll + public static void setup() { + client = WebTestClient.bindToServer() + .baseUrl(BASE_URL) + .build(); + } + + @Test + public void whenRequestingDebugHookOn_thenObtainExpectedMessage() { + ResponseSpec response = client.get() + .uri(DEBUG_HOOK_ON) + .exchange(); + response.expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("DEBUG HOOK ON"); + } + + @Test + public void whenRequestingDebugHookOff_thenObtainExpectedMessage() { + ResponseSpec response = client.get() + .uri(DEBUG_HOOK_OFF) + .exchange(); + response.expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("DEBUG HOOK OFF"); + } + +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/utils/ListAppender.java b/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/utils/ListAppender.java new file mode 100644 index 0000000000..fe8b04e824 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/debugging/consumer/utils/ListAppender.java @@ -0,0 +1,25 @@ +package com.baeldung.reactive.debugging.consumer.utils; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; + +import java.util.ArrayList; +import java.util.List; + +public class ListAppender extends AppenderBase { + + static private List events = new ArrayList<>(); + + @Override + protected void append(ILoggingEvent eventObject) { + events.add(eventObject); + } + + public static List getEvents() { + return events; + } + + public static void clearEventList() { + events.clear(); + } +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/introduction/ReactorIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/introduction/ReactorIntegrationTest.java new file mode 100644 index 0000000000..e60d2e9b94 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/introduction/ReactorIntegrationTest.java @@ -0,0 +1,123 @@ +package com.baeldung.reactive.introduction; + +import org.junit.Test; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.ConnectableFlux; +import reactor.core.publisher.Flux; +import reactor.core.scheduler.Schedulers; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ReactorIntegrationTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReactorIntegrationTest.class); + + @Test + public void givenFlux_whenSubscribing_thenStream() { + + List elements = new ArrayList<>(); + + Flux.just(1, 2, 3, 4) + .log() + .map(i -> { + LOGGER.debug("{}:{}", i, Thread.currentThread()); + return i * 2; + }) + .subscribe(elements::add); + + assertThat(elements).containsExactly(2, 4, 6, 8); + } + + @Test + public void givenFlux_whenZipping_thenCombine() { + List elements = new ArrayList<>(); + + Flux.just(1, 2, 3, 4) + .log() + .map(i -> i * 2) + .zipWith(Flux.range(0, Integer.MAX_VALUE).log(), (one, two) -> String.format("First Flux: %d, Second Flux: %d", one, two)) + .subscribe(elements::add); + + assertThat(elements).containsExactly( + "First Flux: 2, Second Flux: 0", + "First Flux: 4, Second Flux: 1", + "First Flux: 6, Second Flux: 2", + "First Flux: 8, Second Flux: 3"); + } + + @Test + public void givenFlux_whenApplyingBackPressure_thenPushElementsInBatches() { + + List elements = new ArrayList<>(); + + Flux.just(1, 2, 3, 4) + .log() + .subscribe(new Subscriber() { + private Subscription s; + int onNextAmount; + + @Override + public void onSubscribe(final Subscription s) { + this.s = s; + s.request(2); + } + + @Override + public void onNext(final Integer integer) { + elements.add(integer); + onNextAmount++; + if (onNextAmount % 2 == 0) { + s.request(2); + } + } + + @Override + public void onError(final Throwable t) { + } + + @Override + public void onComplete() { + } + }); + + assertThat(elements).containsExactly(1, 2, 3, 4); + } + + @Test + public void givenFlux_whenInParallel_thenSubscribeInDifferentThreads() throws InterruptedException { + List threadNames = new ArrayList<>(); + + Flux.just(1, 2, 3, 4) + .log() + .map(i -> Thread.currentThread().getName()) + .subscribeOn(Schedulers.parallel()) + .subscribe(threadNames::add); + + Thread.sleep(1000); + + assertThat(threadNames).containsExactly("parallel-1", "parallel-1", "parallel-1", "parallel-1"); + } + + @Test + public void givenConnectableFlux_whenConnected_thenShouldStream() { + + List elements = new ArrayList<>(); + + final ConnectableFlux publish = Flux.just(1, 2, 3, 4).publish(); + + publish.subscribe(elements::add); + + assertThat(elements).isEmpty(); + + publish.connect(); + + assertThat(elements).containsExactly(1, 2, 3, 4); + } + +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/security/SecurityIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/security/SecurityIntegrationTest.java new file mode 100644 index 0000000000..06644fbf77 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/security/SecurityIntegrationTest.java @@ -0,0 +1,37 @@ +package com.baeldung.reactive.security; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = SpringSecurity5Application.class) +public class SecurityIntegrationTest { + + @Autowired + ApplicationContext context; + + private WebTestClient rest; + + @BeforeEach + public void setup() { + this.rest = WebTestClient.bindToApplicationContext(this.context).configureClient().build(); + } + + @Test + public void whenNoCredentials_thenRedirectToLogin() { + this.rest.get().uri("/").exchange().expectStatus().is3xxRedirection(); + } + + @Test + @WithMockUser + public void whenHasCredentials_thenSeesGreeting() { + this.rest.get().uri("/").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("Hello, user"); + } +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/webclient/SpringContextTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/webclient/SpringContextTest.java new file mode 100644 index 0000000000..4a1fc4390a --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/webclient/SpringContextTest.java @@ -0,0 +1,12 @@ +package com.baeldung.reactive.webclient; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = WebClientApplication.class) +public class SpringContextTest { + + @Test + public void whenSpringContextIsBootstrapped_thenNoExceptions() { + } +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebClientIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebClientIntegrationTest.java new file mode 100644 index 0000000000..28b4c19a10 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebClientIntegrationTest.java @@ -0,0 +1,325 @@ +package com.baeldung.reactive.webclient; + +import io.netty.channel.ChannelOption; +import io.netty.handler.timeout.ReadTimeoutException; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.handler.timeout.WriteTimeoutHandler; +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.web.server.LocalServerPort; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.codec.CodecException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ReactiveHttpOutputMessage; +import org.springframework.http.client.reactive.ClientHttpRequest; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec; +import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec; +import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec; +import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec; +import org.springframework.web.reactive.function.client.WebClient.ResponseSpec; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; +import reactor.test.StepVerifier; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = WebClientApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT) +public class WebClientIntegrationTest { + + @LocalServerPort + private int port; + + private static final String BODY_VALUE = "bodyValue"; + private static final ParameterizedTypeReference> MAP_RESPONSE_REF = new ParameterizedTypeReference>() { + }; + + @Test + public void givenDifferentWebClientCreationMethods_whenUsed_thenObtainExpectedResponse() { + // WebClient creation + WebClient client1 = WebClient.create(); + WebClient client2 = WebClient.create("http://localhost:" + port); + WebClient client3 = WebClient.builder() + .baseUrl("http://localhost:" + port) + .defaultCookie("cookieKey", "cookieValue") + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080")) + .build(); + + // response assertions + StepVerifier.create(retrieveResponse(client1.post() + .uri("http://localhost:" + port + "/resource"))) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(client2)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(client3)) + .expectNext("processed-bodyValue") + .verifyComplete(); + // assert response without specifying URI + StepVerifier.create(retrieveResponse(client1)) + .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() + .contains("Connection refused")) + .verify(); + } + + @Test + public void givenDifferentMethodSpecifications_whenUsed_thenObtainExpectedResponse() { + // request specification + RequestBodyUriSpec uriSpecPost1 = createDefaultClient().method(HttpMethod.POST); + RequestBodyUriSpec uriSpecPost2 = createDefaultClient().post(); + RequestHeadersUriSpec requestGet = createDefaultClient().get(); + + // response assertions + StepVerifier.create(retrieveResponse(uriSpecPost1)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(uriSpecPost2)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveGetResponse(requestGet)) + .expectNextMatches(nextMap -> nextMap.get("field") + .equals("value")) + .verifyComplete(); + } + + @Test + public void givenDifferentUriSpecifications_whenUsed_thenObtainExpectedResponse() { + // uri specification + RequestBodySpec bodySpecUsingString = createDefaultPostRequest().uri("/resource"); + RequestBodySpec bodySpecUsingUriBuilder = createDefaultPostRequest().uri(uriBuilder -> uriBuilder.pathSegment("resource") + .build()); + RequestBodySpec bodySpecusingURI = createDefaultPostRequest().uri(URI.create("http://localhost:" + port + "/resource")); + RequestBodySpec bodySpecOverridenBaseUri = createDefaultPostRequest().uri(URI.create("/resource")); + RequestBodySpec bodySpecOverridenBaseUri2 = WebClient.builder() + .baseUrl("http://localhost:" + port) + .build() + .post() + .uri(URI.create("/resource")); + + // response assertions + StepVerifier.create(retrieveResponse(bodySpecUsingString)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(bodySpecUsingUriBuilder)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(bodySpecusingURI)) + .expectNext("processed-bodyValue") + .verifyComplete(); + // assert sending request overriding base URI + StepVerifier.create(retrieveResponse(bodySpecOverridenBaseUri)) + .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() + .contains("Connection refused")) + .verify(); + StepVerifier.create(retrieveResponse(bodySpecOverridenBaseUri2)) + .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() + .contains("Connection refused")) + .verify(); + } + + @Test + public void givenDifferentBodySpecifications_whenUsed_thenObtainExpectedResponse() { + // request body specifications + RequestHeadersSpec headersSpecPost1 = createDefaultPostResourceRequest().body(BodyInserters.fromPublisher(Mono.just(BODY_VALUE), String.class)); + RequestHeadersSpec headersSpecPost2 = createDefaultPostResourceRequest().body(BodyInserters.fromValue(BODY_VALUE)); + RequestHeadersSpec headersSpecPost3 = createDefaultPostResourceRequest().bodyValue(BODY_VALUE); + RequestHeadersSpec headersSpecFooPost = createDefaultPostRequest().uri("/resource-foo") + .body(Mono.just(new Foo("fooName")), Foo.class); + BodyInserter inserterPlainObject = BodyInserters.fromValue(new Object()); + RequestHeadersSpec headersSpecPlainObject = createDefaultPostResourceRequest().body(inserterPlainObject); + + // request body specifications - using other inserter method (multipart request) + LinkedMultiValueMap map = new LinkedMultiValueMap<>(); + map.add("key1", "multipartValue1"); + map.add("key2", "multipartValue2"); + BodyInserter, ClientHttpRequest> inserterMultipart = BodyInserters.fromMultipartData(map); + RequestHeadersSpec headersSpecInserterMultipart = createDefaultPostRequest().uri("/resource-multipart") + .body(inserterMultipart); + + // response assertions + StepVerifier.create(retrieveResponse(headersSpecPost1)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(headersSpecPost2)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(headersSpecPost3)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(headersSpecFooPost)) + .expectNext("processedFoo-fooName") + .verifyComplete(); + StepVerifier.create(retrieveResponse(headersSpecInserterMultipart)) + .expectNext("processed-multipartValue1-multipartValue2") + .verifyComplete(); + // assert error plain `new Object()` as request body + StepVerifier.create(retrieveResponse(headersSpecPlainObject)) + .expectError(CodecException.class) + .verify(); + // assert response for request with no body + Mono> responsePostWithNoBody = createDefaultPostResourceRequest().exchangeToMono(responseHandler -> { + assertThat(responseHandler.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + return responseHandler.bodyToMono(MAP_RESPONSE_REF); + }); + StepVerifier.create(responsePostWithNoBody) + .expectNextMatches(nextMap -> nextMap.get("error") + .equals("Bad Request")) + .verifyComplete(); + } + + @Test + public void givenPostSpecifications_whenHeadersAdded_thenObtainExpectedResponse() { + // request header specification + RequestHeadersSpec headersSpecInserterStringWithHeaders = createDefaultPostResourceRequestResponse().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) + .acceptCharset(StandardCharsets.UTF_8) + .ifNoneMatch("*") + .ifModifiedSince(ZonedDateTime.now()); + + // response assertions + StepVerifier.create(retrieveResponse(headersSpecInserterStringWithHeaders)) + .expectNext("processed-bodyValue") + .verifyComplete(); + } + + @Test + public void givenDifferentResponseSpecifications_whenUsed_thenObtainExpectedResponse() { + ResponseSpec responseSpecPostString = createDefaultPostResourceRequestResponse().retrieve(); + Mono responsePostString = responseSpecPostString.bodyToMono(String.class); + Mono responsePostString2 = createDefaultPostResourceRequestResponse().exchangeToMono(response -> { + if (response.statusCode() == HttpStatus.OK) { + return response.bodyToMono(String.class); + } else if (response.statusCode() + .is4xxClientError()) { + return Mono.just("Error response"); + } else { + return response.createException() + .flatMap(Mono::error); + } + }); + Mono responsePostNoBody = createDefaultPostResourceRequest().exchangeToMono(response -> { + if (response.statusCode() == HttpStatus.OK) { + return response.bodyToMono(String.class); + } else if (response.statusCode() + .is4xxClientError()) { + return Mono.just("Error response"); + } else { + return response.createException() + .flatMap(Mono::error); + } + }); + Mono> responseGet = createDefaultClient().get() + .uri("/resource") + .retrieve() + .bodyToMono(MAP_RESPONSE_REF); + + // response assertions + StepVerifier.create(responsePostString) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(responsePostString2) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(responsePostNoBody) + .expectNext("Error response") + .verifyComplete(); + StepVerifier.create(responseGet) + .expectNextMatches(nextMap -> nextMap.get("field") + .equals("value")) + .verifyComplete(); + } + + @Test + public void givenWebClientWithTimeoutConfigurations_whenRequestUsingWronglyConfiguredPublisher_thenObtainTimeout() { + HttpClient httpClient = HttpClient.create() + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000) + .responseTimeout(Duration.ofMillis(1000)) + .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(1000, TimeUnit.MILLISECONDS)) + .addHandlerLast(new WriteTimeoutHandler(1000, TimeUnit.MILLISECONDS))); + + WebClient timeoutClient = WebClient.builder() + .baseUrl("http://localhost:" + port) + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .build(); + + RequestHeadersSpec neverendingMonoBodyRequest = timeoutClient.post() + .uri("/resource") + .body(Mono.never(), String.class); + + StepVerifier.create(neverendingMonoBodyRequest.retrieve() + .bodyToMono(String.class)) + .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ReadTimeoutException.class.isAssignableFrom(ex.getCause() + .getClass())) + .verify(); + } + + // helper methods to create default instances + private WebClient createDefaultClient() { + return WebClient.create("http://localhost:" + port); + } + + private RequestBodyUriSpec createDefaultPostRequest() { + return createDefaultClient().post(); + } + + private RequestBodySpec createDefaultPostResourceRequest() { + return createDefaultPostRequest().uri("/resource"); + } + + private RequestHeadersSpec createDefaultPostResourceRequestResponse() { + return createDefaultPostResourceRequest().bodyValue(BODY_VALUE); + } + + // helper methods to retrieve a response based on different steps of the process (specs) + private Mono retrieveResponse(WebClient client) { + return client.post() + .uri("/resource") + .bodyValue(BODY_VALUE) + .retrieve() + .bodyToMono(String.class); + } + + private Mono retrieveResponse(RequestBodyUriSpec spec) { + return spec.uri("/resource") + .bodyValue(BODY_VALUE) + .retrieve() + .bodyToMono(String.class); + } + + private Mono> retrieveGetResponse(RequestHeadersUriSpec spec) { + return spec.uri("/resource") + .retrieve() + .bodyToMono(MAP_RESPONSE_REF); + } + + private Mono retrieveResponse(RequestBodySpec spec) { + return spec.bodyValue(BODY_VALUE) + .retrieve() + .bodyToMono(String.class); + } + + private Mono retrieveResponse(RequestHeadersSpec spec) { + return spec.retrieve() + .bodyToMono(String.class); + } +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebControllerIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebControllerIntegrationTest.java new file mode 100644 index 0000000000..3c0683a419 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebControllerIntegrationTest.java @@ -0,0 +1,51 @@ +package com.baeldung.reactive.webclient; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WebClientApplication.class) +public class WebControllerIntegrationTest { + + @LocalServerPort + int randomServerPort; + + @Autowired + private WebTestClient testClient; + + @Autowired + private WebController webController; + + @Before + public void setup() { + webController.setServerPort(randomServerPort); + } + + @Test + public void whenEndpointWithBlockingClientIsCalled_thenThreeTweetsAreReceived() { + testClient.get() + .uri("/tweets-blocking") + .exchange() + .expectStatus() + .isOk() + .expectBodyList(Tweet.class) + .hasSize(3); + } + + @Test + public void whenEndpointWithNonBlockingClientIsCalled_thenThreeTweetsAreReceived() { + testClient.get() + .uri("/tweets-non-blocking") + .exchange() + .expectStatus() + .isOk() + .expectBodyList(Tweet.class) + .hasSize(3); + } +} \ No newline at end of file diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebTestClientIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebTestClientIntegrationTest.java new file mode 100644 index 0000000000..90f4a0eca2 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/webclient/WebTestClientIntegrationTest.java @@ -0,0 +1,98 @@ +package com.baeldung.reactive.webclient; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.ApplicationContext; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.WebHandler; +import reactor.core.publisher.Mono; + +@SpringBootTest(classes = WebClientApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class WebTestClientIntegrationTest { + + @LocalServerPort + private int port; + + @Autowired + private ApplicationContext context; + + @Autowired + private WebClientController controller; + + private final RouterFunction ROUTER_FUNCTION = RouterFunctions.route(RequestPredicates.GET("/resource"), request -> ServerResponse.ok() + .build()); + private final WebHandler WEB_HANDLER = exchange -> Mono.empty(); + + @Test + public void testWebTestClientWithServerWebHandler() { + WebTestClient.bindToWebHandler(WEB_HANDLER) + .build(); + } + + @Test + public void testWebTestClientWithRouterFunction() { + WebTestClient.bindToRouterFunction(ROUTER_FUNCTION) + .build() + .get() + .uri("/resource") + .exchange() + .expectStatus() + .isOk() + .expectBody() + .isEmpty(); + } + + @Test + @WithMockUser + public void testWebTestClientWithServerURL() { + WebTestClient.bindToServer() + .baseUrl("http://localhost:" + port) + .build() + .get() + .uri("/resource") + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("field") + .isEqualTo("value"); + ; + } + + @Test + @WithMockUser + public void testWebTestClientWithApplicationContext() { + WebTestClient.bindToApplicationContext(context) + .build() + .get() + .uri("/resource") + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("field") + .isEqualTo("value"); + } + + @Test + public void testWebTestClientWithController() { + WebTestClient.bindToController(controller) + .build() + .get() + .uri("/resource") + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("field") + .isEqualTo("value"); + } + +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/webclientrequests/WebClientRequestsUnitTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/webclientrequests/WebClientRequestsUnitTest.java new file mode 100644 index 0000000000..ff59f12391 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/webclientrequests/WebClientRequestsUnitTest.java @@ -0,0 +1,176 @@ +package com.baeldung.reactive.webclientrequests; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.util.DefaultUriBuilderFactory; +import reactor.core.publisher.Mono; + +import java.time.Duration; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +@WebFluxTest +public class WebClientRequestsUnitTest { + + private static final String BASE_URL = "https://example.com"; + + private WebClient webClient; + + @Captor + private ArgumentCaptor argumentCaptor; + + private ExchangeFunction exchangeFunction; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + this.exchangeFunction = mock(ExchangeFunction.class); + ClientResponse mockResponse = mock(ClientResponse.class); + when(this.exchangeFunction.exchange(this.argumentCaptor.capture())).thenReturn(Mono.just(mockResponse)); + this.webClient = WebClient + .builder() + .baseUrl(BASE_URL) + .exchangeFunction(exchangeFunction) + .build(); + } + + @Test + public void whenCallSimpleURI_thenURIMatched() { + this.webClient.get() + .uri("/products") + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products"); + } + + @Test + public void whenCallSinglePathSegmentUri_thenURIMatched() { + this.webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/products/{id}") + .build(2)) + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products/2"); + } + + @Test + public void whenCallMultiplePathSegmentsUri_thenURIMatched() { + this.webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/products/{id}/attributes/{attributeId}") + .build(2, 13)) + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products/2/attributes/13"); + } + + @Test + public void whenCallSingleQueryParams_thenURIMatched() { + this.webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/products/") + .queryParam("name", "AndroidPhone") + .queryParam("color", "black") + .queryParam("deliveryDate", "13/04/2019") + .build()) + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products/?name=AndroidPhone&color=black&deliveryDate=13/04/2019"); + } + + @Test + public void whenCallSingleQueryParamsPlaceholders_thenURIMatched() { + this.webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/products/") + .queryParam("name", "{title}") + .queryParam("color", "{authorId}") + .queryParam("deliveryDate", "{date}") + .build("AndroidPhone", "black", "13/04/2019")) + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products/?name=AndroidPhone&color=black&deliveryDate=13%2F04%2F2019"); + } + + @Test + public void whenCallArrayQueryParamsBrackets_thenURIMatched() { + this.webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/products/") + .queryParam("tag[]", "Snapdragon", "NFC") + .build()) + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products/?tag%5B%5D=Snapdragon&tag%5B%5D=NFC"); + } + + + @Test + public void whenCallArrayQueryParams_thenURIMatched() { + this.webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/products/") + .queryParam("category", "Phones", "Tablets") + .build()) + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products/?category=Phones&category=Tablets"); + } + + @Test + public void whenCallArrayQueryParamsComma_thenURIMatched() { + this.webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/products/") + .queryParam("category", String.join(",", "Phones", "Tablets")) + .build()) + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products/?category=Phones,Tablets"); + } + + @Test + public void whenUriComponentEncoding_thenQueryParamsNotEscaped() { + DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL); + factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT); + this.webClient = WebClient + .builder() + .uriBuilderFactory(factory) + .baseUrl(BASE_URL) + .exchangeFunction(exchangeFunction) + .build(); + this.webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/products/") + .queryParam("name", "AndroidPhone") + .queryParam("color", "black") + .queryParam("deliveryDate", "13/04/2019") + .build()) + .exchange() + .block(Duration.ofSeconds(1)); + verifyCalledUrl("/products/?name=AndroidPhone&color=black&deliveryDate=13/04/2019"); + } + + private void verifyCalledUrl(String relativeUrl) { + ClientRequest request = this.argumentCaptor.getValue(); + Assert.assertEquals(String.format("%s%s", BASE_URL, relativeUrl), request.url().toString()); + Mockito.verify(this.exchangeFunction).exchange(request); + verifyNoMoreInteractions(this.exchangeFunction); + } +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/webflux/annotation/EmployeeControllerIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/webflux/annotation/EmployeeControllerIntegrationTest.java new file mode 100644 index 0000000000..24c4303e83 --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/webflux/annotation/EmployeeControllerIntegrationTest.java @@ -0,0 +1,74 @@ +package com.baeldung.reactive.webflux.annotation; + +import com.baeldung.reactive.webflux.Employee; +import com.baeldung.reactive.webflux.EmployeeRepository; +import com.baeldung.reactive.webflux.annotation.EmployeeSpringApplication; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.BDDMockito.given; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes= EmployeeSpringApplication.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class EmployeeControllerIntegrationTest { + + @Autowired + private WebTestClient testClient; + + @MockBean + private EmployeeRepository employeeRepository; + + @Test + public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { + + Employee employee = new Employee("1", "Employee 1 Name"); + + given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); + testClient.get() + .uri("/employees/1") + .exchange() + .expectStatus() + .isOk() + .expectBody(Employee.class) + .isEqualTo(employee); + } + + @Test + public void whenGetAllEmployees_thenCorrectEmployees() { + + List employeeList = new ArrayList<>(); + + Employee employee1 = new Employee("1", "Employee 1 Name"); + Employee employee2 = new Employee("2", "Employee 2 Name"); + Employee employee3 = new Employee("3", "Employee 3 Name"); + + employeeList.add(employee1); + employeeList.add(employee2); + employeeList.add(employee3); + + Flux employeeFlux = Flux.fromIterable(employeeList); + + given(employeeRepository.findAllEmployees()).willReturn(employeeFlux); + testClient.get() + .uri("/employees") + .exchange() + .expectStatus() + .isOk() + .expectBodyList(Employee.class) + .hasSize(3) + .isEqualTo(employeeList); + } +} diff --git a/spring-reactive/src/test/java/com/baeldung/reactive/webflux/functional/EmployeeSpringFunctionalIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/webflux/functional/EmployeeSpringFunctionalIntegrationTest.java new file mode 100644 index 0000000000..da866724cc --- /dev/null +++ b/spring-reactive/src/test/java/com/baeldung/reactive/webflux/functional/EmployeeSpringFunctionalIntegrationTest.java @@ -0,0 +1,92 @@ +package com.baeldung.reactive.webflux.functional; + +import com.baeldung.reactive.webflux.Employee; +import com.baeldung.reactive.webflux.EmployeeRepository; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = EmployeeSpringFunctionalApplication.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class EmployeeSpringFunctionalIntegrationTest { + + @Autowired + private EmployeeFunctionalConfig config; + + @MockBean + private EmployeeRepository employeeRepository; + + @Test + public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { + WebTestClient client = WebTestClient + .bindToRouterFunction(config.getEmployeeByIdRoute()) + .build(); + + Employee employee = new Employee("1", "Employee 1"); + + given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); + + client.get() + .uri("/employees/1") + .exchange() + .expectStatus() + .isOk() + .expectBody(Employee.class) + .isEqualTo(employee); + } + + @Test + public void whenGetAllEmployees_thenCorrectEmployees() { + WebTestClient client = WebTestClient + .bindToRouterFunction(config.getAllEmployeesRoute()) + .build(); + + List employees = Arrays.asList( + new Employee("1", "Employee 1"), + new Employee("2", "Employee 2")); + + Flux employeeFlux = Flux.fromIterable(employees); + given(employeeRepository.findAllEmployees()).willReturn(employeeFlux); + + client.get() + .uri("/employees") + .exchange() + .expectStatus() + .isOk() + .expectBodyList(Employee.class) + .isEqualTo(employees); + } + + @Test + public void whenUpdateEmployee_thenEmployeeUpdated() { + WebTestClient client = WebTestClient + .bindToRouterFunction(config.updateEmployeeRoute()) + .build(); + + Employee employee = new Employee("1", "Employee 1 Updated"); + + client.post() + .uri("/employees/update") + .body(Mono.just(employee), Employee.class) + .exchange() + .expectStatus() + .isOk(); + + verify(employeeRepository).updateEmployee(employee); + } +} diff --git a/spring-reactive/src/test/resources/logback-test.xml b/spring-reactive/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..8d4771e308 --- /dev/null +++ b/spring-reactive/src/test/resources/logback-test.xml @@ -0,0 +1,12 @@ + + + + + [%d{ISO8601}]-[%thread] %-5level %logger - %msg%n + + + + + + + \ No newline at end of file From 7bce366f782c82172ec10033e42c10368ac7ebbd Mon Sep 17 00:00:00 2001 From: Haroon Khan Date: Fri, 24 Dec 2021 00:06:01 +0000 Subject: [PATCH 10/48] [JAVA-8983] Fix envers integration test --- .../com/baeldung/persistence/model/Bar.java | 51 ++++++++----------- .../com/baeldung/persistence/model/Foo.java | 17 +++---- .../EnversFooBarAuditIntegrationTest.java | 46 +++++------------ 3 files changed, 44 insertions(+), 70 deletions(-) diff --git a/persistence-modules/spring-data-jpa-query-2/src/main/java/com/baeldung/persistence/model/Bar.java b/persistence-modules/spring-data-jpa-query-2/src/main/java/com/baeldung/persistence/model/Bar.java index c7f05254cc..0ceb2d5626 100644 --- a/persistence-modules/spring-data-jpa-query-2/src/main/java/com/baeldung/persistence/model/Bar.java +++ b/persistence-modules/spring-data-jpa-query-2/src/main/java/com/baeldung/persistence/model/Bar.java @@ -1,8 +1,14 @@ package com.baeldung.persistence.model; -import java.io.Serializable; -import java.util.Date; -import java.util.Set; +import com.google.common.collect.Sets; +import org.hibernate.annotations.OrderBy; +import org.hibernate.envers.Audited; +import org.jboss.logging.Logger; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -17,17 +23,9 @@ import javax.persistence.OneToMany; import javax.persistence.PrePersist; import javax.persistence.PreRemove; import javax.persistence.PreUpdate; - -import org.hibernate.annotations.OrderBy; -import org.hibernate.envers.Audited; -import org.jboss.logging.Logger; -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -import com.google.common.collect.Sets; +import java.io.Serializable; +import java.util.Date; +import java.util.Set; @Entity @NamedQuery(name = "Bar.findAll", query = "SELECT b FROM Bar b") @@ -35,11 +33,11 @@ import com.google.common.collect.Sets; @EntityListeners(AuditingEntityListener.class) public class Bar implements Serializable { - private static Logger logger = Logger.getLogger(Bar.class); + private static final Logger LOGGER = Logger.getLogger(Bar.class); public enum OPERATION { INSERT, UPDATE, DELETE; - private String value; + private final String value; OPERATION() { value = toString(); @@ -70,7 +68,7 @@ public class Bar implements Serializable { private String name; @OneToMany(mappedBy = "bar", cascade = CascadeType.ALL, fetch = FetchType.LAZY) - @OrderBy(clause = "NAME DESC") + @OrderBy(clause = "name DESC") // @NotAudited private Set fooSet = Sets.newHashSet(); @@ -102,7 +100,6 @@ public class Bar implements Serializable { public Bar(final String name) { super(); - this.name = name; } @@ -202,35 +199,31 @@ public class Bar implements Serializable { return false; final Bar other = (Bar) obj; if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; + return other.name == null; + } else + return name.equals(other.name); } @Override public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("Bar [name=").append(name).append("]"); - return builder.toString(); + return "Bar [name=" + name + "]"; } @PrePersist public void onPrePersist() { - logger.info("@PrePersist"); + LOGGER.info("@PrePersist"); audit(OPERATION.INSERT); } @PreUpdate public void onPreUpdate() { - logger.info("@PreUpdate"); + LOGGER.info("@PreUpdate"); audit(OPERATION.UPDATE); } @PreRemove public void onPreRemove() { - logger.info("@PreRemove"); + LOGGER.info("@PreRemove"); audit(OPERATION.DELETE); } diff --git a/persistence-modules/spring-data-jpa-query-2/src/main/java/com/baeldung/persistence/model/Foo.java b/persistence-modules/spring-data-jpa-query-2/src/main/java/com/baeldung/persistence/model/Foo.java index d36a1e58cf..eec15c5e56 100644 --- a/persistence-modules/spring-data-jpa-query-2/src/main/java/com/baeldung/persistence/model/Foo.java +++ b/persistence-modules/spring-data-jpa-query-2/src/main/java/com/baeldung/persistence/model/Foo.java @@ -16,7 +16,10 @@ import javax.persistence.NamedNativeQuery; import org.hibernate.envers.Audited; -@NamedNativeQueries({ @NamedNativeQuery(name = "callGetAllFoos", query = "CALL GetAllFoos()", resultClass = Foo.class), @NamedNativeQuery(name = "callGetFoosByName", query = "CALL GetFoosByName(:fooName)", resultClass = Foo.class) }) +@NamedNativeQueries({ + @NamedNativeQuery(name = "callGetAllFoos", query = "CALL GetAllFoos()", resultClass = Foo.class), + @NamedNativeQuery(name = "callGetFoosByName", query = "CALL GetFoosByName(:fooName)", resultClass = Foo.class) +}) @Entity @Audited // @Proxy(lazy = false) @@ -89,17 +92,13 @@ public class Foo implements Serializable { return false; final Foo other = (Foo) obj; if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; + return other.name == null; + } else + return name.equals(other.name); } @Override public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("Foo [name=").append(name).append("]"); - return builder.toString(); + return "Foo [name=" + name + "]"; } } diff --git a/persistence-modules/spring-data-jpa-query-2/src/test/java/com/baeldung/persistence/audit/EnversFooBarAuditIntegrationTest.java b/persistence-modules/spring-data-jpa-query-2/src/test/java/com/baeldung/persistence/audit/EnversFooBarAuditIntegrationTest.java index b6361a5e36..09d44956b3 100644 --- a/persistence-modules/spring-data-jpa-query-2/src/test/java/com/baeldung/persistence/audit/EnversFooBarAuditIntegrationTest.java +++ b/persistence-modules/spring-data-jpa-query-2/src/test/java/com/baeldung/persistence/audit/EnversFooBarAuditIntegrationTest.java @@ -1,18 +1,14 @@ package com.baeldung.persistence.audit; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.List; - +import com.baeldung.persistence.model.Bar; +import com.baeldung.persistence.model.Foo; +import com.baeldung.persistence.service.IBarAuditableService; +import com.baeldung.persistence.service.IFooAuditableService; +import com.baeldung.spring.config.PersistenceTestConfig; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; @@ -24,28 +20,17 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; -import com.baeldung.persistence.model.Bar; -import com.baeldung.persistence.model.Foo; -import com.baeldung.persistence.service.IBarAuditableService; -import com.baeldung.persistence.service.IFooAuditableService; -import com.baeldung.spring.config.PersistenceTestConfig; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceTestConfig.class }, loader = AnnotationConfigContextLoader.class) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) public class EnversFooBarAuditIntegrationTest { - private static Logger logger = LoggerFactory.getLogger(EnversFooBarAuditIntegrationTest.class); - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - logger.info("setUpBeforeClass()"); - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - logger.info("tearDownAfterClass()"); - } + private static final Logger LOGGER = LoggerFactory.getLogger(EnversFooBarAuditIntegrationTest.class); @Autowired @Qualifier("fooHibernateAuditableService") @@ -61,15 +46,15 @@ public class EnversFooBarAuditIntegrationTest { private Session session; @Before - public void setUp() throws Exception { - logger.info("setUp()"); + public void setUp() { + LOGGER.info("setUp()"); makeRevisions(); session = sessionFactory.openSession(); } @After - public void tearDown() throws Exception { - logger.info("tearDown()"); + public void tearDown() { + LOGGER.info("tearDown()"); session.close(); } @@ -98,20 +83,17 @@ public class EnversFooBarAuditIntegrationTest { // REV #3: update BAR private void rev3(final Bar bar) { - bar.setName("BAR1"); barService.update(bar); } // REV #4: insert FOO3 & update BAR private void rev4(final Bar bar) { - final Foo foo3 = new Foo("FOO3"); foo3.setBar(bar); fooService.create(foo3); } - @Ignore("Fixing after Spring Boot 2.6.1 upgrade") @Test public final void whenFooBarsModified_thenFooBarsAudited() { From 6bf03f09c917bee5daaf2335f2e42fda4b168010 Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 29 Dec 2021 19:14:09 +0800 Subject: [PATCH 11/48] Update README.md --- spring-security-modules/spring-security-web-boot-3/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-security-modules/spring-security-web-boot-3/README.md b/spring-security-modules/spring-security-web-boot-3/README.md index e95071b825..45fede7a4c 100644 --- a/spring-security-modules/spring-security-web-boot-3/README.md +++ b/spring-security-modules/spring-security-web-boot-3/README.md @@ -11,4 +11,5 @@ The "REST With Spring" Classes: http://github.learnspringsecurity.com - [Spring Security – Request Rejected Exception](https://www.baeldung.com/spring-security-request-rejected-exception) - [Spring Security – Cache Control Headers](https://www.baeldung.com/spring-security-cache-control-headers) - [Fixing 401s with CORS Preflights and Spring Security](https://www.baeldung.com/spring-security-cors-preflight) +- [Content Security Policy with Spring Security](https://www.baeldung.com/spring-security-csp) - More articles: [[<-- prev]](/spring-security-modules/spring-security-web-boot-2) From d3832bb58ab6bea8ab7ef9215e15bd8a389bbe99 Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 29 Dec 2021 19:17:22 +0800 Subject: [PATCH 12/48] Update README.md --- apache-poi-2/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/apache-poi-2/README.md b/apache-poi-2/README.md index e31c219819..d810c40815 100644 --- a/apache-poi-2/README.md +++ b/apache-poi-2/README.md @@ -6,4 +6,5 @@ This module contains articles about Apache POI - [Adding a Column to an Excel Sheet Using Apache POI](https://www.baeldung.com/java-excel-add-column) - [Add an Image to a Cell in an Excel File With Java](https://www.baeldung.com/java-add-image-excel) +- [Numeric Format Using POI](https://www.baeldung.com/apache-poi-numeric-format) - More articles: [[<-- prev]](/apache-poi) From 7432cc2ee246e8adc4bb00d77917a9bed69d1b2c Mon Sep 17 00:00:00 2001 From: chaos2418 <> Date: Wed, 15 Dec 2021 09:37:47 +0530 Subject: [PATCH 13/48] JAVA-3090: updating README.md for spring-reactive --- pom.xml | 2 - reactor-core/README.md | 1 - .../introduction/ReactorIntegrationTest.java | 119 ------- spring-5-reactive-2/README.md | 2 - .../ConsumerDebuggingApplication.java | 34 -- .../consumer/chronjobs/ChronJobs.java | 154 -------- .../ReactiveConfigsToggleRestController.java | 23 -- .../debugging/consumer/model/Foo.java | 27 -- .../debugging/consumer/model/FooDto.java | 16 - .../consumer/service/FooNameHelper.java | 45 --- .../consumer/service/FooQuantityHelper.java | 31 -- .../consumer/service/FooReporter.java | 26 -- .../consumer/service/FooService.java | 120 ------- .../server/ServerDebuggingApplication.java | 29 -- .../server/handlers/ServerHandler.java | 47 --- .../baeldung/debugging/server/model/Foo.java | 13 - .../server/routers/ServerRouter.java | 22 -- .../java/com/baeldung/webclient/Tweet.java | 13 - .../TweetsSlowServiceController.java | 20 -- .../webclient/WebClientApplication.java | 23 -- .../com/baeldung/webclient/WebController.java | 60 ---- .../ConsumerFooServiceIntegrationTest.java | 64 ---- .../consumer/ConsumerFooServiceLiveTest.java | 54 --- .../consumer/utils/ListAppender.java | 25 -- .../WebControllerIntegrationTest.java | 51 --- spring-5-reactive-security/README.md | 3 - spring-5-reactive-security/pom.xml | 2 +- .../functional/EmployeeFunctionalConfig.java | 75 ---- .../EmployeeSpringFunctionalApplication.java | 13 - .../reactive/security/GreetController.java | 37 -- .../reactive/security/GreetService.java | 15 - .../reactive/security/SecurityConfig.java | 60 ---- .../security/SpringSecurity5Application.java | 35 -- .../baeldung/webflux/EmployeeController.java | 38 -- .../baeldung/webflux/EmployeeRepository.java | 64 ---- .../webflux/EmployeeSpringApplication.java | 17 - .../baeldung/webflux/EmployeeWebClient.java | 28 -- .../java/com/baeldung/SpringContextTest.java | 5 +- ...ployeeSpringFunctionalIntegrationTest.java | 92 ----- .../EmployeeControllerIntegrationTest.java | 76 ---- .../security/SecurityIntegrationTest.java | 41 --- spring-5-reactive/README.md | 2 - .../errorhandling/GlobalErrorAttributes.java | 53 --- .../GlobalErrorWebExceptionHandler.java | 50 --- .../errorhandling/NameRequiredException.java | 12 - .../errorhandling/handlers/Handler1.java | 29 -- .../errorhandling/handlers/Handler2.java | 38 -- .../errorhandling/handlers/Handler3.java | 34 -- .../errorhandling/handlers/Handler4.java | 30 -- .../errorhandling/handlers/Handler5.java | 22 -- .../errorhandling/routers/Router1.java | 22 -- .../errorhandling/routers/Router2.java | 22 -- .../errorhandling/routers/Router3.java | 22 -- .../errorhandling/routers/Router4.java | 22 -- .../errorhandling/routers/Router5.java | 22 -- .../java/com/baeldung/web/reactive/Task.java | 28 -- .../com/baeldung/web/reactive/client/Foo.java | 24 -- .../reactive/client/WebClientApplication.java | 14 - .../reactive/client/WebClientController.java | 42 --- .../web/client/SpringContextTest.java | 14 - .../web/client/WebClientIntegrationTest.java | 331 ------------------ .../client/WebTestClientIntegrationTest.java | 102 ------ spring-5-webflux/README.md | 1 - .../SpringWebClientRequestsApp.java | 12 - .../WebClientRequestsUnitTest.java | 176 ---------- spring-reactive/README.md | 7 +- .../ErrorHandlingIntegrationTest.java | 8 +- spring-webflux-threads/README.md | 3 - spring-webflux-threads/pom.xml | 65 ---- .../com/baeldung/webflux/Application.java | 17 - .../java/com/baeldung/webflux/Controller.java | 128 ------- .../java/com/baeldung/webflux/Person.java | 27 -- .../baeldung/webflux/PersonRepository.java | 6 - .../src/main/resources/logback.xml | 13 - 74 files changed, 10 insertions(+), 3010 deletions(-) delete mode 100644 reactor-core/src/test/java/com/baeldung/reactor/introduction/ReactorIntegrationTest.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/ConsumerDebuggingApplication.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/chronjobs/ChronJobs.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/controllers/ReactiveConfigsToggleRestController.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/model/Foo.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/model/FooDto.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooNameHelper.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooQuantityHelper.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooReporter.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooService.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/ServerDebuggingApplication.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/handlers/ServerHandler.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/model/Foo.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/routers/ServerRouter.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/webclient/Tweet.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/webclient/TweetsSlowServiceController.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/webclient/WebClientApplication.java delete mode 100644 spring-5-reactive-2/src/main/java/com/baeldung/webclient/WebController.java delete mode 100644 spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/ConsumerFooServiceIntegrationTest.java delete mode 100644 spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/ConsumerFooServiceLiveTest.java delete mode 100644 spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/utils/ListAppender.java delete mode 100644 spring-5-reactive-2/src/test/java/com/baeldung/webclient/WebControllerIntegrationTest.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/reactive/functional/EmployeeFunctionalConfig.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/reactive/functional/EmployeeSpringFunctionalApplication.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/GreetController.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/GreetService.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/SecurityConfig.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/SpringSecurity5Application.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeController.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeRepository.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeSpringApplication.java delete mode 100644 spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeWebClient.java delete mode 100644 spring-5-reactive-security/src/test/java/com/baeldung/reactive/functional/EmployeeSpringFunctionalIntegrationTest.java delete mode 100644 spring-5-reactive-security/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerIntegrationTest.java delete mode 100644 spring-5-reactive-security/src/test/java/com/baeldung/security/SecurityIntegrationTest.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/web/reactive/Task.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/Foo.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientApplication.java delete mode 100644 spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java delete mode 100644 spring-5-reactive/src/test/java/com/baeldung/web/client/SpringContextTest.java delete mode 100644 spring-5-reactive/src/test/java/com/baeldung/web/client/WebClientIntegrationTest.java delete mode 100644 spring-5-reactive/src/test/java/com/baeldung/web/client/WebTestClientIntegrationTest.java delete mode 100644 spring-5-webflux/src/main/java/com/baeldung/spring/webclientrequests/SpringWebClientRequestsApp.java delete mode 100644 spring-5-webflux/src/test/java/com/baeldung/spring/webclientrequests/WebClientRequestsUnitTest.java rename {spring-5-reactive => spring-reactive}/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java (100%) delete mode 100644 spring-webflux-threads/README.md delete mode 100644 spring-webflux-threads/pom.xml delete mode 100644 spring-webflux-threads/src/main/java/com/baeldung/webflux/Application.java delete mode 100644 spring-webflux-threads/src/main/java/com/baeldung/webflux/Controller.java delete mode 100644 spring-webflux-threads/src/main/java/com/baeldung/webflux/Person.java delete mode 100644 spring-webflux-threads/src/main/java/com/baeldung/webflux/PersonRepository.java delete mode 100644 spring-webflux-threads/src/main/resources/logback.xml diff --git a/pom.xml b/pom.xml index 3770c05b49..1560e99d98 100644 --- a/pom.xml +++ b/pom.xml @@ -557,7 +557,6 @@ atomikos reactive-systems slack - spring-webflux-threads @@ -1037,7 +1036,6 @@ atomikos reactive-systems slack - spring-webflux-threads diff --git a/reactor-core/README.md b/reactor-core/README.md index a4ced49096..7ca3b5773f 100644 --- a/reactor-core/README.md +++ b/reactor-core/README.md @@ -4,7 +4,6 @@ This module contains articles about Reactor Core. ### Relevant articles -- [Intro To Reactor Core](https://www.baeldung.com/reactor-core) - [Combining Publishers in Project Reactor](https://www.baeldung.com/reactor-combine-streams) - [Programmatically Creating Sequences with Project Reactor](https://www.baeldung.com/flux-sequences-reactor) - [How to Extract a Mono’s Content in Java](https://www.baeldung.com/java-string-from-mono) diff --git a/reactor-core/src/test/java/com/baeldung/reactor/introduction/ReactorIntegrationTest.java b/reactor-core/src/test/java/com/baeldung/reactor/introduction/ReactorIntegrationTest.java deleted file mode 100644 index a1acffac91..0000000000 --- a/reactor-core/src/test/java/com/baeldung/reactor/introduction/ReactorIntegrationTest.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.baeldung.reactor.introduction; - -import org.junit.Test; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import reactor.core.publisher.ConnectableFlux; -import reactor.core.publisher.Flux; -import reactor.core.scheduler.Schedulers; - -import java.util.ArrayList; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ReactorIntegrationTest { - - @Test - public void givenFlux_whenSubscribing_thenStream() { - - List elements = new ArrayList<>(); - - Flux.just(1, 2, 3, 4) - .log() - .map(i -> { - System.out.println(i + ":" + Thread.currentThread()); - return i * 2; - }) - .subscribe(elements::add); - - assertThat(elements).containsExactly(2, 4, 6, 8); - } - - @Test - public void givenFlux_whenZipping_thenCombine() { - List elements = new ArrayList<>(); - - Flux.just(1, 2, 3, 4) - .log() - .map(i -> i * 2) - .zipWith(Flux.range(0, Integer.MAX_VALUE).log(), (one, two) -> String.format("First Flux: %d, Second Flux: %d", one, two)) - .subscribe(elements::add); - - assertThat(elements).containsExactly( - "First Flux: 2, Second Flux: 0", - "First Flux: 4, Second Flux: 1", - "First Flux: 6, Second Flux: 2", - "First Flux: 8, Second Flux: 3"); - } - - @Test - public void givenFlux_whenApplyingBackPressure_thenPushElementsInBatches() { - - List elements = new ArrayList<>(); - - Flux.just(1, 2, 3, 4) - .log() - .subscribe(new Subscriber() { - private Subscription s; - int onNextAmount; - - @Override - public void onSubscribe(final Subscription s) { - this.s = s; - s.request(2); - } - - @Override - public void onNext(final Integer integer) { - elements.add(integer); - onNextAmount++; - if (onNextAmount % 2 == 0) { - s.request(2); - } - } - - @Override - public void onError(final Throwable t) { - } - - @Override - public void onComplete() { - } - }); - - assertThat(elements).containsExactly(1, 2, 3, 4); - } - - @Test - public void givenFlux_whenInParallel_thenSubscribeInDifferentThreads() throws InterruptedException { - List threadNames = new ArrayList<>(); - - Flux.just(1, 2, 3, 4) - .log() - .map(i -> Thread.currentThread().getName()) - .subscribeOn(Schedulers.parallel()) - .subscribe(threadNames::add); - - Thread.sleep(1000); - - assertThat(threadNames).containsExactly("parallel-1", "parallel-1", "parallel-1", "parallel-1"); - } - - @Test - public void givenConnectableFlux_whenConnected_thenShouldStream() { - - List elements = new ArrayList<>(); - - final ConnectableFlux publish = Flux.just(1, 2, 3, 4).publish(); - - publish.subscribe(elements::add); - - assertThat(elements).isEmpty(); - - publish.connect(); - - assertThat(elements).containsExactly(1, 2, 3, 4); - } - -} diff --git a/spring-5-reactive-2/README.md b/spring-5-reactive-2/README.md index 98a5f26433..39752c0cf1 100644 --- a/spring-5-reactive-2/README.md +++ b/spring-5-reactive-2/README.md @@ -2,11 +2,9 @@ This module contains articles about reactive Spring 5 -- [Spring WebClient vs. RestTemplate](https://www.baeldung.com/spring-webclient-resttemplate) - [Validation for Functional Endpoints in Spring 5](https://www.baeldung.com/spring-functional-endpoints-validation) - [Logging a Reactive Sequence](https://www.baeldung.com/spring-reactive-sequence-logging) - [Testing Reactive Streams Using StepVerifier and TestPublisher](https://www.baeldung.com/reactive-streams-step-verifier-test-publisher) -- [Debugging Reactive Streams in Java](https://www.baeldung.com/spring-debugging-reactive-streams) - [Static Content in Spring WebFlux](https://www.baeldung.com/spring-webflux-static-content) - [Server-Sent Events in Spring](https://www.baeldung.com/spring-server-sent-events) - [Backpressure Mechanism in Spring WebFlux](https://www.baeldung.com/spring-webflux-backpressure) diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/ConsumerDebuggingApplication.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/ConsumerDebuggingApplication.java deleted file mode 100644 index 3f01310006..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/ConsumerDebuggingApplication.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.baeldung.debugging.consumer; - -import java.util.Collections; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; - -import reactor.core.publisher.Hooks; - -@SpringBootApplication(exclude = MongoReactiveAutoConfiguration.class) -@EnableScheduling -public class ConsumerDebuggingApplication { - - public static void main(String[] args) { - Hooks.onOperatorDebug(); - SpringApplication app = new SpringApplication(ConsumerDebuggingApplication.class); - app.setDefaultProperties(Collections.singletonMap("server.port", "8082")); - app.run(args); - } - - @Bean - public SecurityWebFilterChain debuggingConsumerSpringSecurityFilterChain(ServerHttpSecurity http) { - http.authorizeExchange() - .anyExchange() - .permitAll(); - http.csrf().disable(); - return http.build(); - } -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/chronjobs/ChronJobs.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/chronjobs/ChronJobs.java deleted file mode 100644 index bf96ab56d6..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/chronjobs/ChronJobs.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.baeldung.debugging.consumer.chronjobs; - -import java.time.Duration; -import java.util.concurrent.ThreadLocalRandom; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.client.WebClient; - -import com.baeldung.debugging.consumer.model.Foo; -import com.baeldung.debugging.consumer.model.FooDto; -import com.baeldung.debugging.consumer.service.FooService; - -import reactor.core.publisher.Flux; - -@Component -public class ChronJobs { - - private static Logger logger = LoggerFactory.getLogger(ChronJobs.class); - private WebClient client = WebClient.create("http://localhost:8081"); - - @Autowired - private FooService service; - - @Scheduled(fixedRate = 10000) - public void consumeInfiniteFlux() { - Flux fluxFoo = client.get() - .uri("/functional-reactive/periodic-foo") - .accept(MediaType.TEXT_EVENT_STREAM) - .retrieve() - .bodyToFlux(FooDto.class) - .delayElements(Duration.ofMillis(100)) - .map(dto -> { - logger.debug("process 1 with dto id {} name{}", dto.getId(), dto.getName()); - return new Foo(dto); - }); - Integer random = ThreadLocalRandom.current() - .nextInt(0, 3); - switch (random) { - case 0: - logger.info("process 1 with approach 1"); - service.processFoo(fluxFoo); - break; - case 1: - logger.info("process 1 with approach 1 EH"); - service.processUsingApproachOneWithErrorHandling(fluxFoo); - break; - default: - logger.info("process 1 with approach 2"); - service.processFooInAnotherScenario(fluxFoo); - break; - - } - } - - @Scheduled(fixedRate = 20000) - public void consumeFiniteFlux2() { - Flux fluxFoo = client.get() - .uri("/functional-reactive/periodic-foo-2") - .accept(MediaType.TEXT_EVENT_STREAM) - .retrieve() - .bodyToFlux(FooDto.class) - .delayElements(Duration.ofMillis(100)) - .map(dto -> { - logger.debug("process 2 with dto id {} name{}", dto.getId(), dto.getName()); - return new Foo(dto); - }); - Integer random = ThreadLocalRandom.current() - .nextInt(0, 3); - switch (random) { - case 0: - logger.info("process 2 with approach 1"); - service.processFoo(fluxFoo); - break; - case 1: - logger.info("process 2 with approach 1 EH"); - service.processUsingApproachOneWithErrorHandling(fluxFoo); - break; - default: - logger.info("process 2 with approach 2"); - service.processFooInAnotherScenario(fluxFoo); - break; - - } - } - - @Scheduled(fixedRate = 20000) - public void consumeFiniteFlux3() { - Flux fluxFoo = client.get() - .uri("/functional-reactive/periodic-foo-2") - .accept(MediaType.TEXT_EVENT_STREAM) - .retrieve() - .bodyToFlux(FooDto.class) - .delayElements(Duration.ofMillis(100)) - .map(dto -> { - logger.debug("process 3 with dto id {} name{}", dto.getId(), dto.getName()); - return new Foo(dto); - }); - logger.info("process 3 with approach 3"); - service.processUsingApproachThree(fluxFoo); - } - - @Scheduled(fixedRate = 20000) - public void consumeFiniteFluxWithCheckpoint4() { - Flux fluxFoo = client.get() - .uri("/functional-reactive/periodic-foo-2") - .accept(MediaType.TEXT_EVENT_STREAM) - .retrieve() - .bodyToFlux(FooDto.class) - .delayElements(Duration.ofMillis(100)) - .map(dto -> { - logger.debug("process 4 with dto id {} name{}", dto.getId(), dto.getName()); - return new Foo(dto); - }); - logger.info("process 4 with approach 4"); - service.processUsingApproachFourWithCheckpoint(fluxFoo); - } - - @Scheduled(fixedRate = 20000) - public void consumeFiniteFluxWitParallelScheduler() { - Flux fluxFoo = client.get() - .uri("/functional-reactive/periodic-foo-2") - .accept(MediaType.TEXT_EVENT_STREAM) - .retrieve() - .bodyToFlux(FooDto.class) - .delayElements(Duration.ofMillis(100)) - .map(dto -> { - logger.debug("process 5-parallel with dto id {} name{}", dto.getId(), dto.getName()); - return new Foo(dto); - }); - logger.info("process 5-parallel with approach 5-parallel"); - service.processUsingApproachFivePublishingToDifferentParallelThreads(fluxFoo); - } - - @Scheduled(fixedRate = 20000) - public void consumeFiniteFluxWithSingleSchedulers() { - Flux fluxFoo = client.get() - .uri("/functional-reactive/periodic-foo-2") - .accept(MediaType.TEXT_EVENT_STREAM) - .retrieve() - .bodyToFlux(FooDto.class) - .delayElements(Duration.ofMillis(100)) - .map(dto -> { - logger.debug("process 5-single with dto id {} name{}", dto.getId(), dto.getName()); - return new Foo(dto); - }); - logger.info("process 5-single with approach 5-single"); - service.processUsingApproachFivePublishingToDifferentSingleThreads(fluxFoo); - } -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/controllers/ReactiveConfigsToggleRestController.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/controllers/ReactiveConfigsToggleRestController.java deleted file mode 100644 index 3dcdc6c7c0..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/controllers/ReactiveConfigsToggleRestController.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.baeldung.debugging.consumer.controllers; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import reactor.core.publisher.Hooks; - -@RestController -public class ReactiveConfigsToggleRestController { - - @GetMapping("/debug-hook-on") - public String setReactiveDebugOn() { - Hooks.onOperatorDebug(); - return "DEBUG HOOK ON"; - } - - @GetMapping("/debug-hook-off") - public String setReactiveDebugOff() { - Hooks.resetOnOperatorDebug(); - return "DEBUG HOOK OFF"; - } - -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/model/Foo.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/model/Foo.java deleted file mode 100644 index 916ca93bfc..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/model/Foo.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.baeldung.debugging.consumer.model; - -import java.util.concurrent.ThreadLocalRandom; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -public class Foo { - - private Integer id; - private String formattedName; - private Integer quantity; - - public Foo(FooDto dto) { - this.id = (ThreadLocalRandom.current() - .nextInt(0, 100) == 0) ? null : dto.getId(); - this.formattedName = dto.getName(); - this.quantity = ThreadLocalRandom.current() - .nextInt(0, 10); - } -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/model/FooDto.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/model/FooDto.java deleted file mode 100644 index 33f19c4e60..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/model/FooDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.baeldung.debugging.consumer.model; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -public class FooDto { - - private Integer id; - private String name; -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooNameHelper.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooNameHelper.java deleted file mode 100644 index 772b360437..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooNameHelper.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.baeldung.debugging.consumer.service; - -import java.util.concurrent.ThreadLocalRandom; - -import com.baeldung.debugging.consumer.model.Foo; - -import reactor.core.publisher.Flux; - -public class FooNameHelper { - - public static Flux concatAndSubstringFooName(Flux flux) { - flux = concatFooName(flux); - flux = substringFooName(flux); - return flux; - } - - public static Flux concatFooName(Flux flux) { - flux = flux.map(foo -> { - String processedName = null; - Integer random = ThreadLocalRandom.current() - .nextInt(0, 80); - processedName = (random != 0) ? foo.getFormattedName() : foo.getFormattedName() + "-bael"; - foo.setFormattedName(processedName); - return foo; - }); - return flux; - } - - public static Flux substringFooName(Flux flux) { - return flux.map(foo -> { - String processedName; - Integer random = ThreadLocalRandom.current() - .nextInt(0, 100); - - processedName = (random == 0) ? foo.getFormattedName() - .substring(10, 15) - : foo.getFormattedName() - .substring(0, 5); - - foo.setFormattedName(processedName); - return foo; - }); - } - -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooQuantityHelper.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooQuantityHelper.java deleted file mode 100644 index 615239313d..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooQuantityHelper.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.baeldung.debugging.consumer.service; - -import java.util.concurrent.ThreadLocalRandom; - -import com.baeldung.debugging.consumer.model.Foo; - -import reactor.core.publisher.Flux; - -public class FooQuantityHelper { - - public static Flux processFooReducingQuantity(Flux flux) { - flux = flux.map(foo -> { - Integer result; - Integer random = ThreadLocalRandom.current() - .nextInt(0, 90); - result = (random == 0) ? result = 0 : foo.getQuantity() + 2; - foo.setQuantity(result); - return foo; - }); - return divideFooQuantity(flux); - } - - public static Flux divideFooQuantity(Flux flux) { - return flux.map(foo -> { - Integer result = Math.round(5 / foo.getQuantity()); - foo.setQuantity(result); - return foo; - }); - } - -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooReporter.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooReporter.java deleted file mode 100644 index f53cd238e0..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooReporter.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.baeldung.debugging.consumer.service; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.baeldung.debugging.consumer.model.Foo; - -import reactor.core.publisher.Flux; - -public class FooReporter { - - private static Logger logger = LoggerFactory.getLogger(FooReporter.class); - - public static Flux reportResult(Flux input, String approach) { - return input.map(foo -> { - if (foo.getId() == null) - throw new IllegalArgumentException("Null id is not valid!"); - logger.info("Reporting for approach {}: Foo with id '{}' name '{}' and quantity '{}'", approach, foo.getId(), foo.getFormattedName(), foo.getQuantity()); - return foo; - }); - } - - public static Flux reportResult(Flux input) { - return reportResult(input, "default"); - } -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooService.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooService.java deleted file mode 100644 index 438f6d473c..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/consumer/service/FooService.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.baeldung.debugging.consumer.service; - -import static com.baeldung.debugging.consumer.service.FooNameHelper.concatAndSubstringFooName; -import static com.baeldung.debugging.consumer.service.FooNameHelper.substringFooName; -import static com.baeldung.debugging.consumer.service.FooQuantityHelper.divideFooQuantity; -import static com.baeldung.debugging.consumer.service.FooQuantityHelper.processFooReducingQuantity; -import static com.baeldung.debugging.consumer.service.FooReporter.reportResult; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import com.baeldung.debugging.consumer.model.Foo; - -import reactor.core.publisher.Flux; -import reactor.core.scheduler.Schedulers; - -@Component -public class FooService { - - private static Logger logger = LoggerFactory.getLogger(FooService.class); - - public void processFoo(Flux flux) { - flux = FooNameHelper.concatFooName(flux); - flux = FooNameHelper.substringFooName(flux); - flux = flux.log(); - flux = FooReporter.reportResult(flux); - flux = flux.doOnError(error -> { - logger.error("The following error happened on processFoo method!", error); - }); - flux.subscribe(); - } - - public void processFooInAnotherScenario(Flux flux) { - flux = FooNameHelper.substringFooName(flux); - flux = FooQuantityHelper.divideFooQuantity(flux); - flux.subscribe(); - } - - public void processUsingApproachOneWithErrorHandling(Flux flux) { - logger.info("starting approach one w error handling!"); - flux = concatAndSubstringFooName(flux); - flux = concatAndSubstringFooName(flux); - flux = substringFooName(flux); - flux = processFooReducingQuantity(flux); - flux = processFooReducingQuantity(flux); - flux = processFooReducingQuantity(flux); - flux = reportResult(flux, "ONE w/ EH"); - flux = flux.doOnError(error -> { - logger.error("Approach 1 with Error Handling failed!", error); - }); - flux.subscribe(); - } - - public void processUsingApproachThree(Flux flux) { - logger.info("starting approach three!"); - flux = concatAndSubstringFooName(flux); - flux = reportResult(flux, "THREE"); - flux = flux.doOnError(error -> { - logger.error("Approach 3 failed!", error); - }); - flux.subscribe(); - } - - public void processUsingApproachFourWithCheckpoint(Flux flux) { - logger.info("starting approach four!"); - flux = concatAndSubstringFooName(flux); - flux = flux.checkpoint("CHECKPOINT 1"); - flux = concatAndSubstringFooName(flux); - flux = divideFooQuantity(flux); - flux = flux.checkpoint("CHECKPOINT 2", true); - flux = reportResult(flux, "FOUR"); - flux = concatAndSubstringFooName(flux).doOnError(error -> { - logger.error("Approach 4 failed!", error); - }); - flux.subscribe(); - } - - public void processUsingApproachFourWithInitialCheckpoint(Flux flux) { - logger.info("starting approach four!"); - flux = concatAndSubstringFooName(flux); - flux = flux.checkpoint("CHECKPOINT 1", true); - flux = concatAndSubstringFooName(flux); - flux = divideFooQuantity(flux); - flux = reportResult(flux, "FOUR"); - flux = flux.doOnError(error -> { - logger.error("Approach 4-2 failed!", error); - }); - flux.subscribe(); - } - - public void processUsingApproachFivePublishingToDifferentParallelThreads(Flux flux) { - logger.info("starting approach five-parallel!"); - flux = concatAndSubstringFooName(flux).publishOn(Schedulers.newParallel("five-parallel-foo")) - .log(); - flux = concatAndSubstringFooName(flux); - flux = divideFooQuantity(flux); - flux = reportResult(flux, "FIVE-PARALLEL").publishOn(Schedulers.newSingle("five-parallel-bar")); - flux = concatAndSubstringFooName(flux).doOnError(error -> { - logger.error("Approach 5-parallel failed!", error); - }); - flux.subscribeOn(Schedulers.newParallel("five-parallel-starter")) - .subscribe(); - } - - public void processUsingApproachFivePublishingToDifferentSingleThreads(Flux flux) { - logger.info("starting approach five-single!"); - flux = flux.log() - .subscribeOn(Schedulers.newSingle("five-single-starter")); - flux = concatAndSubstringFooName(flux).publishOn(Schedulers.newSingle("five-single-foo")); - flux = concatAndSubstringFooName(flux); - flux = divideFooQuantity(flux); - flux = reportResult(flux, "FIVE-SINGLE").publishOn(Schedulers.newSingle("five-single-bar")); - flux = concatAndSubstringFooName(flux).doOnError(error -> { - logger.error("Approach 5-single failed!", error); - }); - flux.subscribe(); - } - -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/ServerDebuggingApplication.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/ServerDebuggingApplication.java deleted file mode 100644 index 4fdc1dd137..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/ServerDebuggingApplication.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.baeldung.debugging.server; - -import java.util.Collections; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; -import org.springframework.web.reactive.config.EnableWebFlux; - -@EnableWebFlux -@SpringBootApplication -public class ServerDebuggingApplication { - - public static void main(String[] args) { - SpringApplication app = new SpringApplication(ServerDebuggingApplication.class); - app.setDefaultProperties(Collections.singletonMap("server.port", "8081")); - app.run(args); - } - - @Bean - public SecurityWebFilterChain debuggingServerSpringSecurityFilterChain(ServerHttpSecurity http) { - http.authorizeExchange() - .anyExchange() - .permitAll(); - return http.build(); - } -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/handlers/ServerHandler.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/handlers/ServerHandler.java deleted file mode 100644 index 759cd9b01d..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/handlers/ServerHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.baeldung.debugging.server.handlers; - -import java.time.Duration; -import java.util.concurrent.ThreadLocalRandom; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; - -import com.baeldung.debugging.server.model.Foo; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -@Component -public class ServerHandler { - - private static Logger logger = LoggerFactory.getLogger(ServerHandler.class); - - public Mono useHandler(final ServerRequest request) { - // there are chances that something goes wrong here... - return ServerResponse.ok() - .contentType(MediaType.TEXT_EVENT_STREAM) - .body(Flux.interval(Duration.ofSeconds(1)) - .map(sequence -> { - logger.info("retrieving Foo. Sequence: {}", sequence); - if (ThreadLocalRandom.current() - .nextInt(0, 50) == 1) { - throw new RuntimeException("There was an error retrieving the Foo!"); - } - return new Foo(sequence, "name" + sequence); - - }), Foo.class); - } - - public Mono useHandlerFinite(final ServerRequest request) { - return ServerResponse.ok() - .contentType(MediaType.TEXT_EVENT_STREAM) - .body(Flux.range(0, 50) - .map(sequence -> { - return new Foo(new Long(sequence), "theFooNameNumber" + sequence); - }), Foo.class); - } -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/model/Foo.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/model/Foo.java deleted file mode 100644 index 2d9491f3dd..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/model/Foo.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.baeldung.debugging.server.model; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class Foo { - - private Long id; - private String name; - -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/routers/ServerRouter.java b/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/routers/ServerRouter.java deleted file mode 100644 index 6378b2213d..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/debugging/server/routers/ServerRouter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.baeldung.debugging.server.routers; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -import com.baeldung.debugging.server.handlers.ServerHandler; - -@Configuration -public class ServerRouter { - - @Bean - public RouterFunction responseRoute(@Autowired ServerHandler handler) { - return RouterFunctions.route(RequestPredicates.GET("/functional-reactive/periodic-foo"), handler::useHandler) - .andRoute(RequestPredicates.GET("/functional-reactive/periodic-foo-2"), handler::useHandlerFinite); - } - -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/webclient/Tweet.java b/spring-5-reactive-2/src/main/java/com/baeldung/webclient/Tweet.java deleted file mode 100644 index 8d294955f3..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/webclient/Tweet.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.baeldung.webclient; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class Tweet { - private String text; - private String username; -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/webclient/TweetsSlowServiceController.java b/spring-5-reactive-2/src/main/java/com/baeldung/webclient/TweetsSlowServiceController.java deleted file mode 100644 index fecaca25ef..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/webclient/TweetsSlowServiceController.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.baeldung.webclient; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Arrays; -import java.util.List; - -@RestController -public class TweetsSlowServiceController { - - @GetMapping("/slow-service-tweets") - private List getAllTweets() throws Exception { - Thread.sleep(2000L); // delay - return Arrays.asList( - new Tweet("RestTemplate rules", "@user1"), - new Tweet("WebClient is better", "@user2"), - new Tweet("OK, both are useful", "@user1")); - } -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/webclient/WebClientApplication.java b/spring-5-reactive-2/src/main/java/com/baeldung/webclient/WebClientApplication.java deleted file mode 100644 index 3c53a2c1d3..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/webclient/WebClientApplication.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.baeldung.webclient; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; - -@SpringBootApplication -public class WebClientApplication { - public static void main(String[] args) { - SpringApplication.run(WebClientApplication.class, args); - } - - @Bean - public SecurityWebFilterChain functionalValidationsSpringSecurityFilterChain(ServerHttpSecurity http) { - http.authorizeExchange() - .anyExchange() - .permitAll(); - http.csrf().disable(); - return http.build(); - } -} diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/webclient/WebController.java b/spring-5-reactive-2/src/main/java/com/baeldung/webclient/WebController.java deleted file mode 100644 index 73f5724819..0000000000 --- a/spring-5-reactive-2/src/main/java/com/baeldung/webclient/WebController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.baeldung.webclient; - -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Flux; - -import java.util.List; - -@Slf4j -@RestController -public class WebController { - - private static final int DEFAULT_PORT = 8080; - - @Setter - private int serverPort = DEFAULT_PORT; - - @GetMapping("/tweets-blocking") - public List getTweetsBlocking() { - log.info("Starting BLOCKING Controller!"); - final String uri = getSlowServiceUri(); - - RestTemplate restTemplate = new RestTemplate(); - ResponseEntity> response = restTemplate.exchange( - uri, HttpMethod.GET, null, - new ParameterizedTypeReference>(){}); - - List result = response.getBody(); - result.forEach(tweet -> log.info(tweet.toString())); - log.info("Exiting BLOCKING Controller!"); - return result; - } - - @GetMapping(value = "/tweets-non-blocking", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux getTweetsNonBlocking() { - log.info("Starting NON-BLOCKING Controller!"); - Flux tweetFlux = WebClient.create() - .get() - .uri(getSlowServiceUri()) - .retrieve() - .bodyToFlux(Tweet.class); - - tweetFlux.subscribe(tweet -> log.info(tweet.toString())); - log.info("Exiting NON-BLOCKING Controller!"); - return tweetFlux; - } - - private String getSlowServiceUri() { - return "http://localhost:" + serverPort + "/slow-service-tweets"; - } - -} diff --git a/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/ConsumerFooServiceIntegrationTest.java b/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/ConsumerFooServiceIntegrationTest.java deleted file mode 100644 index 9d04541f8d..0000000000 --- a/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/ConsumerFooServiceIntegrationTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.baeldung.debugging.consumer; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.baeldung.debugging.consumer.model.Foo; -import com.baeldung.debugging.consumer.service.FooService; -import com.baeldung.debugging.consumer.utils.ListAppender; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.IThrowableProxy; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Hooks; - -public class ConsumerFooServiceIntegrationTest { - - FooService service = new FooService(); - - @BeforeEach - public void clearLogList() { - Hooks.onOperatorDebug(); - ListAppender.clearEventList(); - } - - @Test - public void givenFooWithNullId_whenProcessFoo_thenLogsWithDebugTrace() { - Foo one = new Foo(1, "nameverylong", 8); - Foo two = new Foo(null, "nameverylong", 4); - Flux flux = Flux.just(one, two); - - service.processFoo(flux); - - Collection allLoggedEntries = ListAppender.getEvents() - .stream() - .map(ILoggingEvent::getFormattedMessage) - .collect(Collectors.toList()); - - Collection allSuppressedEntries = ListAppender.getEvents() - .stream() - .map(ILoggingEvent::getThrowableProxy) - .flatMap(t -> { - return Optional.ofNullable(t) - .map(IThrowableProxy::getSuppressed) - .map(Arrays::stream) - .orElse(Stream.empty()); - }) - .map(IThrowableProxy::getClassName) - .collect(Collectors.toList()); - assertThat(allLoggedEntries).anyMatch(entry -> entry.contains("The following error happened on processFoo method!")) - .anyMatch(entry -> entry.contains("| onSubscribe")) - .anyMatch(entry -> entry.contains("| cancel()")); - - assertThat(allSuppressedEntries) - .anyMatch(entry -> entry.contains("reactor.core.publisher.FluxOnAssembly$OnAssemblyException")); - } -} diff --git a/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/ConsumerFooServiceLiveTest.java b/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/ConsumerFooServiceLiveTest.java deleted file mode 100644 index e61ea9e155..0000000000 --- a/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/ConsumerFooServiceLiveTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.baeldung.debugging.consumer; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec; - -import com.baeldung.debugging.consumer.service.FooService; - -/** - * In order to run this live test, start the following classes: - * - com.baeldung.debugging.server.ServerDebuggingApplication - * - com.baeldung.debugging.consumer.ConsumerDebuggingApplication - */ -public class ConsumerFooServiceLiveTest { - - FooService service = new FooService(); - - private static final String BASE_URL = "http://localhost:8082"; - private static final String DEBUG_HOOK_ON = BASE_URL + "/debug-hook-on"; - private static final String DEBUG_HOOK_OFF = BASE_URL + "/debug-hook-off"; - - private static WebTestClient client; - - @BeforeAll - public static void setup() { - client = WebTestClient.bindToServer() - .baseUrl(BASE_URL) - .build(); - } - - @Test - public void whenRequestingDebugHookOn_thenObtainExpectedMessage() { - ResponseSpec response = client.get() - .uri(DEBUG_HOOK_ON) - .exchange(); - response.expectStatus() - .isOk() - .expectBody(String.class) - .isEqualTo("DEBUG HOOK ON"); - } - - @Test - public void whenRequestingDebugHookOff_thenObtainExpectedMessage() { - ResponseSpec response = client.get() - .uri(DEBUG_HOOK_OFF) - .exchange(); - response.expectStatus() - .isOk() - .expectBody(String.class) - .isEqualTo("DEBUG HOOK OFF"); - } - -} diff --git a/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/utils/ListAppender.java b/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/utils/ListAppender.java deleted file mode 100644 index c8c1c110bb..0000000000 --- a/spring-5-reactive-2/src/test/java/com/baeldung/debugging/consumer/utils/ListAppender.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.baeldung.debugging.consumer.utils; - -import java.util.ArrayList; -import java.util.List; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.AppenderBase; - -public class ListAppender extends AppenderBase { - - static private List events = new ArrayList<>(); - - @Override - protected void append(ILoggingEvent eventObject) { - events.add(eventObject); - } - - public static List getEvents() { - return events; - } - - public static void clearEventList() { - events.clear(); - } -} diff --git a/spring-5-reactive-2/src/test/java/com/baeldung/webclient/WebControllerIntegrationTest.java b/spring-5-reactive-2/src/test/java/com/baeldung/webclient/WebControllerIntegrationTest.java deleted file mode 100644 index 09c3a5fb84..0000000000 --- a/spring-5-reactive-2/src/test/java/com/baeldung/webclient/WebControllerIntegrationTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.baeldung.webclient; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WebClientApplication.class) -public class WebControllerIntegrationTest { - - @LocalServerPort - int randomServerPort; - - @Autowired - private WebTestClient testClient; - - @Autowired - private WebController webController; - - @Before - public void setup() { - webController.setServerPort(randomServerPort); - } - - @Test - public void whenEndpointWithBlockingClientIsCalled_thenThreeTweetsAreReceived() { - testClient.get() - .uri("/tweets-blocking") - .exchange() - .expectStatus() - .isOk() - .expectBodyList(Tweet.class) - .hasSize(3); - } - - @Test - public void whenEndpointWithNonBlockingClientIsCalled_thenThreeTweetsAreReceived() { - testClient.get() - .uri("/tweets-non-blocking") - .exchange() - .expectStatus() - .isOk() - .expectBodyList(Tweet.class) - .hasSize(3); - } -} \ No newline at end of file diff --git a/spring-5-reactive-security/README.md b/spring-5-reactive-security/README.md index 915f74cd78..37f999648c 100644 --- a/spring-5-reactive-security/README.md +++ b/spring-5-reactive-security/README.md @@ -8,8 +8,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring ### Relevant Articles - [Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuators) -- [Spring Security 5 for Reactive Applications](https://www.baeldung.com/spring-security-5-reactive) -- [Guide to Spring 5 WebFlux](https://www.baeldung.com/spring-webflux) -- [Introduction to the Functional Web Framework in Spring 5](https://www.baeldung.com/spring-5-functional-web) - [Guide to the AuthenticationManagerResolver in Spring Security](https://www.baeldung.com/spring-security-authenticationmanagerresolver) - [Spring Webflux and CORS](https://www.baeldung.com/spring-webflux-cors) diff --git a/spring-5-reactive-security/pom.xml b/spring-5-reactive-security/pom.xml index 140c6b8296..7b697c7b00 100644 --- a/spring-5-reactive-security/pom.xml +++ b/spring-5-reactive-security/pom.xml @@ -110,7 +110,7 @@ org.springframework.boot spring-boot-maven-plugin - com.baeldung.webflux.EmployeeSpringApplication + com.baeldung.reactive.actuator.Spring5ReactiveApplication JAR diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/functional/EmployeeFunctionalConfig.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/functional/EmployeeFunctionalConfig.java deleted file mode 100644 index 76b697c1aa..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/functional/EmployeeFunctionalConfig.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.baeldung.reactive.functional; - -import static org.springframework.web.reactive.function.BodyExtractors.toMono; -import static org.springframework.web.reactive.function.server.RequestPredicates.GET; -import static org.springframework.web.reactive.function.server.RequestPredicates.POST; -import static org.springframework.web.reactive.function.server.RouterFunctions.route; -import static org.springframework.web.reactive.function.server.ServerResponse.ok; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.ServerResponse; - -import com.baeldung.webflux.Employee; -import com.baeldung.webflux.EmployeeRepository; - -@Configuration -public class EmployeeFunctionalConfig { - - @Bean - EmployeeRepository employeeRepository() { - return new EmployeeRepository(); - } - - @Bean - RouterFunction getAllEmployeesRoute() { - return route(GET("/employees"), - req -> ok().body( - employeeRepository().findAllEmployees(), Employee.class)); - } - - @Bean - RouterFunction getEmployeeByIdRoute() { - return route(GET("/employees/{id}"), - req -> ok().body( - employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class)); - } - - @Bean - RouterFunction updateEmployeeRoute() { - return route(POST("/employees/update"), - req -> req.body(toMono(Employee.class)) - .doOnNext(employeeRepository()::updateEmployee) - .then(ok().build())); - } - - @Bean - RouterFunction composedRoutes() { - return - route(GET("/employees"), - req -> ok().body( - employeeRepository().findAllEmployees(), Employee.class)) - - .and(route(GET("/employees/{id}"), - req -> ok().body( - employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class))) - - .and(route(POST("/employees/update"), - req -> req.body(toMono(Employee.class)) - .doOnNext(employeeRepository()::updateEmployee) - .then(ok().build()))); - } - - @Bean - public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { - http.csrf() - .disable() - .authorizeExchange() - .anyExchange() - .permitAll(); - return http.build(); - } -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/functional/EmployeeSpringFunctionalApplication.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/functional/EmployeeSpringFunctionalApplication.java deleted file mode 100644 index 1f2bd871fc..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/functional/EmployeeSpringFunctionalApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.baeldung.reactive.functional; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class EmployeeSpringFunctionalApplication { - - public static void main(String[] args) { - SpringApplication.run(EmployeeSpringFunctionalApplication.class, args); - } - -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/GreetController.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/GreetController.java deleted file mode 100644 index 99b79d88ea..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/GreetController.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.baeldung.reactive.security; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Mono; - -import java.security.Principal; - -@RestController -public class GreetController { - - private GreetService greetService; - - public GreetController(GreetService greetService) { - this.greetService = greetService; - } - - @GetMapping("/") - public Mono greet(Mono principal) { - return principal - .map(Principal::getName) - .map(name -> String.format("Hello, %s", name)); - } - - @GetMapping("/admin") - public Mono greetAdmin(Mono principal) { - return principal - .map(Principal::getName) - .map(name -> String.format("Admin access: %s", name)); - } - - @GetMapping("/greetService") - public Mono greetService() { - return greetService.greet(); - } - -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/GreetService.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/GreetService.java deleted file mode 100644 index 93df64bced..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/GreetService.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.baeldung.reactive.security; - -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; -import reactor.core.publisher.Mono; - -@Service -public class GreetService { - - @PreAuthorize("hasRole('ADMIN')") - public Mono greet() { - return Mono.just("Hello from service!"); - } - -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/SecurityConfig.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/SecurityConfig.java deleted file mode 100644 index 64e96ddae1..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/SecurityConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.baeldung.reactive.security; - -import org.springframework.boot.actuate.autoconfigure.security.reactive.EndpointRequest; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.server.SecurityWebFilterChain; - -import com.baeldung.reactive.actuator.FeaturesEndpoint; - -@EnableWebFluxSecurity -@EnableReactiveMethodSecurity -public class SecurityConfig { - - @Bean - public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) { - return http.authorizeExchange() - .pathMatchers("/admin") - .hasAuthority("ROLE_ADMIN") - .matchers(EndpointRequest.to(FeaturesEndpoint.class)) - .permitAll() - .anyExchange() - .authenticated() - .and() - .formLogin() - .and() - .csrf() - .disable() - .build(); - } - - @Bean - public MapReactiveUserDetailsService userDetailsService() { - UserDetails user = User - .withUsername("user") - .password(passwordEncoder().encode("password")) - .roles("USER") - .build(); - - UserDetails admin = User - .withUsername("admin") - .password(passwordEncoder().encode("password")) - .roles("ADMIN") - .build(); - - return new MapReactiveUserDetailsService(user, admin); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/SpringSecurity5Application.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/SpringSecurity5Application.java deleted file mode 100644 index bb0f007ada..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/security/SpringSecurity5Application.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.baeldung.reactive.security; - -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; -import org.springframework.web.reactive.config.EnableWebFlux; -import org.springframework.web.server.adapter.WebHttpHandlerBuilder; - -import reactor.netty.DisposableServer; -import reactor.netty.http.server.HttpServer; - -@ComponentScan(basePackages = {"com.baeldung.reactive.security"}) -@EnableWebFlux -public class SpringSecurity5Application { - - public static void main(String[] args) { - try (AnnotationConfigApplicationContext context = - new AnnotationConfigApplicationContext(SpringSecurity5Application.class)) { - context.getBean(DisposableServer.class).onDispose().block(); - } - } - - @Bean - public DisposableServer disposableServer(ApplicationContext context) { - HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context) - .build(); - ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler); - HttpServer httpServer = HttpServer.create().host("localhost").port(8083); - return httpServer.handle(adapter).bindNow(); - } - -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeController.java b/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeController.java deleted file mode 100644 index 34e44afc8b..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeController.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.baeldung.webflux; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -@RestController -@RequestMapping("/employees") -public class EmployeeController { - - private EmployeeRepository employeeRepository; - - public EmployeeController(EmployeeRepository employeeRepository) { - this.employeeRepository = employeeRepository; - } - - @GetMapping("/{id}") - private Mono getEmployeeById(@PathVariable String id) { - return employeeRepository.findEmployeeById(id); - } - - @GetMapping - private Flux getAllEmployees() { - return employeeRepository.findAllEmployees(); - } - - @PostMapping("/update") - private Mono updateEmployee(@RequestBody Employee employee) { - return employeeRepository.updateEmployee(employee); - } - -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeRepository.java b/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeRepository.java deleted file mode 100644 index d7f618f178..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeRepository.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.baeldung.webflux; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.stereotype.Repository; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -@Repository -public class EmployeeRepository { - - static Map employeeData; - - static Map employeeAccessData; - - static - { - employeeData = new HashMap<>(); - employeeData.put("1",new Employee("1","Employee 1")); - employeeData.put("2",new Employee("2","Employee 2")); - employeeData.put("3",new Employee("3","Employee 3")); - employeeData.put("4",new Employee("4","Employee 4")); - employeeData.put("5",new Employee("5","Employee 5")); - employeeData.put("6",new Employee("6","Employee 6")); - employeeData.put("7",new Employee("7","Employee 7")); - employeeData.put("8",new Employee("8","Employee 8")); - employeeData.put("9",new Employee("9","Employee 9")); - employeeData.put("10",new Employee("10","Employee 10")); - - employeeAccessData=new HashMap<>(); - employeeAccessData.put("1", "Employee 1 Access Key"); - employeeAccessData.put("2", "Employee 2 Access Key"); - employeeAccessData.put("3", "Employee 3 Access Key"); - employeeAccessData.put("4", "Employee 4 Access Key"); - employeeAccessData.put("5", "Employee 5 Access Key"); - employeeAccessData.put("6", "Employee 6 Access Key"); - employeeAccessData.put("7", "Employee 7 Access Key"); - employeeAccessData.put("8", "Employee 8 Access Key"); - employeeAccessData.put("9", "Employee 9 Access Key"); - employeeAccessData.put("10", "Employee 10 Access Key"); - } - - public Mono findEmployeeById(String id) - { - return Mono.just(employeeData.get(id)); - } - - public Flux findAllEmployees() - { - return Flux.fromIterable(employeeData.values()); - } - - public Mono updateEmployee(Employee employee) - { - Employee existingEmployee=employeeData.get(employee.getId()); - if(existingEmployee!=null) - { - existingEmployee.setName(employee.getName()); - } - return Mono.just(existingEmployee); - } -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeSpringApplication.java b/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeSpringApplication.java deleted file mode 100644 index 2652c36695..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeSpringApplication.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.baeldung.webflux; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class EmployeeSpringApplication { - - public static void main(String[] args) { - - SpringApplication.run(EmployeeSpringApplication.class, args); - - EmployeeWebClient employeeWebClient = new EmployeeWebClient(); - employeeWebClient.consume(); - } - -} diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeWebClient.java b/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeWebClient.java deleted file mode 100644 index eb32408a7f..0000000000 --- a/spring-5-reactive-security/src/main/java/com/baeldung/webflux/EmployeeWebClient.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.baeldung.webflux; - -import org.springframework.web.reactive.function.client.WebClient; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -public class EmployeeWebClient { - - WebClient client = WebClient.create("http://localhost:8080"); - - public void consume() { - - Mono employeeMono = client.get() - .uri("/employees/{id}", "1") - .retrieve() - .bodyToMono(Employee.class); - - employeeMono.subscribe(System.out::println); - - Flux employeeFlux = client.get() - .uri("/employees") - .retrieve() - .bodyToFlux(Employee.class); - - employeeFlux.subscribe(System.out::println); - } -} \ No newline at end of file diff --git a/spring-5-reactive-security/src/test/java/com/baeldung/SpringContextTest.java b/spring-5-reactive-security/src/test/java/com/baeldung/SpringContextTest.java index b4f4118527..e6123de118 100644 --- a/spring-5-reactive-security/src/test/java/com/baeldung/SpringContextTest.java +++ b/spring-5-reactive-security/src/test/java/com/baeldung/SpringContextTest.java @@ -1,14 +1,13 @@ package com.baeldung; +import com.baeldung.reactive.actuator.Spring5ReactiveApplication; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; -import com.baeldung.reactive.security.SpringSecurity5Application; - @RunWith(SpringRunner.class) -@SpringBootTest(classes = SpringSecurity5Application.class) +@SpringBootTest(classes = Spring5ReactiveApplication.class) public class SpringContextTest { @Test diff --git a/spring-5-reactive-security/src/test/java/com/baeldung/reactive/functional/EmployeeSpringFunctionalIntegrationTest.java b/spring-5-reactive-security/src/test/java/com/baeldung/reactive/functional/EmployeeSpringFunctionalIntegrationTest.java deleted file mode 100644 index d8e2f0b23b..0000000000 --- a/spring-5-reactive-security/src/test/java/com/baeldung/reactive/functional/EmployeeSpringFunctionalIntegrationTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.baeldung.reactive.functional; - -import com.baeldung.webflux.Employee; -import com.baeldung.webflux.EmployeeRepository; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.Arrays; -import java.util.List; - -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; - -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = EmployeeSpringFunctionalApplication.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class EmployeeSpringFunctionalIntegrationTest { - - @Autowired - private EmployeeFunctionalConfig config; - - @MockBean - private EmployeeRepository employeeRepository; - - @Test - public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { - WebTestClient client = WebTestClient - .bindToRouterFunction(config.getEmployeeByIdRoute()) - .build(); - - Employee employee = new Employee("1", "Employee 1"); - - given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); - - client.get() - .uri("/employees/1") - .exchange() - .expectStatus() - .isOk() - .expectBody(Employee.class) - .isEqualTo(employee); - } - - @Test - public void whenGetAllEmployees_thenCorrectEmployees() { - WebTestClient client = WebTestClient - .bindToRouterFunction(config.getAllEmployeesRoute()) - .build(); - - List employees = Arrays.asList( - new Employee("1", "Employee 1"), - new Employee("2", "Employee 2")); - - Flux employeeFlux = Flux.fromIterable(employees); - given(employeeRepository.findAllEmployees()).willReturn(employeeFlux); - - client.get() - .uri("/employees") - .exchange() - .expectStatus() - .isOk() - .expectBodyList(Employee.class) - .isEqualTo(employees); - } - - @Test - public void whenUpdateEmployee_thenEmployeeUpdated() { - WebTestClient client = WebTestClient - .bindToRouterFunction(config.updateEmployeeRoute()) - .build(); - - Employee employee = new Employee("1", "Employee 1 Updated"); - - client.post() - .uri("/employees/update") - .body(Mono.just(employee), Employee.class) - .exchange() - .expectStatus() - .isOk(); - - verify(employeeRepository).updateEmployee(employee); - } -} diff --git a/spring-5-reactive-security/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerIntegrationTest.java b/spring-5-reactive-security/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerIntegrationTest.java deleted file mode 100644 index 12a21ed4ef..0000000000 --- a/spring-5-reactive-security/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerIntegrationTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.baeldung.reactive.webflux; - -import static org.mockito.BDDMockito.given; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -import com.baeldung.webflux.EmployeeSpringApplication; -import com.baeldung.webflux.Employee; -import com.baeldung.webflux.EmployeeRepository; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes=EmployeeSpringApplication.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class EmployeeControllerIntegrationTest { - - @Autowired - private WebTestClient testClient; - - @MockBean - private EmployeeRepository employeeRepository; - - @Test - public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { - - Employee employee = new Employee("1", "Employee 1 Name"); - - given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); - testClient.get() - .uri("/employees/1") - .exchange() - .expectStatus() - .isOk() - .expectBody(Employee.class) - .isEqualTo(employee); - } - - @Test - public void whenGetAllEmployees_thenCorrectEmployees() { - - List employeeList = new ArrayList<>(); - - Employee employee1 = new Employee("1", "Employee 1 Name"); - Employee employee2 = new Employee("2", "Employee 2 Name"); - Employee employee3 = new Employee("3", "Employee 3 Name"); - - employeeList.add(employee1); - employeeList.add(employee2); - employeeList.add(employee3); - - Flux employeeFlux = Flux.fromIterable(employeeList); - - given(employeeRepository.findAllEmployees()).willReturn(employeeFlux); - testClient.get() - .uri("/employees") - .exchange() - .expectStatus() - .isOk() - .expectBodyList(Employee.class) - .hasSize(3) - .isEqualTo(employeeList); - } -} diff --git a/spring-5-reactive-security/src/test/java/com/baeldung/security/SecurityIntegrationTest.java b/spring-5-reactive-security/src/test/java/com/baeldung/security/SecurityIntegrationTest.java deleted file mode 100644 index 423500e09c..0000000000 --- a/spring-5-reactive-security/src/test/java/com/baeldung/security/SecurityIntegrationTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.baeldung.security; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -import com.baeldung.reactive.security.SpringSecurity5Application; - -@RunWith(SpringRunner.class) -@ContextConfiguration(classes = SpringSecurity5Application.class) -public class SecurityIntegrationTest { - - @Autowired - ApplicationContext context; - - private WebTestClient rest; - - @Before - public void setup() { - this.rest = WebTestClient.bindToApplicationContext(this.context).configureClient().build(); - } - - @Test - public void whenNoCredentials_thenRedirectToLogin() { - this.rest.get().uri("/").exchange().expectStatus().is3xxRedirection(); - } - - @Test - @Ignore - @WithMockUser - public void whenHasCredentials_thenSeesGreeting() { - this.rest.get().uri("/").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("Hello, user"); - } -} diff --git a/spring-5-reactive/README.md b/spring-5-reactive/README.md index 1945b7ea33..0f4756c53e 100644 --- a/spring-5-reactive/README.md +++ b/spring-5-reactive/README.md @@ -7,11 +7,9 @@ The "REST With Spring" Classes: https://bit.ly/restwithspring ### Relevant Articles -- [Spring 5 WebClient](https://www.baeldung.com/spring-5-webclient) - [Exploring the Spring 5 WebFlux URL Matching](https://www.baeldung.com/spring-5-mvc-url-matching) - [Reactive WebSockets with Spring 5](https://www.baeldung.com/spring-5-reactive-websockets) - [Spring Webflux Filters](https://www.baeldung.com/spring-webflux-filters) - [How to Set a Header on a Response with Spring 5](https://www.baeldung.com/spring-response-header) -- [Handling Errors in Spring WebFlux](https://www.baeldung.com/spring-webflux-errors) - [A Guide to Spring Session Reactive Support: WebSession](https://www.baeldung.com/spring-session-reactive) - More articles: [[next -->]](/spring-5-reactive-2) diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java deleted file mode 100644 index 5885ac50d0..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java +++ /dev/null @@ -1,53 +0,0 @@ - -package com.baeldung.reactive.errorhandling; - -import java.util.Map; - -import org.springframework.boot.web.error.ErrorAttributeOptions; -import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; - -@Component -public class GlobalErrorAttributes extends DefaultErrorAttributes{ - - private HttpStatus status = HttpStatus.BAD_REQUEST; - private String message = "please provide a name"; - - @Override - public Map getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) { - Map map = super.getErrorAttributes(request, options); - map.put("status", getStatus()); - map.put("message", getMessage()); - return map; - } - - /** - * @return the status - */ - public HttpStatus getStatus() { - return status; - } - - /** - * @param status the status to set - */ - public void setStatus(HttpStatus status) { - this.status = status; - } - - /** - * @return the message - */ - public String getMessage() { - return message; - } - - /** - * @param message the message to set - */ - public void setMessage(String message) { - this.message = message; - } -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java deleted file mode 100644 index 4f3f1795da..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java +++ /dev/null @@ -1,50 +0,0 @@ - -package com.baeldung.reactive.errorhandling; - -import java.util.Map; - -import org.springframework.boot.autoconfigure.web.WebProperties; -import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler; -import org.springframework.boot.web.error.ErrorAttributeOptions; -import org.springframework.boot.web.reactive.error.ErrorAttributes; -import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.Order; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.codec.ServerCodecConfigurer; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; - -import reactor.core.publisher.Mono; - -@Component -@Order(-2) -public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler { - - public GlobalErrorWebExceptionHandler(GlobalErrorAttributes g, ApplicationContext applicationContext, - ServerCodecConfigurer serverCodecConfigurer) { - super(g, new WebProperties.Resources(), applicationContext); - super.setMessageWriters(serverCodecConfigurer.getWriters()); - super.setMessageReaders(serverCodecConfigurer.getReaders()); - } - - @Override - protected RouterFunction getRoutingFunction(final ErrorAttributes errorAttributes) { - return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse); - } - - private Mono renderErrorResponse(final ServerRequest request) { - - final Map errorPropertiesMap = getErrorAttributes(request, ErrorAttributeOptions.defaults()); - - return ServerResponse.status(HttpStatus.BAD_REQUEST) - .contentType(MediaType.APPLICATION_JSON) - .body(BodyInserters.fromValue(errorPropertiesMap)); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java deleted file mode 100644 index 38d35544a7..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java +++ /dev/null @@ -1,12 +0,0 @@ - -package com.baeldung.reactive.errorhandling; - -import org.springframework.http.HttpStatus; -import org.springframework.web.server.ResponseStatusException; - -public class NameRequiredException extends ResponseStatusException { - - public NameRequiredException(HttpStatus status, String message, Throwable e) { - super(status, message, e); - } -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java deleted file mode 100644 index c71c8ecac0..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java +++ /dev/null @@ -1,29 +0,0 @@ - -package com.baeldung.reactive.errorhandling.handlers; - -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; -import reactor.core.publisher.Mono; - -@Component -public class Handler1 { - - public Mono handleRequest1(ServerRequest request) { - return sayHello(request).onErrorReturn("Hello, Stranger") - .flatMap(s -> ServerResponse.ok() - .contentType(MediaType.TEXT_PLAIN) - .bodyValue(s)); - } - - private Mono sayHello(ServerRequest request) { - try { - return Mono.just("Hello, " + request.queryParam("name") - .get()); - } catch (Exception e) { - return Mono.error(e); - } - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java deleted file mode 100644 index 92e881543e..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java +++ /dev/null @@ -1,38 +0,0 @@ - -package com.baeldung.reactive.errorhandling.handlers; - -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; -import reactor.core.publisher.Mono; - -@Component -public class Handler2 { - -public Mono handleRequest2(ServerRequest request) { - return - sayHello(request) - .flatMap(s -> ServerResponse.ok() - .contentType(MediaType.TEXT_PLAIN) - .bodyValue(s)) - .onErrorResume(e -> sayHelloFallback() - .flatMap(s -> ServerResponse.ok() - .contentType(MediaType.TEXT_PLAIN) - .bodyValue(s))); -} - - private Mono sayHello(ServerRequest request) { - try { - return Mono.just("Hello, " + request.queryParam("name") - .get()); - } catch (Exception e) { - return Mono.error(e); - } - } - - private Mono sayHelloFallback() { - return Mono.just("Hello, Stranger"); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java deleted file mode 100644 index 8c988a6633..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java +++ /dev/null @@ -1,34 +0,0 @@ - -package com.baeldung.reactive.errorhandling.handlers; - -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; -import reactor.core.publisher.Mono; - -@Component -public class Handler3 { - - public Mono handleRequest3(ServerRequest request) { - return - sayHello(request) - .flatMap(s -> ServerResponse.ok() - .contentType(MediaType.TEXT_PLAIN) - .bodyValue(s)) - .onErrorResume(e -> (Mono.just("Hi, I looked around for your name but found: " + - e.getMessage())).flatMap(s -> ServerResponse.ok() - .contentType(MediaType.TEXT_PLAIN) - .bodyValue(s))); - } - - private Mono sayHello(ServerRequest request) { - try { - return Mono.just("Hello, " + request.queryParam("name") - .get()); - } catch (Exception e) { - return Mono.error(e); - } - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java deleted file mode 100644 index 3d6ef258d3..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java +++ /dev/null @@ -1,30 +0,0 @@ - -package com.baeldung.reactive.errorhandling.handlers; - -import com.baeldung.reactive.errorhandling.NameRequiredException; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; -import reactor.core.publisher.Mono; - -@Component -public class Handler4 { - -public Mono handleRequest4(ServerRequest request) { - return ServerResponse.ok() - .body(sayHello(request) - .onErrorResume(e -> - Mono.error(new NameRequiredException( - HttpStatus.BAD_REQUEST, "please provide a name", e))), String.class); -} - - private Mono sayHello(ServerRequest request) { - try { - return Mono.just("Hello, " + request.queryParam("name").get()); - } catch (Exception e) { - return Mono.error(e); - } - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java deleted file mode 100644 index 41605b355b..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java +++ /dev/null @@ -1,22 +0,0 @@ - -package com.baeldung.reactive.errorhandling.handlers; - -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; -import reactor.core.publisher.Mono; - -@Component -public class Handler5 { - - public Mono handleRequest5(ServerRequest request) { - return ServerResponse.ok() - .body(sayHello(request), String.class); - - } - - private Mono sayHello(ServerRequest request) { - return Mono.just("Hello, " + request.queryParam("name").get()); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java deleted file mode 100644 index 91be24571c..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java +++ /dev/null @@ -1,22 +0,0 @@ - -package com.baeldung.reactive.errorhandling.routers; - -import com.baeldung.reactive.errorhandling.handlers.Handler1; -import org.springframework.context.annotation.Bean; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -@Component -public class Router1 { - - @Bean - public RouterFunction routeRequest1(Handler1 handler) { - return RouterFunctions.route(RequestPredicates.GET("/api/endpoint1") - .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest1); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java deleted file mode 100644 index bc7831f494..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java +++ /dev/null @@ -1,22 +0,0 @@ - -package com.baeldung.reactive.errorhandling.routers; - -import com.baeldung.reactive.errorhandling.handlers.Handler2; -import org.springframework.context.annotation.Bean; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -@Component -public class Router2 { - - @Bean - public RouterFunction routeRequest2(Handler2 handler) { - return RouterFunctions.route(RequestPredicates.GET("/api/endpoint2") - .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest2); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java deleted file mode 100644 index 461e6fe9e7..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java +++ /dev/null @@ -1,22 +0,0 @@ - -package com.baeldung.reactive.errorhandling.routers; - -import com.baeldung.reactive.errorhandling.handlers.Handler3; -import org.springframework.context.annotation.Bean; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -@Component -public class Router3 { - - @Bean - public RouterFunction routeRequest3(Handler3 handler) { - return RouterFunctions.route(RequestPredicates.GET("/api/endpoint3") - .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest3); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java deleted file mode 100644 index 9dccc6858f..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java +++ /dev/null @@ -1,22 +0,0 @@ - -package com.baeldung.reactive.errorhandling.routers; - -import com.baeldung.reactive.errorhandling.handlers.Handler4; -import org.springframework.context.annotation.Bean; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -@Component -public class Router4 { - - @Bean - public RouterFunction routeRequest4(Handler4 handler) { - return RouterFunctions.route(RequestPredicates.GET("/api/endpoint4") - .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest4); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java deleted file mode 100644 index 59fd587fc8..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java +++ /dev/null @@ -1,22 +0,0 @@ - -package com.baeldung.reactive.errorhandling.routers; - -import com.baeldung.reactive.errorhandling.handlers.Handler5; -import org.springframework.context.annotation.Bean; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -@Component -public class Router5 { - - @Bean - public RouterFunction routeRequest5(Handler5 handler) { - return RouterFunctions.route(RequestPredicates.GET("/api/endpoint5") - .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest5); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/Task.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/Task.java deleted file mode 100644 index 725fd931e1..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/Task.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.baeldung.web.reactive; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class Task { - - private final String name; - - private final int id; - - public Task(@JsonProperty("name") String name, @JsonProperty("id") int id) { - this.name = name; - this.id = id; - } - - public String getName() { - return this.name; - } - - public int getId() { - return this.id; - } - - @Override - public String toString() { - return "Task{" + "name='" + name + '\'' + ", id=" + id + '}'; - } -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/Foo.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/Foo.java deleted file mode 100644 index c6e3678832..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/Foo.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.baeldung.web.reactive.client; - -public class Foo { - - private String name; - - public Foo() { - super(); - } - - public Foo(String name) { - super(); - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientApplication.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientApplication.java deleted file mode 100644 index aa9b81de4f..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientApplication.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.baeldung.web.reactive.client; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; - -@SpringBootApplication(exclude = { ReactiveSecurityAutoConfiguration.class }) -public class WebClientApplication { - - public static void main(String[] args) { - SpringApplication.run(WebClientApplication.class, args); - } - -} diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java deleted file mode 100644 index 1a91001807..0000000000 --- a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.baeldung.web.reactive.client; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; - -import reactor.core.publisher.Mono; - -@RestController -public class WebClientController { - - @ResponseStatus(HttpStatus.OK) - @GetMapping("/resource") - public Map getResource() { - Map response = new HashMap<>(); - response.put("field", "value"); - return response; - } - - @PostMapping("/resource") - public Mono postStringResource(@RequestBody Mono bodyString) { - return bodyString.map(body -> "processed-" + body); - } - - @PostMapping("/resource-foo") - public Mono postFooResource(@RequestBody Mono bodyFoo) { - return bodyFoo.map(foo -> "processedFoo-" + foo.getName()); - } - - @PostMapping(value = "/resource-multipart", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public String handleFormUpload(@RequestPart("key1") String value1, @RequestPart("key2") String value2) { - return "processed-" + value1 + "-" + value2; - } -} diff --git a/spring-5-reactive/src/test/java/com/baeldung/web/client/SpringContextTest.java b/spring-5-reactive/src/test/java/com/baeldung/web/client/SpringContextTest.java deleted file mode 100644 index 8d2ca41451..0000000000 --- a/spring-5-reactive/src/test/java/com/baeldung/web/client/SpringContextTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.baeldung.web.client; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -import com.baeldung.web.reactive.client.WebClientApplication; - -@SpringBootTest(classes = WebClientApplication.class) -public class SpringContextTest { - - @Test - public void whenSpringContextIsBootstrapped_thenNoExceptions() { - } -} diff --git a/spring-5-reactive/src/test/java/com/baeldung/web/client/WebClientIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/web/client/WebClientIntegrationTest.java deleted file mode 100644 index 7e1fc86847..0000000000 --- a/spring-5-reactive/src/test/java/com/baeldung/web/client/WebClientIntegrationTest.java +++ /dev/null @@ -1,331 +0,0 @@ -package com.baeldung.web.client; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.ZonedDateTime; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -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.web.server.LocalServerPort; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.core.codec.CodecException; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ReactiveHttpOutputMessage; -import org.springframework.http.client.reactive.ClientHttpRequest; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyInserter; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec; -import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec; -import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec; -import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec; -import org.springframework.web.reactive.function.client.WebClient.ResponseSpec; -import org.springframework.web.reactive.function.client.WebClientRequestException; - -import com.baeldung.web.reactive.client.Foo; -import com.baeldung.web.reactive.client.WebClientApplication; - -import io.netty.channel.ChannelOption; -import io.netty.handler.timeout.ReadTimeoutException; -import io.netty.handler.timeout.ReadTimeoutHandler; -import io.netty.handler.timeout.WriteTimeoutHandler; -import reactor.core.publisher.Mono; -import reactor.netty.http.client.HttpClient; -import reactor.test.StepVerifier; - -@SpringBootTest(classes = WebClientApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT) -public class WebClientIntegrationTest { - - @LocalServerPort - private int port; - - private static final String BODY_VALUE = "bodyValue"; - private static final ParameterizedTypeReference> MAP_RESPONSE_REF = new ParameterizedTypeReference>() { - }; - - @Test - public void givenDifferentWebClientCreationMethods_whenUsed_thenObtainExpectedResponse() { - // WebClient creation - WebClient client1 = WebClient.create(); - WebClient client2 = WebClient.create("http://localhost:" + port); - WebClient client3 = WebClient.builder() - .baseUrl("http://localhost:" + port) - .defaultCookie("cookieKey", "cookieValue") - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080")) - .build(); - - // response assertions - StepVerifier.create(retrieveResponse(client1.post() - .uri("http://localhost:" + port + "/resource"))) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveResponse(client2)) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveResponse(client3)) - .expectNext("processed-bodyValue") - .verifyComplete(); - // assert response without specifying URI - StepVerifier.create(retrieveResponse(client1)) - .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() - .contains("Connection refused")) - .verify(); - } - - @Test - public void givenDifferentMethodSpecifications_whenUsed_thenObtainExpectedResponse() { - // request specification - RequestBodyUriSpec uriSpecPost1 = createDefaultClient().method(HttpMethod.POST); - RequestBodyUriSpec uriSpecPost2 = createDefaultClient().post(); - RequestHeadersUriSpec requestGet = createDefaultClient().get(); - - // response assertions - StepVerifier.create(retrieveResponse(uriSpecPost1)) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveResponse(uriSpecPost2)) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveGetResponse(requestGet)) - .expectNextMatches(nextMap -> nextMap.get("field") - .equals("value")) - .verifyComplete(); - } - - @Test - public void givenDifferentUriSpecifications_whenUsed_thenObtainExpectedResponse() { - // uri specification - RequestBodySpec bodySpecUsingString = createDefaultPostRequest().uri("/resource"); - RequestBodySpec bodySpecUsingUriBuilder = createDefaultPostRequest().uri(uriBuilder -> uriBuilder.pathSegment("resource") - .build()); - RequestBodySpec bodySpecusingURI = createDefaultPostRequest().uri(URI.create("http://localhost:" + port + "/resource")); - RequestBodySpec bodySpecOverridenBaseUri = createDefaultPostRequest().uri(URI.create("/resource")); - RequestBodySpec bodySpecOverridenBaseUri2 = WebClient.builder() - .baseUrl("http://localhost:" + port) - .build() - .post() - .uri(URI.create("/resource")); - - // response assertions - StepVerifier.create(retrieveResponse(bodySpecUsingString)) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveResponse(bodySpecUsingUriBuilder)) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveResponse(bodySpecusingURI)) - .expectNext("processed-bodyValue") - .verifyComplete(); - // assert sending request overriding base URI - StepVerifier.create(retrieveResponse(bodySpecOverridenBaseUri)) - .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() - .contains("Connection refused")) - .verify(); - StepVerifier.create(retrieveResponse(bodySpecOverridenBaseUri2)) - .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() - .contains("Connection refused")) - .verify(); - } - - @Test - public void givenDifferentBodySpecifications_whenUsed_thenObtainExpectedResponse() { - // request body specifications - RequestHeadersSpec headersSpecPost1 = createDefaultPostResourceRequest().body(BodyInserters.fromPublisher(Mono.just(BODY_VALUE), String.class)); - RequestHeadersSpec headersSpecPost2 = createDefaultPostResourceRequest().body(BodyInserters.fromValue(BODY_VALUE)); - RequestHeadersSpec headersSpecPost3 = createDefaultPostResourceRequest().bodyValue(BODY_VALUE); - RequestHeadersSpec headersSpecFooPost = createDefaultPostRequest().uri("/resource-foo") - .body(Mono.just(new Foo("fooName")), Foo.class); - BodyInserter inserterPlainObject = BodyInserters.fromValue(new Object()); - RequestHeadersSpec headersSpecPlainObject = createDefaultPostResourceRequest().body(inserterPlainObject); - - // request body specifications - using other inserter method (multipart request) - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - map.add("key1", "multipartValue1"); - map.add("key2", "multipartValue2"); - BodyInserter, ClientHttpRequest> inserterMultipart = BodyInserters.fromMultipartData(map); - RequestHeadersSpec headersSpecInserterMultipart = createDefaultPostRequest().uri("/resource-multipart") - .body(inserterMultipart); - - // response assertions - StepVerifier.create(retrieveResponse(headersSpecPost1)) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveResponse(headersSpecPost2)) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveResponse(headersSpecPost3)) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(retrieveResponse(headersSpecFooPost)) - .expectNext("processedFoo-fooName") - .verifyComplete(); - StepVerifier.create(retrieveResponse(headersSpecInserterMultipart)) - .expectNext("processed-multipartValue1-multipartValue2") - .verifyComplete(); - // assert error plain `new Object()` as request body - StepVerifier.create(retrieveResponse(headersSpecPlainObject)) - .expectError(CodecException.class) - .verify(); - // assert response for request with no body - Mono> responsePostWithNoBody = createDefaultPostResourceRequest().exchangeToMono(responseHandler -> { - assertThat(responseHandler.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - return responseHandler.bodyToMono(MAP_RESPONSE_REF); - }); - StepVerifier.create(responsePostWithNoBody) - .expectNextMatches(nextMap -> nextMap.get("error") - .equals("Bad Request")) - .verifyComplete(); - } - - @Test - public void givenPostSpecifications_whenHeadersAdded_thenObtainExpectedResponse() { - // request header specification - RequestHeadersSpec headersSpecInserterStringWithHeaders = createDefaultPostResourceRequestResponse().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) - .acceptCharset(StandardCharsets.UTF_8) - .ifNoneMatch("*") - .ifModifiedSince(ZonedDateTime.now()); - - // response assertions - StepVerifier.create(retrieveResponse(headersSpecInserterStringWithHeaders)) - .expectNext("processed-bodyValue") - .verifyComplete(); - } - - @Test - public void givenDifferentResponseSpecifications_whenUsed_thenObtainExpectedResponse() { - ResponseSpec responseSpecPostString = createDefaultPostResourceRequestResponse().retrieve(); - Mono responsePostString = responseSpecPostString.bodyToMono(String.class); - Mono responsePostString2 = createDefaultPostResourceRequestResponse().exchangeToMono(response -> { - if (response.statusCode() - .equals(HttpStatus.OK)) { - return response.bodyToMono(String.class); - } else if (response.statusCode() - .is4xxClientError()) { - return Mono.just("Error response"); - } else { - return response.createException() - .flatMap(Mono::error); - } - }); - Mono responsePostNoBody = createDefaultPostResourceRequest().exchangeToMono(response -> { - if (response.statusCode() - .equals(HttpStatus.OK)) { - return response.bodyToMono(String.class); - } else if (response.statusCode() - .is4xxClientError()) { - return Mono.just("Error response"); - } else { - return response.createException() - .flatMap(Mono::error); - } - }); - Mono> responseGet = createDefaultClient().get() - .uri("/resource") - .retrieve() - .bodyToMono(MAP_RESPONSE_REF); - - // response assertions - StepVerifier.create(responsePostString) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(responsePostString2) - .expectNext("processed-bodyValue") - .verifyComplete(); - StepVerifier.create(responsePostNoBody) - .expectNext("Error response") - .verifyComplete(); - StepVerifier.create(responseGet) - .expectNextMatches(nextMap -> nextMap.get("field") - .equals("value")) - .verifyComplete(); - } - - @Test - public void givenWebClientWithTimeoutConfigurations_whenRequestUsingWronglyConfiguredPublisher_thenObtainTimeout() { - HttpClient httpClient = HttpClient.create() - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000) - .responseTimeout(Duration.ofMillis(1000)) - .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(1000, TimeUnit.MILLISECONDS)) - .addHandlerLast(new WriteTimeoutHandler(1000, TimeUnit.MILLISECONDS))); - - WebClient timeoutClient = WebClient.builder() - .baseUrl("http://localhost:" + port) - .clientConnector(new ReactorClientHttpConnector(httpClient)) - .build(); - - RequestHeadersSpec neverendingMonoBodyRequest = timeoutClient.post() - .uri("/resource") - .body(Mono.never(), String.class); - - StepVerifier.create(neverendingMonoBodyRequest.retrieve() - .bodyToMono(String.class)) - .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ReadTimeoutException.class.isAssignableFrom(ex.getCause() - .getClass())) - .verify(); - } - - // helper methods to create default instances - private WebClient createDefaultClient() { - return WebClient.create("http://localhost:" + port); - } - - private RequestBodyUriSpec createDefaultPostRequest() { - return createDefaultClient().post(); - } - - private RequestBodySpec createDefaultPostResourceRequest() { - return createDefaultPostRequest().uri("/resource"); - } - - private RequestHeadersSpec createDefaultPostResourceRequestResponse() { - return createDefaultPostResourceRequest().bodyValue(BODY_VALUE); - } - - // helper methods to retrieve a response based on different steps of the process (specs) - private Mono retrieveResponse(WebClient client) { - return client.post() - .uri("/resource") - .bodyValue(BODY_VALUE) - .retrieve() - .bodyToMono(String.class); - } - - private Mono retrieveResponse(RequestBodyUriSpec spec) { - return spec.uri("/resource") - .bodyValue(BODY_VALUE) - .retrieve() - .bodyToMono(String.class); - } - - private Mono> retrieveGetResponse(RequestHeadersUriSpec spec) { - return spec.uri("/resource") - .retrieve() - .bodyToMono(MAP_RESPONSE_REF); - } - - private Mono retrieveResponse(RequestBodySpec spec) { - return spec.bodyValue(BODY_VALUE) - .retrieve() - .bodyToMono(String.class); - } - - private Mono retrieveResponse(RequestHeadersSpec spec) { - return spec.retrieve() - .bodyToMono(String.class); - } -} diff --git a/spring-5-reactive/src/test/java/com/baeldung/web/client/WebTestClientIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/web/client/WebTestClientIntegrationTest.java deleted file mode 100644 index 07a4c81c91..0000000000 --- a/spring-5-reactive/src/test/java/com/baeldung/web/client/WebTestClientIntegrationTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.baeldung.web.client; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.context.ApplicationContext; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; -import org.springframework.web.server.WebHandler; - -import com.baeldung.web.reactive.client.WebClientApplication; -import com.baeldung.web.reactive.client.WebClientController; - -import reactor.core.publisher.Mono; - -@SpringBootTest(classes = WebClientApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class WebTestClientIntegrationTest { - - @LocalServerPort - private int port; - - @Autowired - private ApplicationContext context; - - @Autowired - private WebClientController controller; - - private final RouterFunction ROUTER_FUNCTION = RouterFunctions.route(RequestPredicates.GET("/resource"), request -> ServerResponse.ok() - .build()); - private final WebHandler WEB_HANDLER = exchange -> Mono.empty(); - - @Test - public void testWebTestClientWithServerWebHandler() { - WebTestClient.bindToWebHandler(WEB_HANDLER) - .build(); - } - - @Test - public void testWebTestClientWithRouterFunction() { - WebTestClient.bindToRouterFunction(ROUTER_FUNCTION) - .build() - .get() - .uri("/resource") - .exchange() - .expectStatus() - .isOk() - .expectBody() - .isEmpty(); - } - - @Test - @WithMockUser - public void testWebTestClientWithServerURL() { - WebTestClient.bindToServer() - .baseUrl("http://localhost:" + port) - .build() - .get() - .uri("/resource") - .exchange() - .expectStatus() - .isOk() - .expectBody() - .jsonPath("field") - .isEqualTo("value"); - ; - } - - @Test - @WithMockUser - public void testWebTestClientWithApplicationContext() { - WebTestClient.bindToApplicationContext(context) - .build() - .get() - .uri("/resource") - .exchange() - .expectStatus() - .isOk() - .expectBody() - .jsonPath("field") - .isEqualTo("value"); - } - - @Test - public void testWebTestClientWithController() { - WebTestClient.bindToController(controller) - .build() - .get() - .uri("/resource") - .exchange() - .expectStatus() - .isOk() - .expectBody() - .jsonPath("field") - .isEqualTo("value"); - } - -} diff --git a/spring-5-webflux/README.md b/spring-5-webflux/README.md index 889f211fc6..f6467a8993 100644 --- a/spring-5-webflux/README.md +++ b/spring-5-webflux/README.md @@ -6,7 +6,6 @@ This module contains articles about Spring 5 WebFlux - [Spring Boot Reactor Netty Configuration](https://www.baeldung.com/spring-boot-reactor-netty) - [How to Return 404 with Spring WebFlux](https://www.baeldung.com/spring-webflux-404) -- [Spring WebClient Requests with Parameters](https://www.baeldung.com/webflux-webclient-parameters) - [RSocket Using Spring Boot](https://www.baeldung.com/spring-boot-rsocket) - [Spring MVC Async vs Spring WebFlux](https://www.baeldung.com/spring-mvc-async-vs-webflux) - [Set a Timeout in Spring 5 Webflux WebClient](https://www.baeldung.com/spring-webflux-timeout) diff --git a/spring-5-webflux/src/main/java/com/baeldung/spring/webclientrequests/SpringWebClientRequestsApp.java b/spring-5-webflux/src/main/java/com/baeldung/spring/webclientrequests/SpringWebClientRequestsApp.java deleted file mode 100644 index 314fe2fdf9..0000000000 --- a/spring-5-webflux/src/main/java/com/baeldung/spring/webclientrequests/SpringWebClientRequestsApp.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.baeldung.spring.webclientrequests; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class SpringWebClientRequestsApp { - - public static void main(String[] args) { - SpringApplication.run(SpringWebClientRequestsApp.class, args); - } -} diff --git a/spring-5-webflux/src/test/java/com/baeldung/spring/webclientrequests/WebClientRequestsUnitTest.java b/spring-5-webflux/src/test/java/com/baeldung/spring/webclientrequests/WebClientRequestsUnitTest.java deleted file mode 100644 index 353cb24d0a..0000000000 --- a/spring-5-webflux/src/test/java/com/baeldung/spring/webclientrequests/WebClientRequestsUnitTest.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.baeldung.spring.webclientrequests; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import java.time.Duration; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.reactive.function.client.ClientRequest; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.ExchangeFunction; -import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.util.DefaultUriBuilderFactory; -import reactor.core.publisher.Mono; - -@RunWith(SpringRunner.class) -@WebFluxTest -public class WebClientRequestsUnitTest { - - private static final String BASE_URL = "https://example.com"; - - private WebClient webClient; - - @Captor - private ArgumentCaptor argumentCaptor; - - private ExchangeFunction exchangeFunction; - - @Before - public void init() { - MockitoAnnotations.initMocks(this); - this.exchangeFunction = mock(ExchangeFunction.class); - ClientResponse mockResponse = mock(ClientResponse.class); - when(this.exchangeFunction.exchange(this.argumentCaptor.capture())).thenReturn(Mono.just(mockResponse)); - this.webClient = WebClient - .builder() - .baseUrl(BASE_URL) - .exchangeFunction(exchangeFunction) - .build(); - } - - - @Test - public void whenCallSimpleURI_thenURIMatched() { - this.webClient.get() - .uri("/products") - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products"); - } - - @Test - public void whenCallSinglePathSegmentUri_thenURIMatched() { - this.webClient.get() - .uri(uriBuilder -> uriBuilder - .path("/products/{id}") - .build(2)) - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products/2"); - } - - @Test - public void whenCallMultiplePathSegmentsUri_thenURIMatched() { - this.webClient.get() - .uri(uriBuilder -> uriBuilder - .path("/products/{id}/attributes/{attributeId}") - .build(2, 13)) - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products/2/attributes/13"); - } - - @Test - public void whenCallSingleQueryParams_thenURIMatched() { - this.webClient.get() - .uri(uriBuilder -> uriBuilder - .path("/products/") - .queryParam("name", "AndroidPhone") - .queryParam("color", "black") - .queryParam("deliveryDate", "13/04/2019") - .build()) - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products/?name=AndroidPhone&color=black&deliveryDate=13/04/2019"); - } - - @Test - public void whenCallSingleQueryParamsPlaceholders_thenURIMatched() { - this.webClient.get() - .uri(uriBuilder -> uriBuilder - .path("/products/") - .queryParam("name", "{title}") - .queryParam("color", "{authorId}") - .queryParam("deliveryDate", "{date}") - .build("AndroidPhone", "black", "13/04/2019")) - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products/?name=AndroidPhone&color=black&deliveryDate=13%2F04%2F2019"); - } - - @Test - public void whenCallArrayQueryParamsBrackets_thenURIMatched() { - this.webClient.get() - .uri(uriBuilder -> uriBuilder - .path("/products/") - .queryParam("tag[]", "Snapdragon", "NFC") - .build()) - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products/?tag%5B%5D=Snapdragon&tag%5B%5D=NFC"); - } - - - @Test - public void whenCallArrayQueryParams_thenURIMatched() { - this.webClient.get() - .uri(uriBuilder -> uriBuilder - .path("/products/") - .queryParam("category", "Phones", "Tablets") - .build()) - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products/?category=Phones&category=Tablets"); - } - - @Test - public void whenCallArrayQueryParamsComma_thenURIMatched() { - this.webClient.get() - .uri(uriBuilder -> uriBuilder - .path("/products/") - .queryParam("category", String.join(",", "Phones", "Tablets")) - .build()) - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products/?category=Phones,Tablets"); - } - - @Test - public void whenUriComponentEncoding_thenQueryParamsNotEscaped() { - DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL); - factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT); - this.webClient = WebClient - .builder() - .uriBuilderFactory(factory) - .baseUrl(BASE_URL) - .exchangeFunction(exchangeFunction) - .build(); - this.webClient.get() - .uri(uriBuilder -> uriBuilder - .path("/products/") - .queryParam("name", "AndroidPhone") - .queryParam("color", "black") - .queryParam("deliveryDate", "13/04/2019") - .build()) - .exchange() - .block(Duration.ofSeconds(1)); - verifyCalledUrl("/products/?name=AndroidPhone&color=black&deliveryDate=13/04/2019"); - } - - private void verifyCalledUrl(String relativeUrl) { - ClientRequest request = this.argumentCaptor.getValue(); - Assert.assertEquals(String.format("%s%s", BASE_URL, relativeUrl), request.url().toString()); - Mockito.verify(this.exchangeFunction).exchange(request); - verifyNoMoreInteractions(this.exchangeFunction); - } -} diff --git a/spring-reactive/README.md b/spring-reactive/README.md index 6e887fe2f8..9f1852d912 100644 --- a/spring-reactive/README.md +++ b/spring-reactive/README.md @@ -1,17 +1,16 @@ ## Spring Reactive -This module contains articles about Spring Reactor. +This module contains articles describing reactive processing in Spring. ## Relevant articles: -- [Introduction to Project Reactor Bus](https://www.baeldung.com/reactor-bus) - [Intro To Reactor Core](https://www.baeldung.com/reactor-core) - [Debugging Reactive Streams in Java](https://www.baeldung.com/spring-debugging-reactive-streams) - [Guide to Spring 5 WebFlux](https://www.baeldung.com/spring-webflux) - [Introduction to the Functional Web Framework in Spring 5](https://www.baeldung.com/spring-5-functional-web) - [Spring 5 WebClient](https://www.baeldung.com/spring-5-webclient) - [Spring WebClient vs. RestTemplate](https://www.baeldung.com/spring-webclient-resttemplate) -- [https://www.baeldung.com/webflux-webclient-parameters](https://www.baeldung.com/webflux-webclient-parameters) +- [Spring WebClient Requests with Parameters](https://www.baeldung.com/webflux-webclient-parameters) - [Handling Errors in Spring WebFlux](https://www.baeldung.com/spring-webflux-errors) - [Spring Security 5 for Reactive Applications](https://www.baeldung.com/spring-security-5-reactive) -- [https://www.baeldung.com/spring-webflux-concurrency](https://www.baeldung.com/spring-webflux-concurrency) \ No newline at end of file +- [Concurrency in Spring WebFlux](https://www.baeldung.com/spring-webflux-concurrency) \ No newline at end of file diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java b/spring-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java similarity index 100% rename from spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java rename to spring-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java index 38443a4eac..1167792542 100644 --- a/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java +++ b/spring-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java @@ -1,9 +1,5 @@ package com.baeldung.reactive.errorhandling; -import static org.junit.Assert.assertEquals; - -import java.io.IOException; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -16,6 +12,10 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @DirtiesContext diff --git a/spring-webflux-threads/README.md b/spring-webflux-threads/README.md deleted file mode 100644 index 746514feda..0000000000 --- a/spring-webflux-threads/README.md +++ /dev/null @@ -1,3 +0,0 @@ -### Relevant Articles: - -- [Concurrency in Spring WebFlux](https://www.baeldung.com/spring-webflux-concurrency) diff --git a/spring-webflux-threads/pom.xml b/spring-webflux-threads/pom.xml deleted file mode 100644 index fedfaa6967..0000000000 --- a/spring-webflux-threads/pom.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - 4.0.0 - com.baeldung.spring - spring-webflux-threads - 1.0.0-SNAPSHOT - spring-webflux-threads - jar - Spring WebFlux Threads Sample - - - com.baeldung - parent-boot-2 - 0.0.1-SNAPSHOT - ../parent-boot-2 - - - - - org.springframework.boot - spring-boot-starter-webflux - - - - - - - - - io.reactivex.rxjava2 - rxjava - 2.2.19 - - - org.springframework.boot - spring-boot-starter-data-mongodb-reactive - - - io.projectreactor.kafka - reactor-kafka - 1.2.2.RELEASE - - - com.fasterxml.jackson.core - jackson-databind - - - org.springframework.boot - spring-boot-starter-test - test - - - io.projectreactor - reactor-test - test - - - - \ No newline at end of file diff --git a/spring-webflux-threads/src/main/java/com/baeldung/webflux/Application.java b/spring-webflux-threads/src/main/java/com/baeldung/webflux/Application.java deleted file mode 100644 index 06a148a77f..0000000000 --- a/spring-webflux-threads/src/main/java/com/baeldung/webflux/Application.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.baeldung.webflux; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** -* Please note we assume Mongo and Kafka are running in the local machine and on default configuration. -* Additionally, if you want to experiment with Tomcat/Jetty instead of Netty, just uncomment the lines in pom.xml and rebuild. -*/ -@SpringBootApplication -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } - -} diff --git a/spring-webflux-threads/src/main/java/com/baeldung/webflux/Controller.java b/spring-webflux-threads/src/main/java/com/baeldung/webflux/Controller.java deleted file mode 100644 index 3c7e4e41ca..0000000000 --- a/spring-webflux-threads/src/main/java/com/baeldung/webflux/Controller.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.baeldung.webflux; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.kafka.clients.consumer.ConsumerConfig; -import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.common.serialization.IntegerDeserializer; -import org.apache.kafka.common.serialization.IntegerSerializer; -import org.apache.kafka.common.serialization.StringDeserializer; -import org.apache.kafka.common.serialization.StringSerializer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.reactive.function.client.WebClient; - -import io.reactivex.Observable; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Scheduler; -import reactor.core.scheduler.Schedulers; -import reactor.kafka.receiver.KafkaReceiver; -import reactor.kafka.receiver.ReceiverOptions; -import reactor.kafka.receiver.ReceiverRecord; -import reactor.kafka.sender.KafkaSender; -import reactor.kafka.sender.SenderOptions; -import reactor.kafka.sender.SenderRecord; - -@RestController -@RequestMapping("/") -public class Controller { - - @Autowired - private PersonRepository personRepository; - - private Scheduler scheduler = Schedulers.newBoundedElastic(5, 10, "MyThreadGroup"); - - private Logger logger = LoggerFactory.getLogger(Controller.class); - - @GetMapping("/threads/webflux") - public Flux getThreadsWebflux() { - return Flux.fromIterable(getThreads()); - } - - @GetMapping("/threads/webclient") - public Flux getThreadsWebClient() { - WebClient.create("http://localhost:8080/index") - .get() - .retrieve() - .bodyToMono(String.class) - .subscribeOn(scheduler) - .publishOn(scheduler) - .doOnNext(s -> logger.info("Response: {}", s)) - .subscribe(); - return Flux.fromIterable(getThreads()); - } - - @GetMapping("/threads/rxjava") - public Observable getIndexRxJava() { - Observable.fromIterable(Arrays.asList("Hello", "World")) - .map(s -> s.toUpperCase()) - .observeOn(io.reactivex.schedulers.Schedulers.trampoline()) - .doOnNext(s -> logger.info("String: {}", s)) - .subscribe(); - return Observable.fromIterable(getThreads()); - } - - @GetMapping("/threads/mongodb") - public Flux getIndexMongo() { - personRepository.findAll() - .doOnNext(p -> logger.info("Person: {}", p)) - .subscribe(); - return Flux.fromIterable(getThreads()); - } - - @GetMapping("/threads/reactor-kafka") - public Flux getIndexKafka() { - Map producerProps = new HashMap<>(); - producerProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); - producerProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class); - producerProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - SenderOptions senderOptions = SenderOptions.create(producerProps); - KafkaSender sender = KafkaSender.create(senderOptions); - Flux> outboundFlux = Flux.range(1, 10) - .map(i -> SenderRecord.create(new ProducerRecord<>("reactive-test", i, "Message_" + i), i)); - sender.send(outboundFlux) - .subscribe(); - - Map consumerProps = new HashMap<>(); - consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); - consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "my-consumer"); - consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); - consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, IntegerDeserializer.class); - consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); - consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); - ReceiverOptions receiverOptions = ReceiverOptions.create(consumerProps); - receiverOptions.subscription(Collections.singleton("reactive-test")); - KafkaReceiver receiver = KafkaReceiver.create(receiverOptions); - Flux> inboundFlux = receiver.receive(); - inboundFlux.subscribe(r -> { - logger.info("Received message: {}", r.value()); - r.receiverOffset() - .acknowledge(); - }); - return Flux.fromIterable(getThreads()); - } - - @GetMapping("/index") - public Mono getIndex() { - return Mono.just("Hello world!"); - } - - private List getThreads() { - return Thread.getAllStackTraces() - .keySet() - .stream() - .map(t -> String.format("%-20s \t %s \t %d \t %s\n", t.getName(), t.getState(), t.getPriority(), t.isDaemon() ? "Daemon" : "Normal")) - .collect(Collectors.toList()); - } -} diff --git a/spring-webflux-threads/src/main/java/com/baeldung/webflux/Person.java b/spring-webflux-threads/src/main/java/com/baeldung/webflux/Person.java deleted file mode 100644 index 4c6bd5f585..0000000000 --- a/spring-webflux-threads/src/main/java/com/baeldung/webflux/Person.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.baeldung.webflux; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; - -@Document -public class Person { - @Id - String id; - - public Person(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - @Override - public String toString() { - return "Person{" + "id='" + id + '\'' + '}'; - } -} diff --git a/spring-webflux-threads/src/main/java/com/baeldung/webflux/PersonRepository.java b/spring-webflux-threads/src/main/java/com/baeldung/webflux/PersonRepository.java deleted file mode 100644 index 38fbd3d431..0000000000 --- a/spring-webflux-threads/src/main/java/com/baeldung/webflux/PersonRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.baeldung.webflux; - -import org.springframework.data.mongodb.repository.ReactiveMongoRepository; - -public interface PersonRepository extends ReactiveMongoRepository { -} diff --git a/spring-webflux-threads/src/main/resources/logback.xml b/spring-webflux-threads/src/main/resources/logback.xml deleted file mode 100644 index 7d900d8ea8..0000000000 --- a/spring-webflux-threads/src/main/resources/logback.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - \ No newline at end of file From eb49143a4889cc79f282b4cf7594671e21d106ff Mon Sep 17 00:00:00 2001 From: Vartika Nigam <63852792+vnigam2702@users.noreply.github.com> Date: Thu, 30 Dec 2021 07:43:09 +0530 Subject: [PATCH 14/48] initial commit for BAEL-5185 (#11484) * initial commit for BAEL-5185 * fixed issues * fixed issues * Added Junit test case Co-authored-by: Vartika_Nigam --- .../VolatileVarNotThreadSafe.java | 47 +++++++++++++++++++ .../VolatileVarNotThreadSafeUnitTest.java | 13 +++++ 2 files changed, 60 insertions(+) create mode 100644 core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/volatilekeywordthreadsafety/VolatileVarNotThreadSafe.java create mode 100644 core-java-modules/core-java-concurrency-advanced-4/src/test/java/com/baeldung/volatilekeywordthreadsafety/VolatileVarNotThreadSafeUnitTest.java diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/volatilekeywordthreadsafety/VolatileVarNotThreadSafe.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/volatilekeywordthreadsafety/VolatileVarNotThreadSafe.java new file mode 100644 index 0000000000..cf9b7fd20b --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/volatilekeywordthreadsafety/VolatileVarNotThreadSafe.java @@ -0,0 +1,47 @@ +package com.baeldung.volatilekeywordthreadsafety; + +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +public class VolatileVarNotThreadSafe { + + private static final Logger LOG = LoggerFactory.getLogger(VolatileVarNotThreadSafe.class); + private static volatile int count = 0; + private static final int MAX_LIMIT = 1000; + + public static void increment() { + count++; + } + + public static int getCount() { + return count; + } + + public static void main(String[] args) throws InterruptedException { + Thread t1 = new Thread(new Runnable() { + @Override + public void run() { + for(int index=0; index 0); + } +} From c5250929f57a454d5f0fd75dde13892e3010797b Mon Sep 17 00:00:00 2001 From: chaos2418 <> Date: Thu, 30 Dec 2021 10:34:14 +0530 Subject: [PATCH 15/48] JAVA-8695: GitHub Issue: Order in FilterRegistrationBean is ignored --- .../main/java/com/baeldung/bootcustomfilters/FilterConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/bootcustomfilters/FilterConfig.java b/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/bootcustomfilters/FilterConfig.java index 42da61fe14..2bc1b8a55c 100644 --- a/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/bootcustomfilters/FilterConfig.java +++ b/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/bootcustomfilters/FilterConfig.java @@ -15,6 +15,7 @@ public class FilterConfig { registrationBean.setFilter(new RequestResponseLoggingFilter()); registrationBean.addUrlPatterns("/users/*"); + registrationBean.setOrder(2); return registrationBean; From 9eaa8f4dffaeeadd9f02ebe6ce0db57d70805b38 Mon Sep 17 00:00:00 2001 From: sampadawagde Date: Thu, 30 Dec 2021 20:39:11 +0530 Subject: [PATCH 16/48] JAVA-8280: Split or move spring-aop module --- pom.xml | 2 + spring-aop-2/README.md | 10 ++++ spring-aop-2/pom.xml | 60 +++++++++++++++++++ .../main/java/com/baeldung/Application.java | 12 ++++ .../com/baeldung/method/info/Account.java | 0 .../method/info/AccountOperation.java | 0 .../method/info/BankAccountAspect.java | 0 .../method/info/BankAccountService.java | 0 .../method/info/WithdrawLimitException.java | 0 .../performancemonitor/AopConfiguration.java | 0 .../MyPerformanceMonitorInterceptor.java | 0 .../performancemonitor/PerfomanceApp.java | 0 .../baeldung/performancemonitor/Person.java | 0 .../performancemonitor/PersonService.java | 0 .../undeclared/SomeCheckedException.java | 0 .../baeldung/undeclared/ThrowUndeclared.java | 0 .../undeclared/UndeclaredApplication.java | 0 .../baeldung/undeclared/UndeclaredAspect.java | 0 .../undeclared/UndeclaredService.java | 0 spring-aop-2/src/main/resources/logback.xml | 23 +++++++ .../java/com/baeldung/SpringContextTest.java | 15 +++++ .../BankAccountServiceIntegrationTest.java | 0 ...aredThrowableExceptionIntegrationTest.java | 0 .../UndeclaredThrowableExceptionUnitTest.java | 0 spring-aop/README.md | 4 +- spring-aop/src/main/resources/logback.xml | 4 -- 26 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 spring-aop-2/README.md create mode 100644 spring-aop-2/pom.xml create mode 100644 spring-aop-2/src/main/java/com/baeldung/Application.java rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/method/info/Account.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/method/info/AccountOperation.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/method/info/BankAccountAspect.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/method/info/BankAccountService.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/method/info/WithdrawLimitException.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/performancemonitor/AopConfiguration.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/performancemonitor/MyPerformanceMonitorInterceptor.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/performancemonitor/PerfomanceApp.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/performancemonitor/Person.java (100%) rename {spring-aop => spring-aop-2}/src/main/java/com/baeldung/performancemonitor/PersonService.java (100%) rename {spring-aop/src/test => spring-aop-2/src/main}/java/com/baeldung/undeclared/SomeCheckedException.java (100%) rename {spring-aop/src/test => spring-aop-2/src/main}/java/com/baeldung/undeclared/ThrowUndeclared.java (100%) rename {spring-aop/src/test => spring-aop-2/src/main}/java/com/baeldung/undeclared/UndeclaredApplication.java (100%) rename {spring-aop/src/test => spring-aop-2/src/main}/java/com/baeldung/undeclared/UndeclaredAspect.java (100%) rename {spring-aop/src/test => spring-aop-2/src/main}/java/com/baeldung/undeclared/UndeclaredService.java (100%) create mode 100644 spring-aop-2/src/main/resources/logback.xml create mode 100644 spring-aop-2/src/test/java/com/baeldung/SpringContextTest.java rename {spring-aop => spring-aop-2}/src/test/java/com/baeldung/method/info/BankAccountServiceIntegrationTest.java (100%) rename {spring-aop => spring-aop-2}/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionIntegrationTest.java (100%) rename {spring-aop => spring-aop-2}/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionUnitTest.java (100%) diff --git a/pom.xml b/pom.xml index ad002650d7..be02eea2d4 100644 --- a/pom.xml +++ b/pom.xml @@ -624,6 +624,7 @@ spring-akka spring-amqp spring-aop + spring-aop-2 spring-apache-camel spring-batch @@ -1094,6 +1095,7 @@ spring-akka spring-amqp spring-aop + spring-aop-2 spring-apache-camel spring-batch diff --git a/spring-aop-2/README.md b/spring-aop-2/README.md new file mode 100644 index 0000000000..a9694ac236 --- /dev/null +++ b/spring-aop-2/README.md @@ -0,0 +1,10 @@ +## Spring AOP + +This module contains articles about Spring aspect oriented programming (AOP) + +### Relevant articles + +- [Spring Performance Logging](https://www.baeldung.com/spring-performance-logging) +- [When Does Java Throw UndeclaredThrowableException?](https://www.baeldung.com/java-undeclaredthrowableexception) +- [Get Advised Method Info in Spring AOP](https://www.baeldung.com/spring-aop-get-advised-method-info) +- More articles: [[<-- prev]](/spring-aop) \ No newline at end of file diff --git a/spring-aop-2/pom.xml b/spring-aop-2/pom.xml new file mode 100644 index 0000000000..457f3eae6d --- /dev/null +++ b/spring-aop-2/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + spring-aop-2 + spring-aop-2 + war + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + org.aspectj + aspectjrt + + + org.aspectj + aspectjweaver + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-test + test + + + org.mockito + mockito-core + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.basedir}/src/main/resources/logback.xml + + + + + + + + 1.11 + + + \ No newline at end of file diff --git a/spring-aop-2/src/main/java/com/baeldung/Application.java b/spring-aop-2/src/main/java/com/baeldung/Application.java new file mode 100644 index 0000000000..c0490d50c6 --- /dev/null +++ b/spring-aop-2/src/main/java/com/baeldung/Application.java @@ -0,0 +1,12 @@ +package com.baeldung; + +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/spring-aop/src/main/java/com/baeldung/method/info/Account.java b/spring-aop-2/src/main/java/com/baeldung/method/info/Account.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/method/info/Account.java rename to spring-aop-2/src/main/java/com/baeldung/method/info/Account.java diff --git a/spring-aop/src/main/java/com/baeldung/method/info/AccountOperation.java b/spring-aop-2/src/main/java/com/baeldung/method/info/AccountOperation.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/method/info/AccountOperation.java rename to spring-aop-2/src/main/java/com/baeldung/method/info/AccountOperation.java diff --git a/spring-aop/src/main/java/com/baeldung/method/info/BankAccountAspect.java b/spring-aop-2/src/main/java/com/baeldung/method/info/BankAccountAspect.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/method/info/BankAccountAspect.java rename to spring-aop-2/src/main/java/com/baeldung/method/info/BankAccountAspect.java diff --git a/spring-aop/src/main/java/com/baeldung/method/info/BankAccountService.java b/spring-aop-2/src/main/java/com/baeldung/method/info/BankAccountService.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/method/info/BankAccountService.java rename to spring-aop-2/src/main/java/com/baeldung/method/info/BankAccountService.java diff --git a/spring-aop/src/main/java/com/baeldung/method/info/WithdrawLimitException.java b/spring-aop-2/src/main/java/com/baeldung/method/info/WithdrawLimitException.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/method/info/WithdrawLimitException.java rename to spring-aop-2/src/main/java/com/baeldung/method/info/WithdrawLimitException.java diff --git a/spring-aop/src/main/java/com/baeldung/performancemonitor/AopConfiguration.java b/spring-aop-2/src/main/java/com/baeldung/performancemonitor/AopConfiguration.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/performancemonitor/AopConfiguration.java rename to spring-aop-2/src/main/java/com/baeldung/performancemonitor/AopConfiguration.java diff --git a/spring-aop/src/main/java/com/baeldung/performancemonitor/MyPerformanceMonitorInterceptor.java b/spring-aop-2/src/main/java/com/baeldung/performancemonitor/MyPerformanceMonitorInterceptor.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/performancemonitor/MyPerformanceMonitorInterceptor.java rename to spring-aop-2/src/main/java/com/baeldung/performancemonitor/MyPerformanceMonitorInterceptor.java diff --git a/spring-aop/src/main/java/com/baeldung/performancemonitor/PerfomanceApp.java b/spring-aop-2/src/main/java/com/baeldung/performancemonitor/PerfomanceApp.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/performancemonitor/PerfomanceApp.java rename to spring-aop-2/src/main/java/com/baeldung/performancemonitor/PerfomanceApp.java diff --git a/spring-aop/src/main/java/com/baeldung/performancemonitor/Person.java b/spring-aop-2/src/main/java/com/baeldung/performancemonitor/Person.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/performancemonitor/Person.java rename to spring-aop-2/src/main/java/com/baeldung/performancemonitor/Person.java diff --git a/spring-aop/src/main/java/com/baeldung/performancemonitor/PersonService.java b/spring-aop-2/src/main/java/com/baeldung/performancemonitor/PersonService.java similarity index 100% rename from spring-aop/src/main/java/com/baeldung/performancemonitor/PersonService.java rename to spring-aop-2/src/main/java/com/baeldung/performancemonitor/PersonService.java diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/SomeCheckedException.java b/spring-aop-2/src/main/java/com/baeldung/undeclared/SomeCheckedException.java similarity index 100% rename from spring-aop/src/test/java/com/baeldung/undeclared/SomeCheckedException.java rename to spring-aop-2/src/main/java/com/baeldung/undeclared/SomeCheckedException.java diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/ThrowUndeclared.java b/spring-aop-2/src/main/java/com/baeldung/undeclared/ThrowUndeclared.java similarity index 100% rename from spring-aop/src/test/java/com/baeldung/undeclared/ThrowUndeclared.java rename to spring-aop-2/src/main/java/com/baeldung/undeclared/ThrowUndeclared.java diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredApplication.java b/spring-aop-2/src/main/java/com/baeldung/undeclared/UndeclaredApplication.java similarity index 100% rename from spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredApplication.java rename to spring-aop-2/src/main/java/com/baeldung/undeclared/UndeclaredApplication.java diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredAspect.java b/spring-aop-2/src/main/java/com/baeldung/undeclared/UndeclaredAspect.java similarity index 100% rename from spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredAspect.java rename to spring-aop-2/src/main/java/com/baeldung/undeclared/UndeclaredAspect.java diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredService.java b/spring-aop-2/src/main/java/com/baeldung/undeclared/UndeclaredService.java similarity index 100% rename from spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredService.java rename to spring-aop-2/src/main/java/com/baeldung/undeclared/UndeclaredService.java diff --git a/spring-aop-2/src/main/resources/logback.xml b/spring-aop-2/src/main/resources/logback.xml new file mode 100644 index 0000000000..4eaa556705 --- /dev/null +++ b/spring-aop-2/src/main/resources/logback.xml @@ -0,0 +1,23 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-aop-2/src/test/java/com/baeldung/SpringContextTest.java b/spring-aop-2/src/test/java/com/baeldung/SpringContextTest.java new file mode 100644 index 0000000000..13c1c162f1 --- /dev/null +++ b/spring-aop-2/src/test/java/com/baeldung/SpringContextTest.java @@ -0,0 +1,15 @@ +package com.baeldung; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class SpringContextTest { + + @Test + public void whenSpringContextIsBootstrapped_thenNoExceptions() { + } +} diff --git a/spring-aop/src/test/java/com/baeldung/method/info/BankAccountServiceIntegrationTest.java b/spring-aop-2/src/test/java/com/baeldung/method/info/BankAccountServiceIntegrationTest.java similarity index 100% rename from spring-aop/src/test/java/com/baeldung/method/info/BankAccountServiceIntegrationTest.java rename to spring-aop-2/src/test/java/com/baeldung/method/info/BankAccountServiceIntegrationTest.java diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionIntegrationTest.java b/spring-aop-2/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionIntegrationTest.java similarity index 100% rename from spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionIntegrationTest.java rename to spring-aop-2/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionIntegrationTest.java diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionUnitTest.java b/spring-aop-2/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionUnitTest.java similarity index 100% rename from spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionUnitTest.java rename to spring-aop-2/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionUnitTest.java diff --git a/spring-aop/README.md b/spring-aop/README.md index b49c2bd457..4ef8c35fde 100644 --- a/spring-aop/README.md +++ b/spring-aop/README.md @@ -6,11 +6,9 @@ This module contains articles about Spring aspect oriented programming (AOP) - [Implementing a Custom Spring AOP Annotation](https://www.baeldung.com/spring-aop-annotation) - [Intro to AspectJ](https://www.baeldung.com/aspectj) -- [Spring Performance Logging](https://www.baeldung.com/spring-performance-logging) - [Introduction to Spring AOP](https://www.baeldung.com/spring-aop) - [Introduction to Pointcut Expressions in Spring](https://www.baeldung.com/spring-aop-pointcut-tutorial) - [Introduction to Advice Types in Spring](https://www.baeldung.com/spring-aop-advice-tutorial) -- [When Does Java Throw UndeclaredThrowableException?](https://www.baeldung.com/java-undeclaredthrowableexception) -- [Get Advised Method Info in Spring AOP](https://www.baeldung.com/spring-aop-get-advised-method-info) - [Advise Methods on Annotated Classes With AspectJ](https://www.baeldung.com/aspectj-advise-methods) - [Joinpoint vs. ProceedingJoinPoint in AspectJ](https://www.baeldung.com/aspectj-joinpoint-proceedingjoinpoint) +- More articles: [[next -->]](/spring-aop-2) diff --git a/spring-aop/src/main/resources/logback.xml b/spring-aop/src/main/resources/logback.xml index 84885fae62..fe4dfdee56 100644 --- a/spring-aop/src/main/resources/logback.xml +++ b/spring-aop/src/main/resources/logback.xml @@ -13,10 +13,6 @@ - - - - From 58c8a4e230a197cc2cd0a934cc2cbe804416ef3e Mon Sep 17 00:00:00 2001 From: Haroon Khan Date: Thu, 30 Dec 2021 21:44:45 +0000 Subject: [PATCH 17/48] [JAVA-8364] Split spring-5-reactive-2 module --- pom.xml | 2 + spring-5-reactive-2/README.md | 5 +- spring-5-reactive-3/.gitignore | 12 +++ spring-5-reactive-3/README.md | 6 ++ spring-5-reactive-3/pom.xml | 79 +++++++++++++++++++ .../logging/WebFluxLoggingExample.java | 0 .../src/main/resources/application.properties | 1 + .../src/test/resources/logback-test.xml | 12 +++ 8 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 spring-5-reactive-3/.gitignore create mode 100644 spring-5-reactive-3/README.md create mode 100644 spring-5-reactive-3/pom.xml rename {spring-5-reactive-2 => spring-5-reactive-3}/src/main/java/com/baeldung/webflux/logging/WebFluxLoggingExample.java (100%) create mode 100644 spring-5-reactive-3/src/main/resources/application.properties create mode 100644 spring-5-reactive-3/src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index e540cd32eb..476d358569 100644 --- a/pom.xml +++ b/pom.xml @@ -614,6 +614,7 @@ spring-5-data-reactive spring-5-reactive spring-5-reactive-2 + spring-5-reactive-3 spring-5-reactive-client spring-5-reactive-oauth spring-5-reactive-security @@ -1086,6 +1087,7 @@ spring-5-data-reactive spring-5-reactive spring-5-reactive-2 + spring-5-reactive-3 spring-5-reactive-client spring-5-reactive-oauth spring-5-reactive-security diff --git a/spring-5-reactive-2/README.md b/spring-5-reactive-2/README.md index 98a5f26433..33f9ee2b45 100644 --- a/spring-5-reactive-2/README.md +++ b/spring-5-reactive-2/README.md @@ -1,13 +1,12 @@ ## Spring 5 Reactive Project -This module contains articles about reactive Spring 5 +This module contains articles about reactive Spring 5. - [Spring WebClient vs. RestTemplate](https://www.baeldung.com/spring-webclient-resttemplate) - [Validation for Functional Endpoints in Spring 5](https://www.baeldung.com/spring-functional-endpoints-validation) -- [Logging a Reactive Sequence](https://www.baeldung.com/spring-reactive-sequence-logging) - [Testing Reactive Streams Using StepVerifier and TestPublisher](https://www.baeldung.com/reactive-streams-step-verifier-test-publisher) - [Debugging Reactive Streams in Java](https://www.baeldung.com/spring-debugging-reactive-streams) - [Static Content in Spring WebFlux](https://www.baeldung.com/spring-webflux-static-content) - [Server-Sent Events in Spring](https://www.baeldung.com/spring-server-sent-events) - [Backpressure Mechanism in Spring WebFlux](https://www.baeldung.com/spring-webflux-backpressure) -- More articles: [[<-- prev]](/spring-5-reactive) +- More articles: [[<-- prev]](../spring-5-reactive) [[next -->]](../spring-5-reactive-3) diff --git a/spring-5-reactive-3/.gitignore b/spring-5-reactive-3/.gitignore new file mode 100644 index 0000000000..dec013dfa4 --- /dev/null +++ b/spring-5-reactive-3/.gitignore @@ -0,0 +1,12 @@ +#folders# +.idea +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear \ No newline at end of file diff --git a/spring-5-reactive-3/README.md b/spring-5-reactive-3/README.md new file mode 100644 index 0000000000..da44bf98fc --- /dev/null +++ b/spring-5-reactive-3/README.md @@ -0,0 +1,6 @@ +## Spring 5 Reactive Project + +This module contains articles about reactive Spring 5. + +- [Logging a Reactive Sequence](https://www.baeldung.com/spring-reactive-sequence-logging) +- More articles: [[<-- prev]](../spring-5-reactive-2) diff --git a/spring-5-reactive-3/pom.xml b/spring-5-reactive-3/pom.xml new file mode 100644 index 0000000000..1e9248f30c --- /dev/null +++ b/spring-5-reactive-3/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + spring-5-reactive-3 + 0.0.1-SNAPSHOT + spring-5-reactive-3 + jar + spring 5 sample project about new features + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + org.projectreactor + reactor-spring + ${reactor-spring.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + + + + integration-lite-first + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.basedir}/src/test/resources/logback-test.xml + + + + + + + + integration-lite-second + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.basedir}/src/test/resources/logback-test.xml + + + + + + + + + + 1.0.1.RELEASE + + \ No newline at end of file diff --git a/spring-5-reactive-2/src/main/java/com/baeldung/webflux/logging/WebFluxLoggingExample.java b/spring-5-reactive-3/src/main/java/com/baeldung/webflux/logging/WebFluxLoggingExample.java similarity index 100% rename from spring-5-reactive-2/src/main/java/com/baeldung/webflux/logging/WebFluxLoggingExample.java rename to spring-5-reactive-3/src/main/java/com/baeldung/webflux/logging/WebFluxLoggingExample.java diff --git a/spring-5-reactive-3/src/main/resources/application.properties b/spring-5-reactive-3/src/main/resources/application.properties new file mode 100644 index 0000000000..815cc2b76d --- /dev/null +++ b/spring-5-reactive-3/src/main/resources/application.properties @@ -0,0 +1 @@ +# application properties \ No newline at end of file diff --git a/spring-5-reactive-3/src/test/resources/logback-test.xml b/spring-5-reactive-3/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..cedd46414d --- /dev/null +++ b/spring-5-reactive-3/src/test/resources/logback-test.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file From 94c6cb3872c2df73fc28f7a02469f581f64b67e6 Mon Sep 17 00:00:00 2001 From: Haroon Khan Date: Sat, 1 Jan 2022 19:11:19 +0000 Subject: [PATCH 18/48] [JAVA-9056] Upgrade JSON-java lib version --- json/README.md | 2 +- json/pom.xml | 2 +- .../java/com/baeldung/jsonjava/CookieIntegrationTest.java | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/json/README.md b/json/README.md index 0a4782cd86..73db055679 100644 --- a/json/README.md +++ b/json/README.md @@ -13,4 +13,4 @@ This module contains articles about JSON. - [Iterating Over an Instance of org.json.JSONObject](https://www.baeldung.com/jsonobject-iteration) - [Escape JSON String in Java](https://www.baeldung.com/java-json-escaping) - [Reducing JSON Data Size](https://www.baeldung.com/json-reduce-data-size) -- More Articles: [[next -->]](/json-2) +- More Articles: [[next -->]](../json-2) diff --git a/json/pom.xml b/json/pom.xml index 0d83f523b8..9b49f4cc7e 100644 --- a/json/pom.xml +++ b/json/pom.xml @@ -69,7 +69,7 @@ 1.4.1 1.0 1.0.1 - 20171018 + 20211205 2.8.5 1.1.2 diff --git a/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java index c1a3505bbc..4de5d8a21c 100644 --- a/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java @@ -1,5 +1,6 @@ package com.baeldung.jsonjava; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import org.json.Cookie; @@ -14,7 +15,7 @@ public class CookieIntegrationTest { assertEquals("{\"path\":\"/\",\"expires\":\"Thu, 18 Dec 2013 12:00:00 UTC\",\"name\":\"username\",\"value\":\"John Doe\"}", cookieJO.toString()); } - + @Test public void givenJSONObject_thenConvertToCookieString() { JSONObject cookieJO = new JSONObject(); @@ -22,8 +23,10 @@ public class CookieIntegrationTest { cookieJO.put("value", "John Doe"); cookieJO.put("expires", "Thu, 18 Dec 2013 12:00:00 UTC"); cookieJO.put("path", "/"); + String cookie = Cookie.toString(cookieJO); - assertEquals("username=John Doe;expires=Thu, 18 Dec 2013 12:00:00 UTC;path=/", cookie.toString()); + assertThat(cookie.split(";")) + .containsExactlyInAnyOrder("username=John Doe", "path=/", "expires=Thu, 18 Dec 2013 12:00:00 UTC"); } } From fd8b39247cdb33924e7947360a7cdabca69fcf09 Mon Sep 17 00:00:00 2001 From: sharifi Date: Sun, 2 Jan 2022 13:09:06 +0330 Subject: [PATCH 19/48] bael-4197: add @ContextConfiguration for loading application context --- .../EmployeeServiceAppContextIntegrationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java index 0ab157744a..e9ee8f3951 100644 --- a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java +++ b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/xmlapplicationcontext/EmployeeServiceAppContextIntegrationTest.java @@ -5,6 +5,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -12,6 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(classes = XmlBeanApplication.class) +//@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/application-context.xml") public class EmployeeServiceAppContextIntegrationTest { @Autowired From cf49235b4ee49b75f9e0bfae503eb0fccb9cdb51 Mon Sep 17 00:00:00 2001 From: Mainak Majumder Date: Mon, 3 Jan 2022 02:59:00 +0100 Subject: [PATCH 20/48] Equals() Vs Contentequals() Method in Java (#11598) * Application source code for the Baeldung article "HTTP PUT vs POST method in REST API" * update indention in pom file, update code in Address class * update indention * rename application * update pom * source code for article "Connection timeout vs read timeout" * Source code for Baeldung article BAEL-4896 * update code * code for article "String Equals() Vs Contentequals() Method in Java" * update code with tests * code update * update test methods * remove upper case and wrong sequence test * remove wrong sequence and upper case string variables --- .../StringEqualsVsContentEqualsUnitTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/equalsvscontentequals/StringEqualsVsContentEqualsUnitTest.java diff --git a/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/equalsvscontentequals/StringEqualsVsContentEqualsUnitTest.java b/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/equalsvscontentequals/StringEqualsVsContentEqualsUnitTest.java new file mode 100644 index 0000000000..1b8c97d024 --- /dev/null +++ b/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/equalsvscontentequals/StringEqualsVsContentEqualsUnitTest.java @@ -0,0 +1,30 @@ +package com.baeldung.equalsvscontentequals; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class StringEqualsVsContentEqualsUnitTest { + + String actualString = "baeldung"; + String identicalString = "baeldung"; + CharSequence identicalStringInstance = "baeldung"; + CharSequence identicalStringBufferInstance = new StringBuffer("baeldung"); + + @Test + public void whenIdenticalTestString_thenBothTrue() { + assertTrue(actualString.equals(identicalString)); + assertTrue(actualString.contentEquals(identicalString)); + } + + @Test + public void whenSameContentButDifferentType_thenEqualsIsFalseAndContentEqualsIsTrue() { + assertTrue(actualString.equals(identicalStringInstance)); + assertTrue(actualString.contentEquals(identicalStringInstance)); + + assertFalse(actualString.equals(identicalStringBufferInstance)); + assertTrue(actualString.contentEquals(identicalStringBufferInstance)); + } + +} From 148627f1d0ea00bcab90e4ebc454ec5aaa494ccd Mon Sep 17 00:00:00 2001 From: Azhwani <13301425+azhwani@users.noreply.github.com> Date: Mon, 3 Jan 2022 05:13:38 +0100 Subject: [PATCH 21/48] init commit (#11623) --- .../split/SplitStringEveryNthChar.java | 51 +++++++++++++++++++ .../SplitStringEveryNthCharUnitTest.java | 42 +++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 core-java-modules/core-java-string-operations-4/src/main/java/com/baeldung/split/SplitStringEveryNthChar.java create mode 100644 core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/split/SplitStringEveryNthCharUnitTest.java diff --git a/core-java-modules/core-java-string-operations-4/src/main/java/com/baeldung/split/SplitStringEveryNthChar.java b/core-java-modules/core-java-string-operations-4/src/main/java/com/baeldung/split/SplitStringEveryNthChar.java new file mode 100644 index 0000000000..3ac31d012a --- /dev/null +++ b/core-java-modules/core-java-string-operations-4/src/main/java/com/baeldung/split/SplitStringEveryNthChar.java @@ -0,0 +1,51 @@ +package com.baeldung.split; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; + +public class SplitStringEveryNthChar { + + public static List usingSplitMethod(String text, int n) { + String[] results = text.split("(?<=\\G.{" + n + "})"); + + return Arrays.asList(results); + } + + public static List usingSubstringMethod(String text, int n) { + List results = new ArrayList<>(); + int length = text.length(); + + for (int i = 0; i < length; i += n) { + results.add(text.substring(i, Math.min(length, i + n))); + } + + return results; + } + + public static List usingPattern(String text, int n) { + List results = new ArrayList<>(); + + Pattern pattern = Pattern.compile(".{1," + n + "}"); + Matcher matcher = pattern.matcher(text); + while (matcher.find()) { + String match = text.substring(matcher.start(), matcher.end()); + results.add(match); + } + + return results; + } + + public static List usingGuava(String text, int n) { + Iterable parts = Splitter.fixedLength(n) + .split(text); + + return ImmutableList.copyOf(parts); + } + +} diff --git a/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/split/SplitStringEveryNthCharUnitTest.java b/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/split/SplitStringEveryNthCharUnitTest.java new file mode 100644 index 0000000000..03dcd5312e --- /dev/null +++ b/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/split/SplitStringEveryNthCharUnitTest.java @@ -0,0 +1,42 @@ +package com.baeldung.split; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +public class SplitStringEveryNthCharUnitTest { + + public static final String TEXT = "abcdefgh123456"; + + @Test + public void givenString_whenUsingSplit_thenSplit() { + List results = SplitStringEveryNthChar.usingSplitMethod(TEXT, 3); + + assertThat(results, contains("abc", "def", "gh1", "234", "56")); + } + + @Test + public void givenString_whenUsingSubstring_thenSplit() { + List results = SplitStringEveryNthChar.usingSubstringMethod(TEXT, 4); + + assertThat(results, contains("abcd", "efgh", "1234", "56")); + } + + @Test + public void givenString_whenUsingPattern_thenSplit() { + List results = SplitStringEveryNthChar.usingPattern(TEXT, 5); + + assertThat(results, contains("abcde", "fgh12", "3456")); + } + + @Test + public void givenString_whenUsingGuava_thenSplit() { + List results = SplitStringEveryNthChar.usingGuava(TEXT, 6); + + assertThat(results, contains("abcdef", "gh1234", "56")); + } + +} From cbf74d1a943474ed17b2728bbde2757b5437a281 Mon Sep 17 00:00:00 2001 From: sampadawagde Date: Mon, 3 Jan 2022 22:54:14 +0530 Subject: [PATCH 22/48] JAVA-8280: remove surefire plugin --- spring-aop-2/pom.xml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/spring-aop-2/pom.xml b/spring-aop-2/pom.xml index 457f3eae6d..76e4780e72 100644 --- a/spring-aop-2/pom.xml +++ b/spring-aop-2/pom.xml @@ -39,20 +39,6 @@ - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${project.basedir}/src/main/resources/logback.xml - - - - - - 1.11 From cfafac94b108285e701ed50f5d7456a34e439f6a Mon Sep 17 00:00:00 2001 From: Haroon Khan Date: Mon, 3 Jan 2022 21:16:56 +0000 Subject: [PATCH 23/48] [JAVA-8364] Clean up POM file --- spring-5-reactive-3/pom.xml | 35 ------------------- .../src/test/resources/logback-test.xml | 8 ++--- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/spring-5-reactive-3/pom.xml b/spring-5-reactive-3/pom.xml index 1e9248f30c..89af34732f 100644 --- a/spring-5-reactive-3/pom.xml +++ b/spring-5-reactive-3/pom.xml @@ -38,41 +38,6 @@ - - - integration-lite-first - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${project.basedir}/src/test/resources/logback-test.xml - - - - - - - - integration-lite-second - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${project.basedir}/src/test/resources/logback-test.xml - - - - - - - - 1.0.1.RELEASE diff --git a/spring-5-reactive-3/src/test/resources/logback-test.xml b/spring-5-reactive-3/src/test/resources/logback-test.xml index cedd46414d..25f2184c85 100644 --- a/spring-5-reactive-3/src/test/resources/logback-test.xml +++ b/spring-5-reactive-3/src/test/resources/logback-test.xml @@ -1,12 +1,8 @@ - - - + + - \ No newline at end of file From 6bcc01f60a1a47a2a0f28ab0d9d1b94dbe867bae Mon Sep 17 00:00:00 2001 From: Ralf Ueberfuhr <40685729+ueberfuhr@users.noreply.github.com> Date: Wed, 5 Jan 2022 02:58:21 +0100 Subject: [PATCH 24/48] BAEL-5251: Spring Boot with Swagger UI and Keycloak-Integration (#11632) --- spring-boot-modules/pom.xml | 3 +- .../spring-boot-swagger-keycloak/pom.xml | 67 ++++++++++++++ .../swaggerkeycloak/GlobalSecurityConfig.java | 59 +++++++++++++ .../KeycloakConfigResolverConfig.java | 23 +++++ .../OpenAPISecurityConfig.java | 83 ++++++++++++++++++ .../swaggerkeycloak/SwaggerUIConfig.java | 36 ++++++++ .../com/baeldung/swaggerkeycloak/Todo.java | 44 ++++++++++ .../swaggerkeycloak/TodosApplication.java | 13 +++ .../swaggerkeycloak/TodosController.java | 40 +++++++++ .../springfox-swagger-ui/favicon-16x16.png | Bin 0 -> 665 bytes .../springfox-swagger-ui/favicon-32x32.png | Bin 0 -> 628 bytes .../springfox-swagger-ui/oauth2-redirect.html | 75 ++++++++++++++++ .../springfox-swagger-ui/swagger-ui.css | 4 + .../src/main/resources/application.yml | 9 ++ 14 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/pom.xml create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/GlobalSecurityConfig.java create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/KeycloakConfigResolverConfig.java create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/OpenAPISecurityConfig.java create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/SwaggerUIConfig.java create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/Todo.java create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/TodosApplication.java create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/TodosController.java create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/favicon-16x16.png create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/favicon-32x32.png create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/oauth2-redirect.html create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/swagger-ui.css create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/application.yml diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index 29da696494..ac0deaa9e5 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -70,6 +70,7 @@ spring-boot-springdoc spring-boot-swagger spring-boot-swagger-jwt + spring-boot-swagger-keycloak spring-boot-testing spring-boot-testing-2 spring-boot-vue @@ -97,4 +98,4 @@ - \ No newline at end of file + diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/pom.xml b/spring-boot-modules/spring-boot-swagger-keycloak/pom.xml new file mode 100644 index 0000000000..82e1951b8e --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + spring-boot-swagger-keycloak + 0.1.0-SNAPSHOT + spring-boot-swagger-keycloak + jar + Module For Spring Boot Swagger UI with Keycloak + + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + + + + + + org.keycloak.bom + keycloak-adapter-bom + ${keycloak.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + io.springfox + springfox-boot-starter + ${springfox.version} + + + + org.keycloak + keycloak-spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-security + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + 2.4.5 + 3.0.0 + 15.0.2 + + + diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/GlobalSecurityConfig.java b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/GlobalSecurityConfig.java new file mode 100644 index 0000000000..985cbf7d06 --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/GlobalSecurityConfig.java @@ -0,0 +1,59 @@ +package com.baeldung.swaggerkeycloak; + +import org.keycloak.adapters.springsecurity.KeycloakConfiguration; +import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; +import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; +import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; +import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; + +@KeycloakConfiguration +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class GlobalSecurityConfig extends KeycloakWebSecurityConfigurerAdapter { + + @Override + protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { + return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); + } + + // otherwise, we'll get an error 'permitAll only works with HttpSecurity.authorizeRequests()' + @Override + protected void configure(HttpSecurity http) throws Exception { + super.configure(http); + http + .csrf().disable() + .authorizeRequests() + // we can set up authorization here alternatively to @Secured methods + .antMatchers(HttpMethod.OPTIONS).permitAll() + .antMatchers("/api/**").authenticated() + // force authentication for all requests (and use global method security) + .anyRequest().permitAll(); + } + + /* + * re-configure Spring Security to use + * registers the KeycloakAuthenticationProvider with the authentication manager + */ + @Autowired + void configureGlobal(AuthenticationManagerBuilder auth) { + KeycloakAuthenticationProvider provider = keycloakAuthenticationProvider(); + provider.setGrantedAuthoritiesMapper(authoritiesMapper()); + auth.authenticationProvider(provider); + } + + GrantedAuthoritiesMapper authoritiesMapper() { + SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); + mapper.setPrefix("ROLE_"); // Spring Security adds a prefix to the authority/role names (we use the default here) + mapper.setConvertToUpperCase(true); // convert names to uppercase + mapper.setDefaultAuthority("ROLE_ANONYMOUS"); // set a default authority + return mapper; + } + +} diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/KeycloakConfigResolverConfig.java b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/KeycloakConfigResolverConfig.java new file mode 100644 index 0000000000..bca764a17d --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/KeycloakConfigResolverConfig.java @@ -0,0 +1,23 @@ +package com.baeldung.swaggerkeycloak; + +import org.keycloak.adapters.KeycloakConfigResolver; +import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class KeycloakConfigResolverConfig { + + /* + * re-configure keycloak adapter for Spring Boot environment, + * i.e. to read config from application.yml + * (otherwise, we need a keycloak.json file) + */ + @Bean + public KeycloakConfigResolver configResolver() { + return new KeycloakSpringBootConfigResolver(); + } + + + +} diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/OpenAPISecurityConfig.java b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/OpenAPISecurityConfig.java new file mode 100644 index 0000000000..d3ab26e1ee --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/OpenAPISecurityConfig.java @@ -0,0 +1,83 @@ +package com.baeldung.swaggerkeycloak; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import springfox.documentation.builders.OAuth2SchemeBuilder; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger.web.SecurityConfiguration; +import springfox.documentation.swagger.web.SecurityConfigurationBuilder; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@Configuration +public class OpenAPISecurityConfig { + + @Value("${keycloak.auth-server-url}") + String authServerUrl; + @Value("${keycloak.realm}") + String realm; + @Value("${keycloak.resource}") + private String clientId; + @Value("${keycloak.credentials.secret}") + private String clientSecret; + + @Autowired + void addSecurity(Docket docket) { + docket + .securitySchemes(Collections.singletonList(authenticationScheme())) + .securityContexts(Collections.singletonList(securityContext())); + } + + private SecurityScheme authenticationScheme() { + return new OAuth2SchemeBuilder("implicit") + .name("my_oAuth_security_schema") + .authorizationUrl(authServerUrl + "/realms/" + realm) + .scopes(authorizationScopes()) + .build(); + } + + private List authorizationScopes() { + return Arrays.asList( + new AuthorizationScope("read_access", "read data"), + new AuthorizationScope("write_access", "modify data") + ); + } + + private SecurityContext securityContext() { + return SecurityContext. + builder(). + securityReferences(readAccessAuth()) + .operationSelector(operationContext -> HttpMethod.GET.equals(operationContext.httpMethod())) + .build(); + } + + private List readAccessAuth() { + AuthorizationScope[] authorizationScopes = new AuthorizationScope[] { authorizationScopes().get(0) }; + return Collections.singletonList( + new SecurityReference("my_oAuth_security_schema", authorizationScopes) + ); + } + + @Bean + public SecurityConfiguration security() { + return SecurityConfigurationBuilder.builder() + .clientId(clientId) + .clientSecret(clientSecret) + .realm(realm) + .appName(clientId) + .scopeSeparator(",") + .additionalQueryStringParams(null) + .useBasicAuthenticationWithAccessCodeGrant(false) + .build(); + } + +} diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/SwaggerUIConfig.java b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/SwaggerUIConfig.java new file mode 100644 index 0000000000..1207fe5b9e --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/SwaggerUIConfig.java @@ -0,0 +1,36 @@ +package com.baeldung.swaggerkeycloak; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.oas.annotations.EnableOpenApi; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +import static springfox.documentation.builders.RequestHandlerSelectors.basePackage; + +@EnableOpenApi +@Configuration +class SwaggerUIConfig { + + @Bean + Docket api() { + return new Docket(DocumentationType.OAS_30) + .useDefaultResponseMessages(false) + .select() + .apis(basePackage(TodosApplication.class.getPackage().getName())) + .paths(PathSelectors.any()) + .build() + .apiInfo(apiInfo()); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder().title("Todos Management Service") + .description("A service providing todos.") + .version("1.0") + .build(); + } + +} diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/Todo.java b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/Todo.java new file mode 100644 index 0000000000..5423f9e342 --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/Todo.java @@ -0,0 +1,44 @@ +package com.baeldung.swaggerkeycloak; + +import java.time.LocalDate; + +public class Todo { + + private Long id; + private String title; + private LocalDate dueDate; + + public Todo() { + } + + public Todo(Long id, String title, LocalDate dueDate) { + this.id = id; + this.title = title; + this.dueDate = dueDate; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public LocalDate getDueDate() { + return dueDate; + } + + public void setDueDate(LocalDate dueDate) { + this.dueDate = dueDate; + } + +} diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/TodosApplication.java b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/TodosApplication.java new file mode 100644 index 0000000000..20abbd8f10 --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/TodosApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.swaggerkeycloak; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TodosApplication { + + public static void main(String[] args) { + SpringApplication.run(TodosApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/TodosController.java b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/TodosController.java new file mode 100644 index 0000000000..6b70072ce3 --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/java/com/baeldung/swaggerkeycloak/TodosController.java @@ -0,0 +1,40 @@ +package com.baeldung.swaggerkeycloak; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.PostConstruct; +import java.time.LocalDate; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/api/v1/todos") +public class TodosController { + + private final Map todos = new HashMap<>(); + + @PostConstruct + public void initData() { + todos.put(1L, new Todo(1L, "Install Keycloak", LocalDate.now().plusDays(14))); + todos.put(2L, new Todo(2L, "Configure realm", LocalDate.now().plusDays(21))); + } + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + @ApiOperation("Read all todos") + @ApiResponses({ + @ApiResponse(code = 200, message = "The todos were found and returned.") + }) + @PreAuthorize("hasAuthority('SCOPE_read_access')") + public Collection readAll() { + return todos.values(); + } + +} diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/favicon-16x16.png b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..8b194e617af1c135e6b37939591d24ac3a5efa18 GIT binary patch literal 665 zcmV;K0%rY*P)}JKSduyL>)s!A4EhTMMEM%Q;aL6%l#xiZiF>S;#Y{N2Zz%pvTGHJduXuC6Lx-)0EGfRy*N{Tv4i8@4oJ41gw zKzThrcRe|7J~(YYIBq{SYCkn-KQm=N8$CrEK1CcqMI1dv9z#VRL_{D)L|`QmF8}}l zJ9JV`Q}p!p_4f7m_U`WQ@apR4;o;!mnU<7}iG_qr zF(e)x9~BG-3IzcG2M4an0002kNkl41`ZiN1i62V%{PM@Ry|IS_+Yc7{bb`MM~xm(7p4|kMHP&!VGuDW4kFixat zXw43VmgwEvB$hXt_u=vZ>+v4i7E}n~eG6;n4Z=zF1n?T*yg<;W6kOfxpC6nao>VR% z?fpr=asSJ&`L*wu^rLJ5Peq*PB0;alL#XazZCBxJLd&giTfw@!hW167F^`7kobi;( ze<<>qNlP|xy7S1zl@lZNIBR7#o9ybJsptO#%}P0hz~sBp00000NkvXXu0mjfUsDF? literal 0 HcmV?d00001 diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/favicon-32x32.png b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..249737fe44558e679f0b67134e274461d988fa98 GIT binary patch literal 628 zcmV-)0*n2LP)Ma*GM0}OV<074bNCP7P7GVd{iMr*I6y~TMLss@FjvgL~HxU z%Vvj33AwpD(Z4*$Mfx=HaU16axM zt2xG_rloN<$iy9j9I5 + + + Swagger UI: OAuth2 Redirect + + + + + diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/swagger-ui.css b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/swagger-ui.css new file mode 100644 index 0000000000..e43a572a13 --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/META-INF/resources/webjars/springfox-swagger-ui/swagger-ui.css @@ -0,0 +1,4 @@ +.swagger-ui{color:#3b4151; + /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */font-family:sans-serif}.swagger-ui html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.15}.swagger-ui body{margin:0}.swagger-ui article,.swagger-ui aside,.swagger-ui footer,.swagger-ui header,.swagger-ui nav,.swagger-ui section{display:block}.swagger-ui h1{font-size:2em;margin:.67em 0}.swagger-ui figcaption,.swagger-ui figure,.swagger-ui main{display:block}.swagger-ui figure{margin:1em 40px}.swagger-ui hr{box-sizing:content-box;height:0;overflow:visible}.swagger-ui pre{font-family:monospace,monospace;font-size:1em}.swagger-ui a{-webkit-text-decoration-skip:objects;background-color:transparent}.swagger-ui abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.swagger-ui b,.swagger-ui strong{font-weight:inherit;font-weight:bolder}.swagger-ui code,.swagger-ui kbd,.swagger-ui samp{font-family:monospace,monospace;font-size:1em}.swagger-ui dfn{font-style:italic}.swagger-ui mark{background-color:#ff0;color:#000}.swagger-ui small{font-size:80%}.swagger-ui sub,.swagger-ui sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.swagger-ui sub{bottom:-.25em}.swagger-ui sup{top:-.5em}.swagger-ui audio,.swagger-ui video{display:inline-block}.swagger-ui audio:not([controls]){display:none;height:0}.swagger-ui img{border-style:none}.swagger-ui svg:not(:root){overflow:hidden}.swagger-ui button,.swagger-ui input,.swagger-ui optgroup,.swagger-ui select,.swagger-ui textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}.swagger-ui button,.swagger-ui input{overflow:visible}.swagger-ui button,.swagger-ui select{text-transform:none}.swagger-ui [type=reset],.swagger-ui [type=submit],.swagger-ui button,.swagger-ui html [type=button]{-webkit-appearance:button}.swagger-ui [type=button]::-moz-focus-inner,.swagger-ui [type=reset]::-moz-focus-inner,.swagger-ui [type=submit]::-moz-focus-inner,.swagger-ui button::-moz-focus-inner{border-style:none;padding:0}.swagger-ui [type=button]:-moz-focusring,.swagger-ui [type=reset]:-moz-focusring,.swagger-ui [type=submit]:-moz-focusring,.swagger-ui button:-moz-focusring{outline:1px dotted ButtonText}.swagger-ui fieldset{padding:.35em .75em .625em}.swagger-ui legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}.swagger-ui progress{display:inline-block;vertical-align:baseline}.swagger-ui textarea{overflow:auto}.swagger-ui [type=checkbox],.swagger-ui [type=radio]{box-sizing:border-box;padding:0}.swagger-ui [type=number]::-webkit-inner-spin-button,.swagger-ui [type=number]::-webkit-outer-spin-button{height:auto}.swagger-ui [type=search]{-webkit-appearance:textfield;outline-offset:-2px}.swagger-ui [type=search]::-webkit-search-cancel-button,.swagger-ui [type=search]::-webkit-search-decoration{-webkit-appearance:none}.swagger-ui ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.swagger-ui details,.swagger-ui menu{display:block}.swagger-ui summary{display:list-item}.swagger-ui canvas{display:inline-block}.swagger-ui [hidden],.swagger-ui template{display:none}.swagger-ui .debug *{outline:1px solid gold}.swagger-ui .debug-white *{outline:1px solid #fff}.swagger-ui .debug-black *{outline:1px solid #000}.swagger-ui .debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTRDOTY4N0U2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTRDOTY4N0Q2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3NjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3NzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsBS+GMAAAAjSURBVHjaYvz//z8DLsD4gcGXiYEAGBIKGBne//fFpwAgwAB98AaF2pjlUQAAAABJRU5ErkJggg==) repeat 0 0}.swagger-ui .debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODYyRjhERDU2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODYyRjhERDQ2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QTY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3QjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvCS01IAAABMSURBVHjaYmR4/5+BFPBfAMFm/MBgx8RAGWCn1AAmSg34Q6kBDKMGMDCwICeMIemF/5QawEipAWwUhwEjMDvbAWlWkvVBwu8vQIABAEwBCph8U6c0AAAAAElFTkSuQmCC) repeat 0 0}.swagger-ui .debug-grid-8-solid{background:#fff url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAAAAD/4QMxaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzExMSA3OS4xNTgzMjUsIDIwMTUvMDkvMTAtMDE6MTA6MjAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE1IChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIxMjI0OTczNjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIxMjI0OTc0NjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjEyMjQ5NzE2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjEyMjQ5NzI2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAbGhopHSlBJiZBQi8vL0JHPz4+P0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHAR0pKTQmND8oKD9HPzU/R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAAIAAgDASIAAhEBAxEB/8QAWQABAQAAAAAAAAAAAAAAAAAAAAYBAQEAAAAAAAAAAAAAAAAAAAIEEAEBAAMBAAAAAAAAAAAAAAABADECA0ERAAEDBQAAAAAAAAAAAAAAAAARITFBUWESIv/aAAwDAQACEQMRAD8AoOnTV1QTD7JJshP3vSM3P//Z) repeat 0 0}.swagger-ui .debug-grid-16-solid{background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzY3MkJEN0U2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzY3MkJEN0Y2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3RDY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pve6J3kAAAAzSURBVHjaYvz//z8D0UDsMwMjSRoYP5Gq4SPNbRjVMEQ1fCRDg+in/6+J1AJUxsgAEGAA31BAJMS0GYEAAAAASUVORK5CYII=) repeat 0 0}.swagger-ui .border-box,.swagger-ui a,.swagger-ui article,.swagger-ui body,.swagger-ui code,.swagger-ui dd,.swagger-ui div,.swagger-ui dl,.swagger-ui dt,.swagger-ui fieldset,.swagger-ui footer,.swagger-ui form,.swagger-ui h1,.swagger-ui h2,.swagger-ui h3,.swagger-ui h4,.swagger-ui h5,.swagger-ui h6,.swagger-ui header,.swagger-ui html,.swagger-ui input[type=email],.swagger-ui input[type=number],.swagger-ui input[type=password],.swagger-ui input[type=tel],.swagger-ui input[type=text],.swagger-ui input[type=url],.swagger-ui legend,.swagger-ui li,.swagger-ui main,.swagger-ui ol,.swagger-ui p,.swagger-ui pre,.swagger-ui section,.swagger-ui table,.swagger-ui td,.swagger-ui textarea,.swagger-ui th,.swagger-ui tr,.swagger-ui ul{box-sizing:border-box}.swagger-ui .aspect-ratio{height:0;position:relative}.swagger-ui .aspect-ratio--16x9{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1{padding-bottom:100%}.swagger-ui .aspect-ratio--object{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100}@media screen and (min-width:30em){.swagger-ui .aspect-ratio-ns{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-ns{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-ns{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-ns{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-ns{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-ns{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-ns{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-ns{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-ns{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-ns{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-ns{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-ns{padding-bottom:100%}.swagger-ui .aspect-ratio--object-ns{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .aspect-ratio-m{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-m{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-m{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-m{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-m{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-m{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-m{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-m{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-m{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-m{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-m{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-m{padding-bottom:100%}.swagger-ui .aspect-ratio--object-m{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100}}@media screen and (min-width:60em){.swagger-ui .aspect-ratio-l{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-l{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-l{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-l{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-l{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-l{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-l{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-l{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-l{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-l{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-l{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-l{padding-bottom:100%}.swagger-ui .aspect-ratio--object-l{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100}}.swagger-ui img{max-width:100%}.swagger-ui .cover{background-size:cover!important}.swagger-ui .contain{background-size:contain!important}@media screen and (min-width:30em){.swagger-ui .cover-ns{background-size:cover!important}.swagger-ui .contain-ns{background-size:contain!important}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .cover-m{background-size:cover!important}.swagger-ui .contain-m{background-size:contain!important}}@media screen and (min-width:60em){.swagger-ui .cover-l{background-size:cover!important}.swagger-ui .contain-l{background-size:contain!important}}.swagger-ui .bg-center{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left{background-position:0;background-repeat:no-repeat}@media screen and (min-width:30em){.swagger-ui .bg-center-ns{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-ns{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-ns{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-ns{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-ns{background-position:0;background-repeat:no-repeat}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .bg-center-m{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-m{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-m{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-m{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-m{background-position:0;background-repeat:no-repeat}}@media screen and (min-width:60em){.swagger-ui .bg-center-l{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-l{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-l{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-l{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-l{background-position:0;background-repeat:no-repeat}}.swagger-ui .outline{outline:1px solid}.swagger-ui .outline-transparent{outline:1px solid transparent}.swagger-ui .outline-0{outline:0}@media screen and (min-width:30em){.swagger-ui .outline-ns{outline:1px solid}.swagger-ui .outline-transparent-ns{outline:1px solid transparent}.swagger-ui .outline-0-ns{outline:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .outline-m{outline:1px solid}.swagger-ui .outline-transparent-m{outline:1px solid transparent}.swagger-ui .outline-0-m{outline:0}}@media screen and (min-width:60em){.swagger-ui .outline-l{outline:1px solid}.swagger-ui .outline-transparent-l{outline:1px solid transparent}.swagger-ui .outline-0-l{outline:0}}.swagger-ui .ba{border-style:solid;border-width:1px}.swagger-ui .bt{border-top-style:solid;border-top-width:1px}.swagger-ui .br{border-right-style:solid;border-right-width:1px}.swagger-ui .bb{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl{border-left-style:solid;border-left-width:1px}.swagger-ui .bn{border-style:none;border-width:0}@media screen and (min-width:30em){.swagger-ui .ba-ns{border-style:solid;border-width:1px}.swagger-ui .bt-ns{border-top-style:solid;border-top-width:1px}.swagger-ui .br-ns{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-ns{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-ns{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-ns{border-style:none;border-width:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .ba-m{border-style:solid;border-width:1px}.swagger-ui .bt-m{border-top-style:solid;border-top-width:1px}.swagger-ui .br-m{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-m{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-m{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-m{border-style:none;border-width:0}}@media screen and (min-width:60em){.swagger-ui .ba-l{border-style:solid;border-width:1px}.swagger-ui .bt-l{border-top-style:solid;border-top-width:1px}.swagger-ui .br-l{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-l{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-l{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-l{border-style:none;border-width:0}}.swagger-ui .b--black{border-color:#000}.swagger-ui .b--near-black{border-color:#111}.swagger-ui .b--dark-gray{border-color:#333}.swagger-ui .b--mid-gray{border-color:#555}.swagger-ui .b--gray{border-color:#777}.swagger-ui .b--silver{border-color:#999}.swagger-ui .b--light-silver{border-color:#aaa}.swagger-ui .b--moon-gray{border-color:#ccc}.swagger-ui .b--light-gray{border-color:#eee}.swagger-ui .b--near-white{border-color:#f4f4f4}.swagger-ui .b--white{border-color:#fff}.swagger-ui .b--white-90{border-color:hsla(0,0%,100%,.9)}.swagger-ui .b--white-80{border-color:hsla(0,0%,100%,.8)}.swagger-ui .b--white-70{border-color:hsla(0,0%,100%,.7)}.swagger-ui .b--white-60{border-color:hsla(0,0%,100%,.6)}.swagger-ui .b--white-50{border-color:hsla(0,0%,100%,.5)}.swagger-ui .b--white-40{border-color:hsla(0,0%,100%,.4)}.swagger-ui .b--white-30{border-color:hsla(0,0%,100%,.3)}.swagger-ui .b--white-20{border-color:hsla(0,0%,100%,.2)}.swagger-ui .b--white-10{border-color:hsla(0,0%,100%,.1)}.swagger-ui .b--white-05{border-color:hsla(0,0%,100%,.05)}.swagger-ui .b--white-025{border-color:hsla(0,0%,100%,.025)}.swagger-ui .b--white-0125{border-color:hsla(0,0%,100%,.013)}.swagger-ui .b--black-90{border-color:rgba(0,0,0,.9)}.swagger-ui .b--black-80{border-color:rgba(0,0,0,.8)}.swagger-ui .b--black-70{border-color:rgba(0,0,0,.7)}.swagger-ui .b--black-60{border-color:rgba(0,0,0,.6)}.swagger-ui .b--black-50{border-color:rgba(0,0,0,.5)}.swagger-ui .b--black-40{border-color:rgba(0,0,0,.4)}.swagger-ui .b--black-30{border-color:rgba(0,0,0,.3)}.swagger-ui .b--black-20{border-color:rgba(0,0,0,.2)}.swagger-ui .b--black-10{border-color:rgba(0,0,0,.1)}.swagger-ui .b--black-05{border-color:rgba(0,0,0,.05)}.swagger-ui .b--black-025{border-color:rgba(0,0,0,.025)}.swagger-ui .b--black-0125{border-color:rgba(0,0,0,.013)}.swagger-ui .b--dark-red{border-color:#e7040f}.swagger-ui .b--red{border-color:#ff4136}.swagger-ui .b--light-red{border-color:#ff725c}.swagger-ui .b--orange{border-color:#ff6300}.swagger-ui .b--gold{border-color:#ffb700}.swagger-ui .b--yellow{border-color:gold}.swagger-ui .b--light-yellow{border-color:#fbf1a9}.swagger-ui .b--purple{border-color:#5e2ca5}.swagger-ui .b--light-purple{border-color:#a463f2}.swagger-ui .b--dark-pink{border-color:#d5008f}.swagger-ui .b--hot-pink{border-color:#ff41b4}.swagger-ui .b--pink{border-color:#ff80cc}.swagger-ui .b--light-pink{border-color:#ffa3d7}.swagger-ui .b--dark-green{border-color:#137752}.swagger-ui .b--green{border-color:#19a974}.swagger-ui .b--light-green{border-color:#9eebcf}.swagger-ui .b--navy{border-color:#001b44}.swagger-ui .b--dark-blue{border-color:#00449e}.swagger-ui .b--blue{border-color:#357edd}.swagger-ui .b--light-blue{border-color:#96ccff}.swagger-ui .b--lightest-blue{border-color:#cdecff}.swagger-ui .b--washed-blue{border-color:#f6fffe}.swagger-ui .b--washed-green{border-color:#e8fdf5}.swagger-ui .b--washed-yellow{border-color:#fffceb}.swagger-ui .b--washed-red{border-color:#ffdfdf}.swagger-ui .b--transparent{border-color:transparent}.swagger-ui .b--inherit{border-color:inherit}.swagger-ui .br0{border-radius:0}.swagger-ui .br1{border-radius:.125rem}.swagger-ui .br2{border-radius:.25rem}.swagger-ui .br3{border-radius:.5rem}.swagger-ui .br4{border-radius:1rem}.swagger-ui .br-100{border-radius:100%}.swagger-ui .br-pill{border-radius:9999px}.swagger-ui .br--bottom{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left{border-bottom-right-radius:0;border-top-right-radius:0}@media screen and (min-width:30em){.swagger-ui .br0-ns{border-radius:0}.swagger-ui .br1-ns{border-radius:.125rem}.swagger-ui .br2-ns{border-radius:.25rem}.swagger-ui .br3-ns{border-radius:.5rem}.swagger-ui .br4-ns{border-radius:1rem}.swagger-ui .br-100-ns{border-radius:100%}.swagger-ui .br-pill-ns{border-radius:9999px}.swagger-ui .br--bottom-ns{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-ns{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-ns{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-ns{border-bottom-right-radius:0;border-top-right-radius:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .br0-m{border-radius:0}.swagger-ui .br1-m{border-radius:.125rem}.swagger-ui .br2-m{border-radius:.25rem}.swagger-ui .br3-m{border-radius:.5rem}.swagger-ui .br4-m{border-radius:1rem}.swagger-ui .br-100-m{border-radius:100%}.swagger-ui .br-pill-m{border-radius:9999px}.swagger-ui .br--bottom-m{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-m{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-m{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-m{border-bottom-right-radius:0;border-top-right-radius:0}}@media screen and (min-width:60em){.swagger-ui .br0-l{border-radius:0}.swagger-ui .br1-l{border-radius:.125rem}.swagger-ui .br2-l{border-radius:.25rem}.swagger-ui .br3-l{border-radius:.5rem}.swagger-ui .br4-l{border-radius:1rem}.swagger-ui .br-100-l{border-radius:100%}.swagger-ui .br-pill-l{border-radius:9999px}.swagger-ui .br--bottom-l{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-l{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-l{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-l{border-bottom-right-radius:0;border-top-right-radius:0}}.swagger-ui .b--dotted{border-style:dotted}.swagger-ui .b--dashed{border-style:dashed}.swagger-ui .b--solid{border-style:solid}.swagger-ui .b--none{border-style:none}@media screen and (min-width:30em){.swagger-ui .b--dotted-ns{border-style:dotted}.swagger-ui .b--dashed-ns{border-style:dashed}.swagger-ui .b--solid-ns{border-style:solid}.swagger-ui .b--none-ns{border-style:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .b--dotted-m{border-style:dotted}.swagger-ui .b--dashed-m{border-style:dashed}.swagger-ui .b--solid-m{border-style:solid}.swagger-ui .b--none-m{border-style:none}}@media screen and (min-width:60em){.swagger-ui .b--dotted-l{border-style:dotted}.swagger-ui .b--dashed-l{border-style:dashed}.swagger-ui .b--solid-l{border-style:solid}.swagger-ui .b--none-l{border-style:none}}.swagger-ui .bw0{border-width:0}.swagger-ui .bw1{border-width:.125rem}.swagger-ui .bw2{border-width:.25rem}.swagger-ui .bw3{border-width:.5rem}.swagger-ui .bw4{border-width:1rem}.swagger-ui .bw5{border-width:2rem}.swagger-ui .bt-0{border-top-width:0}.swagger-ui .br-0{border-right-width:0}.swagger-ui .bb-0{border-bottom-width:0}.swagger-ui .bl-0{border-left-width:0}@media screen and (min-width:30em){.swagger-ui .bw0-ns{border-width:0}.swagger-ui .bw1-ns{border-width:.125rem}.swagger-ui .bw2-ns{border-width:.25rem}.swagger-ui .bw3-ns{border-width:.5rem}.swagger-ui .bw4-ns{border-width:1rem}.swagger-ui .bw5-ns{border-width:2rem}.swagger-ui .bt-0-ns{border-top-width:0}.swagger-ui .br-0-ns{border-right-width:0}.swagger-ui .bb-0-ns{border-bottom-width:0}.swagger-ui .bl-0-ns{border-left-width:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .bw0-m{border-width:0}.swagger-ui .bw1-m{border-width:.125rem}.swagger-ui .bw2-m{border-width:.25rem}.swagger-ui .bw3-m{border-width:.5rem}.swagger-ui .bw4-m{border-width:1rem}.swagger-ui .bw5-m{border-width:2rem}.swagger-ui .bt-0-m{border-top-width:0}.swagger-ui .br-0-m{border-right-width:0}.swagger-ui .bb-0-m{border-bottom-width:0}.swagger-ui .bl-0-m{border-left-width:0}}@media screen and (min-width:60em){.swagger-ui .bw0-l{border-width:0}.swagger-ui .bw1-l{border-width:.125rem}.swagger-ui .bw2-l{border-width:.25rem}.swagger-ui .bw3-l{border-width:.5rem}.swagger-ui .bw4-l{border-width:1rem}.swagger-ui .bw5-l{border-width:2rem}.swagger-ui .bt-0-l{border-top-width:0}.swagger-ui .br-0-l{border-right-width:0}.swagger-ui .bb-0-l{border-bottom-width:0}.swagger-ui .bl-0-l{border-left-width:0}}.swagger-ui .shadow-1{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}@media screen and (min-width:30em){.swagger-ui .shadow-1-ns{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-ns{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-ns{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-ns{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-ns{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .shadow-1-m{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-m{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-m{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-m{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-m{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}@media screen and (min-width:60em){.swagger-ui .shadow-1-l{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-l{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-l{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-l{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-l{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}.swagger-ui .pre{overflow-x:auto;overflow-y:hidden;overflow:scroll}.swagger-ui .top-0{top:0}.swagger-ui .right-0{right:0}.swagger-ui .bottom-0{bottom:0}.swagger-ui .left-0{left:0}.swagger-ui .top-1{top:1rem}.swagger-ui .right-1{right:1rem}.swagger-ui .bottom-1{bottom:1rem}.swagger-ui .left-1{left:1rem}.swagger-ui .top-2{top:2rem}.swagger-ui .right-2{right:2rem}.swagger-ui .bottom-2{bottom:2rem}.swagger-ui .left-2{left:2rem}.swagger-ui .top--1{top:-1rem}.swagger-ui .right--1{right:-1rem}.swagger-ui .bottom--1{bottom:-1rem}.swagger-ui .left--1{left:-1rem}.swagger-ui .top--2{top:-2rem}.swagger-ui .right--2{right:-2rem}.swagger-ui .bottom--2{bottom:-2rem}.swagger-ui .left--2{left:-2rem}.swagger-ui .absolute--fill{bottom:0;left:0;right:0;top:0}@media screen and (min-width:30em){.swagger-ui .top-0-ns{top:0}.swagger-ui .left-0-ns{left:0}.swagger-ui .right-0-ns{right:0}.swagger-ui .bottom-0-ns{bottom:0}.swagger-ui .top-1-ns{top:1rem}.swagger-ui .left-1-ns{left:1rem}.swagger-ui .right-1-ns{right:1rem}.swagger-ui .bottom-1-ns{bottom:1rem}.swagger-ui .top-2-ns{top:2rem}.swagger-ui .left-2-ns{left:2rem}.swagger-ui .right-2-ns{right:2rem}.swagger-ui .bottom-2-ns{bottom:2rem}.swagger-ui .top--1-ns{top:-1rem}.swagger-ui .right--1-ns{right:-1rem}.swagger-ui .bottom--1-ns{bottom:-1rem}.swagger-ui .left--1-ns{left:-1rem}.swagger-ui .top--2-ns{top:-2rem}.swagger-ui .right--2-ns{right:-2rem}.swagger-ui .bottom--2-ns{bottom:-2rem}.swagger-ui .left--2-ns{left:-2rem}.swagger-ui .absolute--fill-ns{bottom:0;left:0;right:0;top:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .top-0-m{top:0}.swagger-ui .left-0-m{left:0}.swagger-ui .right-0-m{right:0}.swagger-ui .bottom-0-m{bottom:0}.swagger-ui .top-1-m{top:1rem}.swagger-ui .left-1-m{left:1rem}.swagger-ui .right-1-m{right:1rem}.swagger-ui .bottom-1-m{bottom:1rem}.swagger-ui .top-2-m{top:2rem}.swagger-ui .left-2-m{left:2rem}.swagger-ui .right-2-m{right:2rem}.swagger-ui .bottom-2-m{bottom:2rem}.swagger-ui .top--1-m{top:-1rem}.swagger-ui .right--1-m{right:-1rem}.swagger-ui .bottom--1-m{bottom:-1rem}.swagger-ui .left--1-m{left:-1rem}.swagger-ui .top--2-m{top:-2rem}.swagger-ui .right--2-m{right:-2rem}.swagger-ui .bottom--2-m{bottom:-2rem}.swagger-ui .left--2-m{left:-2rem}.swagger-ui .absolute--fill-m{bottom:0;left:0;right:0;top:0}}@media screen and (min-width:60em){.swagger-ui .top-0-l{top:0}.swagger-ui .left-0-l{left:0}.swagger-ui .right-0-l{right:0}.swagger-ui .bottom-0-l{bottom:0}.swagger-ui .top-1-l{top:1rem}.swagger-ui .left-1-l{left:1rem}.swagger-ui .right-1-l{right:1rem}.swagger-ui .bottom-1-l{bottom:1rem}.swagger-ui .top-2-l{top:2rem}.swagger-ui .left-2-l{left:2rem}.swagger-ui .right-2-l{right:2rem}.swagger-ui .bottom-2-l{bottom:2rem}.swagger-ui .top--1-l{top:-1rem}.swagger-ui .right--1-l{right:-1rem}.swagger-ui .bottom--1-l{bottom:-1rem}.swagger-ui .left--1-l{left:-1rem}.swagger-ui .top--2-l{top:-2rem}.swagger-ui .right--2-l{right:-2rem}.swagger-ui .bottom--2-l{bottom:-2rem}.swagger-ui .left--2-l{left:-2rem}.swagger-ui .absolute--fill-l{bottom:0;left:0;right:0;top:0}}.swagger-ui .cf:after,.swagger-ui .cf:before{content:" ";display:table}.swagger-ui .cf:after{clear:both}.swagger-ui .cf{*zoom:1}.swagger-ui .cl{clear:left}.swagger-ui .cr{clear:right}.swagger-ui .cb{clear:both}.swagger-ui .cn{clear:none}@media screen and (min-width:30em){.swagger-ui .cl-ns{clear:left}.swagger-ui .cr-ns{clear:right}.swagger-ui .cb-ns{clear:both}.swagger-ui .cn-ns{clear:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .cl-m{clear:left}.swagger-ui .cr-m{clear:right}.swagger-ui .cb-m{clear:both}.swagger-ui .cn-m{clear:none}}@media screen and (min-width:60em){.swagger-ui .cl-l{clear:left}.swagger-ui .cr-l{clear:right}.swagger-ui .cb-l{clear:both}.swagger-ui .cn-l{clear:none}}.swagger-ui .flex{display:flex}.swagger-ui .inline-flex{display:inline-flex}.swagger-ui .flex-auto{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none{flex:none}.swagger-ui .flex-column{flex-direction:column}.swagger-ui .flex-row{flex-direction:row}.swagger-ui .flex-wrap{flex-wrap:wrap}.swagger-ui .flex-nowrap{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse{flex-direction:column-reverse}.swagger-ui .flex-row-reverse{flex-direction:row-reverse}.swagger-ui .items-start{align-items:flex-start}.swagger-ui .items-end{align-items:flex-end}.swagger-ui .items-center{align-items:center}.swagger-ui .items-baseline{align-items:baseline}.swagger-ui .items-stretch{align-items:stretch}.swagger-ui .self-start{align-self:flex-start}.swagger-ui .self-end{align-self:flex-end}.swagger-ui .self-center{align-self:center}.swagger-ui .self-baseline{align-self:baseline}.swagger-ui .self-stretch{align-self:stretch}.swagger-ui .justify-start{justify-content:flex-start}.swagger-ui .justify-end{justify-content:flex-end}.swagger-ui .justify-center{justify-content:center}.swagger-ui .justify-between{justify-content:space-between}.swagger-ui .justify-around{justify-content:space-around}.swagger-ui .content-start{align-content:flex-start}.swagger-ui .content-end{align-content:flex-end}.swagger-ui .content-center{align-content:center}.swagger-ui .content-between{align-content:space-between}.swagger-ui .content-around{align-content:space-around}.swagger-ui .content-stretch{align-content:stretch}.swagger-ui .order-0{order:0}.swagger-ui .order-1{order:1}.swagger-ui .order-2{order:2}.swagger-ui .order-3{order:3}.swagger-ui .order-4{order:4}.swagger-ui .order-5{order:5}.swagger-ui .order-6{order:6}.swagger-ui .order-7{order:7}.swagger-ui .order-8{order:8}.swagger-ui .order-last{order:99999}.swagger-ui .flex-grow-0{flex-grow:0}.swagger-ui .flex-grow-1{flex-grow:1}.swagger-ui .flex-shrink-0{flex-shrink:0}.swagger-ui .flex-shrink-1{flex-shrink:1}@media screen and (min-width:30em){.swagger-ui .flex-ns{display:flex}.swagger-ui .inline-flex-ns{display:inline-flex}.swagger-ui .flex-auto-ns{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-ns{flex:none}.swagger-ui .flex-column-ns{flex-direction:column}.swagger-ui .flex-row-ns{flex-direction:row}.swagger-ui .flex-wrap-ns{flex-wrap:wrap}.swagger-ui .flex-nowrap-ns{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-ns{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-ns{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-ns{flex-direction:row-reverse}.swagger-ui .items-start-ns{align-items:flex-start}.swagger-ui .items-end-ns{align-items:flex-end}.swagger-ui .items-center-ns{align-items:center}.swagger-ui .items-baseline-ns{align-items:baseline}.swagger-ui .items-stretch-ns{align-items:stretch}.swagger-ui .self-start-ns{align-self:flex-start}.swagger-ui .self-end-ns{align-self:flex-end}.swagger-ui .self-center-ns{align-self:center}.swagger-ui .self-baseline-ns{align-self:baseline}.swagger-ui .self-stretch-ns{align-self:stretch}.swagger-ui .justify-start-ns{justify-content:flex-start}.swagger-ui .justify-end-ns{justify-content:flex-end}.swagger-ui .justify-center-ns{justify-content:center}.swagger-ui .justify-between-ns{justify-content:space-between}.swagger-ui .justify-around-ns{justify-content:space-around}.swagger-ui .content-start-ns{align-content:flex-start}.swagger-ui .content-end-ns{align-content:flex-end}.swagger-ui .content-center-ns{align-content:center}.swagger-ui .content-between-ns{align-content:space-between}.swagger-ui .content-around-ns{align-content:space-around}.swagger-ui .content-stretch-ns{align-content:stretch}.swagger-ui .order-0-ns{order:0}.swagger-ui .order-1-ns{order:1}.swagger-ui .order-2-ns{order:2}.swagger-ui .order-3-ns{order:3}.swagger-ui .order-4-ns{order:4}.swagger-ui .order-5-ns{order:5}.swagger-ui .order-6-ns{order:6}.swagger-ui .order-7-ns{order:7}.swagger-ui .order-8-ns{order:8}.swagger-ui .order-last-ns{order:99999}.swagger-ui .flex-grow-0-ns{flex-grow:0}.swagger-ui .flex-grow-1-ns{flex-grow:1}.swagger-ui .flex-shrink-0-ns{flex-shrink:0}.swagger-ui .flex-shrink-1-ns{flex-shrink:1}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .flex-m{display:flex}.swagger-ui .inline-flex-m{display:inline-flex}.swagger-ui .flex-auto-m{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-m{flex:none}.swagger-ui .flex-column-m{flex-direction:column}.swagger-ui .flex-row-m{flex-direction:row}.swagger-ui .flex-wrap-m{flex-wrap:wrap}.swagger-ui .flex-nowrap-m{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-m{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-m{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-m{flex-direction:row-reverse}.swagger-ui .items-start-m{align-items:flex-start}.swagger-ui .items-end-m{align-items:flex-end}.swagger-ui .items-center-m{align-items:center}.swagger-ui .items-baseline-m{align-items:baseline}.swagger-ui .items-stretch-m{align-items:stretch}.swagger-ui .self-start-m{align-self:flex-start}.swagger-ui .self-end-m{align-self:flex-end}.swagger-ui .self-center-m{align-self:center}.swagger-ui .self-baseline-m{align-self:baseline}.swagger-ui .self-stretch-m{align-self:stretch}.swagger-ui .justify-start-m{justify-content:flex-start}.swagger-ui .justify-end-m{justify-content:flex-end}.swagger-ui .justify-center-m{justify-content:center}.swagger-ui .justify-between-m{justify-content:space-between}.swagger-ui .justify-around-m{justify-content:space-around}.swagger-ui .content-start-m{align-content:flex-start}.swagger-ui .content-end-m{align-content:flex-end}.swagger-ui .content-center-m{align-content:center}.swagger-ui .content-between-m{align-content:space-between}.swagger-ui .content-around-m{align-content:space-around}.swagger-ui .content-stretch-m{align-content:stretch}.swagger-ui .order-0-m{order:0}.swagger-ui .order-1-m{order:1}.swagger-ui .order-2-m{order:2}.swagger-ui .order-3-m{order:3}.swagger-ui .order-4-m{order:4}.swagger-ui .order-5-m{order:5}.swagger-ui .order-6-m{order:6}.swagger-ui .order-7-m{order:7}.swagger-ui .order-8-m{order:8}.swagger-ui .order-last-m{order:99999}.swagger-ui .flex-grow-0-m{flex-grow:0}.swagger-ui .flex-grow-1-m{flex-grow:1}.swagger-ui .flex-shrink-0-m{flex-shrink:0}.swagger-ui .flex-shrink-1-m{flex-shrink:1}}@media screen and (min-width:60em){.swagger-ui .flex-l{display:flex}.swagger-ui .inline-flex-l{display:inline-flex}.swagger-ui .flex-auto-l{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-l{flex:none}.swagger-ui .flex-column-l{flex-direction:column}.swagger-ui .flex-row-l{flex-direction:row}.swagger-ui .flex-wrap-l{flex-wrap:wrap}.swagger-ui .flex-nowrap-l{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-l{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-l{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-l{flex-direction:row-reverse}.swagger-ui .items-start-l{align-items:flex-start}.swagger-ui .items-end-l{align-items:flex-end}.swagger-ui .items-center-l{align-items:center}.swagger-ui .items-baseline-l{align-items:baseline}.swagger-ui .items-stretch-l{align-items:stretch}.swagger-ui .self-start-l{align-self:flex-start}.swagger-ui .self-end-l{align-self:flex-end}.swagger-ui .self-center-l{align-self:center}.swagger-ui .self-baseline-l{align-self:baseline}.swagger-ui .self-stretch-l{align-self:stretch}.swagger-ui .justify-start-l{justify-content:flex-start}.swagger-ui .justify-end-l{justify-content:flex-end}.swagger-ui .justify-center-l{justify-content:center}.swagger-ui .justify-between-l{justify-content:space-between}.swagger-ui .justify-around-l{justify-content:space-around}.swagger-ui .content-start-l{align-content:flex-start}.swagger-ui .content-end-l{align-content:flex-end}.swagger-ui .content-center-l{align-content:center}.swagger-ui .content-between-l{align-content:space-between}.swagger-ui .content-around-l{align-content:space-around}.swagger-ui .content-stretch-l{align-content:stretch}.swagger-ui .order-0-l{order:0}.swagger-ui .order-1-l{order:1}.swagger-ui .order-2-l{order:2}.swagger-ui .order-3-l{order:3}.swagger-ui .order-4-l{order:4}.swagger-ui .order-5-l{order:5}.swagger-ui .order-6-l{order:6}.swagger-ui .order-7-l{order:7}.swagger-ui .order-8-l{order:8}.swagger-ui .order-last-l{order:99999}.swagger-ui .flex-grow-0-l{flex-grow:0}.swagger-ui .flex-grow-1-l{flex-grow:1}.swagger-ui .flex-shrink-0-l{flex-shrink:0}.swagger-ui .flex-shrink-1-l{flex-shrink:1}}.swagger-ui .dn{display:none}.swagger-ui .di{display:inline}.swagger-ui .db{display:block}.swagger-ui .dib{display:inline-block}.swagger-ui .dit{display:inline-table}.swagger-ui .dt{display:table}.swagger-ui .dtc{display:table-cell}.swagger-ui .dt-row{display:table-row}.swagger-ui .dt-row-group{display:table-row-group}.swagger-ui .dt-column{display:table-column}.swagger-ui .dt-column-group{display:table-column-group}.swagger-ui .dt--fixed{table-layout:fixed;width:100%}@media screen and (min-width:30em){.swagger-ui .dn-ns{display:none}.swagger-ui .di-ns{display:inline}.swagger-ui .db-ns{display:block}.swagger-ui .dib-ns{display:inline-block}.swagger-ui .dit-ns{display:inline-table}.swagger-ui .dt-ns{display:table}.swagger-ui .dtc-ns{display:table-cell}.swagger-ui .dt-row-ns{display:table-row}.swagger-ui .dt-row-group-ns{display:table-row-group}.swagger-ui .dt-column-ns{display:table-column}.swagger-ui .dt-column-group-ns{display:table-column-group}.swagger-ui .dt--fixed-ns{table-layout:fixed;width:100%}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .dn-m{display:none}.swagger-ui .di-m{display:inline}.swagger-ui .db-m{display:block}.swagger-ui .dib-m{display:inline-block}.swagger-ui .dit-m{display:inline-table}.swagger-ui .dt-m{display:table}.swagger-ui .dtc-m{display:table-cell}.swagger-ui .dt-row-m{display:table-row}.swagger-ui .dt-row-group-m{display:table-row-group}.swagger-ui .dt-column-m{display:table-column}.swagger-ui .dt-column-group-m{display:table-column-group}.swagger-ui .dt--fixed-m{table-layout:fixed;width:100%}}@media screen and (min-width:60em){.swagger-ui .dn-l{display:none}.swagger-ui .di-l{display:inline}.swagger-ui .db-l{display:block}.swagger-ui .dib-l{display:inline-block}.swagger-ui .dit-l{display:inline-table}.swagger-ui .dt-l{display:table}.swagger-ui .dtc-l{display:table-cell}.swagger-ui .dt-row-l{display:table-row}.swagger-ui .dt-row-group-l{display:table-row-group}.swagger-ui .dt-column-l{display:table-column}.swagger-ui .dt-column-group-l{display:table-column-group}.swagger-ui .dt--fixed-l{table-layout:fixed;width:100%}}.swagger-ui .fl{_display:inline;float:left}.swagger-ui .fr{_display:inline;float:right}.swagger-ui .fn{float:none}@media screen and (min-width:30em){.swagger-ui .fl-ns{_display:inline;float:left}.swagger-ui .fr-ns{_display:inline;float:right}.swagger-ui .fn-ns{float:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .fl-m{_display:inline;float:left}.swagger-ui .fr-m{_display:inline;float:right}.swagger-ui .fn-m{float:none}}@media screen and (min-width:60em){.swagger-ui .fl-l{_display:inline;float:left}.swagger-ui .fr-l{_display:inline;float:right}.swagger-ui .fn-l{float:none}}.swagger-ui .sans-serif{font-family:-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica,helvetica neue,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.swagger-ui .serif{font-family:georgia,serif}.swagger-ui .system-sans-serif{font-family:sans-serif}.swagger-ui .system-serif{font-family:serif}.swagger-ui .code,.swagger-ui code{font-family:Consolas,monaco,monospace}.swagger-ui .courier{font-family:Courier Next,courier,monospace}.swagger-ui .helvetica{font-family:helvetica neue,helvetica,sans-serif}.swagger-ui .avenir{font-family:avenir next,avenir,sans-serif}.swagger-ui .athelas{font-family:athelas,georgia,serif}.swagger-ui .georgia{font-family:georgia,serif}.swagger-ui .times{font-family:times,serif}.swagger-ui .bodoni{font-family:Bodoni MT,serif}.swagger-ui .calisto{font-family:Calisto MT,serif}.swagger-ui .garamond{font-family:garamond,serif}.swagger-ui .baskerville{font-family:baskerville,serif}.swagger-ui .i{font-style:italic}.swagger-ui .fs-normal{font-style:normal}@media screen and (min-width:30em){.swagger-ui .i-ns{font-style:italic}.swagger-ui .fs-normal-ns{font-style:normal}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .i-m{font-style:italic}.swagger-ui .fs-normal-m{font-style:normal}}@media screen and (min-width:60em){.swagger-ui .i-l{font-style:italic}.swagger-ui .fs-normal-l{font-style:normal}}.swagger-ui .normal{font-weight:400}.swagger-ui .b{font-weight:700}.swagger-ui .fw1{font-weight:100}.swagger-ui .fw2{font-weight:200}.swagger-ui .fw3{font-weight:300}.swagger-ui .fw4{font-weight:400}.swagger-ui .fw5{font-weight:500}.swagger-ui .fw6{font-weight:600}.swagger-ui .fw7{font-weight:700}.swagger-ui .fw8{font-weight:800}.swagger-ui .fw9{font-weight:900}@media screen and (min-width:30em){.swagger-ui .normal-ns{font-weight:400}.swagger-ui .b-ns{font-weight:700}.swagger-ui .fw1-ns{font-weight:100}.swagger-ui .fw2-ns{font-weight:200}.swagger-ui .fw3-ns{font-weight:300}.swagger-ui .fw4-ns{font-weight:400}.swagger-ui .fw5-ns{font-weight:500}.swagger-ui .fw6-ns{font-weight:600}.swagger-ui .fw7-ns{font-weight:700}.swagger-ui .fw8-ns{font-weight:800}.swagger-ui .fw9-ns{font-weight:900}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .normal-m{font-weight:400}.swagger-ui .b-m{font-weight:700}.swagger-ui .fw1-m{font-weight:100}.swagger-ui .fw2-m{font-weight:200}.swagger-ui .fw3-m{font-weight:300}.swagger-ui .fw4-m{font-weight:400}.swagger-ui .fw5-m{font-weight:500}.swagger-ui .fw6-m{font-weight:600}.swagger-ui .fw7-m{font-weight:700}.swagger-ui .fw8-m{font-weight:800}.swagger-ui .fw9-m{font-weight:900}}@media screen and (min-width:60em){.swagger-ui .normal-l{font-weight:400}.swagger-ui .b-l{font-weight:700}.swagger-ui .fw1-l{font-weight:100}.swagger-ui .fw2-l{font-weight:200}.swagger-ui .fw3-l{font-weight:300}.swagger-ui .fw4-l{font-weight:400}.swagger-ui .fw5-l{font-weight:500}.swagger-ui .fw6-l{font-weight:600}.swagger-ui .fw7-l{font-weight:700}.swagger-ui .fw8-l{font-weight:800}.swagger-ui .fw9-l{font-weight:900}}.swagger-ui .input-reset{-webkit-appearance:none;-moz-appearance:none}.swagger-ui .button-reset::-moz-focus-inner,.swagger-ui .input-reset::-moz-focus-inner{border:0;padding:0}.swagger-ui .h1{height:1rem}.swagger-ui .h2{height:2rem}.swagger-ui .h3{height:4rem}.swagger-ui .h4{height:8rem}.swagger-ui .h5{height:16rem}.swagger-ui .h-25{height:25%}.swagger-ui .h-50{height:50%}.swagger-ui .h-75{height:75%}.swagger-ui .h-100{height:100%}.swagger-ui .min-h-100{min-height:100%}.swagger-ui .vh-25{height:25vh}.swagger-ui .vh-50{height:50vh}.swagger-ui .vh-75{height:75vh}.swagger-ui .vh-100{height:100vh}.swagger-ui .min-vh-100{min-height:100vh}.swagger-ui .h-auto{height:auto}.swagger-ui .h-inherit{height:inherit}@media screen and (min-width:30em){.swagger-ui .h1-ns{height:1rem}.swagger-ui .h2-ns{height:2rem}.swagger-ui .h3-ns{height:4rem}.swagger-ui .h4-ns{height:8rem}.swagger-ui .h5-ns{height:16rem}.swagger-ui .h-25-ns{height:25%}.swagger-ui .h-50-ns{height:50%}.swagger-ui .h-75-ns{height:75%}.swagger-ui .h-100-ns{height:100%}.swagger-ui .min-h-100-ns{min-height:100%}.swagger-ui .vh-25-ns{height:25vh}.swagger-ui .vh-50-ns{height:50vh}.swagger-ui .vh-75-ns{height:75vh}.swagger-ui .vh-100-ns{height:100vh}.swagger-ui .min-vh-100-ns{min-height:100vh}.swagger-ui .h-auto-ns{height:auto}.swagger-ui .h-inherit-ns{height:inherit}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .h1-m{height:1rem}.swagger-ui .h2-m{height:2rem}.swagger-ui .h3-m{height:4rem}.swagger-ui .h4-m{height:8rem}.swagger-ui .h5-m{height:16rem}.swagger-ui .h-25-m{height:25%}.swagger-ui .h-50-m{height:50%}.swagger-ui .h-75-m{height:75%}.swagger-ui .h-100-m{height:100%}.swagger-ui .min-h-100-m{min-height:100%}.swagger-ui .vh-25-m{height:25vh}.swagger-ui .vh-50-m{height:50vh}.swagger-ui .vh-75-m{height:75vh}.swagger-ui .vh-100-m{height:100vh}.swagger-ui .min-vh-100-m{min-height:100vh}.swagger-ui .h-auto-m{height:auto}.swagger-ui .h-inherit-m{height:inherit}}@media screen and (min-width:60em){.swagger-ui .h1-l{height:1rem}.swagger-ui .h2-l{height:2rem}.swagger-ui .h3-l{height:4rem}.swagger-ui .h4-l{height:8rem}.swagger-ui .h5-l{height:16rem}.swagger-ui .h-25-l{height:25%}.swagger-ui .h-50-l{height:50%}.swagger-ui .h-75-l{height:75%}.swagger-ui .h-100-l{height:100%}.swagger-ui .min-h-100-l{min-height:100%}.swagger-ui .vh-25-l{height:25vh}.swagger-ui .vh-50-l{height:50vh}.swagger-ui .vh-75-l{height:75vh}.swagger-ui .vh-100-l{height:100vh}.swagger-ui .min-vh-100-l{min-height:100vh}.swagger-ui .h-auto-l{height:auto}.swagger-ui .h-inherit-l{height:inherit}}.swagger-ui .tracked{letter-spacing:.1em}.swagger-ui .tracked-tight{letter-spacing:-.05em}.swagger-ui .tracked-mega{letter-spacing:.25em}@media screen and (min-width:30em){.swagger-ui .tracked-ns{letter-spacing:.1em}.swagger-ui .tracked-tight-ns{letter-spacing:-.05em}.swagger-ui .tracked-mega-ns{letter-spacing:.25em}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .tracked-m{letter-spacing:.1em}.swagger-ui .tracked-tight-m{letter-spacing:-.05em}.swagger-ui .tracked-mega-m{letter-spacing:.25em}}@media screen and (min-width:60em){.swagger-ui .tracked-l{letter-spacing:.1em}.swagger-ui .tracked-tight-l{letter-spacing:-.05em}.swagger-ui .tracked-mega-l{letter-spacing:.25em}}.swagger-ui .lh-solid{line-height:1}.swagger-ui .lh-title{line-height:1.25}.swagger-ui .lh-copy{line-height:1.5}@media screen and (min-width:30em){.swagger-ui .lh-solid-ns{line-height:1}.swagger-ui .lh-title-ns{line-height:1.25}.swagger-ui .lh-copy-ns{line-height:1.5}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .lh-solid-m{line-height:1}.swagger-ui .lh-title-m{line-height:1.25}.swagger-ui .lh-copy-m{line-height:1.5}}@media screen and (min-width:60em){.swagger-ui .lh-solid-l{line-height:1}.swagger-ui .lh-title-l{line-height:1.25}.swagger-ui .lh-copy-l{line-height:1.5}}.swagger-ui .link{text-decoration:none}.swagger-ui .link,.swagger-ui .link:active,.swagger-ui .link:focus,.swagger-ui .link:hover,.swagger-ui .link:link,.swagger-ui .link:visited{transition:color .15s ease-in}.swagger-ui .link:focus{outline:1px dotted currentColor}.swagger-ui .list{list-style-type:none}.swagger-ui .mw-100{max-width:100%}.swagger-ui .mw1{max-width:1rem}.swagger-ui .mw2{max-width:2rem}.swagger-ui .mw3{max-width:4rem}.swagger-ui .mw4{max-width:8rem}.swagger-ui .mw5{max-width:16rem}.swagger-ui .mw6{max-width:32rem}.swagger-ui .mw7{max-width:48rem}.swagger-ui .mw8{max-width:64rem}.swagger-ui .mw9{max-width:96rem}.swagger-ui .mw-none{max-width:none}@media screen and (min-width:30em){.swagger-ui .mw-100-ns{max-width:100%}.swagger-ui .mw1-ns{max-width:1rem}.swagger-ui .mw2-ns{max-width:2rem}.swagger-ui .mw3-ns{max-width:4rem}.swagger-ui .mw4-ns{max-width:8rem}.swagger-ui .mw5-ns{max-width:16rem}.swagger-ui .mw6-ns{max-width:32rem}.swagger-ui .mw7-ns{max-width:48rem}.swagger-ui .mw8-ns{max-width:64rem}.swagger-ui .mw9-ns{max-width:96rem}.swagger-ui .mw-none-ns{max-width:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .mw-100-m{max-width:100%}.swagger-ui .mw1-m{max-width:1rem}.swagger-ui .mw2-m{max-width:2rem}.swagger-ui .mw3-m{max-width:4rem}.swagger-ui .mw4-m{max-width:8rem}.swagger-ui .mw5-m{max-width:16rem}.swagger-ui .mw6-m{max-width:32rem}.swagger-ui .mw7-m{max-width:48rem}.swagger-ui .mw8-m{max-width:64rem}.swagger-ui .mw9-m{max-width:96rem}.swagger-ui .mw-none-m{max-width:none}}@media screen and (min-width:60em){.swagger-ui .mw-100-l{max-width:100%}.swagger-ui .mw1-l{max-width:1rem}.swagger-ui .mw2-l{max-width:2rem}.swagger-ui .mw3-l{max-width:4rem}.swagger-ui .mw4-l{max-width:8rem}.swagger-ui .mw5-l{max-width:16rem}.swagger-ui .mw6-l{max-width:32rem}.swagger-ui .mw7-l{max-width:48rem}.swagger-ui .mw8-l{max-width:64rem}.swagger-ui .mw9-l{max-width:96rem}.swagger-ui .mw-none-l{max-width:none}}.swagger-ui .w1{width:1rem}.swagger-ui .w2{width:2rem}.swagger-ui .w3{width:4rem}.swagger-ui .w4{width:8rem}.swagger-ui .w5{width:16rem}.swagger-ui .w-10{width:10%}.swagger-ui .w-20{width:20%}.swagger-ui .w-25{width:25%}.swagger-ui .w-30{width:30%}.swagger-ui .w-33{width:33%}.swagger-ui .w-34{width:34%}.swagger-ui .w-40{width:40%}.swagger-ui .w-50{width:50%}.swagger-ui .w-60{width:60%}.swagger-ui .w-70{width:70%}.swagger-ui .w-75{width:75%}.swagger-ui .w-80{width:80%}.swagger-ui .w-90{width:90%}.swagger-ui .w-100{width:100%}.swagger-ui .w-third{width:33.3333333333%}.swagger-ui .w-two-thirds{width:66.6666666667%}.swagger-ui .w-auto{width:auto}@media screen and (min-width:30em){.swagger-ui .w1-ns{width:1rem}.swagger-ui .w2-ns{width:2rem}.swagger-ui .w3-ns{width:4rem}.swagger-ui .w4-ns{width:8rem}.swagger-ui .w5-ns{width:16rem}.swagger-ui .w-10-ns{width:10%}.swagger-ui .w-20-ns{width:20%}.swagger-ui .w-25-ns{width:25%}.swagger-ui .w-30-ns{width:30%}.swagger-ui .w-33-ns{width:33%}.swagger-ui .w-34-ns{width:34%}.swagger-ui .w-40-ns{width:40%}.swagger-ui .w-50-ns{width:50%}.swagger-ui .w-60-ns{width:60%}.swagger-ui .w-70-ns{width:70%}.swagger-ui .w-75-ns{width:75%}.swagger-ui .w-80-ns{width:80%}.swagger-ui .w-90-ns{width:90%}.swagger-ui .w-100-ns{width:100%}.swagger-ui .w-third-ns{width:33.3333333333%}.swagger-ui .w-two-thirds-ns{width:66.6666666667%}.swagger-ui .w-auto-ns{width:auto}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .w1-m{width:1rem}.swagger-ui .w2-m{width:2rem}.swagger-ui .w3-m{width:4rem}.swagger-ui .w4-m{width:8rem}.swagger-ui .w5-m{width:16rem}.swagger-ui .w-10-m{width:10%}.swagger-ui .w-20-m{width:20%}.swagger-ui .w-25-m{width:25%}.swagger-ui .w-30-m{width:30%}.swagger-ui .w-33-m{width:33%}.swagger-ui .w-34-m{width:34%}.swagger-ui .w-40-m{width:40%}.swagger-ui .w-50-m{width:50%}.swagger-ui .w-60-m{width:60%}.swagger-ui .w-70-m{width:70%}.swagger-ui .w-75-m{width:75%}.swagger-ui .w-80-m{width:80%}.swagger-ui .w-90-m{width:90%}.swagger-ui .w-100-m{width:100%}.swagger-ui .w-third-m{width:33.3333333333%}.swagger-ui .w-two-thirds-m{width:66.6666666667%}.swagger-ui .w-auto-m{width:auto}}@media screen and (min-width:60em){.swagger-ui .w1-l{width:1rem}.swagger-ui .w2-l{width:2rem}.swagger-ui .w3-l{width:4rem}.swagger-ui .w4-l{width:8rem}.swagger-ui .w5-l{width:16rem}.swagger-ui .w-10-l{width:10%}.swagger-ui .w-20-l{width:20%}.swagger-ui .w-25-l{width:25%}.swagger-ui .w-30-l{width:30%}.swagger-ui .w-33-l{width:33%}.swagger-ui .w-34-l{width:34%}.swagger-ui .w-40-l{width:40%}.swagger-ui .w-50-l{width:50%}.swagger-ui .w-60-l{width:60%}.swagger-ui .w-70-l{width:70%}.swagger-ui .w-75-l{width:75%}.swagger-ui .w-80-l{width:80%}.swagger-ui .w-90-l{width:90%}.swagger-ui .w-100-l{width:100%}.swagger-ui .w-third-l{width:33.3333333333%}.swagger-ui .w-two-thirds-l{width:66.6666666667%}.swagger-ui .w-auto-l{width:auto}}.swagger-ui .overflow-visible{overflow:visible}.swagger-ui .overflow-hidden{overflow:hidden}.swagger-ui .overflow-scroll{overflow:scroll}.swagger-ui .overflow-auto{overflow:auto}.swagger-ui .overflow-x-visible{overflow-x:visible}.swagger-ui .overflow-x-hidden{overflow-x:hidden}.swagger-ui .overflow-x-scroll{overflow-x:scroll}.swagger-ui .overflow-x-auto{overflow-x:auto}.swagger-ui .overflow-y-visible{overflow-y:visible}.swagger-ui .overflow-y-hidden{overflow-y:hidden}.swagger-ui .overflow-y-scroll{overflow-y:scroll}.swagger-ui .overflow-y-auto{overflow-y:auto}@media screen and (min-width:30em){.swagger-ui .overflow-visible-ns{overflow:visible}.swagger-ui .overflow-hidden-ns{overflow:hidden}.swagger-ui .overflow-scroll-ns{overflow:scroll}.swagger-ui .overflow-auto-ns{overflow:auto}.swagger-ui .overflow-x-visible-ns{overflow-x:visible}.swagger-ui .overflow-x-hidden-ns{overflow-x:hidden}.swagger-ui .overflow-x-scroll-ns{overflow-x:scroll}.swagger-ui .overflow-x-auto-ns{overflow-x:auto}.swagger-ui .overflow-y-visible-ns{overflow-y:visible}.swagger-ui .overflow-y-hidden-ns{overflow-y:hidden}.swagger-ui .overflow-y-scroll-ns{overflow-y:scroll}.swagger-ui .overflow-y-auto-ns{overflow-y:auto}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .overflow-visible-m{overflow:visible}.swagger-ui .overflow-hidden-m{overflow:hidden}.swagger-ui .overflow-scroll-m{overflow:scroll}.swagger-ui .overflow-auto-m{overflow:auto}.swagger-ui .overflow-x-visible-m{overflow-x:visible}.swagger-ui .overflow-x-hidden-m{overflow-x:hidden}.swagger-ui .overflow-x-scroll-m{overflow-x:scroll}.swagger-ui .overflow-x-auto-m{overflow-x:auto}.swagger-ui .overflow-y-visible-m{overflow-y:visible}.swagger-ui .overflow-y-hidden-m{overflow-y:hidden}.swagger-ui .overflow-y-scroll-m{overflow-y:scroll}.swagger-ui .overflow-y-auto-m{overflow-y:auto}}@media screen and (min-width:60em){.swagger-ui .overflow-visible-l{overflow:visible}.swagger-ui .overflow-hidden-l{overflow:hidden}.swagger-ui .overflow-scroll-l{overflow:scroll}.swagger-ui .overflow-auto-l{overflow:auto}.swagger-ui .overflow-x-visible-l{overflow-x:visible}.swagger-ui .overflow-x-hidden-l{overflow-x:hidden}.swagger-ui .overflow-x-scroll-l{overflow-x:scroll}.swagger-ui .overflow-x-auto-l{overflow-x:auto}.swagger-ui .overflow-y-visible-l{overflow-y:visible}.swagger-ui .overflow-y-hidden-l{overflow-y:hidden}.swagger-ui .overflow-y-scroll-l{overflow-y:scroll}.swagger-ui .overflow-y-auto-l{overflow-y:auto}}.swagger-ui .static{position:static}.swagger-ui .relative{position:relative}.swagger-ui .absolute{position:absolute}.swagger-ui .fixed{position:fixed}@media screen and (min-width:30em){.swagger-ui .static-ns{position:static}.swagger-ui .relative-ns{position:relative}.swagger-ui .absolute-ns{position:absolute}.swagger-ui .fixed-ns{position:fixed}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .static-m{position:static}.swagger-ui .relative-m{position:relative}.swagger-ui .absolute-m{position:absolute}.swagger-ui .fixed-m{position:fixed}}@media screen and (min-width:60em){.swagger-ui .static-l{position:static}.swagger-ui .relative-l{position:relative}.swagger-ui .absolute-l{position:absolute}.swagger-ui .fixed-l{position:fixed}}.swagger-ui .o-100{opacity:1}.swagger-ui .o-90{opacity:.9}.swagger-ui .o-80{opacity:.8}.swagger-ui .o-70{opacity:.7}.swagger-ui .o-60{opacity:.6}.swagger-ui .o-50{opacity:.5}.swagger-ui .o-40{opacity:.4}.swagger-ui .o-30{opacity:.3}.swagger-ui .o-20{opacity:.2}.swagger-ui .o-10{opacity:.1}.swagger-ui .o-05{opacity:.05}.swagger-ui .o-025{opacity:.025}.swagger-ui .o-0{opacity:0}.swagger-ui .rotate-45{transform:rotate(45deg)}.swagger-ui .rotate-90{transform:rotate(90deg)}.swagger-ui .rotate-135{transform:rotate(135deg)}.swagger-ui .rotate-180{transform:rotate(180deg)}.swagger-ui .rotate-225{transform:rotate(225deg)}.swagger-ui .rotate-270{transform:rotate(270deg)}.swagger-ui .rotate-315{transform:rotate(315deg)}@media screen and (min-width:30em){.swagger-ui .rotate-45-ns{transform:rotate(45deg)}.swagger-ui .rotate-90-ns{transform:rotate(90deg)}.swagger-ui .rotate-135-ns{transform:rotate(135deg)}.swagger-ui .rotate-180-ns{transform:rotate(180deg)}.swagger-ui .rotate-225-ns{transform:rotate(225deg)}.swagger-ui .rotate-270-ns{transform:rotate(270deg)}.swagger-ui .rotate-315-ns{transform:rotate(315deg)}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .rotate-45-m{transform:rotate(45deg)}.swagger-ui .rotate-90-m{transform:rotate(90deg)}.swagger-ui .rotate-135-m{transform:rotate(135deg)}.swagger-ui .rotate-180-m{transform:rotate(180deg)}.swagger-ui .rotate-225-m{transform:rotate(225deg)}.swagger-ui .rotate-270-m{transform:rotate(270deg)}.swagger-ui .rotate-315-m{transform:rotate(315deg)}}@media screen and (min-width:60em){.swagger-ui .rotate-45-l{transform:rotate(45deg)}.swagger-ui .rotate-90-l{transform:rotate(90deg)}.swagger-ui .rotate-135-l{transform:rotate(135deg)}.swagger-ui .rotate-180-l{transform:rotate(180deg)}.swagger-ui .rotate-225-l{transform:rotate(225deg)}.swagger-ui .rotate-270-l{transform:rotate(270deg)}.swagger-ui .rotate-315-l{transform:rotate(315deg)}}.swagger-ui .black-90{color:rgba(0,0,0,.9)}.swagger-ui .black-80{color:rgba(0,0,0,.8)}.swagger-ui .black-70{color:rgba(0,0,0,.7)}.swagger-ui .black-60{color:rgba(0,0,0,.6)}.swagger-ui .black-50{color:rgba(0,0,0,.5)}.swagger-ui .black-40{color:rgba(0,0,0,.4)}.swagger-ui .black-30{color:rgba(0,0,0,.3)}.swagger-ui .black-20{color:rgba(0,0,0,.2)}.swagger-ui .black-10{color:rgba(0,0,0,.1)}.swagger-ui .black-05{color:rgba(0,0,0,.05)}.swagger-ui .white-90{color:hsla(0,0%,100%,.9)}.swagger-ui .white-80{color:hsla(0,0%,100%,.8)}.swagger-ui .white-70{color:hsla(0,0%,100%,.7)}.swagger-ui .white-60{color:hsla(0,0%,100%,.6)}.swagger-ui .white-50{color:hsla(0,0%,100%,.5)}.swagger-ui .white-40{color:hsla(0,0%,100%,.4)}.swagger-ui .white-30{color:hsla(0,0%,100%,.3)}.swagger-ui .white-20{color:hsla(0,0%,100%,.2)}.swagger-ui .white-10{color:hsla(0,0%,100%,.1)}.swagger-ui .black{color:#000}.swagger-ui .near-black{color:#111}.swagger-ui .dark-gray{color:#333}.swagger-ui .mid-gray{color:#555}.swagger-ui .gray{color:#777}.swagger-ui .silver{color:#999}.swagger-ui .light-silver{color:#aaa}.swagger-ui .moon-gray{color:#ccc}.swagger-ui .light-gray{color:#eee}.swagger-ui .near-white{color:#f4f4f4}.swagger-ui .white{color:#fff}.swagger-ui .dark-red{color:#e7040f}.swagger-ui .red{color:#ff4136}.swagger-ui .light-red{color:#ff725c}.swagger-ui .orange{color:#ff6300}.swagger-ui .gold{color:#ffb700}.swagger-ui .yellow{color:gold}.swagger-ui .light-yellow{color:#fbf1a9}.swagger-ui .purple{color:#5e2ca5}.swagger-ui .light-purple{color:#a463f2}.swagger-ui .dark-pink{color:#d5008f}.swagger-ui .hot-pink{color:#ff41b4}.swagger-ui .pink{color:#ff80cc}.swagger-ui .light-pink{color:#ffa3d7}.swagger-ui .dark-green{color:#137752}.swagger-ui .green{color:#19a974}.swagger-ui .light-green{color:#9eebcf}.swagger-ui .navy{color:#001b44}.swagger-ui .dark-blue{color:#00449e}.swagger-ui .blue{color:#357edd}.swagger-ui .light-blue{color:#96ccff}.swagger-ui .lightest-blue{color:#cdecff}.swagger-ui .washed-blue{color:#f6fffe}.swagger-ui .washed-green{color:#e8fdf5}.swagger-ui .washed-yellow{color:#fffceb}.swagger-ui .washed-red{color:#ffdfdf}.swagger-ui .color-inherit{color:inherit}.swagger-ui .bg-black-90{background-color:rgba(0,0,0,.9)}.swagger-ui .bg-black-80{background-color:rgba(0,0,0,.8)}.swagger-ui .bg-black-70{background-color:rgba(0,0,0,.7)}.swagger-ui .bg-black-60{background-color:rgba(0,0,0,.6)}.swagger-ui .bg-black-50{background-color:rgba(0,0,0,.5)}.swagger-ui .bg-black-40{background-color:rgba(0,0,0,.4)}.swagger-ui .bg-black-30{background-color:rgba(0,0,0,.3)}.swagger-ui .bg-black-20{background-color:rgba(0,0,0,.2)}.swagger-ui .bg-black-10{background-color:rgba(0,0,0,.1)}.swagger-ui .bg-black-05{background-color:rgba(0,0,0,.05)}.swagger-ui .bg-white-90{background-color:hsla(0,0%,100%,.9)}.swagger-ui .bg-white-80{background-color:hsla(0,0%,100%,.8)}.swagger-ui .bg-white-70{background-color:hsla(0,0%,100%,.7)}.swagger-ui .bg-white-60{background-color:hsla(0,0%,100%,.6)}.swagger-ui .bg-white-50{background-color:hsla(0,0%,100%,.5)}.swagger-ui .bg-white-40{background-color:hsla(0,0%,100%,.4)}.swagger-ui .bg-white-30{background-color:hsla(0,0%,100%,.3)}.swagger-ui .bg-white-20{background-color:hsla(0,0%,100%,.2)}.swagger-ui .bg-white-10{background-color:hsla(0,0%,100%,.1)}.swagger-ui .bg-black{background-color:#000}.swagger-ui .bg-near-black{background-color:#111}.swagger-ui .bg-dark-gray{background-color:#333}.swagger-ui .bg-mid-gray{background-color:#555}.swagger-ui .bg-gray{background-color:#777}.swagger-ui .bg-silver{background-color:#999}.swagger-ui .bg-light-silver{background-color:#aaa}.swagger-ui .bg-moon-gray{background-color:#ccc}.swagger-ui .bg-light-gray{background-color:#eee}.swagger-ui .bg-near-white{background-color:#f4f4f4}.swagger-ui .bg-white{background-color:#fff}.swagger-ui .bg-transparent{background-color:transparent}.swagger-ui .bg-dark-red{background-color:#e7040f}.swagger-ui .bg-red{background-color:#ff4136}.swagger-ui .bg-light-red{background-color:#ff725c}.swagger-ui .bg-orange{background-color:#ff6300}.swagger-ui .bg-gold{background-color:#ffb700}.swagger-ui .bg-yellow{background-color:gold}.swagger-ui .bg-light-yellow{background-color:#fbf1a9}.swagger-ui .bg-purple{background-color:#5e2ca5}.swagger-ui .bg-light-purple{background-color:#a463f2}.swagger-ui .bg-dark-pink{background-color:#d5008f}.swagger-ui .bg-hot-pink{background-color:#ff41b4}.swagger-ui .bg-pink{background-color:#ff80cc}.swagger-ui .bg-light-pink{background-color:#ffa3d7}.swagger-ui .bg-dark-green{background-color:#137752}.swagger-ui .bg-green{background-color:#19a974}.swagger-ui .bg-light-green{background-color:#9eebcf}.swagger-ui .bg-navy{background-color:#001b44}.swagger-ui .bg-dark-blue{background-color:#00449e}.swagger-ui .bg-blue{background-color:#357edd}.swagger-ui .bg-light-blue{background-color:#96ccff}.swagger-ui .bg-lightest-blue{background-color:#cdecff}.swagger-ui .bg-washed-blue{background-color:#f6fffe}.swagger-ui .bg-washed-green{background-color:#e8fdf5}.swagger-ui .bg-washed-yellow{background-color:#fffceb}.swagger-ui .bg-washed-red{background-color:#ffdfdf}.swagger-ui .bg-inherit{background-color:inherit}.swagger-ui .hover-black:focus,.swagger-ui .hover-black:hover{color:#000}.swagger-ui .hover-near-black:focus,.swagger-ui .hover-near-black:hover{color:#111}.swagger-ui .hover-dark-gray:focus,.swagger-ui .hover-dark-gray:hover{color:#333}.swagger-ui .hover-mid-gray:focus,.swagger-ui .hover-mid-gray:hover{color:#555}.swagger-ui .hover-gray:focus,.swagger-ui .hover-gray:hover{color:#777}.swagger-ui .hover-silver:focus,.swagger-ui .hover-silver:hover{color:#999}.swagger-ui .hover-light-silver:focus,.swagger-ui .hover-light-silver:hover{color:#aaa}.swagger-ui .hover-moon-gray:focus,.swagger-ui .hover-moon-gray:hover{color:#ccc}.swagger-ui .hover-light-gray:focus,.swagger-ui .hover-light-gray:hover{color:#eee}.swagger-ui .hover-near-white:focus,.swagger-ui .hover-near-white:hover{color:#f4f4f4}.swagger-ui .hover-white:focus,.swagger-ui .hover-white:hover{color:#fff}.swagger-ui .hover-black-90:focus,.swagger-ui .hover-black-90:hover{color:rgba(0,0,0,.9)}.swagger-ui .hover-black-80:focus,.swagger-ui .hover-black-80:hover{color:rgba(0,0,0,.8)}.swagger-ui .hover-black-70:focus,.swagger-ui .hover-black-70:hover{color:rgba(0,0,0,.7)}.swagger-ui .hover-black-60:focus,.swagger-ui .hover-black-60:hover{color:rgba(0,0,0,.6)}.swagger-ui .hover-black-50:focus,.swagger-ui .hover-black-50:hover{color:rgba(0,0,0,.5)}.swagger-ui .hover-black-40:focus,.swagger-ui .hover-black-40:hover{color:rgba(0,0,0,.4)}.swagger-ui .hover-black-30:focus,.swagger-ui .hover-black-30:hover{color:rgba(0,0,0,.3)}.swagger-ui .hover-black-20:focus,.swagger-ui .hover-black-20:hover{color:rgba(0,0,0,.2)}.swagger-ui .hover-black-10:focus,.swagger-ui .hover-black-10:hover{color:rgba(0,0,0,.1)}.swagger-ui .hover-white-90:focus,.swagger-ui .hover-white-90:hover{color:hsla(0,0%,100%,.9)}.swagger-ui .hover-white-80:focus,.swagger-ui .hover-white-80:hover{color:hsla(0,0%,100%,.8)}.swagger-ui .hover-white-70:focus,.swagger-ui .hover-white-70:hover{color:hsla(0,0%,100%,.7)}.swagger-ui .hover-white-60:focus,.swagger-ui .hover-white-60:hover{color:hsla(0,0%,100%,.6)}.swagger-ui .hover-white-50:focus,.swagger-ui .hover-white-50:hover{color:hsla(0,0%,100%,.5)}.swagger-ui .hover-white-40:focus,.swagger-ui .hover-white-40:hover{color:hsla(0,0%,100%,.4)}.swagger-ui .hover-white-30:focus,.swagger-ui .hover-white-30:hover{color:hsla(0,0%,100%,.3)}.swagger-ui .hover-white-20:focus,.swagger-ui .hover-white-20:hover{color:hsla(0,0%,100%,.2)}.swagger-ui .hover-white-10:focus,.swagger-ui .hover-white-10:hover{color:hsla(0,0%,100%,.1)}.swagger-ui .hover-inherit:focus,.swagger-ui .hover-inherit:hover{color:inherit}.swagger-ui .hover-bg-black:focus,.swagger-ui .hover-bg-black:hover{background-color:#000}.swagger-ui .hover-bg-near-black:focus,.swagger-ui .hover-bg-near-black:hover{background-color:#111}.swagger-ui .hover-bg-dark-gray:focus,.swagger-ui .hover-bg-dark-gray:hover{background-color:#333}.swagger-ui .hover-bg-mid-gray:focus,.swagger-ui .hover-bg-mid-gray:hover{background-color:#555}.swagger-ui .hover-bg-gray:focus,.swagger-ui .hover-bg-gray:hover{background-color:#777}.swagger-ui .hover-bg-silver:focus,.swagger-ui .hover-bg-silver:hover{background-color:#999}.swagger-ui .hover-bg-light-silver:focus,.swagger-ui .hover-bg-light-silver:hover{background-color:#aaa}.swagger-ui .hover-bg-moon-gray:focus,.swagger-ui .hover-bg-moon-gray:hover{background-color:#ccc}.swagger-ui .hover-bg-light-gray:focus,.swagger-ui .hover-bg-light-gray:hover{background-color:#eee}.swagger-ui .hover-bg-near-white:focus,.swagger-ui .hover-bg-near-white:hover{background-color:#f4f4f4}.swagger-ui .hover-bg-white:focus,.swagger-ui .hover-bg-white:hover{background-color:#fff}.swagger-ui .hover-bg-transparent:focus,.swagger-ui .hover-bg-transparent:hover{background-color:transparent}.swagger-ui .hover-bg-black-90:focus,.swagger-ui .hover-bg-black-90:hover{background-color:rgba(0,0,0,.9)}.swagger-ui .hover-bg-black-80:focus,.swagger-ui .hover-bg-black-80:hover{background-color:rgba(0,0,0,.8)}.swagger-ui .hover-bg-black-70:focus,.swagger-ui .hover-bg-black-70:hover{background-color:rgba(0,0,0,.7)}.swagger-ui .hover-bg-black-60:focus,.swagger-ui .hover-bg-black-60:hover{background-color:rgba(0,0,0,.6)}.swagger-ui .hover-bg-black-50:focus,.swagger-ui .hover-bg-black-50:hover{background-color:rgba(0,0,0,.5)}.swagger-ui .hover-bg-black-40:focus,.swagger-ui .hover-bg-black-40:hover{background-color:rgba(0,0,0,.4)}.swagger-ui .hover-bg-black-30:focus,.swagger-ui .hover-bg-black-30:hover{background-color:rgba(0,0,0,.3)}.swagger-ui .hover-bg-black-20:focus,.swagger-ui .hover-bg-black-20:hover{background-color:rgba(0,0,0,.2)}.swagger-ui .hover-bg-black-10:focus,.swagger-ui .hover-bg-black-10:hover{background-color:rgba(0,0,0,.1)}.swagger-ui .hover-bg-white-90:focus,.swagger-ui .hover-bg-white-90:hover{background-color:hsla(0,0%,100%,.9)}.swagger-ui .hover-bg-white-80:focus,.swagger-ui .hover-bg-white-80:hover{background-color:hsla(0,0%,100%,.8)}.swagger-ui .hover-bg-white-70:focus,.swagger-ui .hover-bg-white-70:hover{background-color:hsla(0,0%,100%,.7)}.swagger-ui .hover-bg-white-60:focus,.swagger-ui .hover-bg-white-60:hover{background-color:hsla(0,0%,100%,.6)}.swagger-ui .hover-bg-white-50:focus,.swagger-ui .hover-bg-white-50:hover{background-color:hsla(0,0%,100%,.5)}.swagger-ui .hover-bg-white-40:focus,.swagger-ui .hover-bg-white-40:hover{background-color:hsla(0,0%,100%,.4)}.swagger-ui .hover-bg-white-30:focus,.swagger-ui .hover-bg-white-30:hover{background-color:hsla(0,0%,100%,.3)}.swagger-ui .hover-bg-white-20:focus,.swagger-ui .hover-bg-white-20:hover{background-color:hsla(0,0%,100%,.2)}.swagger-ui .hover-bg-white-10:focus,.swagger-ui .hover-bg-white-10:hover{background-color:hsla(0,0%,100%,.1)}.swagger-ui .hover-dark-red:focus,.swagger-ui .hover-dark-red:hover{color:#e7040f}.swagger-ui .hover-red:focus,.swagger-ui .hover-red:hover{color:#ff4136}.swagger-ui .hover-light-red:focus,.swagger-ui .hover-light-red:hover{color:#ff725c}.swagger-ui .hover-orange:focus,.swagger-ui .hover-orange:hover{color:#ff6300}.swagger-ui .hover-gold:focus,.swagger-ui .hover-gold:hover{color:#ffb700}.swagger-ui .hover-yellow:focus,.swagger-ui .hover-yellow:hover{color:gold}.swagger-ui .hover-light-yellow:focus,.swagger-ui .hover-light-yellow:hover{color:#fbf1a9}.swagger-ui .hover-purple:focus,.swagger-ui .hover-purple:hover{color:#5e2ca5}.swagger-ui .hover-light-purple:focus,.swagger-ui .hover-light-purple:hover{color:#a463f2}.swagger-ui .hover-dark-pink:focus,.swagger-ui .hover-dark-pink:hover{color:#d5008f}.swagger-ui .hover-hot-pink:focus,.swagger-ui .hover-hot-pink:hover{color:#ff41b4}.swagger-ui .hover-pink:focus,.swagger-ui .hover-pink:hover{color:#ff80cc}.swagger-ui .hover-light-pink:focus,.swagger-ui .hover-light-pink:hover{color:#ffa3d7}.swagger-ui .hover-dark-green:focus,.swagger-ui .hover-dark-green:hover{color:#137752}.swagger-ui .hover-green:focus,.swagger-ui .hover-green:hover{color:#19a974}.swagger-ui .hover-light-green:focus,.swagger-ui .hover-light-green:hover{color:#9eebcf}.swagger-ui .hover-navy:focus,.swagger-ui .hover-navy:hover{color:#001b44}.swagger-ui .hover-dark-blue:focus,.swagger-ui .hover-dark-blue:hover{color:#00449e}.swagger-ui .hover-blue:focus,.swagger-ui .hover-blue:hover{color:#357edd}.swagger-ui .hover-light-blue:focus,.swagger-ui .hover-light-blue:hover{color:#96ccff}.swagger-ui .hover-lightest-blue:focus,.swagger-ui .hover-lightest-blue:hover{color:#cdecff}.swagger-ui .hover-washed-blue:focus,.swagger-ui .hover-washed-blue:hover{color:#f6fffe}.swagger-ui .hover-washed-green:focus,.swagger-ui .hover-washed-green:hover{color:#e8fdf5}.swagger-ui .hover-washed-yellow:focus,.swagger-ui .hover-washed-yellow:hover{color:#fffceb}.swagger-ui .hover-washed-red:focus,.swagger-ui .hover-washed-red:hover{color:#ffdfdf}.swagger-ui .hover-bg-dark-red:focus,.swagger-ui .hover-bg-dark-red:hover{background-color:#e7040f}.swagger-ui .hover-bg-red:focus,.swagger-ui .hover-bg-red:hover{background-color:#ff4136}.swagger-ui .hover-bg-light-red:focus,.swagger-ui .hover-bg-light-red:hover{background-color:#ff725c}.swagger-ui .hover-bg-orange:focus,.swagger-ui .hover-bg-orange:hover{background-color:#ff6300}.swagger-ui .hover-bg-gold:focus,.swagger-ui .hover-bg-gold:hover{background-color:#ffb700}.swagger-ui .hover-bg-yellow:focus,.swagger-ui .hover-bg-yellow:hover{background-color:gold}.swagger-ui .hover-bg-light-yellow:focus,.swagger-ui .hover-bg-light-yellow:hover{background-color:#fbf1a9}.swagger-ui .hover-bg-purple:focus,.swagger-ui .hover-bg-purple:hover{background-color:#5e2ca5}.swagger-ui .hover-bg-light-purple:focus,.swagger-ui .hover-bg-light-purple:hover{background-color:#a463f2}.swagger-ui .hover-bg-dark-pink:focus,.swagger-ui .hover-bg-dark-pink:hover{background-color:#d5008f}.swagger-ui .hover-bg-hot-pink:focus,.swagger-ui .hover-bg-hot-pink:hover{background-color:#ff41b4}.swagger-ui .hover-bg-pink:focus,.swagger-ui .hover-bg-pink:hover{background-color:#ff80cc}.swagger-ui .hover-bg-light-pink:focus,.swagger-ui .hover-bg-light-pink:hover{background-color:#ffa3d7}.swagger-ui .hover-bg-dark-green:focus,.swagger-ui .hover-bg-dark-green:hover{background-color:#137752}.swagger-ui .hover-bg-green:focus,.swagger-ui .hover-bg-green:hover{background-color:#19a974}.swagger-ui .hover-bg-light-green:focus,.swagger-ui .hover-bg-light-green:hover{background-color:#9eebcf}.swagger-ui .hover-bg-navy:focus,.swagger-ui .hover-bg-navy:hover{background-color:#001b44}.swagger-ui .hover-bg-dark-blue:focus,.swagger-ui .hover-bg-dark-blue:hover{background-color:#00449e}.swagger-ui .hover-bg-blue:focus,.swagger-ui .hover-bg-blue:hover{background-color:#357edd}.swagger-ui .hover-bg-light-blue:focus,.swagger-ui .hover-bg-light-blue:hover{background-color:#96ccff}.swagger-ui .hover-bg-lightest-blue:focus,.swagger-ui .hover-bg-lightest-blue:hover{background-color:#cdecff}.swagger-ui .hover-bg-washed-blue:focus,.swagger-ui .hover-bg-washed-blue:hover{background-color:#f6fffe}.swagger-ui .hover-bg-washed-green:focus,.swagger-ui .hover-bg-washed-green:hover{background-color:#e8fdf5}.swagger-ui .hover-bg-washed-yellow:focus,.swagger-ui .hover-bg-washed-yellow:hover{background-color:#fffceb}.swagger-ui .hover-bg-washed-red:focus,.swagger-ui .hover-bg-washed-red:hover{background-color:#ffdfdf}.swagger-ui .hover-bg-inherit:focus,.swagger-ui .hover-bg-inherit:hover{background-color:inherit}.swagger-ui .pa0{padding:0}.swagger-ui .pa1{padding:.25rem}.swagger-ui .pa2{padding:.5rem}.swagger-ui .pa3{padding:1rem}.swagger-ui .pa4{padding:2rem}.swagger-ui .pa5{padding:4rem}.swagger-ui .pa6{padding:8rem}.swagger-ui .pa7{padding:16rem}.swagger-ui .pl0{padding-left:0}.swagger-ui .pl1{padding-left:.25rem}.swagger-ui .pl2{padding-left:.5rem}.swagger-ui .pl3{padding-left:1rem}.swagger-ui .pl4{padding-left:2rem}.swagger-ui .pl5{padding-left:4rem}.swagger-ui .pl6{padding-left:8rem}.swagger-ui .pl7{padding-left:16rem}.swagger-ui .pr0{padding-right:0}.swagger-ui .pr1{padding-right:.25rem}.swagger-ui .pr2{padding-right:.5rem}.swagger-ui .pr3{padding-right:1rem}.swagger-ui .pr4{padding-right:2rem}.swagger-ui .pr5{padding-right:4rem}.swagger-ui .pr6{padding-right:8rem}.swagger-ui .pr7{padding-right:16rem}.swagger-ui .pb0{padding-bottom:0}.swagger-ui .pb1{padding-bottom:.25rem}.swagger-ui .pb2{padding-bottom:.5rem}.swagger-ui .pb3{padding-bottom:1rem}.swagger-ui .pb4{padding-bottom:2rem}.swagger-ui .pb5{padding-bottom:4rem}.swagger-ui .pb6{padding-bottom:8rem}.swagger-ui .pb7{padding-bottom:16rem}.swagger-ui .pt0{padding-top:0}.swagger-ui .pt1{padding-top:.25rem}.swagger-ui .pt2{padding-top:.5rem}.swagger-ui .pt3{padding-top:1rem}.swagger-ui .pt4{padding-top:2rem}.swagger-ui .pt5{padding-top:4rem}.swagger-ui .pt6{padding-top:8rem}.swagger-ui .pt7{padding-top:16rem}.swagger-ui .pv0{padding-bottom:0;padding-top:0}.swagger-ui .pv1{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0{padding-left:0;padding-right:0}.swagger-ui .ph1{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0{margin:0}.swagger-ui .ma1{margin:.25rem}.swagger-ui .ma2{margin:.5rem}.swagger-ui .ma3{margin:1rem}.swagger-ui .ma4{margin:2rem}.swagger-ui .ma5{margin:4rem}.swagger-ui .ma6{margin:8rem}.swagger-ui .ma7{margin:16rem}.swagger-ui .ml0{margin-left:0}.swagger-ui .ml1{margin-left:.25rem}.swagger-ui .ml2{margin-left:.5rem}.swagger-ui .ml3{margin-left:1rem}.swagger-ui .ml4{margin-left:2rem}.swagger-ui .ml5{margin-left:4rem}.swagger-ui .ml6{margin-left:8rem}.swagger-ui .ml7{margin-left:16rem}.swagger-ui .mr0{margin-right:0}.swagger-ui .mr1{margin-right:.25rem}.swagger-ui .mr2{margin-right:.5rem}.swagger-ui .mr3{margin-right:1rem}.swagger-ui .mr4{margin-right:2rem}.swagger-ui .mr5{margin-right:4rem}.swagger-ui .mr6{margin-right:8rem}.swagger-ui .mr7{margin-right:16rem}.swagger-ui .mb0{margin-bottom:0}.swagger-ui .mb1{margin-bottom:.25rem}.swagger-ui .mb2{margin-bottom:.5rem}.swagger-ui .mb3{margin-bottom:1rem}.swagger-ui .mb4{margin-bottom:2rem}.swagger-ui .mb5{margin-bottom:4rem}.swagger-ui .mb6{margin-bottom:8rem}.swagger-ui .mb7{margin-bottom:16rem}.swagger-ui .mt0{margin-top:0}.swagger-ui .mt1{margin-top:.25rem}.swagger-ui .mt2{margin-top:.5rem}.swagger-ui .mt3{margin-top:1rem}.swagger-ui .mt4{margin-top:2rem}.swagger-ui .mt5{margin-top:4rem}.swagger-ui .mt6{margin-top:8rem}.swagger-ui .mt7{margin-top:16rem}.swagger-ui .mv0{margin-bottom:0;margin-top:0}.swagger-ui .mv1{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0{margin-left:0;margin-right:0}.swagger-ui .mh1{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7{margin-left:16rem;margin-right:16rem}@media screen and (min-width:30em){.swagger-ui .pa0-ns{padding:0}.swagger-ui .pa1-ns{padding:.25rem}.swagger-ui .pa2-ns{padding:.5rem}.swagger-ui .pa3-ns{padding:1rem}.swagger-ui .pa4-ns{padding:2rem}.swagger-ui .pa5-ns{padding:4rem}.swagger-ui .pa6-ns{padding:8rem}.swagger-ui .pa7-ns{padding:16rem}.swagger-ui .pl0-ns{padding-left:0}.swagger-ui .pl1-ns{padding-left:.25rem}.swagger-ui .pl2-ns{padding-left:.5rem}.swagger-ui .pl3-ns{padding-left:1rem}.swagger-ui .pl4-ns{padding-left:2rem}.swagger-ui .pl5-ns{padding-left:4rem}.swagger-ui .pl6-ns{padding-left:8rem}.swagger-ui .pl7-ns{padding-left:16rem}.swagger-ui .pr0-ns{padding-right:0}.swagger-ui .pr1-ns{padding-right:.25rem}.swagger-ui .pr2-ns{padding-right:.5rem}.swagger-ui .pr3-ns{padding-right:1rem}.swagger-ui .pr4-ns{padding-right:2rem}.swagger-ui .pr5-ns{padding-right:4rem}.swagger-ui .pr6-ns{padding-right:8rem}.swagger-ui .pr7-ns{padding-right:16rem}.swagger-ui .pb0-ns{padding-bottom:0}.swagger-ui .pb1-ns{padding-bottom:.25rem}.swagger-ui .pb2-ns{padding-bottom:.5rem}.swagger-ui .pb3-ns{padding-bottom:1rem}.swagger-ui .pb4-ns{padding-bottom:2rem}.swagger-ui .pb5-ns{padding-bottom:4rem}.swagger-ui .pb6-ns{padding-bottom:8rem}.swagger-ui .pb7-ns{padding-bottom:16rem}.swagger-ui .pt0-ns{padding-top:0}.swagger-ui .pt1-ns{padding-top:.25rem}.swagger-ui .pt2-ns{padding-top:.5rem}.swagger-ui .pt3-ns{padding-top:1rem}.swagger-ui .pt4-ns{padding-top:2rem}.swagger-ui .pt5-ns{padding-top:4rem}.swagger-ui .pt6-ns{padding-top:8rem}.swagger-ui .pt7-ns{padding-top:16rem}.swagger-ui .pv0-ns{padding-bottom:0;padding-top:0}.swagger-ui .pv1-ns{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-ns{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-ns{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-ns{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-ns{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-ns{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-ns{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-ns{padding-left:0;padding-right:0}.swagger-ui .ph1-ns{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-ns{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-ns{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-ns{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-ns{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-ns{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-ns{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-ns{margin:0}.swagger-ui .ma1-ns{margin:.25rem}.swagger-ui .ma2-ns{margin:.5rem}.swagger-ui .ma3-ns{margin:1rem}.swagger-ui .ma4-ns{margin:2rem}.swagger-ui .ma5-ns{margin:4rem}.swagger-ui .ma6-ns{margin:8rem}.swagger-ui .ma7-ns{margin:16rem}.swagger-ui .ml0-ns{margin-left:0}.swagger-ui .ml1-ns{margin-left:.25rem}.swagger-ui .ml2-ns{margin-left:.5rem}.swagger-ui .ml3-ns{margin-left:1rem}.swagger-ui .ml4-ns{margin-left:2rem}.swagger-ui .ml5-ns{margin-left:4rem}.swagger-ui .ml6-ns{margin-left:8rem}.swagger-ui .ml7-ns{margin-left:16rem}.swagger-ui .mr0-ns{margin-right:0}.swagger-ui .mr1-ns{margin-right:.25rem}.swagger-ui .mr2-ns{margin-right:.5rem}.swagger-ui .mr3-ns{margin-right:1rem}.swagger-ui .mr4-ns{margin-right:2rem}.swagger-ui .mr5-ns{margin-right:4rem}.swagger-ui .mr6-ns{margin-right:8rem}.swagger-ui .mr7-ns{margin-right:16rem}.swagger-ui .mb0-ns{margin-bottom:0}.swagger-ui .mb1-ns{margin-bottom:.25rem}.swagger-ui .mb2-ns{margin-bottom:.5rem}.swagger-ui .mb3-ns{margin-bottom:1rem}.swagger-ui .mb4-ns{margin-bottom:2rem}.swagger-ui .mb5-ns{margin-bottom:4rem}.swagger-ui .mb6-ns{margin-bottom:8rem}.swagger-ui .mb7-ns{margin-bottom:16rem}.swagger-ui .mt0-ns{margin-top:0}.swagger-ui .mt1-ns{margin-top:.25rem}.swagger-ui .mt2-ns{margin-top:.5rem}.swagger-ui .mt3-ns{margin-top:1rem}.swagger-ui .mt4-ns{margin-top:2rem}.swagger-ui .mt5-ns{margin-top:4rem}.swagger-ui .mt6-ns{margin-top:8rem}.swagger-ui .mt7-ns{margin-top:16rem}.swagger-ui .mv0-ns{margin-bottom:0;margin-top:0}.swagger-ui .mv1-ns{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-ns{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-ns{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-ns{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-ns{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-ns{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-ns{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-ns{margin-left:0;margin-right:0}.swagger-ui .mh1-ns{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-ns{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-ns{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-ns{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-ns{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-ns{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-ns{margin-left:16rem;margin-right:16rem}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .pa0-m{padding:0}.swagger-ui .pa1-m{padding:.25rem}.swagger-ui .pa2-m{padding:.5rem}.swagger-ui .pa3-m{padding:1rem}.swagger-ui .pa4-m{padding:2rem}.swagger-ui .pa5-m{padding:4rem}.swagger-ui .pa6-m{padding:8rem}.swagger-ui .pa7-m{padding:16rem}.swagger-ui .pl0-m{padding-left:0}.swagger-ui .pl1-m{padding-left:.25rem}.swagger-ui .pl2-m{padding-left:.5rem}.swagger-ui .pl3-m{padding-left:1rem}.swagger-ui .pl4-m{padding-left:2rem}.swagger-ui .pl5-m{padding-left:4rem}.swagger-ui .pl6-m{padding-left:8rem}.swagger-ui .pl7-m{padding-left:16rem}.swagger-ui .pr0-m{padding-right:0}.swagger-ui .pr1-m{padding-right:.25rem}.swagger-ui .pr2-m{padding-right:.5rem}.swagger-ui .pr3-m{padding-right:1rem}.swagger-ui .pr4-m{padding-right:2rem}.swagger-ui .pr5-m{padding-right:4rem}.swagger-ui .pr6-m{padding-right:8rem}.swagger-ui .pr7-m{padding-right:16rem}.swagger-ui .pb0-m{padding-bottom:0}.swagger-ui .pb1-m{padding-bottom:.25rem}.swagger-ui .pb2-m{padding-bottom:.5rem}.swagger-ui .pb3-m{padding-bottom:1rem}.swagger-ui .pb4-m{padding-bottom:2rem}.swagger-ui .pb5-m{padding-bottom:4rem}.swagger-ui .pb6-m{padding-bottom:8rem}.swagger-ui .pb7-m{padding-bottom:16rem}.swagger-ui .pt0-m{padding-top:0}.swagger-ui .pt1-m{padding-top:.25rem}.swagger-ui .pt2-m{padding-top:.5rem}.swagger-ui .pt3-m{padding-top:1rem}.swagger-ui .pt4-m{padding-top:2rem}.swagger-ui .pt5-m{padding-top:4rem}.swagger-ui .pt6-m{padding-top:8rem}.swagger-ui .pt7-m{padding-top:16rem}.swagger-ui .pv0-m{padding-bottom:0;padding-top:0}.swagger-ui .pv1-m{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-m{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-m{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-m{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-m{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-m{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-m{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-m{padding-left:0;padding-right:0}.swagger-ui .ph1-m{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-m{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-m{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-m{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-m{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-m{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-m{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-m{margin:0}.swagger-ui .ma1-m{margin:.25rem}.swagger-ui .ma2-m{margin:.5rem}.swagger-ui .ma3-m{margin:1rem}.swagger-ui .ma4-m{margin:2rem}.swagger-ui .ma5-m{margin:4rem}.swagger-ui .ma6-m{margin:8rem}.swagger-ui .ma7-m{margin:16rem}.swagger-ui .ml0-m{margin-left:0}.swagger-ui .ml1-m{margin-left:.25rem}.swagger-ui .ml2-m{margin-left:.5rem}.swagger-ui .ml3-m{margin-left:1rem}.swagger-ui .ml4-m{margin-left:2rem}.swagger-ui .ml5-m{margin-left:4rem}.swagger-ui .ml6-m{margin-left:8rem}.swagger-ui .ml7-m{margin-left:16rem}.swagger-ui .mr0-m{margin-right:0}.swagger-ui .mr1-m{margin-right:.25rem}.swagger-ui .mr2-m{margin-right:.5rem}.swagger-ui .mr3-m{margin-right:1rem}.swagger-ui .mr4-m{margin-right:2rem}.swagger-ui .mr5-m{margin-right:4rem}.swagger-ui .mr6-m{margin-right:8rem}.swagger-ui .mr7-m{margin-right:16rem}.swagger-ui .mb0-m{margin-bottom:0}.swagger-ui .mb1-m{margin-bottom:.25rem}.swagger-ui .mb2-m{margin-bottom:.5rem}.swagger-ui .mb3-m{margin-bottom:1rem}.swagger-ui .mb4-m{margin-bottom:2rem}.swagger-ui .mb5-m{margin-bottom:4rem}.swagger-ui .mb6-m{margin-bottom:8rem}.swagger-ui .mb7-m{margin-bottom:16rem}.swagger-ui .mt0-m{margin-top:0}.swagger-ui .mt1-m{margin-top:.25rem}.swagger-ui .mt2-m{margin-top:.5rem}.swagger-ui .mt3-m{margin-top:1rem}.swagger-ui .mt4-m{margin-top:2rem}.swagger-ui .mt5-m{margin-top:4rem}.swagger-ui .mt6-m{margin-top:8rem}.swagger-ui .mt7-m{margin-top:16rem}.swagger-ui .mv0-m{margin-bottom:0;margin-top:0}.swagger-ui .mv1-m{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-m{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-m{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-m{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-m{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-m{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-m{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-m{margin-left:0;margin-right:0}.swagger-ui .mh1-m{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-m{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-m{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-m{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-m{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-m{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-m{margin-left:16rem;margin-right:16rem}}@media screen and (min-width:60em){.swagger-ui .pa0-l{padding:0}.swagger-ui .pa1-l{padding:.25rem}.swagger-ui .pa2-l{padding:.5rem}.swagger-ui .pa3-l{padding:1rem}.swagger-ui .pa4-l{padding:2rem}.swagger-ui .pa5-l{padding:4rem}.swagger-ui .pa6-l{padding:8rem}.swagger-ui .pa7-l{padding:16rem}.swagger-ui .pl0-l{padding-left:0}.swagger-ui .pl1-l{padding-left:.25rem}.swagger-ui .pl2-l{padding-left:.5rem}.swagger-ui .pl3-l{padding-left:1rem}.swagger-ui .pl4-l{padding-left:2rem}.swagger-ui .pl5-l{padding-left:4rem}.swagger-ui .pl6-l{padding-left:8rem}.swagger-ui .pl7-l{padding-left:16rem}.swagger-ui .pr0-l{padding-right:0}.swagger-ui .pr1-l{padding-right:.25rem}.swagger-ui .pr2-l{padding-right:.5rem}.swagger-ui .pr3-l{padding-right:1rem}.swagger-ui .pr4-l{padding-right:2rem}.swagger-ui .pr5-l{padding-right:4rem}.swagger-ui .pr6-l{padding-right:8rem}.swagger-ui .pr7-l{padding-right:16rem}.swagger-ui .pb0-l{padding-bottom:0}.swagger-ui .pb1-l{padding-bottom:.25rem}.swagger-ui .pb2-l{padding-bottom:.5rem}.swagger-ui .pb3-l{padding-bottom:1rem}.swagger-ui .pb4-l{padding-bottom:2rem}.swagger-ui .pb5-l{padding-bottom:4rem}.swagger-ui .pb6-l{padding-bottom:8rem}.swagger-ui .pb7-l{padding-bottom:16rem}.swagger-ui .pt0-l{padding-top:0}.swagger-ui .pt1-l{padding-top:.25rem}.swagger-ui .pt2-l{padding-top:.5rem}.swagger-ui .pt3-l{padding-top:1rem}.swagger-ui .pt4-l{padding-top:2rem}.swagger-ui .pt5-l{padding-top:4rem}.swagger-ui .pt6-l{padding-top:8rem}.swagger-ui .pt7-l{padding-top:16rem}.swagger-ui .pv0-l{padding-bottom:0;padding-top:0}.swagger-ui .pv1-l{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-l{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-l{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-l{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-l{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-l{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-l{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-l{padding-left:0;padding-right:0}.swagger-ui .ph1-l{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-l{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-l{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-l{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-l{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-l{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-l{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-l{margin:0}.swagger-ui .ma1-l{margin:.25rem}.swagger-ui .ma2-l{margin:.5rem}.swagger-ui .ma3-l{margin:1rem}.swagger-ui .ma4-l{margin:2rem}.swagger-ui .ma5-l{margin:4rem}.swagger-ui .ma6-l{margin:8rem}.swagger-ui .ma7-l{margin:16rem}.swagger-ui .ml0-l{margin-left:0}.swagger-ui .ml1-l{margin-left:.25rem}.swagger-ui .ml2-l{margin-left:.5rem}.swagger-ui .ml3-l{margin-left:1rem}.swagger-ui .ml4-l{margin-left:2rem}.swagger-ui .ml5-l{margin-left:4rem}.swagger-ui .ml6-l{margin-left:8rem}.swagger-ui .ml7-l{margin-left:16rem}.swagger-ui .mr0-l{margin-right:0}.swagger-ui .mr1-l{margin-right:.25rem}.swagger-ui .mr2-l{margin-right:.5rem}.swagger-ui .mr3-l{margin-right:1rem}.swagger-ui .mr4-l{margin-right:2rem}.swagger-ui .mr5-l{margin-right:4rem}.swagger-ui .mr6-l{margin-right:8rem}.swagger-ui .mr7-l{margin-right:16rem}.swagger-ui .mb0-l{margin-bottom:0}.swagger-ui .mb1-l{margin-bottom:.25rem}.swagger-ui .mb2-l{margin-bottom:.5rem}.swagger-ui .mb3-l{margin-bottom:1rem}.swagger-ui .mb4-l{margin-bottom:2rem}.swagger-ui .mb5-l{margin-bottom:4rem}.swagger-ui .mb6-l{margin-bottom:8rem}.swagger-ui .mb7-l{margin-bottom:16rem}.swagger-ui .mt0-l{margin-top:0}.swagger-ui .mt1-l{margin-top:.25rem}.swagger-ui .mt2-l{margin-top:.5rem}.swagger-ui .mt3-l{margin-top:1rem}.swagger-ui .mt4-l{margin-top:2rem}.swagger-ui .mt5-l{margin-top:4rem}.swagger-ui .mt6-l{margin-top:8rem}.swagger-ui .mt7-l{margin-top:16rem}.swagger-ui .mv0-l{margin-bottom:0;margin-top:0}.swagger-ui .mv1-l{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-l{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-l{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-l{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-l{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-l{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-l{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-l{margin-left:0;margin-right:0}.swagger-ui .mh1-l{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-l{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-l{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-l{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-l{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-l{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-l{margin-left:16rem;margin-right:16rem}}.swagger-ui .na1{margin:-.25rem}.swagger-ui .na2{margin:-.5rem}.swagger-ui .na3{margin:-1rem}.swagger-ui .na4{margin:-2rem}.swagger-ui .na5{margin:-4rem}.swagger-ui .na6{margin:-8rem}.swagger-ui .na7{margin:-16rem}.swagger-ui .nl1{margin-left:-.25rem}.swagger-ui .nl2{margin-left:-.5rem}.swagger-ui .nl3{margin-left:-1rem}.swagger-ui .nl4{margin-left:-2rem}.swagger-ui .nl5{margin-left:-4rem}.swagger-ui .nl6{margin-left:-8rem}.swagger-ui .nl7{margin-left:-16rem}.swagger-ui .nr1{margin-right:-.25rem}.swagger-ui .nr2{margin-right:-.5rem}.swagger-ui .nr3{margin-right:-1rem}.swagger-ui .nr4{margin-right:-2rem}.swagger-ui .nr5{margin-right:-4rem}.swagger-ui .nr6{margin-right:-8rem}.swagger-ui .nr7{margin-right:-16rem}.swagger-ui .nb1{margin-bottom:-.25rem}.swagger-ui .nb2{margin-bottom:-.5rem}.swagger-ui .nb3{margin-bottom:-1rem}.swagger-ui .nb4{margin-bottom:-2rem}.swagger-ui .nb5{margin-bottom:-4rem}.swagger-ui .nb6{margin-bottom:-8rem}.swagger-ui .nb7{margin-bottom:-16rem}.swagger-ui .nt1{margin-top:-.25rem}.swagger-ui .nt2{margin-top:-.5rem}.swagger-ui .nt3{margin-top:-1rem}.swagger-ui .nt4{margin-top:-2rem}.swagger-ui .nt5{margin-top:-4rem}.swagger-ui .nt6{margin-top:-8rem}.swagger-ui .nt7{margin-top:-16rem}@media screen and (min-width:30em){.swagger-ui .na1-ns{margin:-.25rem}.swagger-ui .na2-ns{margin:-.5rem}.swagger-ui .na3-ns{margin:-1rem}.swagger-ui .na4-ns{margin:-2rem}.swagger-ui .na5-ns{margin:-4rem}.swagger-ui .na6-ns{margin:-8rem}.swagger-ui .na7-ns{margin:-16rem}.swagger-ui .nl1-ns{margin-left:-.25rem}.swagger-ui .nl2-ns{margin-left:-.5rem}.swagger-ui .nl3-ns{margin-left:-1rem}.swagger-ui .nl4-ns{margin-left:-2rem}.swagger-ui .nl5-ns{margin-left:-4rem}.swagger-ui .nl6-ns{margin-left:-8rem}.swagger-ui .nl7-ns{margin-left:-16rem}.swagger-ui .nr1-ns{margin-right:-.25rem}.swagger-ui .nr2-ns{margin-right:-.5rem}.swagger-ui .nr3-ns{margin-right:-1rem}.swagger-ui .nr4-ns{margin-right:-2rem}.swagger-ui .nr5-ns{margin-right:-4rem}.swagger-ui .nr6-ns{margin-right:-8rem}.swagger-ui .nr7-ns{margin-right:-16rem}.swagger-ui .nb1-ns{margin-bottom:-.25rem}.swagger-ui .nb2-ns{margin-bottom:-.5rem}.swagger-ui .nb3-ns{margin-bottom:-1rem}.swagger-ui .nb4-ns{margin-bottom:-2rem}.swagger-ui .nb5-ns{margin-bottom:-4rem}.swagger-ui .nb6-ns{margin-bottom:-8rem}.swagger-ui .nb7-ns{margin-bottom:-16rem}.swagger-ui .nt1-ns{margin-top:-.25rem}.swagger-ui .nt2-ns{margin-top:-.5rem}.swagger-ui .nt3-ns{margin-top:-1rem}.swagger-ui .nt4-ns{margin-top:-2rem}.swagger-ui .nt5-ns{margin-top:-4rem}.swagger-ui .nt6-ns{margin-top:-8rem}.swagger-ui .nt7-ns{margin-top:-16rem}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .na1-m{margin:-.25rem}.swagger-ui .na2-m{margin:-.5rem}.swagger-ui .na3-m{margin:-1rem}.swagger-ui .na4-m{margin:-2rem}.swagger-ui .na5-m{margin:-4rem}.swagger-ui .na6-m{margin:-8rem}.swagger-ui .na7-m{margin:-16rem}.swagger-ui .nl1-m{margin-left:-.25rem}.swagger-ui .nl2-m{margin-left:-.5rem}.swagger-ui .nl3-m{margin-left:-1rem}.swagger-ui .nl4-m{margin-left:-2rem}.swagger-ui .nl5-m{margin-left:-4rem}.swagger-ui .nl6-m{margin-left:-8rem}.swagger-ui .nl7-m{margin-left:-16rem}.swagger-ui .nr1-m{margin-right:-.25rem}.swagger-ui .nr2-m{margin-right:-.5rem}.swagger-ui .nr3-m{margin-right:-1rem}.swagger-ui .nr4-m{margin-right:-2rem}.swagger-ui .nr5-m{margin-right:-4rem}.swagger-ui .nr6-m{margin-right:-8rem}.swagger-ui .nr7-m{margin-right:-16rem}.swagger-ui .nb1-m{margin-bottom:-.25rem}.swagger-ui .nb2-m{margin-bottom:-.5rem}.swagger-ui .nb3-m{margin-bottom:-1rem}.swagger-ui .nb4-m{margin-bottom:-2rem}.swagger-ui .nb5-m{margin-bottom:-4rem}.swagger-ui .nb6-m{margin-bottom:-8rem}.swagger-ui .nb7-m{margin-bottom:-16rem}.swagger-ui .nt1-m{margin-top:-.25rem}.swagger-ui .nt2-m{margin-top:-.5rem}.swagger-ui .nt3-m{margin-top:-1rem}.swagger-ui .nt4-m{margin-top:-2rem}.swagger-ui .nt5-m{margin-top:-4rem}.swagger-ui .nt6-m{margin-top:-8rem}.swagger-ui .nt7-m{margin-top:-16rem}}@media screen and (min-width:60em){.swagger-ui .na1-l{margin:-.25rem}.swagger-ui .na2-l{margin:-.5rem}.swagger-ui .na3-l{margin:-1rem}.swagger-ui .na4-l{margin:-2rem}.swagger-ui .na5-l{margin:-4rem}.swagger-ui .na6-l{margin:-8rem}.swagger-ui .na7-l{margin:-16rem}.swagger-ui .nl1-l{margin-left:-.25rem}.swagger-ui .nl2-l{margin-left:-.5rem}.swagger-ui .nl3-l{margin-left:-1rem}.swagger-ui .nl4-l{margin-left:-2rem}.swagger-ui .nl5-l{margin-left:-4rem}.swagger-ui .nl6-l{margin-left:-8rem}.swagger-ui .nl7-l{margin-left:-16rem}.swagger-ui .nr1-l{margin-right:-.25rem}.swagger-ui .nr2-l{margin-right:-.5rem}.swagger-ui .nr3-l{margin-right:-1rem}.swagger-ui .nr4-l{margin-right:-2rem}.swagger-ui .nr5-l{margin-right:-4rem}.swagger-ui .nr6-l{margin-right:-8rem}.swagger-ui .nr7-l{margin-right:-16rem}.swagger-ui .nb1-l{margin-bottom:-.25rem}.swagger-ui .nb2-l{margin-bottom:-.5rem}.swagger-ui .nb3-l{margin-bottom:-1rem}.swagger-ui .nb4-l{margin-bottom:-2rem}.swagger-ui .nb5-l{margin-bottom:-4rem}.swagger-ui .nb6-l{margin-bottom:-8rem}.swagger-ui .nb7-l{margin-bottom:-16rem}.swagger-ui .nt1-l{margin-top:-.25rem}.swagger-ui .nt2-l{margin-top:-.5rem}.swagger-ui .nt3-l{margin-top:-1rem}.swagger-ui .nt4-l{margin-top:-2rem}.swagger-ui .nt5-l{margin-top:-4rem}.swagger-ui .nt6-l{margin-top:-8rem}.swagger-ui .nt7-l{margin-top:-16rem}}.swagger-ui .collapse{border-collapse:collapse;border-spacing:0}.swagger-ui .striped--light-silver:nth-child(odd){background-color:#aaa}.swagger-ui .striped--moon-gray:nth-child(odd){background-color:#ccc}.swagger-ui .striped--light-gray:nth-child(odd){background-color:#eee}.swagger-ui .striped--near-white:nth-child(odd){background-color:#f4f4f4}.swagger-ui .stripe-light:nth-child(odd){background-color:hsla(0,0%,100%,.1)}.swagger-ui .stripe-dark:nth-child(odd){background-color:rgba(0,0,0,.1)}.swagger-ui .strike{text-decoration:line-through}.swagger-ui .underline{text-decoration:underline}.swagger-ui .no-underline{text-decoration:none}@media screen and (min-width:30em){.swagger-ui .strike-ns{text-decoration:line-through}.swagger-ui .underline-ns{text-decoration:underline}.swagger-ui .no-underline-ns{text-decoration:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .strike-m{text-decoration:line-through}.swagger-ui .underline-m{text-decoration:underline}.swagger-ui .no-underline-m{text-decoration:none}}@media screen and (min-width:60em){.swagger-ui .strike-l{text-decoration:line-through}.swagger-ui .underline-l{text-decoration:underline}.swagger-ui .no-underline-l{text-decoration:none}}.swagger-ui .tl{text-align:left}.swagger-ui .tr{text-align:right}.swagger-ui .tc{text-align:center}.swagger-ui .tj{text-align:justify}@media screen and (min-width:30em){.swagger-ui .tl-ns{text-align:left}.swagger-ui .tr-ns{text-align:right}.swagger-ui .tc-ns{text-align:center}.swagger-ui .tj-ns{text-align:justify}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .tl-m{text-align:left}.swagger-ui .tr-m{text-align:right}.swagger-ui .tc-m{text-align:center}.swagger-ui .tj-m{text-align:justify}}@media screen and (min-width:60em){.swagger-ui .tl-l{text-align:left}.swagger-ui .tr-l{text-align:right}.swagger-ui .tc-l{text-align:center}.swagger-ui .tj-l{text-align:justify}}.swagger-ui .ttc{text-transform:capitalize}.swagger-ui .ttl{text-transform:lowercase}.swagger-ui .ttu{text-transform:uppercase}.swagger-ui .ttn{text-transform:none}@media screen and (min-width:30em){.swagger-ui .ttc-ns{text-transform:capitalize}.swagger-ui .ttl-ns{text-transform:lowercase}.swagger-ui .ttu-ns{text-transform:uppercase}.swagger-ui .ttn-ns{text-transform:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .ttc-m{text-transform:capitalize}.swagger-ui .ttl-m{text-transform:lowercase}.swagger-ui .ttu-m{text-transform:uppercase}.swagger-ui .ttn-m{text-transform:none}}@media screen and (min-width:60em){.swagger-ui .ttc-l{text-transform:capitalize}.swagger-ui .ttl-l{text-transform:lowercase}.swagger-ui .ttu-l{text-transform:uppercase}.swagger-ui .ttn-l{text-transform:none}}.swagger-ui .f-6,.swagger-ui .f-headline{font-size:6rem}.swagger-ui .f-5,.swagger-ui .f-subheadline{font-size:5rem}.swagger-ui .f1{font-size:3rem}.swagger-ui .f2{font-size:2.25rem}.swagger-ui .f3{font-size:1.5rem}.swagger-ui .f4{font-size:1.25rem}.swagger-ui .f5{font-size:1rem}.swagger-ui .f6{font-size:.875rem}.swagger-ui .f7{font-size:.75rem}@media screen and (min-width:30em){.swagger-ui .f-6-ns,.swagger-ui .f-headline-ns{font-size:6rem}.swagger-ui .f-5-ns,.swagger-ui .f-subheadline-ns{font-size:5rem}.swagger-ui .f1-ns{font-size:3rem}.swagger-ui .f2-ns{font-size:2.25rem}.swagger-ui .f3-ns{font-size:1.5rem}.swagger-ui .f4-ns{font-size:1.25rem}.swagger-ui .f5-ns{font-size:1rem}.swagger-ui .f6-ns{font-size:.875rem}.swagger-ui .f7-ns{font-size:.75rem}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .f-6-m,.swagger-ui .f-headline-m{font-size:6rem}.swagger-ui .f-5-m,.swagger-ui .f-subheadline-m{font-size:5rem}.swagger-ui .f1-m{font-size:3rem}.swagger-ui .f2-m{font-size:2.25rem}.swagger-ui .f3-m{font-size:1.5rem}.swagger-ui .f4-m{font-size:1.25rem}.swagger-ui .f5-m{font-size:1rem}.swagger-ui .f6-m{font-size:.875rem}.swagger-ui .f7-m{font-size:.75rem}}@media screen and (min-width:60em){.swagger-ui .f-6-l,.swagger-ui .f-headline-l{font-size:6rem}.swagger-ui .f-5-l,.swagger-ui .f-subheadline-l{font-size:5rem}.swagger-ui .f1-l{font-size:3rem}.swagger-ui .f2-l{font-size:2.25rem}.swagger-ui .f3-l{font-size:1.5rem}.swagger-ui .f4-l{font-size:1.25rem}.swagger-ui .f5-l{font-size:1rem}.swagger-ui .f6-l{font-size:.875rem}.swagger-ui .f7-l{font-size:.75rem}}.swagger-ui .measure{max-width:30em}.swagger-ui .measure-wide{max-width:34em}.swagger-ui .measure-narrow{max-width:20em}.swagger-ui .indent{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media screen and (min-width:30em){.swagger-ui .measure-ns{max-width:30em}.swagger-ui .measure-wide-ns{max-width:34em}.swagger-ui .measure-narrow-ns{max-width:20em}.swagger-ui .indent-ns{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-ns{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-ns{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .measure-m{max-width:30em}.swagger-ui .measure-wide-m{max-width:34em}.swagger-ui .measure-narrow-m{max-width:20em}.swagger-ui .indent-m{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-m{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-m{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}@media screen and (min-width:60em){.swagger-ui .measure-l{max-width:30em}.swagger-ui .measure-wide-l{max-width:34em}.swagger-ui .measure-narrow-l{max-width:20em}.swagger-ui .indent-l{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-l{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-l{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.swagger-ui .overflow-container{overflow-y:scroll}.swagger-ui .center{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto{margin-right:auto}.swagger-ui .ml-auto{margin-left:auto}@media screen and (min-width:30em){.swagger-ui .center-ns{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-ns{margin-right:auto}.swagger-ui .ml-auto-ns{margin-left:auto}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .center-m{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-m{margin-right:auto}.swagger-ui .ml-auto-m{margin-left:auto}}@media screen and (min-width:60em){.swagger-ui .center-l{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-l{margin-right:auto}.swagger-ui .ml-auto-l{margin-left:auto}}.swagger-ui .clip{clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);position:fixed!important;_position:absolute!important}@media screen and (min-width:30em){.swagger-ui .clip-ns{clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);position:fixed!important;_position:absolute!important}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .clip-m{clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);position:fixed!important;_position:absolute!important}}@media screen and (min-width:60em){.swagger-ui .clip-l{clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);position:fixed!important;_position:absolute!important}}.swagger-ui .ws-normal{white-space:normal}.swagger-ui .nowrap{white-space:nowrap}.swagger-ui .pre{white-space:pre}@media screen and (min-width:30em){.swagger-ui .ws-normal-ns{white-space:normal}.swagger-ui .nowrap-ns{white-space:nowrap}.swagger-ui .pre-ns{white-space:pre}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .ws-normal-m{white-space:normal}.swagger-ui .nowrap-m{white-space:nowrap}.swagger-ui .pre-m{white-space:pre}}@media screen and (min-width:60em){.swagger-ui .ws-normal-l{white-space:normal}.swagger-ui .nowrap-l{white-space:nowrap}.swagger-ui .pre-l{white-space:pre}}.swagger-ui .v-base{vertical-align:baseline}.swagger-ui .v-mid{vertical-align:middle}.swagger-ui .v-top{vertical-align:top}.swagger-ui .v-btm{vertical-align:bottom}@media screen and (min-width:30em){.swagger-ui .v-base-ns{vertical-align:baseline}.swagger-ui .v-mid-ns{vertical-align:middle}.swagger-ui .v-top-ns{vertical-align:top}.swagger-ui .v-btm-ns{vertical-align:bottom}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .v-base-m{vertical-align:baseline}.swagger-ui .v-mid-m{vertical-align:middle}.swagger-ui .v-top-m{vertical-align:top}.swagger-ui .v-btm-m{vertical-align:bottom}}@media screen and (min-width:60em){.swagger-ui .v-base-l{vertical-align:baseline}.swagger-ui .v-mid-l{vertical-align:middle}.swagger-ui .v-top-l{vertical-align:top}.swagger-ui .v-btm-l{vertical-align:bottom}}.swagger-ui .dim{opacity:1;transition:opacity .15s ease-in}.swagger-ui .dim:focus,.swagger-ui .dim:hover{opacity:.5;transition:opacity .15s ease-in}.swagger-ui .dim:active{opacity:.8;transition:opacity .15s ease-out}.swagger-ui .glow{transition:opacity .15s ease-in}.swagger-ui .glow:focus,.swagger-ui .glow:hover{opacity:1;transition:opacity .15s ease-in}.swagger-ui .hide-child .child{opacity:0;transition:opacity .15s ease-in}.swagger-ui .hide-child:active .child,.swagger-ui .hide-child:focus .child,.swagger-ui .hide-child:hover .child{opacity:1;transition:opacity .15s ease-in}.swagger-ui .underline-hover:focus,.swagger-ui .underline-hover:hover{text-decoration:underline}.swagger-ui .grow{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform:translateZ(0);transition:transform .25s ease-out}.swagger-ui .grow:focus,.swagger-ui .grow:hover{transform:scale(1.05)}.swagger-ui .grow:active{transform:scale(.9)}.swagger-ui .grow-large{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform:translateZ(0);transition:transform .25s ease-in-out}.swagger-ui .grow-large:focus,.swagger-ui .grow-large:hover{transform:scale(1.2)}.swagger-ui .grow-large:active{transform:scale(.95)}.swagger-ui .pointer:hover{cursor:pointer}.swagger-ui .shadow-hover{cursor:pointer;position:relative;transition:all .5s cubic-bezier(.165,.84,.44,1)}.swagger-ui .shadow-hover:after{border-radius:inherit;box-shadow:0 0 16px 2px rgba(0,0,0,.2);content:"";height:100%;left:0;opacity:0;position:absolute;top:0;transition:opacity .5s cubic-bezier(.165,.84,.44,1);width:100%;z-index:-1}.swagger-ui .shadow-hover:focus:after,.swagger-ui .shadow-hover:hover:after{opacity:1}.swagger-ui .bg-animate,.swagger-ui .bg-animate:focus,.swagger-ui .bg-animate:hover{transition:background-color .15s ease-in-out}.swagger-ui .z-0{z-index:0}.swagger-ui .z-1{z-index:1}.swagger-ui .z-2{z-index:2}.swagger-ui .z-3{z-index:3}.swagger-ui .z-4{z-index:4}.swagger-ui .z-5{z-index:5}.swagger-ui .z-999{z-index:999}.swagger-ui .z-9999{z-index:9999}.swagger-ui .z-max{z-index:2147483647}.swagger-ui .z-inherit{z-index:inherit}.swagger-ui .z-initial{z-index:auto}.swagger-ui .z-unset{z-index:unset}.swagger-ui .nested-copy-line-height ol,.swagger-ui .nested-copy-line-height p,.swagger-ui .nested-copy-line-height ul{line-height:1.5}.swagger-ui .nested-headline-line-height h1,.swagger-ui .nested-headline-line-height h2,.swagger-ui .nested-headline-line-height h3,.swagger-ui .nested-headline-line-height h4,.swagger-ui .nested-headline-line-height h5,.swagger-ui .nested-headline-line-height h6{line-height:1.25}.swagger-ui .nested-list-reset ol,.swagger-ui .nested-list-reset ul{list-style-type:none;margin-left:0;padding-left:0}.swagger-ui .nested-copy-indent p+p{margin-bottom:0;margin-top:0;text-indent:.1em}.swagger-ui .nested-copy-seperator p+p{margin-top:1.5em}.swagger-ui .nested-img img{display:block;max-width:100%;width:100%}.swagger-ui .nested-links a{color:#357edd;transition:color .15s ease-in}.swagger-ui .nested-links a:focus,.swagger-ui .nested-links a:hover{color:#96ccff;transition:color .15s ease-in}.swagger-ui .wrapper{box-sizing:border-box;margin:0 auto;max-width:1460px;padding:0 20px;width:100%}.swagger-ui .opblock-tag-section{display:flex;flex-direction:column}.swagger-ui .try-out.btn-group{display:flex;flex:0.1 2 auto;padding:0}.swagger-ui .try-out__btn{margin-left:1.25rem}.swagger-ui .opblock-tag{align-items:center;border-bottom:1px solid rgba(59,65,81,.3);cursor:pointer;display:flex;padding:10px 20px 10px 10px;transition:all .2s}.swagger-ui .opblock-tag:hover{background:rgba(0,0,0,.02)}.swagger-ui .opblock-tag{color:#3b4151;font-family:sans-serif;font-size:24px;margin:0 0 5px}.swagger-ui .opblock-tag.no-desc span{flex:1}.swagger-ui .opblock-tag svg{transition:all .4s}.swagger-ui .opblock-tag small{color:#3b4151;flex:1;font-family:sans-serif;font-size:14px;font-weight:400;padding:0 10px}.swagger-ui .parameter__type{color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;padding:5px 0}.swagger-ui .parameter-controls{margin-top:.75em}.swagger-ui .examples__title{display:block;font-size:1.1em;font-weight:700;margin-bottom:.75em}.swagger-ui .examples__section{margin-top:1.5em}.swagger-ui .examples__section-header{font-size:.9rem;font-weight:700;margin-bottom:.5rem}.swagger-ui .examples-select{display:inline-block;margin-bottom:.75em}.swagger-ui .examples-select .examples-select-element{width:100%}.swagger-ui .examples-select__section-label{font-size:.9rem;font-weight:700;margin-right:.5rem}.swagger-ui .example__section{margin-top:1.5em}.swagger-ui .example__section-header{font-size:.9rem;font-weight:700;margin-bottom:.5rem}.swagger-ui .view-line-link{cursor:pointer;margin:0 5px;position:relative;top:3px;transition:all .5s;width:20px}.swagger-ui .opblock{border:1px solid #000;border-radius:4px;box-shadow:0 0 3px rgba(0,0,0,.19);margin:0 0 15px}.swagger-ui .opblock .tab-header{display:flex;flex:1}.swagger-ui .opblock .tab-header .tab-item{cursor:pointer;padding:0 40px}.swagger-ui .opblock .tab-header .tab-item:first-of-type{padding:0 40px 0 0}.swagger-ui .opblock .tab-header .tab-item.active h4 span{position:relative}.swagger-ui .opblock .tab-header .tab-item.active h4 span:after{background:gray;bottom:-15px;content:"";height:4px;left:50%;position:absolute;transform:translateX(-50%);width:120%}.swagger-ui .opblock.is-open .opblock-summary{border-bottom:1px solid #000}.swagger-ui .opblock .opblock-section-header{align-items:center;background:hsla(0,0%,100%,.8);box-shadow:0 1px 2px rgba(0,0,0,.1);display:flex;min-height:50px;padding:8px 20px}.swagger-ui .opblock .opblock-section-header>label{align-items:center;color:#3b4151;display:flex;font-family:sans-serif;font-size:12px;font-weight:700;margin:0 0 0 auto}.swagger-ui .opblock .opblock-section-header>label>span{padding:0 10px 0 0}.swagger-ui .opblock .opblock-section-header h4{color:#3b4151;flex:1;font-family:sans-serif;font-size:14px;margin:0}.swagger-ui .opblock .opblock-summary-method{background:#000;border-radius:3px;color:#fff;font-family:sans-serif;font-size:14px;font-weight:700;min-width:80px;padding:6px 0;text-align:center;text-shadow:0 1px 0 rgba(0,0,0,.1)}.swagger-ui .opblock .opblock-summary-operation-id,.swagger-ui .opblock .opblock-summary-path,.swagger-ui .opblock .opblock-summary-path__deprecated{align-items:center;color:#3b4151;display:flex;font-family:monospace;font-size:16px;font-weight:600;padding:0 10px;word-break:break-word}@media (max-width:768px){.swagger-ui .opblock .opblock-summary-operation-id,.swagger-ui .opblock .opblock-summary-path,.swagger-ui .opblock .opblock-summary-path__deprecated{font-size:12px}}.swagger-ui .opblock .opblock-summary-path{flex-shrink:0;max-width:calc(100% - 110px - 15rem)}.swagger-ui .opblock .opblock-summary-path__deprecated{text-decoration:line-through}.swagger-ui .opblock .opblock-summary-operation-id{font-size:14px}.swagger-ui .opblock .opblock-summary-description{color:#3b4151;flex:1 1 auto;font-family:sans-serif;font-size:13px;word-break:break-word}.swagger-ui .opblock .opblock-summary{align-items:center;cursor:pointer;display:flex;padding:5px}.swagger-ui .opblock .opblock-summary .view-line-link{cursor:pointer;margin:0;position:relative;top:2px;transition:all .5s;width:0}.swagger-ui .opblock .opblock-summary:hover .view-line-link{margin:0 5px;width:18px}.swagger-ui .opblock.opblock-post{background:rgba(73,204,144,.1);border-color:#49cc90}.swagger-ui .opblock.opblock-post .opblock-summary-method{background:#49cc90}.swagger-ui .opblock.opblock-post .opblock-summary{border-color:#49cc90}.swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span:after{background:#49cc90}.swagger-ui .opblock.opblock-put{background:rgba(252,161,48,.1);border-color:#fca130}.swagger-ui .opblock.opblock-put .opblock-summary-method{background:#fca130}.swagger-ui .opblock.opblock-put .opblock-summary{border-color:#fca130}.swagger-ui .opblock.opblock-put .tab-header .tab-item.active h4 span:after{background:#fca130}.swagger-ui .opblock.opblock-delete{background:rgba(249,62,62,.1);border-color:#f93e3e}.swagger-ui .opblock.opblock-delete .opblock-summary-method{background:#f93e3e}.swagger-ui .opblock.opblock-delete .opblock-summary{border-color:#f93e3e}.swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span:after{background:#f93e3e}.swagger-ui .opblock.opblock-get{background:rgba(97,175,254,.1);border-color:#61affe}.swagger-ui .opblock.opblock-get .opblock-summary-method{background:#61affe}.swagger-ui .opblock.opblock-get .opblock-summary{border-color:#61affe}.swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span:after{background:#61affe}.swagger-ui .opblock.opblock-patch{background:rgba(80,227,194,.1);border-color:#50e3c2}.swagger-ui .opblock.opblock-patch .opblock-summary-method{background:#50e3c2}.swagger-ui .opblock.opblock-patch .opblock-summary{border-color:#50e3c2}.swagger-ui .opblock.opblock-patch .tab-header .tab-item.active h4 span:after{background:#50e3c2}.swagger-ui .opblock.opblock-head{background:rgba(144,18,254,.1);border-color:#9012fe}.swagger-ui .opblock.opblock-head .opblock-summary-method{background:#9012fe}.swagger-ui .opblock.opblock-head .opblock-summary{border-color:#9012fe}.swagger-ui .opblock.opblock-head .tab-header .tab-item.active h4 span:after{background:#9012fe}.swagger-ui .opblock.opblock-options{background:rgba(13,90,167,.1);border-color:#0d5aa7}.swagger-ui .opblock.opblock-options .opblock-summary-method{background:#0d5aa7}.swagger-ui .opblock.opblock-options .opblock-summary{border-color:#0d5aa7}.swagger-ui .opblock.opblock-options .tab-header .tab-item.active h4 span:after{background:#0d5aa7}.swagger-ui .opblock.opblock-deprecated{background:hsla(0,0%,92%,.1);border-color:#ebebeb;opacity:.6}.swagger-ui .opblock.opblock-deprecated .opblock-summary-method{background:#ebebeb}.swagger-ui .opblock.opblock-deprecated .opblock-summary{border-color:#ebebeb}.swagger-ui .opblock.opblock-deprecated .tab-header .tab-item.active h4 span:after{background:#ebebeb}.swagger-ui .opblock .opblock-schemes{padding:8px 20px}.swagger-ui .opblock .opblock-schemes .schemes-title{padding:0 10px 0 0}.swagger-ui .filter .operation-filter-input{border:2px solid #d8dde7;margin:20px 0;padding:10px;width:100%}.swagger-ui .download-url-wrapper .failed,.swagger-ui .filter .failed{color:red}.swagger-ui .download-url-wrapper .loading,.swagger-ui .filter .loading{color:#aaa}.swagger-ui .model-example{margin-top:1em}.swagger-ui .tab{display:flex;list-style:none;padding:0}.swagger-ui .tab li{color:#3b4151;cursor:pointer;font-family:sans-serif;font-size:12px;min-width:60px;padding:0}.swagger-ui .tab li:first-of-type{padding-left:0;padding-right:12px;position:relative}.swagger-ui .tab li:first-of-type:after{background:rgba(0,0,0,.2);content:"";height:100%;position:absolute;right:6px;top:0;width:1px}.swagger-ui .tab li.active{font-weight:700}.swagger-ui .tab li button.tablinks{background:none;border:0;color:inherit;font-family:inherit;font-weight:inherit;padding:0}.swagger-ui .opblock-description-wrapper,.swagger-ui .opblock-external-docs-wrapper,.swagger-ui .opblock-title_normal{color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px;padding:15px 20px}.swagger-ui .opblock-description-wrapper h4,.swagger-ui .opblock-external-docs-wrapper h4,.swagger-ui .opblock-title_normal h4{color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px}.swagger-ui .opblock-description-wrapper p,.swagger-ui .opblock-external-docs-wrapper p,.swagger-ui .opblock-title_normal p{color:#3b4151;font-family:sans-serif;font-size:14px;margin:0}.swagger-ui .opblock-external-docs-wrapper h4{padding-left:0}.swagger-ui .execute-wrapper{padding:20px;text-align:right}.swagger-ui .execute-wrapper .btn{padding:8px 40px;width:100%}.swagger-ui .body-param-options{display:flex;flex-direction:column}.swagger-ui .body-param-options .body-param-edit{padding:10px 0}.swagger-ui .body-param-options label{padding:8px 0}.swagger-ui .body-param-options label select{margin:3px 0 0}.swagger-ui .responses-inner{padding:20px}.swagger-ui .responses-inner h4,.swagger-ui .responses-inner h5{color:#3b4151;font-family:sans-serif;font-size:12px;margin:10px 0 5px}.swagger-ui .responses-inner .curl{white-space:normal}.swagger-ui .response-col_status{color:#3b4151;font-family:sans-serif;font-size:14px}.swagger-ui .response-col_status .response-undocumented{color:#909090;font-family:monospace;font-size:11px;font-weight:600}.swagger-ui .response-col_links{color:#3b4151;font-family:sans-serif;font-size:14px;max-width:40em;padding-left:2em}.swagger-ui .response-col_links .response-undocumented{color:#909090;font-family:monospace;font-size:11px;font-weight:600}.swagger-ui .response-col_links .operation-link{margin-bottom:1.5em}.swagger-ui .response-col_links .operation-link .description{margin-bottom:.5em}.swagger-ui .opblock-body .opblock-loading-animation{display:block;margin:3em auto}.swagger-ui .opblock-body pre.microlight{word-wrap:break-word;background:#333;border-radius:4px;color:#fff;font-family:monospace;font-size:12px;font-weight:600;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto;margin:0;padding:10px;white-space:pre-wrap;word-break:break-all;word-break:break-word}.swagger-ui .opblock-body pre.microlight .headerline{display:block}.swagger-ui .highlight-code{position:relative}.swagger-ui .highlight-code>.microlight{max-height:400px;min-height:6em;overflow-y:auto}.swagger-ui .highlight-code>.microlight code{white-space:pre-wrap!important;word-break:break-all}.swagger-ui .curl-command{position:relative}.swagger-ui .download-contents{align-items:center;background:#7d8293;border-radius:4px;bottom:10px;color:#fff;cursor:pointer;display:flex;font-family:sans-serif;font-size:14px;font-weight:600;height:30px;justify-content:center;padding:5px;position:absolute;right:10px;text-align:center}.swagger-ui .scheme-container{background:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.15);margin:0 0 20px;padding:30px 0}.swagger-ui .scheme-container .schemes{align-items:flex-end;display:flex}.swagger-ui .scheme-container .schemes>label{color:#3b4151;display:flex;flex-direction:column;font-family:sans-serif;font-size:12px;font-weight:700;margin:-20px 15px 0 0}.swagger-ui .scheme-container .schemes>label select{min-width:130px;text-transform:uppercase}.swagger-ui .loading-container{align-items:center;display:flex;flex-direction:column;justify-content:center;margin-top:1em;min-height:1px;padding:40px 0 60px}.swagger-ui .loading-container .loading{position:relative}.swagger-ui .loading-container .loading:after{color:#3b4151;content:"loading";font-family:sans-serif;font-size:10px;font-weight:700;left:50%;position:absolute;text-transform:uppercase;top:50%;transform:translate(-50%,-50%)}.swagger-ui .loading-container .loading:before{-webkit-animation:rotation 1s linear infinite,opacity .5s;animation:rotation 1s linear infinite,opacity .5s;-webkit-backface-visibility:hidden;backface-visibility:hidden;border:2px solid rgba(85,85,85,.1);border-radius:100%;border-top-color:rgba(0,0,0,.6);content:"";display:block;height:60px;left:50%;margin:-30px;opacity:1;position:absolute;top:50%;width:60px}@-webkit-keyframes rotation{to{transform:rotate(1turn)}}@keyframes rotation{to{transform:rotate(1turn)}}.swagger-ui .response-controls{display:flex;padding-top:1em}.swagger-ui .response-control-media-type{margin-right:1em}.swagger-ui .response-control-media-type--accept-controller select{border-color:green}.swagger-ui .response-control-media-type__accept-message{color:green;font-size:.7em}.swagger-ui .response-control-examples__title,.swagger-ui .response-control-media-type__title{display:block;font-size:.7em;margin-bottom:.2em}@-webkit-keyframes blinker{50%{opacity:0}}@keyframes blinker{50%{opacity:0}}.swagger-ui .hidden{display:none}.swagger-ui .no-margin{border:none;height:auto;margin:0;padding:0}.swagger-ui .float-right{float:right}.swagger-ui .svg-assets{height:0;position:absolute;width:0}.swagger-ui section h3{color:#3b4151;font-family:sans-serif}.swagger-ui a.nostyle{display:inline}.swagger-ui a.nostyle,.swagger-ui a.nostyle:visited{color:inherit;cursor:pointer;text-decoration:inherit}.swagger-ui .fallback{color:#aaa;padding:1em}.swagger-ui .version-pragma{height:100%;padding:5em 0}.swagger-ui .version-pragma__message{display:flex;font-size:1.2em;height:100%;justify-content:center;line-height:1.5em;padding:0 .6em;text-align:center}.swagger-ui .version-pragma__message>div{flex:1;max-width:55ch}.swagger-ui .version-pragma__message code{background-color:#dedede;padding:4px 4px 2px;white-space:pre}.swagger-ui .opblock-link{font-weight:400}.swagger-ui .opblock-link.shown{font-weight:700}.swagger-ui span.token-string{color:#555}.swagger-ui span.token-not-formatted{color:#555;font-weight:700}.swagger-ui .btn{background:transparent;border:2px solid gray;border-radius:4px;box-shadow:0 1px 2px rgba(0,0,0,.1);color:#3b4151;font-family:sans-serif;font-size:14px;font-weight:700;padding:5px 23px;transition:all .3s}.swagger-ui .btn.btn-sm{font-size:12px;padding:4px 23px}.swagger-ui .btn[disabled]{cursor:not-allowed;opacity:.3}.swagger-ui .btn:hover{box-shadow:0 0 5px rgba(0,0,0,.3)}.swagger-ui .btn.cancel{background-color:transparent;border-color:#ff6060;color:#ff6060;font-family:sans-serif}.swagger-ui .btn.authorize{background-color:transparent;border-color:#49cc90;color:#49cc90;display:inline;line-height:1}.swagger-ui .btn.authorize span{float:left;padding:4px 20px 0 0}.swagger-ui .btn.authorize svg{fill:#49cc90}.swagger-ui .btn.execute{background-color:#4990e2;border-color:#4990e2;color:#fff}.swagger-ui .btn-group{display:flex;padding:30px}.swagger-ui .btn-group .btn{flex:1}.swagger-ui .btn-group .btn:first-child{border-radius:4px 0 0 4px}.swagger-ui .btn-group .btn:last-child{border-radius:0 4px 4px 0}.swagger-ui .authorization__btn{background:none;border:none;padding:0 10px}.swagger-ui .authorization__btn.locked{opacity:1}.swagger-ui .authorization__btn.unlocked{opacity:.4}.swagger-ui .model-box-control,.swagger-ui .models-control,.swagger-ui .opblock-summary-control{all:inherit;border-bottom:0;cursor:pointer;flex:1;padding:0}.swagger-ui .model-box-control:focus,.swagger-ui .models-control:focus,.swagger-ui .opblock-summary-control:focus{outline:auto}.swagger-ui .expand-methods,.swagger-ui .expand-operation{background:none;border:none}.swagger-ui .expand-methods svg,.swagger-ui .expand-operation svg{height:20px;width:20px}.swagger-ui .expand-methods{padding:0 10px}.swagger-ui .expand-methods:hover svg{fill:#404040}.swagger-ui .expand-methods svg{fill:#707070;transition:all .3s}.swagger-ui button{cursor:pointer}.swagger-ui button.invalid{-webkit-animation:shake .4s 1;animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui .copy-to-clipboard{align-items:center;background:#7d8293;border:none;border-radius:4px;bottom:10px;display:flex;height:30px;justify-content:center;position:absolute;right:100px;width:30px}.swagger-ui .copy-to-clipboard button{background:url('data:image/svg+xml;charset=utf-8,') 50% no-repeat;border:none;flex-grow:1;flex-shrink:1;height:25px}.swagger-ui .curl-command .copy-to-clipboard{bottom:5px;height:20px;right:10px;width:20px}.swagger-ui .curl-command .copy-to-clipboard button{height:18px}.swagger-ui select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#f7f7f7 url('data:image/svg+xml;charset=utf-8,') right 10px center no-repeat;background-size:20px;border:2px solid #41444e;border-radius:4px;box-shadow:0 1px 2px 0 rgba(0,0,0,.25);color:#3b4151;font-family:sans-serif;font-size:14px;font-weight:700;padding:5px 40px 5px 10px}.swagger-ui select[multiple]{background:#f7f7f7;margin:5px 0;padding:5px}.swagger-ui select.invalid{-webkit-animation:shake .4s 1;animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui .opblock-body select{min-width:230px}@media (max-width:768px){.swagger-ui .opblock-body select{min-width:180px}}.swagger-ui label{color:#3b4151;font-family:sans-serif;font-size:12px;font-weight:700;margin:0 0 5px}@media (max-width:768px){.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text]{max-width:175px}}.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text],.swagger-ui textarea{background:#fff;border:1px solid #d9d9d9;border-radius:4px;margin:5px 0;min-width:100px;padding:8px 10px}.swagger-ui input[type=email].invalid,.swagger-ui input[type=file].invalid,.swagger-ui input[type=password].invalid,.swagger-ui input[type=search].invalid,.swagger-ui input[type=text].invalid,.swagger-ui textarea.invalid{-webkit-animation:shake .4s 1;animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui input[disabled],.swagger-ui select[disabled],.swagger-ui textarea[disabled]{background-color:#fafafa;color:#888;cursor:not-allowed}.swagger-ui select[disabled]{border-color:#888}.swagger-ui textarea[disabled]{background-color:#41444e;color:#fff}@-webkit-keyframes shake{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}@keyframes shake{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}.swagger-ui textarea{background:hsla(0,0%,100%,.8);border:none;border-radius:4px;color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;min-height:280px;outline:none;padding:10px;width:100%}.swagger-ui textarea:focus{border:2px solid #61affe}.swagger-ui textarea.curl{background:#41444e;border-radius:4px;color:#fff;font-family:monospace;font-size:12px;font-weight:600;margin:0;min-height:100px;padding:10px;resize:none}.swagger-ui .checkbox{color:#303030;padding:5px 0 10px;transition:opacity .5s}.swagger-ui .checkbox label{display:flex}.swagger-ui .checkbox p{color:#3b4151;font-family:monospace;font-style:italic;font-weight:400!important;font-weight:600;margin:0!important}.swagger-ui .checkbox input[type=checkbox]{display:none}.swagger-ui .checkbox input[type=checkbox]+label>.item{background:#e8e8e8;border-radius:1px;box-shadow:0 0 0 2px #e8e8e8;cursor:pointer;display:inline-block;flex:none;height:16px;margin:0 8px 0 0;padding:5px;position:relative;top:3px;width:16px}.swagger-ui .checkbox input[type=checkbox]+label>.item:active{transform:scale(.9)}.swagger-ui .checkbox input[type=checkbox]:checked+label>.item{background:#e8e8e8 url('data:image/svg+xml;charset=utf-8,') 50% no-repeat}.swagger-ui .dialog-ux{bottom:0;left:0;position:fixed;right:0;top:0;z-index:9999}.swagger-ui .dialog-ux .backdrop-ux{background:rgba(0,0,0,.8);bottom:0;left:0;position:fixed;right:0;top:0}.swagger-ui .dialog-ux .modal-ux{background:#fff;border:1px solid #ebebeb;border-radius:4px;box-shadow:0 10px 30px 0 rgba(0,0,0,.2);left:50%;max-width:650px;min-width:300px;position:absolute;top:50%;transform:translate(-50%,-50%);width:100%;z-index:9999}.swagger-ui .dialog-ux .modal-ux-content{max-height:540px;overflow-y:auto;padding:20px}.swagger-ui .dialog-ux .modal-ux-content p{color:#41444e;color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px}.swagger-ui .dialog-ux .modal-ux-content h4{color:#3b4151;font-family:sans-serif;font-size:18px;font-weight:600;margin:15px 0 0}.swagger-ui .dialog-ux .modal-ux-header{align-items:center;border-bottom:1px solid #ebebeb;display:flex;padding:12px 0}.swagger-ui .dialog-ux .modal-ux-header .close-modal{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:none;padding:0 10px}.swagger-ui .dialog-ux .modal-ux-header h3{color:#3b4151;flex:1;font-family:sans-serif;font-size:20px;font-weight:600;margin:0;padding:0 20px}.swagger-ui .model{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300;font-weight:600}.swagger-ui .model .deprecated span,.swagger-ui .model .deprecated td{color:#a0a0a0!important}.swagger-ui .model .deprecated>td:first-of-type{text-decoration:line-through}.swagger-ui .model-toggle{cursor:pointer;display:inline-block;font-size:10px;margin:auto .3em;position:relative;top:6px;transform:rotate(90deg);transform-origin:50% 50%;transition:transform .15s ease-in}.swagger-ui .model-toggle.collapsed{transform:rotate(0deg)}.swagger-ui .model-toggle:after{background:url('data:image/svg+xml;charset=utf-8,') 50% no-repeat;background-size:100%;content:"";display:block;height:20px;width:20px}.swagger-ui .model-jump-to-path{cursor:pointer;position:relative}.swagger-ui .model-jump-to-path .view-line-link{cursor:pointer;position:absolute;top:-.4em}.swagger-ui .model-title{position:relative}.swagger-ui .model-title:hover .model-hint{visibility:visible}.swagger-ui .model-hint{background:rgba(0,0,0,.7);border-radius:4px;color:#ebebeb;padding:.1em .5em;position:absolute;top:-1.8em;visibility:hidden;white-space:nowrap}.swagger-ui .model p{margin:0 0 1em}.swagger-ui .model .property{color:#999;font-style:italic}.swagger-ui .model .property.primitive{color:#6b6b6b}.swagger-ui table.model tr.description{color:#666;font-weight:400}.swagger-ui table.model tr.description td:first-child,.swagger-ui table.model tr.property-row.required td:first-child{font-weight:700}.swagger-ui table.model tr.property-row td{vertical-align:top}.swagger-ui table.model tr.property-row td:first-child{padding-right:.2em}.swagger-ui table.model tr.property-row .star{color:red}.swagger-ui table.model tr.extension{color:#777}.swagger-ui table.model tr.extension td:last-child{vertical-align:top}.swagger-ui section.models{border:1px solid rgba(59,65,81,.3);border-radius:4px;margin:30px 0}.swagger-ui section.models .pointer{cursor:pointer}.swagger-ui section.models.is-open{padding:0 0 20px}.swagger-ui section.models.is-open h4{border-bottom:1px solid rgba(59,65,81,.3);margin:0 0 5px}.swagger-ui section.models h4{align-items:center;color:#606060;cursor:pointer;display:flex;font-family:sans-serif;font-size:16px;margin:0;padding:10px 20px 10px 10px;transition:all .2s}.swagger-ui section.models h4 svg{transition:all .4s}.swagger-ui section.models h4 span{flex:1}.swagger-ui section.models h4:hover{background:rgba(0,0,0,.02)}.swagger-ui section.models h5{color:#707070;font-family:sans-serif;font-size:16px;margin:0 0 10px}.swagger-ui section.models .model-jump-to-path{position:relative;top:5px}.swagger-ui section.models .model-container{background:rgba(0,0,0,.05);border-radius:4px;margin:0 20px 15px;position:relative;transition:all .5s}.swagger-ui section.models .model-container:hover{background:rgba(0,0,0,.07)}.swagger-ui section.models .model-container:first-of-type{margin:20px}.swagger-ui section.models .model-container:last-of-type{margin:0 20px}.swagger-ui section.models .model-container .models-jump-to-path{opacity:.65;position:absolute;right:5px;top:8px}.swagger-ui section.models .model-box{background:none}.swagger-ui .model-box{background:rgba(0,0,0,.1);border-radius:4px;display:inline-block;padding:10px}.swagger-ui .model-box .model-jump-to-path{position:relative;top:4px}.swagger-ui .model-box.deprecated{opacity:.5}.swagger-ui .model-title{color:#505050;font-family:sans-serif;font-size:16px}.swagger-ui .model-title img{bottom:0;margin-left:1em;position:relative}.swagger-ui .model-deprecated-warning{color:#f93e3e;font-family:sans-serif;font-size:16px;font-weight:600;margin-right:1em}.swagger-ui span>span.model .brace-close{padding:0 0 0 10px}.swagger-ui .prop-name{display:inline-block;margin-right:1em}.swagger-ui .prop-type{color:#55a}.swagger-ui .prop-enum{display:block}.swagger-ui .prop-format{color:#606060}.swagger-ui .servers>label{color:#3b4151;font-family:sans-serif;font-size:12px;margin:-20px 15px 0 0}.swagger-ui .servers>label select{max-width:100%;min-width:130px}.swagger-ui .servers h4.message{padding-bottom:2em}.swagger-ui .servers table tr{width:30em}.swagger-ui .servers table td{display:inline-block;max-width:15em;padding-bottom:10px;padding-top:10px;vertical-align:middle}.swagger-ui .servers table td:first-of-type{padding-right:1em}.swagger-ui .servers table td input{height:100%;width:100%}.swagger-ui .servers .computed-url{margin:2em 0}.swagger-ui .servers .computed-url code{display:inline-block;font-size:16px;margin:0 1em;padding:4px}.swagger-ui .servers-title{font-size:12px;font-weight:700}.swagger-ui .operation-servers h4.message{margin-bottom:2em}.swagger-ui table{border-collapse:collapse;padding:0 10px;width:100%}.swagger-ui table.model tbody tr td{padding:0;vertical-align:top}.swagger-ui table.model tbody tr td:first-of-type{padding:0 0 0 2em;width:174px}.swagger-ui table.headers td{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300;font-weight:600;vertical-align:middle}.swagger-ui table.headers .header-example{color:#999;font-style:italic}.swagger-ui table tbody tr td{padding:10px 0 0;vertical-align:top}.swagger-ui table tbody tr td:first-of-type{min-width:6em;padding:10px 0}.swagger-ui table thead tr td,.swagger-ui table thead tr th{border-bottom:1px solid rgba(59,65,81,.2);color:#3b4151;font-family:sans-serif;font-size:12px;font-weight:700;padding:12px 0;text-align:left}.swagger-ui .parameters-col_description{margin-bottom:2em;width:99%}.swagger-ui .parameters-col_description input[type=text]{max-width:340px;width:100%}.swagger-ui .parameters-col_description select{border-width:1px}.swagger-ui .parameter__name{color:#3b4151;font-family:sans-serif;font-size:16px;font-weight:400;margin-right:.75em}.swagger-ui .parameter__name.required{font-weight:700}.swagger-ui .parameter__name.required span{color:red}.swagger-ui .parameter__name.required:after{color:rgba(255,0,0,.6);content:"required";font-size:10px;padding:5px;position:relative;top:-6px}.swagger-ui .parameter__extension,.swagger-ui .parameter__in{color:gray;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .parameter__deprecated{color:red;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .parameter__empty_value_toggle{display:block;font-size:13px;padding-bottom:12px;padding-top:5px}.swagger-ui .parameter__empty_value_toggle input{margin-right:7px}.swagger-ui .parameter__empty_value_toggle.disabled{opacity:.7}.swagger-ui .table-container{padding:20px}.swagger-ui .response-col_description{width:99%}.swagger-ui .response-col_links{min-width:6em}.swagger-ui .response__extension{color:gray;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .topbar{background-color:#1b1b1b;padding:10px 0}.swagger-ui .topbar .topbar-wrapper,.swagger-ui .topbar a{align-items:center;display:flex}.swagger-ui .topbar a{color:#fff;flex:1;font-family:sans-serif;font-size:1.5em;font-weight:700;max-width:300px;text-decoration:none}.swagger-ui .topbar a span{margin:0;padding:0 10px}.swagger-ui .topbar .download-url-wrapper{display:flex;flex:3;justify-content:flex-end}.swagger-ui .topbar .download-url-wrapper input[type=text]{border:2px solid #62a03f;border-radius:4px 0 0 4px;margin:0;outline:none;width:100%}.swagger-ui .topbar .download-url-wrapper .select-label{align-items:center;color:#f0f0f0;display:flex;margin:0;max-width:600px;width:100%}.swagger-ui .topbar .download-url-wrapper .select-label span{flex:1;font-size:16px;padding:0 10px 0 0;text-align:right}.swagger-ui .topbar .download-url-wrapper .select-label select{border:2px solid #62a03f;box-shadow:none;flex:2;outline:none;width:100%}.swagger-ui .topbar .download-url-wrapper .download-url-button{background:#62a03f;border:none;border-radius:0 4px 4px 0;color:#fff;font-family:sans-serif;font-size:16px;font-weight:700;padding:4px 30px}.swagger-ui .info{margin:50px 0}.swagger-ui .info.failed-config{margin-left:auto;margin-right:auto;max-width:880px;text-align:center}.swagger-ui .info hgroup.main{margin:0 0 20px}.swagger-ui .info hgroup.main a{font-size:12px}.swagger-ui .info pre{font-size:14px}.swagger-ui .info li,.swagger-ui .info p,.swagger-ui .info table{color:#3b4151;font-family:sans-serif;font-size:14px}.swagger-ui .info h1,.swagger-ui .info h2,.swagger-ui .info h3,.swagger-ui .info h4,.swagger-ui .info h5{color:#3b4151;font-family:sans-serif}.swagger-ui .info a{color:#4990e2;font-family:sans-serif;font-size:14px;transition:all .4s}.swagger-ui .info a:hover{color:#1f69c0}.swagger-ui .info>div{margin:0 0 5px}.swagger-ui .info .base-url{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300!important;font-weight:600;margin:0}.swagger-ui .info .title{color:#3b4151;font-family:sans-serif;font-size:36px;margin:0}.swagger-ui .info .title small{background:#7d8492;border-radius:57px;display:inline-block;font-size:10px;margin:0 0 0 5px;padding:2px 4px;position:relative;top:-5px;vertical-align:super}.swagger-ui .info .title small.version-stamp{background-color:#89bf04}.swagger-ui .info .title small pre{color:#fff;font-family:sans-serif;margin:0;padding:0}.swagger-ui .auth-btn-wrapper{display:flex;justify-content:center;padding:10px 0}.swagger-ui .auth-btn-wrapper .btn-done{margin-right:1em}.swagger-ui .auth-wrapper{display:flex;flex:1;justify-content:flex-end}.swagger-ui .auth-wrapper .authorize{margin-right:10px;padding-right:20px}.swagger-ui .auth-container{border-bottom:1px solid #ebebeb;margin:0 0 10px;padding:10px 20px}.swagger-ui .auth-container:last-of-type{border:0;margin:0;padding:10px 20px}.swagger-ui .auth-container h4{margin:5px 0 15px!important}.swagger-ui .auth-container .wrapper{margin:0;padding:0}.swagger-ui .auth-container input[type=password],.swagger-ui .auth-container input[type=text]{min-width:230px}.swagger-ui .auth-container .errors{background-color:#fee;border-radius:4px;color:red;color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;margin:1em;padding:10px}.swagger-ui .auth-container .errors b{margin-right:1em;text-transform:capitalize}.swagger-ui .scopes h2{color:#3b4151;font-family:sans-serif;font-size:14px}.swagger-ui .scopes h2 a{color:#4990e2;cursor:pointer;font-size:12px;padding-left:10px;text-decoration:underline}.swagger-ui .scope-def{padding:0 0 20px}.swagger-ui .errors-wrapper{-webkit-animation:scaleUp .5s;animation:scaleUp .5s;background:rgba(249,62,62,.1);border:2px solid #f93e3e;border-radius:4px;margin:20px;padding:10px 20px}.swagger-ui .errors-wrapper .error-wrapper{margin:0 0 10px}.swagger-ui .errors-wrapper .errors h4{color:#3b4151;font-family:monospace;font-size:14px;font-weight:600;margin:0}.swagger-ui .errors-wrapper .errors small{color:#606060}.swagger-ui .errors-wrapper .errors .message{white-space:pre-line}.swagger-ui .errors-wrapper .errors .message.thrown{max-width:100%}.swagger-ui .errors-wrapper .errors .error-line{cursor:pointer;text-decoration:underline}.swagger-ui .errors-wrapper hgroup{align-items:center;display:flex}.swagger-ui .errors-wrapper hgroup h4{color:#3b4151;flex:1;font-family:sans-serif;font-size:20px;margin:0}@-webkit-keyframes scaleUp{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes scaleUp{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}.swagger-ui .Resizer.vertical.disabled{display:none}.swagger-ui .markdown p,.swagger-ui .markdown pre,.swagger-ui .renderedMarkdown p,.swagger-ui .renderedMarkdown pre{margin:1em auto;word-break:break-all;word-break:break-word}.swagger-ui .markdown pre,.swagger-ui .renderedMarkdown pre{background:none;color:#000;font-weight:400;padding:0;white-space:pre-wrap}.swagger-ui .markdown code,.swagger-ui .renderedMarkdown code{background:rgba(0,0,0,.05);border-radius:4px;color:#9012fe;font-family:monospace;font-size:14px;font-weight:600;padding:5px 7px}.swagger-ui .markdown pre>code,.swagger-ui .renderedMarkdown pre>code{display:block} + +/*# sourceMappingURL=swagger-ui.css.map*/ \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/application.yml b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/application.yml new file mode 100644 index 0000000000..e4bcdb5888 --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/application.yml @@ -0,0 +1,9 @@ +keycloak: + auth-server-url: https://api.example.com/auth # Keycloak server url + realm: todos-service-realm # Keycloak Realm + resource: todos-service-clients # Keycloak Client + public-client: true + principal-attribute: preferred_username + ssl-required: external + credentials: + secret: 00000000-0000-0000-0000-000000000000 From c41716248a26a1ae62a0d6c89308603b0cdb36b9 Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 19:22:24 +0800 Subject: [PATCH 25/48] Update README.md --- core-java-modules/core-java-8-datetime/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-java-modules/core-java-8-datetime/README.md b/core-java-modules/core-java-8-datetime/README.md index c35683a589..03d2a9609d 100644 --- a/core-java-modules/core-java-8-datetime/README.md +++ b/core-java-modules/core-java-8-datetime/README.md @@ -5,7 +5,7 @@ This module contains articles about the Date and Time API introduced with Java 8 ### Relevant Articles: - [Introduction to the Java 8 Date/Time API](http://www.baeldung.com/java-8-date-time-intro) - [Migrating to the New Java 8 Date Time API](http://www.baeldung.com/migrating-to-java-8-date-time-api) -- [Get the Current Date, Time and Timestamp in Java 8](http://www.baeldung.com/current-date-time-and-timestamp-in-java-8) +- [Get the Current Date and Time in Java](https://www.baeldung.com/current-date-time-and-timestamp-in-java-8) - [TemporalAdjuster in Java](http://www.baeldung.com/java-temporal-adjuster) - [ZoneOffset in Java](https://www.baeldung.com/java-zone-offset) - [Differences Between ZonedDateTime and OffsetDateTime](https://www.baeldung.com/java-zoneddatetime-offsetdatetime) From 408b2ecf038503f8bf6cb3c895815fe3bd723d11 Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 19:34:08 +0800 Subject: [PATCH 26/48] Create README.md --- jbang/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 jbang/README.md diff --git a/jbang/README.md b/jbang/README.md new file mode 100644 index 0000000000..bd8d2a151e --- /dev/null +++ b/jbang/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Guide to JBang](https://www.baeldung.com/jbang-guide) From fb19c5e98cc398bf973797ae19ebb9f0f77c90b7 Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 19:39:58 +0800 Subject: [PATCH 27/48] Update README.md --- core-java-modules/core-java-concurrency-basic-2/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java-modules/core-java-concurrency-basic-2/README.md b/core-java-modules/core-java-concurrency-basic-2/README.md index d2ff75ca95..1dad941e57 100644 --- a/core-java-modules/core-java-concurrency-basic-2/README.md +++ b/core-java-modules/core-java-concurrency-basic-2/README.md @@ -14,4 +14,5 @@ This module contains articles about basic Java concurrency - [How to Stop Execution After a Certain Time in Java](https://www.baeldung.com/java-stop-execution-after-certain-time) - [How to Handle InterruptedException in Java](https://www.baeldung.com/java-interrupted-exception) - [How to Get the Number of Threads in a Java Process](https://www.baeldung.com/java-get-number-of-threads) +- [Set the Name of a Thread in Java](https://www.baeldung.com/java-set-thread-name) - [[<-- Prev]](/core-java-modules/core-java-concurrency-basic) From 9deeba7850063ad1f46228276759aa4b779945b6 Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 20:14:44 +0800 Subject: [PATCH 28/48] Update README.md --- core-java-modules/core-java-string-conversions-2/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java-modules/core-java-string-conversions-2/README.md b/core-java-modules/core-java-string-conversions-2/README.md index 9cbece4ec3..7abcd5e97a 100644 --- a/core-java-modules/core-java-string-conversions-2/README.md +++ b/core-java-modules/core-java-string-conversions-2/README.md @@ -10,4 +10,5 @@ This module contains articles about string conversions from/to another type. - [Converting String to BigInteger in Java](https://www.baeldung.com/java-string-to-biginteger) - [Convert a String to Camel Case](https://www.baeldung.com/java-string-to-camel-case) - [Convert a ByteBuffer to String in Java](https://www.baeldung.com/java-bytebuffer-to-string) +- [Convert String to Float and Back in Java](https://www.baeldung.com/java-string-to-float) - More articles: [[<-- prev]](/core-java-string-conversions) From 75b6aa213ec3fe9ea19da05fb28637cf5621d62f Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 20:16:34 +0800 Subject: [PATCH 29/48] Update README.md --- metrics/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/README.md b/metrics/README.md index 6050b2b310..d386a2264a 100644 --- a/metrics/README.md +++ b/metrics/README.md @@ -8,3 +8,4 @@ This module contains articles about metrics. - [Introduction to Netflix Servo](https://www.baeldung.com/netflix-servo) - [Quick Guide to Micrometer](https://www.baeldung.com/micrometer) - [@Timed Annotation Using Metrics and AspectJ](https://www.baeldung.com/timed-metrics-aspectj) +- [Guide to Netflix Spectator](https://www.baeldung.com/java-netflix-spectator) From 9cc3165ab4fcf329f90e012b4bc426272de1872d Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 20:22:48 +0800 Subject: [PATCH 30/48] Update README.md --- core-java-modules/core-java-concurrency-advanced-4/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java-modules/core-java-concurrency-advanced-4/README.md b/core-java-modules/core-java-concurrency-advanced-4/README.md index db856a2cd6..446a553b88 100644 --- a/core-java-modules/core-java-concurrency-advanced-4/README.md +++ b/core-java-modules/core-java-concurrency-advanced-4/README.md @@ -3,3 +3,4 @@ - [Binary Semaphore vs Reentrant Lock](https://www.baeldung.com/java-binary-semaphore-vs-reentrant-lock) - [Bad Practices With Synchronization](https://www.baeldung.com/java-synchronization-bad-practices) - [Start Two Threads at the Exact Same Time in Java](https://www.baeldung.com/java-start-two-threads-at-same-time) +- [Volatile Variables and Thread Safety](https://www.baeldung.com/java-volatile-variables-thread-safety) From 91d7c165244275d3b57a2a99d4149730561a4af4 Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 20:34:20 +0800 Subject: [PATCH 31/48] Update README.md --- core-java-modules/core-java-exceptions-3/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java-modules/core-java-exceptions-3/README.md b/core-java-modules/core-java-exceptions-3/README.md index f79eb41a8b..d269444ccb 100644 --- a/core-java-modules/core-java-exceptions-3/README.md +++ b/core-java-modules/core-java-exceptions-3/README.md @@ -9,3 +9,4 @@ - [Explanation of ClassCastException in Java](https://www.baeldung.com/java-classcastexception) - [NoSuchFieldError in Java](https://www.baeldung.com/java-nosuchfielderror) - [IllegalAccessError in Java](https://www.baeldung.com/java-illegalaccesserror) +- [Working with (Unknown Source) Stack Traces in Java](https://www.baeldung.com/java-unknown-source-stack-trace) From 9dbe5bca8988020068d5252767894b2c093d719b Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 20:38:06 +0800 Subject: [PATCH 32/48] Update README.md --- core-java-modules/core-java-string-operations-4/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java-modules/core-java-string-operations-4/README.md b/core-java-modules/core-java-string-operations-4/README.md index 83bfeb4d05..10479f52f4 100644 --- a/core-java-modules/core-java-string-operations-4/README.md +++ b/core-java-modules/core-java-string-operations-4/README.md @@ -3,4 +3,5 @@ - [Ignoring Commas in Quotes When Splitting a Comma-separated String](https://www.baeldung.com/java-split-string-commas) - [Compare Strings While Ignoring Whitespace in Java](https://www.baeldung.com/java-compare-string-whitespace) - [Concatenating Null Strings in Java](https://www.baeldung.com/java-concat-null-string) +- [Split a String Every n Characters in Java](https://www.baeldung.com/java-string-split-every-n-characters) From 377708d2426944b507d218cf9c3c83343595cd2e Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 20:40:01 +0800 Subject: [PATCH 33/48] Update README.md --- core-java-modules/core-java-string-operations-4/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java-modules/core-java-string-operations-4/README.md b/core-java-modules/core-java-string-operations-4/README.md index 10479f52f4..e7192b5beb 100644 --- a/core-java-modules/core-java-string-operations-4/README.md +++ b/core-java-modules/core-java-string-operations-4/README.md @@ -4,4 +4,5 @@ - [Compare Strings While Ignoring Whitespace in Java](https://www.baeldung.com/java-compare-string-whitespace) - [Concatenating Null Strings in Java](https://www.baeldung.com/java-concat-null-string) - [Split a String Every n Characters in Java](https://www.baeldung.com/java-string-split-every-n-characters) +- [String equals() Vs contentEquals() in Java](https://www.baeldung.com/java-string-equals-vs-contentequals) From ba5b780b51bb9a7f280c1e90909467737541378a Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 20:43:08 +0800 Subject: [PATCH 34/48] Update README.md --- lombok/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/lombok/README.md b/lombok/README.md index 69b547f10c..a92840f698 100644 --- a/lombok/README.md +++ b/lombok/README.md @@ -16,3 +16,4 @@ This module contains articles about Project Lombok. - [Omitting Getter or Setter in Lombok](https://www.baeldung.com/lombok-omit-getter-setter) - [Declaring Val and Var Variables in Lombok](https://www.baeldung.com/java-lombok-val-var) - [Lombok Using @With Annotations](https://www.baeldung.com/lombok-with-annotations) +- [Lombok Configuration System](https://www.baeldung.com/lombok-configuration-system) From 95f5f30421ea9fcddb082cef199b088f9dff645d Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Wed, 5 Jan 2022 20:46:29 +0800 Subject: [PATCH 35/48] Create README.md --- spring-boot-modules/spring-boot-swagger-keycloak/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 spring-boot-modules/spring-boot-swagger-keycloak/README.md diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/README.md b/spring-boot-modules/spring-boot-swagger-keycloak/README.md new file mode 100644 index 0000000000..9c6513d9bf --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger-keycloak/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Keycloak Integration – OAuth2 and OpenID with Swagger UI](https://www.baeldung.com/keycloak-oauth2-openid-swagger) From 8bf612c07a74b0650368386c9ed35eb5cd881b9f Mon Sep 17 00:00:00 2001 From: Daniel Strmecki Date: Thu, 6 Jan 2022 09:54:44 +0100 Subject: [PATCH 36/48] Feature/bael 5176 random number generators (#11641) * BAEL-5176: Add simple test * BAEL-5176: Add benchmark runner * BAEL-5176: Update examples * BAEL-5176: Refactor * BAEL-5176: Refactor * BAEL-5176: Refactor --- core-java-modules/core-java-17/pom.xml | 21 ++++ .../randomgenerators/BenchmarkRunner.java | 115 ++++++++++++++++++ .../randomgenerators/GeneratorFactory.java | 36 ++++++ .../baeldung/randomgenerators/OldRandom.java | 12 ++ .../SplittableGeneratorMultiThread.java | 30 +++++ .../src/main/java/module-info.java | 3 + .../GeneratorFactoryUnitTest.java | 21 ++++ .../GeneratorSelectionUnitTest.java | 25 ++++ .../randomgenerators/OldRandomUnitTest.java | 15 +++ ...plittableGeneratorMultiThreadUnitTest.java | 17 +++ 10 files changed, 295 insertions(+) create mode 100644 core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/BenchmarkRunner.java create mode 100644 core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/GeneratorFactory.java create mode 100644 core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/OldRandom.java create mode 100644 core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/SplittableGeneratorMultiThread.java create mode 100644 core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/GeneratorFactoryUnitTest.java create mode 100644 core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/GeneratorSelectionUnitTest.java create mode 100644 core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/OldRandomUnitTest.java create mode 100644 core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/SplittableGeneratorMultiThreadUnitTest.java diff --git a/core-java-modules/core-java-17/pom.xml b/core-java-modules/core-java-17/pom.xml index 2fe16cad57..bc949d9050 100644 --- a/core-java-modules/core-java-17/pom.xml +++ b/core-java-modules/core-java-17/pom.xml @@ -27,6 +27,13 @@ --enable-preview ${maven.compiler.source.version} ${maven.compiler.target.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh-generator.version} + + @@ -49,6 +56,20 @@ + + + org.openjdk.jmh + jmh-core + ${jmh-core.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh-generator.version} + test + + + 17 17 diff --git a/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/BenchmarkRunner.java b/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/BenchmarkRunner.java new file mode 100644 index 0000000000..9a40a2de4e --- /dev/null +++ b/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/BenchmarkRunner.java @@ -0,0 +1,115 @@ +package com.baeldung.randomgenerators; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; + +import java.util.concurrent.TimeUnit; +import java.util.random.RandomGenerator; + +public class BenchmarkRunner { + + public static void main(String[] args) throws Exception { + org.openjdk.jmh.Main.main(args); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testL128X1024MixRandom() { + generateRandomNumbers(RandomGenerator.of("L128X1024MixRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testL128X128MixRandom() { + generateRandomNumbers(RandomGenerator.of("L128X128MixRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testL128X256MixRandom() { + generateRandomNumbers(RandomGenerator.of("L128X256MixRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testL32X64MixRandom() { + generateRandomNumbers(RandomGenerator.of("L32X64MixRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testL64X1024MixRandom() { + generateRandomNumbers(RandomGenerator.of("L64X1024MixRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testL64X128MixRandom() { + generateRandomNumbers(RandomGenerator.of("L64X128MixRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testL64X128StarStarRandom() { + generateRandomNumbers(RandomGenerator.of("L64X128StarStarRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testL64X256MixRandom() { + generateRandomNumbers(RandomGenerator.of("L64X256MixRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testRandom() { + generateRandomNumbers(RandomGenerator.of("Random")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testSecureRandom() { + generateRandomNumbers(RandomGenerator.of("SecureRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testSplittableRandom() { + generateRandomNumbers(RandomGenerator.of("SplittableRandom")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testXoroshiro128PlusPlus() { + generateRandomNumbers(RandomGenerator.of("Xoroshiro128PlusPlus")); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static void testXoshiro256PlusPlus() { + generateRandomNumbers(RandomGenerator.of("Xoshiro256PlusPlus")); + } + + private static void generateRandomNumbers(RandomGenerator generator) { + generator.nextLong(); + generator.nextInt(); + generator.nextFloat(); + generator.nextDouble(); + } + +} diff --git a/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/GeneratorFactory.java b/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/GeneratorFactory.java new file mode 100644 index 0000000000..64bc81a035 --- /dev/null +++ b/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/GeneratorFactory.java @@ -0,0 +1,36 @@ +package com.baeldung.randomgenerators; + +import java.util.Comparator; +import java.util.random.RandomGenerator; +import java.util.random.RandomGeneratorFactory; + +public class GeneratorFactory { + + public static void main(String[] args) { + System.out.println("Group\tName\tJumpable?\tSplittable?"); + RandomGeneratorFactory.all() + .sorted(Comparator.comparing(RandomGeneratorFactory::name)) + .forEach(factory -> System.out.println(String.format("%s\t%s\t%s\t%s", + factory.group(), + factory.name(), + factory.isJumpable(), + factory.isSplittable()))); + } + + public static int getRandomInt(RandomGenerator generator, int bound) { + return generator.nextInt(bound); + } + + public static RandomGenerator getDefaultGenerator() { + return RandomGeneratorFactory.getDefault().create(); + } + + public static RandomGenerator getJumpableGenerator() { + return RandomGeneratorFactory.all() + .filter(RandomGeneratorFactory::isJumpable) + .findAny() + .map(RandomGeneratorFactory::create) + .orElseThrow(() -> new RuntimeException("Error creating a generator")); + } + +} diff --git a/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/OldRandom.java b/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/OldRandom.java new file mode 100644 index 0000000000..c84d5bc8d3 --- /dev/null +++ b/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/OldRandom.java @@ -0,0 +1,12 @@ +package com.baeldung.randomgenerators; + +import java.util.Random; + +public class OldRandom { + + public static int getRandomInt(int bound) { + Random random = new Random(); + return random.nextInt(bound); + } + +} diff --git a/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/SplittableGeneratorMultiThread.java b/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/SplittableGeneratorMultiThread.java new file mode 100644 index 0000000000..298d840da9 --- /dev/null +++ b/core-java-modules/core-java-17/src/main/java/com/baeldung/randomgenerators/SplittableGeneratorMultiThread.java @@ -0,0 +1,30 @@ +package com.baeldung.randomgenerators; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.random.RandomGenerator; +import java.util.random.RandomGeneratorFactory; + +public class SplittableGeneratorMultiThread { + + public static List generateNumbersInMultipleThreads() { + List numbers = Collections.synchronizedList(new ArrayList<>()); + ExecutorService executorService = Executors.newCachedThreadPool(); + + RandomGenerator.SplittableGenerator sourceGenerator = RandomGeneratorFactory + .of("L128X256MixRandom") + .create(); + + sourceGenerator.splits(20).forEach((splitGenerator) -> { + executorService.submit(() -> { + numbers.add(splitGenerator.nextInt(10)); + }); + }); + + return numbers; + } + +} diff --git a/core-java-modules/core-java-17/src/main/java/module-info.java b/core-java-modules/core-java-17/src/main/java/module-info.java index b1ce62aa68..319678832e 100644 --- a/core-java-modules/core-java-17/src/main/java/module-info.java +++ b/core-java-modules/core-java-17/src/main/java/module-info.java @@ -1,4 +1,7 @@ module core.java { requires jdk.incubator.vector; requires jdk.incubator.foreign; + requires jmh.core; + requires jdk.unsupported; + exports com.baeldung.randomgenerators.jmh_generated; } \ No newline at end of file diff --git a/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/GeneratorFactoryUnitTest.java b/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/GeneratorFactoryUnitTest.java new file mode 100644 index 0000000000..b009b4eeb0 --- /dev/null +++ b/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/GeneratorFactoryUnitTest.java @@ -0,0 +1,21 @@ +package com.baeldung.randomgenerators; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class GeneratorFactoryUnitTest { + + @Test + void givenDefaultGenerator_whenGeneratingAnInt_thenIntInRangeIsGenerated() { + int number = GeneratorFactory.getRandomInt(GeneratorFactory.getDefaultGenerator(), 10); + assertThat(number).isNotNegative().isLessThan(10); + } + + @Test + void givenJumpableGenerator_whenGeneratingAnInt_thenIntInRangeIsGenerated() { + int number = GeneratorFactory.getRandomInt(GeneratorFactory.getJumpableGenerator(), 10); + assertThat(number).isNotNegative().isLessThan(10); + } + +} diff --git a/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/GeneratorSelectionUnitTest.java b/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/GeneratorSelectionUnitTest.java new file mode 100644 index 0000000000..0732c3f54e --- /dev/null +++ b/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/GeneratorSelectionUnitTest.java @@ -0,0 +1,25 @@ +package com.baeldung.randomgenerators; + +import org.junit.jupiter.api.Test; + +import java.util.random.RandomGenerator; + +import static org.assertj.core.api.Assertions.assertThat; + +class GeneratorSelectionUnitTest { + + @Test + void givenDefaultGenerator_whenGeneratingAnInt_thenIntInRangeIsGenerated() { + RandomGenerator generator = RandomGenerator.getDefault(); + int number = generator.nextInt(10); + assertThat(number).isNotNegative().isLessThan(10); + } + + @Test + void givenSpecificGenerator_whenGeneratingAnInt_thenIntInRangeIsGenerated() { + RandomGenerator generator = RandomGenerator.of("L128X256MixRandom"); + int number = generator.nextInt(10); + assertThat(number).isNotNegative().isLessThan(10); + } + +} diff --git a/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/OldRandomUnitTest.java b/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/OldRandomUnitTest.java new file mode 100644 index 0000000000..4a18369686 --- /dev/null +++ b/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/OldRandomUnitTest.java @@ -0,0 +1,15 @@ +package com.baeldung.randomgenerators; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class OldRandomUnitTest { + + @Test + void givenOldRandomApi_whenGeneratingAnInt_thenIntInRangeIsGenerated() { + int number = OldRandom.getRandomInt(10); + assertThat(number).isNotNegative().isLessThan(10); + } + +} diff --git a/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/SplittableGeneratorMultiThreadUnitTest.java b/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/SplittableGeneratorMultiThreadUnitTest.java new file mode 100644 index 0000000000..ba4cd8eb2b --- /dev/null +++ b/core-java-modules/core-java-17/src/test/java/com/baeldung/randomgenerators/SplittableGeneratorMultiThreadUnitTest.java @@ -0,0 +1,17 @@ +package com.baeldung.randomgenerators; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class SplittableGeneratorMultiThreadUnitTest { + + @Test + void givenSplittableGenerator_whenUsingMultipleThreads_thenListOfIntsIsGenerated() { + List numbers = SplittableGeneratorMultiThread.generateNumbersInMultipleThreads(); + assertThat(numbers).hasSize(20).allMatch(number -> number >= 0 && number <= 10); + } + +} From 1eaafd1c513365308d3537b466ea23e3de00c7df Mon Sep 17 00:00:00 2001 From: johnA1331 <53036378+johnA1331@users.noreply.github.com> Date: Fri, 7 Jan 2022 01:22:12 +0800 Subject: [PATCH 37/48] Update README.md --- spring-5-webflux/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-5-webflux/README.md b/spring-5-webflux/README.md index 889f211fc6..bd667468fb 100644 --- a/spring-5-webflux/README.md +++ b/spring-5-webflux/README.md @@ -11,4 +11,3 @@ This module contains articles about Spring 5 WebFlux - [Spring MVC Async vs Spring WebFlux](https://www.baeldung.com/spring-mvc-async-vs-webflux) - [Set a Timeout in Spring 5 Webflux WebClient](https://www.baeldung.com/spring-webflux-timeout) - [Guide to Retry in Spring WebFlux](https://www.baeldung.com/spring-webflux-retry) -- [Spring Webflux and @Cacheable Annotation](https://www.baeldung.com/spring-webflux-cacheable) From c86e2a8655ff813347a89524d04c5e066de0217d Mon Sep 17 00:00:00 2001 From: Haroon Khan Date: Wed, 5 Jan 2022 23:40:22 +0000 Subject: [PATCH 38/48] [JAVA-9317] Update tests to use JsonUnit for improved assertions --- json/pom.xml | 7 ++++ .../baeldung/jsonjava/CDLIntegrationTest.java | 34 ++++++++++++------- .../jsonjava/CookieIntegrationTest.java | 6 ++-- .../jsonjava/HTTPIntegrationTest.java | 14 +++++--- .../JSONArrayGetValueByKeyUnitTest.java | 14 ++++---- .../jsonjava/JSONArrayIntegrationTest.java | 32 ++++++++++------- .../jsonjava/JSONObjectIntegrationTest.java | 27 ++++++++------- .../jsonjava/JSONTokenerIntegrationTest.java | 13 +++---- .../ObjectToFromJSONIntegrationTest.java | 11 +++--- 9 files changed, 97 insertions(+), 61 deletions(-) diff --git a/json/pom.xml b/json/pom.xml index 9b49f4cc7e..87bbad460a 100644 --- a/json/pom.xml +++ b/json/pom.xml @@ -63,6 +63,12 @@ ${commons-collections4.version} test + + net.javacrumbs.json-unit + json-unit-assertj + ${json-unit-assertj.version} + test + @@ -72,6 +78,7 @@ 20211205 2.8.5 1.1.2 + 2.28.0 \ No newline at end of file diff --git a/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java index 441c71e78e..4698b29c6e 100644 --- a/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java @@ -1,36 +1,44 @@ package com.baeldung.jsonjava; -import static org.junit.Assert.assertEquals; - import org.json.CDL; import org.json.JSONArray; import org.json.JSONTokener; import org.junit.Test; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; + public class CDLIntegrationTest { + @Test public void givenCommaDelimitedText_thenConvertToJSONArray() { JSONArray ja = CDL.rowToJSONArray(new JSONTokener("England, USA, Canada")); - assertEquals("[\"England\",\"USA\",\"Canada\"]", ja.toString()); + + assertThatJson(ja) + .isEqualTo("[\"England\",\"USA\",\"Canada\"]"); } @Test public void givenJSONArray_thenConvertToCommaDelimitedText() { JSONArray ja = new JSONArray("[\"England\",\"USA\",\"Canada\"]"); + String cdt = CDL.rowToString(ja); - assertEquals("England,USA,Canada", cdt.toString().trim()); + + assertThat(cdt.trim()).isEqualTo("England,USA,Canada"); } @Test public void givenCommaDelimitedText_thenGetJSONArrayOfJSONObjects() { - String string = + String string = "name, city, age \n" + "john, chicago, 22 \n" + "gary, florida, 35 \n" + "sal, vegas, 18"; - + JSONArray result = CDL.toJSONArray(string); - assertEquals("[{\"name\":\"john\",\"city\":\"chicago\",\"age\":\"22\"},{\"name\":\"gary\",\"city\":\"florida\",\"age\":\"35\"},{\"name\":\"sal\",\"city\":\"vegas\",\"age\":\"18\"}]", result.toString()); + + assertThatJson(result) + .isEqualTo("[{\"name\":\"john\",\"city\":\"chicago\",\"age\":\"22\"},{\"name\":\"gary\",\"city\":\"florida\",\"age\":\"35\"},{\"name\":\"sal\",\"city\":\"vegas\",\"age\":\"18\"}]"); } @Test @@ -39,14 +47,16 @@ public class CDLIntegrationTest { ja.put("name"); ja.put("city"); ja.put("age"); - - String string = + + String string = "john, chicago, 22 \n" + "gary, florida, 35 \n" + "sal, vegas, 18"; - + JSONArray result = CDL.toJSONArray(ja, string); - assertEquals("[{\"name\":\"john\",\"city\":\"chicago\",\"age\":\"22\"},{\"name\":\"gary\",\"city\":\"florida\",\"age\":\"35\"},{\"name\":\"sal\",\"city\":\"vegas\",\"age\":\"18\"}]", result.toString()); + + assertThatJson(result) + .isEqualTo("[{\"name\":\"john\",\"city\":\"chicago\",\"age\":\"22\"},{\"name\":\"gary\",\"city\":\"florida\",\"age\":\"35\"},{\"name\":\"sal\",\"city\":\"vegas\",\"age\":\"18\"}]"); } - + } diff --git a/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java index 4de5d8a21c..accb94e732 100644 --- a/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java @@ -1,19 +1,21 @@ package com.baeldung.jsonjava; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; import org.json.Cookie; import org.json.JSONObject; import org.junit.Test; public class CookieIntegrationTest { + @Test public void givenCookieString_thenConvertToJSONObject() { String cookie = "username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/"; JSONObject cookieJO = Cookie.toJSONObject(cookie); - assertEquals("{\"path\":\"/\",\"expires\":\"Thu, 18 Dec 2013 12:00:00 UTC\",\"name\":\"username\",\"value\":\"John Doe\"}", cookieJO.toString()); + assertThatJson(cookieJO) + .isEqualTo("{\"path\":\"/\",\"expires\":\"Thu, 18 Dec 2013 12:00:00 UTC\",\"name\":\"username\",\"value\":\"John Doe\"}"); } @Test diff --git a/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java index 1aa0427c7e..ad84c22cca 100644 --- a/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java @@ -1,10 +1,12 @@ package com.baeldung.jsonjava; -import static org.junit.Assert.assertEquals; import org.json.HTTP; import org.json.JSONObject; import org.junit.Test; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; + public class HTTPIntegrationTest { @Test public void givenJSONObject_thenConvertToHTTPHeader() { @@ -12,14 +14,16 @@ public class HTTPIntegrationTest { jo.put("Method", "POST"); jo.put("Request-URI", "http://www.example.com/"); jo.put("HTTP-Version", "HTTP/1.1"); - - assertEquals("POST \"http://www.example.com/\" HTTP/1.1"+HTTP.CRLF+HTTP.CRLF, HTTP.toString(jo)); + + assertThat(HTTP.toString(jo)) + .isEqualTo("POST \"http://www.example.com/\" HTTP/1.1" + HTTP.CRLF + HTTP.CRLF); } @Test public void givenHTTPHeader_thenConvertToJSONObject() { JSONObject obj = HTTP.toJSONObject("POST \"http://www.example.com/\" HTTP/1.1"); - - assertEquals("{\"Request-URI\":\"http://www.example.com/\",\"Method\":\"POST\",\"HTTP-Version\":\"HTTP/1.1\"}", obj.toString()); + + assertThatJson(obj) + .isEqualTo("{\"Request-URI\":\"http://www.example.com/\",\"Method\":\"POST\",\"HTTP-Version\":\"HTTP/1.1\"}"); } } diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONArrayGetValueByKeyUnitTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONArrayGetValueByKeyUnitTest.java index 265c603acd..7ea22b94ba 100644 --- a/json/src/test/java/com/baeldung/jsonjava/JSONArrayGetValueByKeyUnitTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/JSONArrayGetValueByKeyUnitTest.java @@ -1,12 +1,10 @@ package com.baeldung.jsonjava; -import java.util.Arrays; -import java.util.List; - import org.junit.Test; -import static org.junit.Assert.assertThat; -import static org.hamcrest.CoreMatchers.equalTo; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; public class JSONArrayGetValueByKeyUnitTest { @@ -19,7 +17,8 @@ public class JSONArrayGetValueByKeyUnitTest { List actualValues = obj.getValuesByKeyInJSONArray(jsonStr, "name"); - assertThat(actualValues, equalTo(Arrays.asList("John", "Gary", "Selena"))); + assertThat(actualValues) + .containsExactlyInAnyOrder("John", "Gary", "Selena"); } @Test @@ -29,7 +28,8 @@ public class JSONArrayGetValueByKeyUnitTest { List actualValues = obj.getValuesByKeyInJSONArrayUsingJava8(jsonStr, "name"); - assertThat(actualValues, equalTo(Arrays.asList("John", "Gary", "Selena"))); + assertThat(actualValues) + .containsExactlyInAnyOrder("John", "Gary", "Selena"); } } diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java index c956232abe..eed7779e27 100644 --- a/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java @@ -1,36 +1,40 @@ package com.baeldung.jsonjava; -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - import org.json.JSONArray; import org.json.JSONObject; import org.junit.Test; +import java.util.ArrayList; +import java.util.List; + +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; + public class JSONArrayIntegrationTest { + @Test public void givenJSONJava_thenCreateNewJSONArrayFromScratch() { JSONArray ja = new JSONArray(); ja.put(Boolean.TRUE); ja.put("lorem ipsum"); - + // We can also put a JSONObject in JSONArray JSONObject jo = new JSONObject(); jo.put("name", "jon doe"); jo.put("age", "22"); jo.put("city", "chicago"); - + ja.put(jo); - - assertEquals("[true,\"lorem ipsum\",{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}]", ja.toString()); + + assertThatJson(ja) + .isEqualTo("[true,\"lorem ipsum\",{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}]"); } - + @Test public void givenJsonString_thenCreateNewJSONArray() { JSONArray ja = new JSONArray("[true, \"lorem ipsum\", 215]"); - assertEquals("[true,\"lorem ipsum\",215]", ja.toString()); + + assertThatJson(ja) + .isEqualTo("[true,\"lorem ipsum\",215]"); } @Test @@ -40,8 +44,10 @@ public class JSONArrayIntegrationTest { list.add("Texas"); list.add("Hawaii"); list.add("Alaska"); - + JSONArray ja = new JSONArray(list); - assertEquals("[\"California\",\"Texas\",\"Hawaii\",\"Alaska\"]", ja.toString()); + + assertThatJson(ja) + .isEqualTo("[\"California\",\"Texas\",\"Hawaii\",\"Alaska\"]"); } } \ No newline at end of file diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java index d8571bfcef..4a435a90d3 100644 --- a/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java @@ -1,23 +1,24 @@ package com.baeldung.jsonjava; -import static org.junit.Assert.assertEquals; - -import java.util.HashMap; -import java.util.Map; - import org.json.JSONObject; import org.junit.Test; +import java.util.HashMap; +import java.util.Map; + +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; + public class JSONObjectIntegrationTest { + @Test public void givenJSONJava_thenCreateNewJSONObject() { JSONObject jo = new JSONObject(); jo.put("name", "jon doe"); jo.put("age", "22"); jo.put("city", "chicago"); - - assertEquals("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}", jo.toString()); - + + assertThatJson(jo) + .isEqualTo("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}"); } @Test @@ -27,8 +28,9 @@ public class JSONObjectIntegrationTest { map.put("age", "22"); map.put("city", "chicago"); JSONObject jo = new JSONObject(map); - - assertEquals("{\"name\":\"jon doe\",\"city\":\"chicago\",\"age\":\"22\"}", jo.toString()); + + assertThatJson(jo) + .isEqualTo("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}"); } @Test @@ -36,7 +38,8 @@ public class JSONObjectIntegrationTest { JSONObject jo = new JSONObject( "{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}" ); - - assertEquals("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}", jo.toString()); + + assertThatJson(jo) + .isEqualTo("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}"); } } diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java index 4fe8f27231..3bd73ca4c2 100644 --- a/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java @@ -1,21 +1,22 @@ package com.baeldung.jsonjava; -import static org.junit.Assert.assertEquals; - import org.json.JSONTokener; import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; + public class JSONTokenerIntegrationTest { + @Test public void givenString_convertItToJSONTokens() { String str = "Sample String"; JSONTokener jt = new JSONTokener(str); - + char[] expectedTokens = str.toCharArray(); int index = 0; - - while(jt.more()) { - assertEquals(expectedTokens[index++], jt.next()); + + while (jt.more()) { + assertThat(jt.next()).isEqualTo(expectedTokens[index++]); } } } diff --git a/json/src/test/java/com/baeldung/jsonjava/ObjectToFromJSONIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/ObjectToFromJSONIntegrationTest.java index 99764c5dc9..d1f536d31a 100644 --- a/json/src/test/java/com/baeldung/jsonjava/ObjectToFromJSONIntegrationTest.java +++ b/json/src/test/java/com/baeldung/jsonjava/ObjectToFromJSONIntegrationTest.java @@ -1,19 +1,22 @@ package com.baeldung.jsonjava; -import static org.junit.Assert.assertEquals; - import org.json.JSONObject; import org.junit.Test; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; + public class ObjectToFromJSONIntegrationTest { + @Test public void givenDemoBean_thenCreateJSONObject() { DemoBean demo = new DemoBean(); demo.setId(1); demo.setName("lorem ipsum"); demo.setActive(true); - + JSONObject jo = new JSONObject(demo); - assertEquals("{\"name\":\"lorem ipsum\",\"active\":true,\"id\":1}", jo.toString()); + + assertThatJson(jo) + .isEqualTo("{\"name\":\"lorem ipsum\",\"active\":true,\"id\":1}"); } } From ca7f2cc36305f0b3fc5e0aa4f97ea1427461ad71 Mon Sep 17 00:00:00 2001 From: Seshu Kumar T Date: Fri, 7 Jan 2022 01:43:40 +0530 Subject: [PATCH 39/48] BAEL 5273 Sequence Input Stream (2nd commit) (#11649) * SequenceInputStream changes * assert statements corrected by keeping order as (expected, actual) Co-authored-by: Seshu Thanneeru --- .../java/com/baeldung/iostreams/InputSequenceUnitTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-java-modules/core-java-io-4/src/test/java/com/baeldung/iostreams/InputSequenceUnitTest.java b/core-java-modules/core-java-io-4/src/test/java/com/baeldung/iostreams/InputSequenceUnitTest.java index 5ee8f8542d..aa2bc24b41 100644 --- a/core-java-modules/core-java-io-4/src/test/java/com/baeldung/iostreams/InputSequenceUnitTest.java +++ b/core-java-modules/core-java-io-4/src/test/java/com/baeldung/iostreams/InputSequenceUnitTest.java @@ -31,7 +31,7 @@ public class InputSequenceUnitTest { InputSequenceHandler inputSequenceHandler = new InputSequenceHandler(file1, file2); String stringValue = inputSequenceHandler.readAsString(); inputSequenceHandler.close(); - assertEquals(stringValue, FILE1_AND_FILE2_CONTENT); + assertEquals(FILE1_AND_FILE2_CONTENT, stringValue); } @Test @@ -43,7 +43,7 @@ public class InputSequenceUnitTest { InputSequenceHandler inputSequenceHandler = new InputSequenceHandler(filesList); String stringValue = inputSequenceHandler.readAsString(); inputSequenceHandler.close(); - assertEquals(stringValue, ALL_FILES_CONTENT); + assertEquals(ALL_FILES_CONTENT, stringValue); } @Test From f27941b044cf06e557f9b666385c2b1dc54dcd89 Mon Sep 17 00:00:00 2001 From: Haroon Khan Date: Thu, 6 Jan 2022 22:57:49 +0000 Subject: [PATCH 40/48] [JAVA-9345] Split apache-poi module --- apache-poi-2/.gitignore | 1 + apache-poi-2/README.md | 6 +++-- .../poi/powerpoint/PowerPointHelper.java | 21 +++++++++++++++--- .../com/baeldung/poi/word/WordDocument.java | 6 ++++- .../src/main/resources/logo-leaf.png | Bin .../src/main/resources/poi-word-para1.txt | 0 .../src/main/resources/poi-word-para2.txt | 0 .../src/main/resources/poi-word-para3.txt | 0 .../powerpoint/PowerPointIntegrationTest.java | 8 +++---- .../poi/word/WordIntegrationTest.java | 16 ++++++------- apache-poi/README.md | 6 ++--- 11 files changed, 42 insertions(+), 22 deletions(-) rename {apache-poi => apache-poi-2}/src/main/java/com/baeldung/poi/powerpoint/PowerPointHelper.java (89%) rename {apache-poi => apache-poi-2}/src/main/java/com/baeldung/poi/word/WordDocument.java (93%) rename {apache-poi => apache-poi-2}/src/main/resources/logo-leaf.png (100%) rename {apache-poi => apache-poi-2}/src/main/resources/poi-word-para1.txt (100%) rename {apache-poi => apache-poi-2}/src/main/resources/poi-word-para2.txt (100%) rename {apache-poi => apache-poi-2}/src/main/resources/poi-word-para3.txt (100%) rename {apache-poi => apache-poi-2}/src/test/java/com/baeldung/poi/powerpoint/PowerPointIntegrationTest.java (99%) rename {apache-poi => apache-poi-2}/src/test/java/com/baeldung/poi/word/WordIntegrationTest.java (100%) diff --git a/apache-poi-2/.gitignore b/apache-poi-2/.gitignore index 9552c1e63d..d5c7c78cb1 100644 --- a/apache-poi-2/.gitignore +++ b/apache-poi-2/.gitignore @@ -1,3 +1,4 @@ *.docx temp.xls temp.xlsx +number_test.xlsx diff --git a/apache-poi-2/README.md b/apache-poi-2/README.md index d810c40815..2fd0135b11 100644 --- a/apache-poi-2/README.md +++ b/apache-poi-2/README.md @@ -1,10 +1,12 @@ ## Apache POI -This module contains articles about Apache POI +This module contains articles about Apache POI. ### Relevant Articles: - [Adding a Column to an Excel Sheet Using Apache POI](https://www.baeldung.com/java-excel-add-column) - [Add an Image to a Cell in an Excel File With Java](https://www.baeldung.com/java-add-image-excel) - [Numeric Format Using POI](https://www.baeldung.com/apache-poi-numeric-format) -- More articles: [[<-- prev]](/apache-poi) +- [Microsoft Word Processing in Java with Apache POI](https://www.baeldung.com/java-microsoft-word-with-apache-poi) +- [Creating a MS PowerPoint Presentation in Java](https://www.baeldung.com/apache-poi-slideshow) +- More articles: [[<-- prev]](../apache-poi) diff --git a/apache-poi/src/main/java/com/baeldung/poi/powerpoint/PowerPointHelper.java b/apache-poi-2/src/main/java/com/baeldung/poi/powerpoint/PowerPointHelper.java similarity index 89% rename from apache-poi/src/main/java/com/baeldung/poi/powerpoint/PowerPointHelper.java rename to apache-poi-2/src/main/java/com/baeldung/poi/powerpoint/PowerPointHelper.java index e2af4f8808..5b40301de5 100644 --- a/apache-poi/src/main/java/com/baeldung/poi/powerpoint/PowerPointHelper.java +++ b/apache-poi-2/src/main/java/com/baeldung/poi/powerpoint/PowerPointHelper.java @@ -5,7 +5,22 @@ import org.apache.poi.sl.usermodel.PictureData; import org.apache.poi.sl.usermodel.TableCell; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.util.IOUtils; -import org.apache.poi.xslf.usermodel.*; +import org.apache.poi.xslf.usermodel.SlideLayout; +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xslf.usermodel.XSLFAutoShape; +import org.apache.poi.xslf.usermodel.XSLFHyperlink; +import org.apache.poi.xslf.usermodel.XSLFPictureData; +import org.apache.poi.xslf.usermodel.XSLFPictureShape; +import org.apache.poi.xslf.usermodel.XSLFShape; +import org.apache.poi.xslf.usermodel.XSLFSlide; +import org.apache.poi.xslf.usermodel.XSLFSlideLayout; +import org.apache.poi.xslf.usermodel.XSLFSlideMaster; +import org.apache.poi.xslf.usermodel.XSLFTable; +import org.apache.poi.xslf.usermodel.XSLFTableCell; +import org.apache.poi.xslf.usermodel.XSLFTableRow; +import org.apache.poi.xslf.usermodel.XSLFTextParagraph; +import org.apache.poi.xslf.usermodel.XSLFTextRun; +import org.apache.poi.xslf.usermodel.XSLFTextShape; import java.awt.*; import java.io.FileInputStream; @@ -155,8 +170,8 @@ public class PowerPointHelper { /** * Retrieve the placeholder inside a slide - * - * @param slide + * + * @param slide * The slide * @return List of placeholder inside a slide */ diff --git a/apache-poi/src/main/java/com/baeldung/poi/word/WordDocument.java b/apache-poi-2/src/main/java/com/baeldung/poi/word/WordDocument.java similarity index 93% rename from apache-poi/src/main/java/com/baeldung/poi/word/WordDocument.java rename to apache-poi-2/src/main/java/com/baeldung/poi/word/WordDocument.java index 599da86eaa..ee754beeb9 100644 --- a/apache-poi/src/main/java/com/baeldung/poi/word/WordDocument.java +++ b/apache-poi-2/src/main/java/com/baeldung/poi/word/WordDocument.java @@ -1,7 +1,11 @@ package com.baeldung.poi.word; import org.apache.poi.util.Units; -import org.apache.poi.xwpf.usermodel.*; +import org.apache.poi.xwpf.usermodel.ParagraphAlignment; +import org.apache.poi.xwpf.usermodel.UnderlinePatterns; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.poi.xwpf.usermodel.XWPFRun; import java.io.FileOutputStream; import java.io.IOException; diff --git a/apache-poi/src/main/resources/logo-leaf.png b/apache-poi-2/src/main/resources/logo-leaf.png similarity index 100% rename from apache-poi/src/main/resources/logo-leaf.png rename to apache-poi-2/src/main/resources/logo-leaf.png diff --git a/apache-poi/src/main/resources/poi-word-para1.txt b/apache-poi-2/src/main/resources/poi-word-para1.txt similarity index 100% rename from apache-poi/src/main/resources/poi-word-para1.txt rename to apache-poi-2/src/main/resources/poi-word-para1.txt diff --git a/apache-poi/src/main/resources/poi-word-para2.txt b/apache-poi-2/src/main/resources/poi-word-para2.txt similarity index 100% rename from apache-poi/src/main/resources/poi-word-para2.txt rename to apache-poi-2/src/main/resources/poi-word-para2.txt diff --git a/apache-poi/src/main/resources/poi-word-para3.txt b/apache-poi-2/src/main/resources/poi-word-para3.txt similarity index 100% rename from apache-poi/src/main/resources/poi-word-para3.txt rename to apache-poi-2/src/main/resources/poi-word-para3.txt diff --git a/apache-poi/src/test/java/com/baeldung/poi/powerpoint/PowerPointIntegrationTest.java b/apache-poi-2/src/test/java/com/baeldung/poi/powerpoint/PowerPointIntegrationTest.java similarity index 99% rename from apache-poi/src/test/java/com/baeldung/poi/powerpoint/PowerPointIntegrationTest.java rename to apache-poi-2/src/test/java/com/baeldung/poi/powerpoint/PowerPointIntegrationTest.java index 7253238e80..aff0ab3821 100644 --- a/apache-poi/src/test/java/com/baeldung/poi/powerpoint/PowerPointIntegrationTest.java +++ b/apache-poi-2/src/test/java/com/baeldung/poi/powerpoint/PowerPointIntegrationTest.java @@ -1,8 +1,5 @@ package com.baeldung.poi.powerpoint; -import java.io.File; -import java.util.List; - import org.apache.poi.xslf.usermodel.XMLSlideShow; import org.apache.poi.xslf.usermodel.XSLFShape; import org.apache.poi.xslf.usermodel.XSLFSlide; @@ -13,12 +10,15 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import java.io.File; +import java.util.List; + public class PowerPointIntegrationTest { private PowerPointHelper pph; private String fileLocation; private static final String FILE_NAME = "presentation.pptx"; - + @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); diff --git a/apache-poi/src/test/java/com/baeldung/poi/word/WordIntegrationTest.java b/apache-poi-2/src/test/java/com/baeldung/poi/word/WordIntegrationTest.java similarity index 100% rename from apache-poi/src/test/java/com/baeldung/poi/word/WordIntegrationTest.java rename to apache-poi-2/src/test/java/com/baeldung/poi/word/WordIntegrationTest.java index 98b5c5b520..fe83aa4a4d 100644 --- a/apache-poi/src/test/java/com/baeldung/poi/word/WordIntegrationTest.java +++ b/apache-poi-2/src/test/java/com/baeldung/poi/word/WordIntegrationTest.java @@ -1,19 +1,19 @@ package com.baeldung.poi.word; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; - import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.junit.BeforeClass; import org.junit.Test; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + public class WordIntegrationTest { static WordDocument wordDocument; diff --git a/apache-poi/README.md b/apache-poi/README.md index 3dc58fe1b4..34e7631087 100644 --- a/apache-poi/README.md +++ b/apache-poi/README.md @@ -1,12 +1,10 @@ ## Apache POI -This module contains articles about Apache POI +This module contains articles about Apache POI. ### Relevant Articles: -- [Microsoft Word Processing in Java with Apache POI](https://www.baeldung.com/java-microsoft-word-with-apache-poi) - [Working with Microsoft Excel in Java](https://www.baeldung.com/java-microsoft-excel) -- [Creating a MS PowerPoint Presentation in Java](https://www.baeldung.com/apache-poi-slideshow) - [Merge Cells in Excel Using Apache POI](https://www.baeldung.com/java-apache-poi-merge-cells) - [Get String Value of Excel Cell with Apache POI](https://www.baeldung.com/java-apache-poi-cell-string-value) - [Read Excel Cell Value Rather Than Formula With Apache POI](https://www.baeldung.com/apache-poi-read-cell-value-formula) @@ -16,4 +14,4 @@ This module contains articles about Apache POI - [Set Background Color of a Cell with Apache POI](https://www.baeldung.com/apache-poi-background-color) - [Add Borders to Excel Cells With Apache POI](https://www.baeldung.com/apache-poi-add-borders) - [Reading Values From Excel in Java](https://www.baeldung.com/java-read-dates-excel) -- More articles: [[next -->]](/apache-poi-2) +- More articles: [[next -->]](../apache-poi-2) From 0ff218a8e18942944f33b8c10e52887b6247f1ee Mon Sep 17 00:00:00 2001 From: Haroon Khan Date: Fri, 7 Jan 2022 22:53:16 +0000 Subject: [PATCH 41/48] [JAVA-9344] Split spring-core module --- spring-core-5/README.md | 5 +++-- .../src/main/java/com/baeldung/lazy/AppConfig.java | 0 .../src/main/java/com/baeldung/lazy/City.java | 0 .../src/main/java/com/baeldung/lazy/Country.java | 0 .../src/main/java/com/baeldung/lazy/Region.java | 0 .../test/java/com/baeldung/lazy}/LazyAnnotationUnitTest.java | 5 +---- spring-core/README.md | 5 ++--- 7 files changed, 6 insertions(+), 9 deletions(-) rename {spring-core => spring-core-5}/src/main/java/com/baeldung/lazy/AppConfig.java (100%) rename {spring-core => spring-core-5}/src/main/java/com/baeldung/lazy/City.java (100%) rename {spring-core => spring-core-5}/src/main/java/com/baeldung/lazy/Country.java (100%) rename {spring-core => spring-core-5}/src/main/java/com/baeldung/lazy/Region.java (100%) rename {spring-core/src/test/java/com/baeldung/Lazy => spring-core-5/src/test/java/com/baeldung/lazy}/LazyAnnotationUnitTest.java (89%) diff --git a/spring-core-5/README.md b/spring-core-5/README.md index cf90d9ffc3..036fea9357 100644 --- a/spring-core-5/README.md +++ b/spring-core-5/README.md @@ -1,6 +1,6 @@ ## Spring Core -This module contains articles about core Spring functionality +This module contains articles about core Spring functionality. ## Relevant Articles: @@ -8,4 +8,5 @@ This module contains articles about core Spring functionality - [Solving Spring’s “not eligible for auto-proxying” Warning](https://www.baeldung.com/spring-not-eligible-for-auto-proxying) - [Spring Bean Names](https://www.baeldung.com/spring-bean-names) - [AliasFor Annotation in Spring](https://www.baeldung.com/spring-aliasfor-annotation) -- More articles: [[<-- prev]](/spring-core-4) +- [A Quick Guide to the Spring @Lazy Annotation](https://www.baeldung.com/spring-lazy-annotation) +- More articles: [[<-- prev]](../spring-core-4) diff --git a/spring-core/src/main/java/com/baeldung/lazy/AppConfig.java b/spring-core-5/src/main/java/com/baeldung/lazy/AppConfig.java similarity index 100% rename from spring-core/src/main/java/com/baeldung/lazy/AppConfig.java rename to spring-core-5/src/main/java/com/baeldung/lazy/AppConfig.java diff --git a/spring-core/src/main/java/com/baeldung/lazy/City.java b/spring-core-5/src/main/java/com/baeldung/lazy/City.java similarity index 100% rename from spring-core/src/main/java/com/baeldung/lazy/City.java rename to spring-core-5/src/main/java/com/baeldung/lazy/City.java diff --git a/spring-core/src/main/java/com/baeldung/lazy/Country.java b/spring-core-5/src/main/java/com/baeldung/lazy/Country.java similarity index 100% rename from spring-core/src/main/java/com/baeldung/lazy/Country.java rename to spring-core-5/src/main/java/com/baeldung/lazy/Country.java diff --git a/spring-core/src/main/java/com/baeldung/lazy/Region.java b/spring-core-5/src/main/java/com/baeldung/lazy/Region.java similarity index 100% rename from spring-core/src/main/java/com/baeldung/lazy/Region.java rename to spring-core-5/src/main/java/com/baeldung/lazy/Region.java diff --git a/spring-core/src/test/java/com/baeldung/Lazy/LazyAnnotationUnitTest.java b/spring-core-5/src/test/java/com/baeldung/lazy/LazyAnnotationUnitTest.java similarity index 89% rename from spring-core/src/test/java/com/baeldung/Lazy/LazyAnnotationUnitTest.java rename to spring-core-5/src/test/java/com/baeldung/lazy/LazyAnnotationUnitTest.java index 187d573557..d0dfe55b84 100644 --- a/spring-core/src/test/java/com/baeldung/Lazy/LazyAnnotationUnitTest.java +++ b/spring-core-5/src/test/java/com/baeldung/lazy/LazyAnnotationUnitTest.java @@ -1,8 +1,5 @@ -package com.baeldung.Lazy; +package com.baeldung.lazy; -import com.baeldung.lazy.AppConfig; -import com.baeldung.lazy.Country; -import com.baeldung.lazy.Region; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; diff --git a/spring-core/README.md b/spring-core/README.md index 3f0fe4b4e4..ffcbf4757b 100644 --- a/spring-core/README.md +++ b/spring-core/README.md @@ -1,16 +1,15 @@ ## Spring Core -This module contains articles about core Spring functionality +This module contains articles about core Spring functionality. ### Relevant Articles: - [Introduction to Spring’s StreamUtils](https://www.baeldung.com/spring-stream-utils) -- [A Quick Guide to the Spring @Lazy Annotation](https://www.baeldung.com/spring-lazy-annotation) - [BeanNameAware and BeanFactoryAware Interfaces in Spring](https://www.baeldung.com/spring-bean-name-factory-aware) - [Access a File from the Classpath in a Spring Application](https://www.baeldung.com/spring-classpath-file-access) - [Spring Application Context Events](https://www.baeldung.com/spring-context-events) - [What is a Spring Bean?](https://www.baeldung.com/spring-bean) - [Spring PostConstruct and PreDestroy Annotations](https://www.baeldung.com/spring-postconstruct-predestroy) - [Intro to the Spring ClassPathXmlApplicationContext](http://www.baeldung.com/spring-classpathxmlapplicationcontext) -- More articles: [[next -->]](/spring-core-2) +- More articles: [[next -->]](../spring-core-2) From b6cff5acd450f4fa186308a90ca1b0714e3585e6 Mon Sep 17 00:00:00 2001 From: Azhwani <13301425+azhwani@users.noreply.github.com> Date: Fri, 7 Jan 2022 23:57:13 +0100 Subject: [PATCH 42/48] init commit (#11643) --- .../StringEndsWithPattern.java | 45 +++++++++++++++++++ .../StringEndsWithPatternUnitTest.java | 42 +++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 core-java-modules/core-java-string-operations-4/src/main/java/com/baeldung/endswithpattern/StringEndsWithPattern.java create mode 100644 core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/endswithpattern/StringEndsWithPatternUnitTest.java diff --git a/core-java-modules/core-java-string-operations-4/src/main/java/com/baeldung/endswithpattern/StringEndsWithPattern.java b/core-java-modules/core-java-string-operations-4/src/main/java/com/baeldung/endswithpattern/StringEndsWithPattern.java new file mode 100644 index 0000000000..345ad6d91f --- /dev/null +++ b/core-java-modules/core-java-string-operations-4/src/main/java/com/baeldung/endswithpattern/StringEndsWithPattern.java @@ -0,0 +1,45 @@ +package com.baeldung.endswithpattern; + +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; + +public class StringEndsWithPattern { + + public static boolean usingStringEndsWithMethod(String text, String suffix) { + if (text == null || suffix == null) { + return false; + } + return text.endsWith(suffix); + } + + public static boolean usingStringMatchesMethod(String text, String suffix) { + if (text == null || suffix == null) { + return false; + } + String regex = ".*" + suffix + "$"; + return text.matches(regex); + } + + public static boolean usingStringRegionMatchesMethod(String text, String suffix) { + if (text == null || suffix == null) { + return false; + } + int toffset = text.length() - suffix.length(); + return text.regionMatches(toffset, suffix, 0, suffix.length()); + } + + public static boolean usingPatternClass(String text, String suffix) { + if (text == null || suffix == null) { + return false; + } + Pattern pattern = Pattern.compile(".*" + suffix + "$"); + return pattern.matcher(text) + .find(); + } + + public static boolean usingApacheCommonsLang(String text, String suffix) { + return StringUtils.endsWith(text, suffix); + } + +} diff --git a/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/endswithpattern/StringEndsWithPatternUnitTest.java b/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/endswithpattern/StringEndsWithPatternUnitTest.java new file mode 100644 index 0000000000..1680df17df --- /dev/null +++ b/core-java-modules/core-java-string-operations-4/src/test/java/com/baeldung/endswithpattern/StringEndsWithPatternUnitTest.java @@ -0,0 +1,42 @@ +package com.baeldung.endswithpattern; + +import static org.junit.Assert.assertTrue; + +import org.junit.jupiter.api.Test; + +public class StringEndsWithPatternUnitTest { + + private static final String TEXT = "Welcome to baeldung.com"; + private static final String SUFFIX = "com"; + + @Test + public void givenStringAndSuffix_whenUsingStringEndsWith_thenCheck() { + + assertTrue(StringEndsWithPattern.usingStringEndsWithMethod(TEXT, SUFFIX)); + } + + @Test + public void givenStringAndSuffix_whenUsingStringMatches_thenCheck() { + + assertTrue(StringEndsWithPattern.usingStringMatchesMethod(TEXT, SUFFIX)); + } + + @Test + public void givenStringAndSuffix_whenUsingStringRegionMatches_thenCheck() { + + assertTrue(StringEndsWithPattern.usingStringRegionMatchesMethod(TEXT, SUFFIX)); + } + + @Test + public void givenStringAndSuffix_whenUsingPatternClass_thenCheck() { + + assertTrue(StringEndsWithPattern.usingPatternClass(TEXT, SUFFIX)); + } + + @Test + public void givenStringAndSuffix_whenUsingApacheCommonsLang_thenCheck() { + + assertTrue(StringEndsWithPattern.usingApacheCommonsLang(TEXT, SUFFIX)); + } + +} From 143346a8505f25190b10a1548cf5b14c042af6f8 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 7 Jan 2022 18:17:59 -0700 Subject: [PATCH 43/48] BAEL-5267: Initial demo server and client commit (#11631) * Initial demo server and client commit * Add spring-cloud-load-balancer to JDK9+ profile * Remove Java 11 dependency from loadbalancer demo * Address comments from PR * Fix indentation --- spring-cloud/pom.xml | 3 +- .../spring-cloud-load-balancer/pom.xml | 38 +++ .../.gitignore | 33 ++ .../.mvn/wrapper/MavenWrapperDownloader.java | 117 +++++++ .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 50710 bytes .../.mvn/wrapper/maven-wrapper.properties | 2 + .../spring-cloud-loadbalancer-client/mvnw | 310 ++++++++++++++++++ .../spring-cloud-loadbalancer-client/mvnw.cmd | 182 ++++++++++ .../spring-cloud-loadbalancer-client/pom.xml | 55 ++++ .../client/ClientApplication.java | 76 +++++ .../src/main/resources/application.properties | 1 + .../.gitignore | 33 ++ .../.mvn/wrapper/MavenWrapperDownloader.java | 117 +++++++ .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 50710 bytes .../.mvn/wrapper/maven-wrapper.properties | 2 + .../spring-cloud-loadbalancer-server/mvnw | 310 ++++++++++++++++++ .../spring-cloud-loadbalancer-server/mvnw.cmd | 182 ++++++++++ .../spring-cloud-loadbalancer-server/pom.xml | 41 +++ .../server/ServerApplication.java | 25 ++ .../src/main/resources/application.properties | 1 + 20 files changed, 1527 insertions(+), 1 deletion(-) create mode 100644 spring-cloud/spring-cloud-load-balancer/pom.xml create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.gitignore create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/MavenWrapperDownloader.java create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/maven-wrapper.jar create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/maven-wrapper.properties create mode 100755 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/mvnw create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/mvnw.cmd create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/pom.xml create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/src/main/java/com/baeldung/spring/cloud/loadbalancer/client/ClientApplication.java create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/src/main/resources/application.properties create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.gitignore create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/MavenWrapperDownloader.java create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/maven-wrapper.jar create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/maven-wrapper.properties create mode 100755 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/mvnw create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/mvnw.cmd create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/pom.xml create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/src/main/java/com/baeldung/spring/cloud/loadbalancer/server/ServerApplication.java create mode 100644 spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/src/main/resources/application.properties diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml index 1b6b18cd6b..f6908f3680 100644 --- a/spring-cloud/pom.xml +++ b/spring-cloud/pom.xml @@ -17,6 +17,7 @@ + spring-cloud-load-balancer spring-cloud-config spring-cloud-eureka spring-cloud-hystrix @@ -88,4 +89,4 @@ 3.1.3 - \ No newline at end of file + diff --git a/spring-cloud/spring-cloud-load-balancer/pom.xml b/spring-cloud/spring-cloud-load-balancer/pom.xml new file mode 100644 index 0000000000..8472bff041 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + spring-cloud-loadbalancer + 1.0.0-SNAPSHOT + spring-cloud-loadbalancer + pom + Spring Cloud Load Balancer Demo Server and Client + + + com.baeldung.spring.cloud + spring-cloud + 1.0.0-SNAPSHOT + .. + + + + spring-cloud-loadbalancer-server + spring-cloud-loadbalancer-client + + + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + test + + + + + 2.6.1 + 2021.0.0 + + + diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.gitignore b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.gitignore new file mode 100644 index 0000000000..549e00a2a9 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000000..e76d1f3241 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present 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 + * + * https://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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/maven-wrapper.jar b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054 GIT binary patch literal 50710 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 EfAhh=DF6Tf literal 0 HcmV?d00001 diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/maven-wrapper.properties b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..a9f1ef87bb --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/mvnw b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/mvnw new file mode 100755 index 0000000000..a16b5431b4 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# https://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/mvnw.cmd b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/mvnw.cmd new file mode 100644 index 0000000000..c8d43372c9 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/pom.xml b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/pom.xml new file mode 100644 index 0000000000..4af43349ee --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + com.baeldung.spring.cloud + spring-cloud-loadbalancer + 1.0.0-SNAPSHOT + .. + + + com.baeldung.springcloud.loadbalancer + spring-cloud-loadbalancer-client + 0.0.1-SNAPSHOT + spring-cloud-loadbalancer-client + Spring Cloud Load Balancer Demo - Client + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/src/main/java/com/baeldung/spring/cloud/loadbalancer/client/ClientApplication.java b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/src/main/java/com/baeldung/spring/cloud/loadbalancer/client/ClientApplication.java new file mode 100644 index 0000000000..e9f1f7ad04 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/src/main/java/com/baeldung/spring/cloud/loadbalancer/client/ClientApplication.java @@ -0,0 +1,76 @@ +package com.baeldung.spring.cloud.loadbalancer.client; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.List; + +@SpringBootApplication +public class ClientApplication { + public static void main(String[] args) { + + ConfigurableApplicationContext ctx = new SpringApplicationBuilder(ClientApplication.class) + .web(WebApplicationType.NONE) + .run(args); + + WebClient loadBalancedClient = ctx.getBean(WebClient.Builder.class).build(); + + for(int i = 1; i <= 10; i++) { + String response = + loadBalancedClient.get().uri("http://example-service/hello") + .retrieve().toEntity(String.class) + .block().getBody(); + System.out.println(response); + } + } +} + +@Configuration +class DemoServerInstanceConfiguration { + @Bean + ServiceInstanceListSupplier serviceInstanceListSupplier() { + return new DemoInstanceSupplier("example-service"); + } +} + +@Configuration +@LoadBalancerClient(name = "example-service", configuration = DemoServerInstanceConfiguration.class) +class WebClientConfig { + @LoadBalanced + @Bean + WebClient.Builder webClientBuilder() { + return WebClient.builder(); + } +} + +class DemoInstanceSupplier implements ServiceInstanceListSupplier { + private final String serviceId; + + public DemoInstanceSupplier(String serviceId) { + this.serviceId = serviceId; + } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public Flux> get() { + return Flux.just(Arrays + .asList(new DefaultServiceInstance(serviceId + "1", serviceId, "localhost", 8080, false), + new DefaultServiceInstance(serviceId + "2", serviceId, "localhost", 8081, false))); + } +} diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/src/main/resources/application.properties b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/src/main/resources/application.properties new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-client/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.gitignore b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.gitignore new file mode 100644 index 0000000000..549e00a2a9 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000000..e76d1f3241 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present 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 + * + * https://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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/maven-wrapper.jar b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054 GIT binary patch literal 50710 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 EfAhh=DF6Tf literal 0 HcmV?d00001 diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/maven-wrapper.properties b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..a9f1ef87bb --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/mvnw b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/mvnw new file mode 100755 index 0000000000..a16b5431b4 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# https://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/mvnw.cmd b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/mvnw.cmd new file mode 100644 index 0000000000..c8d43372c9 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/pom.xml b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/pom.xml new file mode 100644 index 0000000000..e066127262 --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + com.baeldung.spring.cloud + spring-cloud-loadbalancer + 1.0.0-SNAPSHOT + .. + + + com.baeldung.spring.cloud.loadbalancer + spring-cloud-loadbalancer-server + 0.0.1-SNAPSHOT + spring-cloud-loadbalancer-server + Spring Cloud Load Balancer Demo - Server + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/src/main/java/com/baeldung/spring/cloud/loadbalancer/server/ServerApplication.java b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/src/main/java/com/baeldung/spring/cloud/loadbalancer/server/ServerApplication.java new file mode 100644 index 0000000000..17e7c1b50a --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/src/main/java/com/baeldung/spring/cloud/loadbalancer/server/ServerApplication.java @@ -0,0 +1,25 @@ +package com.baeldung.spring.cloud.loadbalancer.server; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class ServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ServerApplication.class, args); + } + + @Value("${server.instance.id}") + String instanceId; + + @GetMapping("/hello") + public String hello() + { + return String.format("Hello from instance %s", instanceId); + } +} diff --git a/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/src/main/resources/application.properties b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/src/main/resources/application.properties new file mode 100644 index 0000000000..4148ed304d --- /dev/null +++ b/spring-cloud/spring-cloud-load-balancer/spring-cloud-loadbalancer-server/src/main/resources/application.properties @@ -0,0 +1 @@ +server.instance.id=1 From 9e7329ce5e0d7318acaf91fc118a1bbf2b83f50e Mon Sep 17 00:00:00 2001 From: sharifi Date: Sat, 8 Jan 2022 12:48:48 +0330 Subject: [PATCH 44/48] bael-4197: indent with spaces instead of tab --- .../xmlapplicationcontext/XmlBeanApplication.java | 2 +- .../src/main/webapp/WEB-INF/application-context.xml | 8 ++++++++ .../src/test/resources/test-context.xml | 9 ++++----- 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 spring-boot-modules/spring-boot-testing/src/main/webapp/WEB-INF/application-context.xml diff --git a/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/XmlBeanApplication.java b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/XmlBeanApplication.java index 4e2af67ab0..ddf1efd396 100644 --- a/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/XmlBeanApplication.java +++ b/spring-boot-modules/spring-boot-testing/src/main/java/com/baeldung/xmlapplicationcontext/XmlBeanApplication.java @@ -9,7 +9,7 @@ import org.springframework.context.annotation.ImportResource; public class XmlBeanApplication { public static void main(String[] args) { - SpringApplication.run(XmlBeanApplication.class, args); + SpringApplication.run(XmlBeanApplication.class, args); } } diff --git a/spring-boot-modules/spring-boot-testing/src/main/webapp/WEB-INF/application-context.xml b/spring-boot-modules/spring-boot-testing/src/main/webapp/WEB-INF/application-context.xml new file mode 100644 index 0000000000..fc0e0fef25 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing/src/main/webapp/WEB-INF/application-context.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/spring-boot-modules/spring-boot-testing/src/test/resources/test-context.xml b/spring-boot-modules/spring-boot-testing/src/test/resources/test-context.xml index 664b528b06..fbd6da32ca 100644 --- a/spring-boot-modules/spring-boot-testing/src/test/resources/test-context.xml +++ b/spring-boot-modules/spring-boot-testing/src/test/resources/test-context.xml @@ -1,9 +1,8 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> - + From 45c495f3270cca2e273f0402dfc4d7bdebfc860d Mon Sep 17 00:00:00 2001 From: sharifi Date: Sat, 8 Jan 2022 12:49:30 +0330 Subject: [PATCH 45/48] bael-4197: add application-context.xml in resource directory --- .../src/main/resources/application-context.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spring-boot-modules/spring-boot-testing/src/main/resources/application-context.xml b/spring-boot-modules/spring-boot-testing/src/main/resources/application-context.xml index a436b39a2f..fc0e0fef25 100644 --- a/spring-boot-modules/spring-boot-testing/src/main/resources/application-context.xml +++ b/spring-boot-modules/spring-boot-testing/src/main/resources/application-context.xml @@ -1,9 +1,8 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> - + From ac0df69dbf1aa45edf73dfe0341ead583a8ca3c2 Mon Sep 17 00:00:00 2001 From: chaos2418 <> Date: Sat, 8 Jan 2022 16:51:06 +0530 Subject: [PATCH 46/48] JAVA-8284: split or move spring-mvc-basics-2 module --- spring-web-modules/spring-mvc-basics-2/README.md | 2 -- spring-web-modules/spring-mvc-basics-5/README.md | 2 ++ .../java/com/baeldung}/requestparam/RequestParamController.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename spring-web-modules/{spring-mvc-basics-2/src/main/java/com/baeldung/spring => spring-mvc-basics-5/src/main/java/com/baeldung}/requestparam/RequestParamController.java (98%) diff --git a/spring-web-modules/spring-mvc-basics-2/README.md b/spring-web-modules/spring-mvc-basics-2/README.md index ae8256fc4c..87ce9984a2 100644 --- a/spring-web-modules/spring-mvc-basics-2/README.md +++ b/spring-web-modules/spring-mvc-basics-2/README.md @@ -10,6 +10,4 @@ This module contains articles about Spring MVC - [Guide to Spring Email](https://www.baeldung.com/spring-email) - [Using ThymeLeaf and FreeMarker Emails Templates with Spring](https://www.baeldung.com/spring-email-templates) - [Request Method Not Supported (405) in Spring](https://www.baeldung.com/spring-request-method-not-supported-405) -- [Spring @RequestParam Annotation](https://www.baeldung.com/spring-request-param) -- [Spring @RequestParam vs @PathVariable Annotations](https://www.baeldung.com/spring-requestparam-vs-pathvariable) - More articles: [[<-- prev]](../spring-mvc-basics)[[more -->]](../spring-mvc-basics-3) diff --git a/spring-web-modules/spring-mvc-basics-5/README.md b/spring-web-modules/spring-mvc-basics-5/README.md index 83a335f4d3..880db7aacd 100644 --- a/spring-web-modules/spring-mvc-basics-5/README.md +++ b/spring-web-modules/spring-mvc-basics-5/README.md @@ -11,4 +11,6 @@ The "REST With Spring" Classes: https://bit.ly/restwithspring - [Using Spring @ResponseStatus to Set HTTP Status Code](https://www.baeldung.com/spring-response-status) - [Spring MVC and the @ModelAttribute Annotation](https://www.baeldung.com/spring-mvc-and-the-modelattribute-annotation) - [The HttpMediaTypeNotAcceptableException in Spring MVC](https://www.baeldung.com/spring-httpmediatypenotacceptable) +- [Spring @RequestParam Annotation](https://www.baeldung.com/spring-request-param) +- [Spring @RequestParam vs @PathVariable Annotations](https://www.baeldung.com/spring-requestparam-vs-pathvariable) - More articles: [[<-- prev]](../spring-mvc-basics-4) diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/requestparam/RequestParamController.java b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/requestparam/RequestParamController.java similarity index 98% rename from spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/requestparam/RequestParamController.java rename to spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/requestparam/RequestParamController.java index eb445f7801..a6b3a885e5 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/requestparam/RequestParamController.java +++ b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/requestparam/RequestParamController.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.requestparam; +package com.baeldung.requestparam; import java.util.List; import java.util.Map; From 75e9a2dfbb8b14cb362de4d54da39a877324f745 Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Sat, 8 Jan 2022 22:34:43 -0600 Subject: [PATCH 47/48] BAEL-5276: add link back to article (#11662) --- core-java-modules/core-java-string-operations-4/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java-modules/core-java-string-operations-4/README.md b/core-java-modules/core-java-string-operations-4/README.md index e7192b5beb..0e6c9e7e2c 100644 --- a/core-java-modules/core-java-string-operations-4/README.md +++ b/core-java-modules/core-java-string-operations-4/README.md @@ -5,4 +5,5 @@ - [Concatenating Null Strings in Java](https://www.baeldung.com/java-concat-null-string) - [Split a String Every n Characters in Java](https://www.baeldung.com/java-string-split-every-n-characters) - [String equals() Vs contentEquals() in Java](https://www.baeldung.com/java-string-equals-vs-contentequals) +- [Check if a String Ends with a Certain Pattern in Java](https://www.baeldung.com/java-string-ends-pattern) From 5521870d6498930287da8fc92dcc8bf88279bae6 Mon Sep 17 00:00:00 2001 From: Haroon Khan Date: Mon, 10 Jan 2022 08:57:45 +0000 Subject: [PATCH 48/48] [JAVA-9347] Split spring-rest-http module --- .../spring-rest-http-2/README.md | 4 ++- .../uribuilder/SpringUriBuilderUnitTest.java} | 26 ++++++++++++------- spring-web-modules/spring-rest-http/README.md | 4 +-- 3 files changed, 22 insertions(+), 12 deletions(-) rename spring-web-modules/{spring-rest-http/src/test/java/com/baeldung/uribuilder/SpringUriBuilderIntegrationTest.java => spring-rest-http-2/src/test/java/com/baeldung/uribuilder/SpringUriBuilderUnitTest.java} (74%) diff --git a/spring-web-modules/spring-rest-http-2/README.md b/spring-web-modules/spring-rest-http-2/README.md index 74e55c7c40..bb9175db8c 100644 --- a/spring-web-modules/spring-rest-http-2/README.md +++ b/spring-web-modules/spring-rest-http-2/README.md @@ -1,6 +1,6 @@ ## Spring REST HTTP 2 -This module contains articles about HTTP in REST APIs with Spring +This module contains articles about HTTP in REST APIs with Spring. ### The Course The "REST With Spring 2" Classes: http://bit.ly/restwithspring @@ -10,3 +10,5 @@ The "REST With Spring 2" Classes: http://bit.ly/restwithspring - [How to Turn Off Swagger-ui in Production](https://www.baeldung.com/swagger-ui-turn-off-in-production) - [Setting a Request Timeout for a Spring REST API](https://www.baeldung.com/spring-rest-timeout) - [Long Polling in Spring MVC](https://www.baeldung.com/spring-mvc-long-polling) +- [Guide to UriComponentsBuilder in Spring](https://www.baeldung.com/spring-uricomponentsbuilder) +- More articles: [[<-- prev]](../spring-rest-http) diff --git a/spring-web-modules/spring-rest-http/src/test/java/com/baeldung/uribuilder/SpringUriBuilderIntegrationTest.java b/spring-web-modules/spring-rest-http-2/src/test/java/com/baeldung/uribuilder/SpringUriBuilderUnitTest.java similarity index 74% rename from spring-web-modules/spring-rest-http/src/test/java/com/baeldung/uribuilder/SpringUriBuilderIntegrationTest.java rename to spring-web-modules/spring-rest-http-2/src/test/java/com/baeldung/uribuilder/SpringUriBuilderUnitTest.java index 0af5cb1e1a..b4f261cd6e 100644 --- a/spring-web-modules/spring-rest-http/src/test/java/com/baeldung/uribuilder/SpringUriBuilderIntegrationTest.java +++ b/spring-web-modules/spring-rest-http-2/src/test/java/com/baeldung/uribuilder/SpringUriBuilderUnitTest.java @@ -1,39 +1,47 @@ package com.baeldung.uribuilder; -import static org.junit.Assert.assertEquals; - -import java.util.Collections; - import org.junit.Test; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; -public class SpringUriBuilderIntegrationTest { +import java.util.Collections; + +import static org.junit.Assert.assertEquals; + +public class SpringUriBuilderUnitTest { @Test public void constructUri() { - UriComponents uriComponents = UriComponentsBuilder.newInstance().scheme("http").host("www.baeldung.com").path("/junit-5").build(); + UriComponents uriComponents = UriComponentsBuilder.newInstance() + .scheme("http").host("www.baeldung.com").path("/junit-5") + .build(); assertEquals("http://www.baeldung.com/junit-5", uriComponents.toUriString()); } @Test public void constructUriEncoded() { - UriComponents uriComponents = UriComponentsBuilder.newInstance().scheme("http").host("www.baeldung.com").path("/junit 5").build().encode(); + UriComponents uriComponents = UriComponentsBuilder.newInstance() + .scheme("http").host("www.baeldung.com").path("/junit 5") + .build().encode(); assertEquals("http://www.baeldung.com/junit%205", uriComponents.toUriString()); } @Test public void constructUriFromTemplate() { - UriComponents uriComponents = UriComponentsBuilder.newInstance().scheme("http").host("www.baeldung.com").path("/{article-name}").buildAndExpand("junit-5"); + UriComponents uriComponents = UriComponentsBuilder.newInstance() + .scheme("http").host("www.baeldung.com").path("/{article-name}") + .buildAndExpand("junit-5"); assertEquals("http://www.baeldung.com/junit-5", uriComponents.toUriString()); } @Test public void constructUriWithQueryParameter() { - UriComponents uriComponents = UriComponentsBuilder.newInstance().scheme("http").host("www.google.com").path("/").query("q={keyword}").buildAndExpand("baeldung"); + UriComponents uriComponents = UriComponentsBuilder.newInstance() + .scheme("http").host("www.google.com").path("/").query("q={keyword}") + .buildAndExpand("baeldung"); assertEquals("http://www.google.com/?q=baeldung", uriComponents.toUriString()); } diff --git a/spring-web-modules/spring-rest-http/README.md b/spring-web-modules/spring-rest-http/README.md index 2271858f0a..43355d27cd 100644 --- a/spring-web-modules/spring-rest-http/README.md +++ b/spring-web-modules/spring-rest-http/README.md @@ -1,13 +1,12 @@ ## Spring REST HTTP -This module contains articles about HTTP in REST APIs with Spring +This module contains articles about HTTP in REST APIs with Spring. ### The Course The "REST With Spring" Classes: http://bit.ly/restwithspring ### Relevant Articles: -- [Guide to UriComponentsBuilder in Spring](https://www.baeldung.com/spring-uricomponentsbuilder) - [How to Set a Header on a Response with Spring 5](https://www.baeldung.com/spring-response-header) - The tests contained for this article rely on the sample application within the [spring-resttemplate](/spring-resttemplate) module - [Returning Custom Status Codes from Spring Controllers](https://www.baeldung.com/spring-mvc-controller-custom-http-status-code) - [Spring RequestMapping](https://www.baeldung.com/spring-requestmapping) @@ -15,3 +14,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Using JSON Patch in Spring REST APIs](https://www.baeldung.com/spring-rest-json-patch) - [OpenAPI JSON Objects as Query Parameters](https://www.baeldung.com/openapi-json-query-parameters) - [Dates in OpenAPI Files](https://www.baeldung.com/openapi-dates) +- More articles: [[next -->]](../spring-rest-http-2)