Merge branch 'eugenp:master' into master

This commit is contained in:
sIvanovKonstantyn 2024-04-14 18:18:06 +02:00 committed by GitHub
commit b1ae6680d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
86 changed files with 1494 additions and 262 deletions

View File

@ -1,23 +1,22 @@
package com.baeldung.algorithms.mergeintervals; package com.baeldung.algorithms.mergeintervals;
import static java.lang.Integer.max;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
public class MergeOverlappingIntervals { public class MergeOverlappingIntervals {
public List<Interval> doMerge(List<Interval> intervals) { public List<Interval> doMerge(List<Interval> intervals) {
// Sort the intervals based on start time intervals.sort(Comparator.comparingInt(interval -> interval.start));
intervals.sort((one, two) -> one.start - two.start);
// Create somewhere to put the merged list, start it off with the earliest starting interval
ArrayList<Interval> merged = new ArrayList<>(); ArrayList<Interval> merged = new ArrayList<>();
merged.add(intervals.get(0)); merged.add(intervals.get(0));
// Loop over each interval and merge if start time is before the end time of the
// previous interval
intervals.forEach(interval -> { intervals.forEach(interval -> {
if (merged.get(merged.size() - 1).end > interval.start) { Interval lastMerged = merged.get(merged.size() - 1);
merged.get(merged.size() - 1).setEnd(interval.end); if (interval.start <= lastMerged.end){
lastMerged.setEnd(max(interval.end, lastMerged.end));
} else { } else {
merged.add(interval); merged.add(interval);
} }
@ -25,5 +24,4 @@ public class MergeOverlappingIntervals {
return merged; return merged;
} }
}
}

View File

@ -1,30 +1,38 @@
package com.baeldung.algorithms.mergeintervals; package com.baeldung.algorithms.mergeintervals;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
class MergeIntervalsUnitTest { class MergeIntervalsUnitTest {
private ArrayList<Interval> intervals = new ArrayList<>(Arrays.asList( private List<Interval> intervals = new ArrayList<>(Arrays.asList(
new Interval(2, 5), // @formatter:off
new Interval(13, 20), new Interval(3, 5),
new Interval(11, 15), new Interval(13, 20),
new Interval(1, 3) new Interval(11, 15),
new Interval(15, 16),
new Interval(1, 3),
new Interval(4, 5),
new Interval(16, 17)
// @formatter:on
)); ));
private ArrayList<Interval> intervalsMerged = new ArrayList<>(Arrays.asList( private List<Interval> intervalsMerged = new ArrayList<>(Arrays.asList(
new Interval(1, 5), // @formatter:off
new Interval(11, 20) new Interval(1, 5),
new Interval(11, 20)
// @formatter:on
)); ));
@Test @Test
void givenIntervals_whenMerging_thenReturnMergedIntervals() { void givenIntervals_whenMerging_thenReturnMergedIntervals() {
MergeOverlappingIntervals merger = new MergeOverlappingIntervals(); MergeOverlappingIntervals merger = new MergeOverlappingIntervals();
ArrayList<Interval> result = (ArrayList<Interval>) merger.doMerge(intervals); List<Interval> result = merger.doMerge(intervals);
assertArrayEquals(intervalsMerged.toArray(), result.toArray()); assertEquals(intervalsMerged, result);
} }
} }

View File

@ -3,3 +3,4 @@
- [Find the Equilibrium Indexes of an Array in Java](https://www.baeldung.com/java-equilibrium-index-array) - [Find the Equilibrium Indexes of an Array in Java](https://www.baeldung.com/java-equilibrium-index-array)
- [Moves Zeros to the End of an Array in Java](https://www.baeldung.com/java-array-sort-move-zeros-end) - [Moves Zeros to the End of an Array in Java](https://www.baeldung.com/java-array-sort-move-zeros-end)
- [Finding the Majority Element of an Array in Java](https://www.baeldung.com/java-array-find-majority-element) - [Finding the Majority Element of an Array in Java](https://www.baeldung.com/java-array-find-majority-element)
- [Set Matrix Elements to Zero in Java](https://www.baeldung.com/java-set-matrix-elements-to-zero)

View File

@ -0,0 +1,147 @@
package com.baeldung.matrixtozero;
import java.util.*;
public class SetMatrixToZero{
static void setZeroesByNaiveApproach(int[][] matrix) {
int row = matrix.length;
int col = matrix[0].length;
int [][] result = new int[row][col];
for(int i = 0; i<row; i++){
for(int j = 0; j < col; j++){
result[i][j] = matrix[i][j];
}
}
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
if(matrix[i][j] == 0){
for(int k = 0; k < col; k++){
result[i][k] = 0;
}
for(int k = 0; k < row; k++){
result[k][j] = 0;
}
}
}
}
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
matrix[i][j] = result[i][j];
}
}
}
static void setZeroesByTimeOptimizedApproach(int[][] matrix) {
int rows = matrix.length;
int cols = matrix[0].length;
Set<Integer> rowSet = new HashSet<>();
Set<Integer> colSet = new HashSet<>();
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
if (matrix[i][j] == 0){
rowSet.add(i);
colSet.add(j);
}
}
}
for(int row: rowSet){
for(int j = 0; j < cols; j++){
matrix[row][j] = 0;
}
}
for(int col: colSet) {
for(int i = 0; i < rows; i++){
matrix[i][col] = 0;
}
}
}
static boolean hasZeroInFirstRow(int[][] matrix, int cols) {
for (int j = 0; j < cols; j++) {
if (matrix[0][j] == 0) {
return true;
}
}
return false;
}
static boolean hasZeroInFirstCol(int[][] matrix, int rows) {
for (int i = 0; i < rows; i++) {
if (matrix[i][0] == 0) {
return true;
}
}
return false;
}
static void markZeroesInMatrix(int[][] matrix, int rows, int cols) {
for (int i = 1; i < rows; i++) {
for (int j = 1; j < cols; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}
}
static void setZeroesInRows(int[][] matrix, int rows, int cols) {
for (int i = 1; i < rows; i++) {
if (matrix[i][0] == 0) {
for (int j = 1; j < cols; j++) {
matrix[i][j] = 0;
}
}
}
}
static void setZeroesInCols(int[][] matrix, int rows, int cols) {
for (int j = 1; j < cols; j++) {
if (matrix[0][j] == 0) {
for (int i = 1; i < rows; i++) {
matrix[i][j] = 0;
}
}
}
}
static void setZeroesInFirstRow(int[][] matrix, int cols) {
for (int j = 0; j < cols; j++) {
matrix[0][j] = 0;
}
}
static void setZeroesInFirstCol(int[][] matrix, int rows) {
for (int i = 0; i < rows; i++) {
matrix[i][0] = 0;
}
}
static void setZeroesByOptimalApproach(int[][] matrix) {
int rows = matrix.length;
int cols = matrix[0].length;
boolean firstRowZero = hasZeroInFirstRow(matrix, cols);
boolean firstColZero = hasZeroInFirstCol(matrix, rows);
markZeroesInMatrix(matrix, rows, cols);
setZeroesInRows(matrix, rows, cols);
setZeroesInCols(matrix, rows, cols);
if (firstRowZero) {
setZeroesInFirstRow(matrix, cols);
}
if (firstColZero) {
setZeroesInFirstCol(matrix, rows);
}
}
}

View File

@ -0,0 +1,54 @@
package com.baeldung.matrixtozero;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import org.junit.jupiter.api.Test;
public class SetMatrixToZeroUnitTest{
@Test
void givenMatrix_whenUsingSetZeroesByNaiveApproach_thenSetZeroes() {
int[][] matrix = {
{1, 2, 3},
{4, 0, 6},
{7, 8, 9}
};
int[][] expected = {
{1, 0, 3},
{0, 0, 0},
{7, 0, 9}
};
SetMatrixToZero.setZeroesByNaiveApproach(matrix);
assertArrayEquals(expected, matrix);
}
@Test
void givenMatrix_whenUsingSetZeroesByTimeOptimizedApproach_thenSetZeroes() {
int[][] matrix = {
{1, 2, 3},
{4, 0, 6},
{7, 8, 9}
};
int[][] expected = {
{1, 0, 3},
{0, 0, 0},
{7, 0, 9}
};
SetMatrixToZero.setZeroesByTimeOptimizedApproach(matrix);
assertArrayEquals(expected, matrix);
}
@Test
void givenMatrix_whenUsingSetZeroesByOptimalApproach_thenSetZeroes() {
int[][] matrix = {
{1, 2, 3},
{4, 0, 6},
{7, 8, 9}
};
int[][] expected = {
{1, 0, 3},
{0, 0, 0},
{7, 0, 9}
};
SetMatrixToZero.setZeroesByOptimalApproach(matrix);
assertArrayEquals(expected, matrix);
}
}

View File

@ -0,0 +1,44 @@
package com.baeldung.printnullvalues;
import java.util.Objects;
import java.util.Optional;
public class Employee {
private String name;
private int age;
private String department;
public Employee(String name, int age, String department) {
this.name = name;
this.age = age;
this.department = department;
}
public String toStringUsingNullCheck() {
return "Name: " + (name != null ? name : "Unknown") +
", Age: " + age +
", Department: " + (department != null ? department : "Unknown");
}
public String toStringUsingOptional() {
return "Name: " + Optional.ofNullable(name).orElse("Unknown") +
", Age: " + age +
", Department: " + Optional.ofNullable(department).orElse("Unknown");
}
private String getDefaultIfNull(String value, String defaultValue) {
return value != null ? value : defaultValue;
}
public String toStringUsingCustomHelper() {
return "Name: " + getDefaultIfNull(name, "Unknown") +
", Age: " + age +
", Department: " + getDefaultIfNull(department, "Unknown");
}
public String toStringUsingObjects() {
return "Name: " + Objects.toString(name, "Unknown") +
", Age: " + age +
", Department: " + Objects.toString(department, "Unknown");
}
}

View File

@ -0,0 +1,30 @@
package com.baeldung.printnullvalues;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class PrintingNullValuesUnitTest {
Employee employee = new Employee(null, 30, null);
String expected = "Name: Unknown, Age: 30, Department: Unknown";
@Test
public void givenNullValues_whenToStringUsingNullCheck_thenCorrectStringReturned() {
assertEquals(expected, employee.toStringUsingNullCheck());
}
@Test
public void givenNullValues_whenToStringUsingOptional_thenCorrectStringReturned() {
assertEquals(expected, employee.toStringUsingOptional());
}
@Test
public void givenNullValues_whenToStringUsingCustomHelper_thenCorrectStringReturned() {
assertEquals(expected, employee.toStringUsingCustomHelper());
}
@Test
public void givenNullValues_whenToStringUsingObjects_thenCorrectStringReturned() {
assertEquals(expected, employee.toStringUsingObjects());
}
}

View File

@ -0,0 +1,2 @@
## Relevant Articles:
- [[<-- Prev]](/core-java-modules/core-java-networking-4)

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>core-java-networking-5</artifactId>
<packaging>jar</packaging>
<name>core-java-networking-5</name>
<parent>
<groupId>com.baeldung.core-java-modules</groupId>
<artifactId>core-java-modules</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>${commons-validator.version}</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>${javax.ws.rs-api.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>${jersey-common.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-web.version}</version>
</dependency>
</dependencies>
<properties>
<commons-validator.version>1.7</commons-validator.version>
<jsoup.version>1.17.2</jsoup.version>
<httpclient.version>4.5.2</httpclient.version>
<javax.ws.rs-api.version>2.1.1</javax.ws.rs-api.version>
<jersey-common.version>2.22.2</jersey-common.version>
<spring-web.version>6.0.6</spring-web.version>
</properties>
</project>

View File

@ -0,0 +1,57 @@
package com.baeldung.redirectedurl;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.Test;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import static org.junit.Assert.assertEquals;
public class RedirectedUrlUnitTest {
String canonicalUrl = "http://www.baeldung.com/";
String expectedRedirectedUrl = "https://www.baeldung.com/";
@Test
public void givenOriginalUrl_whenFindRedirectUrlUsingHttpURLConnection_thenCorrectRedirectedUrlReturned() throws IOException {
URL url = new URL(canonicalUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(true);
int status = connection.getResponseCode();
String redirectedUrl = null;
if (status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_MOVED_TEMP) {
redirectedUrl = connection.getHeaderField("Location");
}
connection.disconnect();
assertEquals(expectedRedirectedUrl, redirectedUrl);
}
@Test
public void givenOriginalUrl_whenFindRedirectUrlUsingHttpClient_thenCorrectRedirectedUrlReturned() throws IOException {
RequestConfig config = RequestConfig.custom()
.setRedirectsEnabled(false)
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(config)
.build()) {
HttpGet httpGet = new HttpGet(canonicalUrl);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
int statusCode = response.getStatusLine().getStatusCode();
String redirectedUrl = null;
if (statusCode == HttpURLConnection.HTTP_MOVED_PERM || statusCode == HttpURLConnection.HTTP_MOVED_TEMP) {
org.apache.http.Header[] headers = response.getHeaders("Location");
if (headers.length > 0) {
redirectedUrl = headers[0].getValue();
}
}
assertEquals(expectedRedirectedUrl, redirectedUrl);
}
}
}
}

View File

@ -165,6 +165,7 @@
<module>core-java-networking</module> <module>core-java-networking</module>
<module>core-java-networking-2</module> <module>core-java-networking-2</module>
<module>core-java-networking-4</module> <module>core-java-networking-4</module>
<module>core-java-networking-5</module>
<module>core-java-nio</module> <module>core-java-nio</module>
<module>core-java-nio-2</module> <module>core-java-nio-2</module>
<module>core-java-numbers</module> <module>core-java-numbers</module>

View File

@ -49,11 +49,23 @@
<properties> <properties>
<jool.version>0.9.12</jool.version> <jool.version>0.9.12</jool.version>
<parallel-collectors.version>2.6.0</parallel-collectors.version> <parallel-collectors.version>3.0.0</parallel-collectors.version>
<vavr.version>0.9.0</vavr.version> <vavr.version>0.9.0</vavr.version>
<eclipse-collections.version>8.2.0</eclipse-collections.version> <eclipse-collections.version>8.2.0</eclipse-collections.version>
<streamex.version>0.8.1</streamex.version> <streamex.version>0.8.1</streamex.version>
<protonpack.version>1.15</protonpack.version> <protonpack.version>1.15</protonpack.version>
</properties> </properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -0,0 +1,73 @@
package com.baeldung.parallel_collectors;
import com.pivovarit.collectors.ParallelCollectors;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
public class ParallelCollectorsVirtualThreadsManualTest {
private static final Logger log = LoggerFactory.getLogger(ParallelCollectorsVirtualThreadsManualTest.class);
// increase the number of parallel processes to find the max number of threads on your machine
@Test
public void givenParallelism_whenUsingOSThreads_thenShouldRunOutOfThreads() {
int parallelProcesses = 50_000;
var e = Executors.newFixedThreadPool(parallelProcesses);
var result = timed(() -> Stream.iterate(0, i -> i + 1).limit(parallelProcesses)
.collect(ParallelCollectors.parallel(i -> fetchById(i), toList(), e, parallelProcesses))
.join());
log.info("{}", result);
}
@Test
public void givenParallelism_whenUsingVThreads_thenShouldProcessInParallel() {
int parallelProcesses = 1000_000;
var result = timed(() -> Stream.iterate(0, i -> i + 1).limit(parallelProcesses)
.collect(ParallelCollectors.parallel(i -> fetchById(i), toList()))
.join());
log.info("{}", result);
}
@Test
public void givenParallelismAndPCollectors2_whenUsingVThreads_thenShouldProcessInParallel() {
int parallelProcesses = 1000_000;
var result = timed(() -> Stream.iterate(0, i -> i + 1).limit(parallelProcesses)
.collect(ParallelCollectors.parallel(i -> fetchById(i), toList(), Executors.newVirtualThreadPerTaskExecutor(), Integer.MAX_VALUE))
.join());
log.info("{}", result);
}
private static String fetchById(int id) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// ignore shamelessly
}
return "user-" + id;
}
private static <T> T timed(Supplier<T> supplier) {
var before = Instant.now();
T result = supplier.get();
var after = Instant.now();
log.info("Execution time: {} ms", Duration.between(before, after).toMillis());
return result;
}
}

View File

@ -34,7 +34,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${spring.webmvc.version}</version> <version>${springdoc-openapi-webmvc-ui.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@ -71,6 +71,7 @@
<java.version>17</java.version> <java.version>17</java.version>
<conductor.client.version>2.0.8</conductor.client.version> <conductor.client.version>2.0.8</conductor.client.version>
<spring.webmvc.version>2.1.0</spring.webmvc.version> <spring.webmvc.version>2.1.0</spring.webmvc.version>
<springdoc-openapi-webmvc-ui.version>2.5.0</springdoc-openapi-webmvc-ui.version>
</properties> </properties>
</project> </project>

View File

@ -19,10 +19,15 @@
<version>${mockito-inline.version}</version> <version>${mockito-inline.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
</dependencies> </dependencies>
<properties> <properties>
<mockito-inline.version>5.2.0</mockito-inline.version> <mockito-inline.version>5.2.0</mockito-inline.version>
</properties> </properties>
</project> </project>

View File

@ -0,0 +1,29 @@
package com.baeldung.builder.implementation;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class GenericBuilder<T> {
private final Supplier<T> supplier;
private GenericBuilder(Supplier<T> supplier) {
this.supplier = supplier;
}
public static <T> GenericBuilder<T> of(Supplier<T> supplier) {
return new GenericBuilder<>(supplier);
}
public <P> GenericBuilder<T> with(BiConsumer<T, P> consumer, P value) {
return new GenericBuilder<>(() -> {
T object = supplier.get();
consumer.accept(object, value);
return object;
});
}
public T build() {
return supplier.get();
}
}

View File

@ -0,0 +1,18 @@
package com.baeldung.builder.implementation;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@Getter
@Setter
public class LombokPost {
private String title;
private String text;
private String category;
}

View File

@ -0,0 +1,74 @@
package com.baeldung.builder.implementation;
public class Post {
private String title;
private String text;
private String category;
Post(Builder builder) {
this.title = builder.title;
this.text = builder.text;
this.category = builder.category;
}
Post() {}
public String getTitle() {
return title;
}
public String getText() {
return text;
}
public String getCategory() {
return category;
}
public void setTitle(String title) {
this.title = title;
}
public void setText(String text) {
this.text = text;
}
public void setCategory(String category) {
this.category = category;
}
@Override
public String toString() {
return "Post{" + "title='" + title + '\'' + ", text='" + text + '\'' + ", category='" + category + '\'' + '}';
}
public static class Builder {
private String title;
private String text;
private String category;
public Builder() {}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder text(String text) {
this.text = text;
return this;
}
public Builder category(String category) {
this.category = category;
return this;
}
public Post build() {
return new Post(this);
}
}
}

View File

@ -0,0 +1,50 @@
package com.baeldung.builder.implementation;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class BuilderImplementationUnitTest {
@Test
void givenClassicBuilder_whenBuild_thenReturnObject() {
Post post = new Post.Builder()
.title("Java Builder Pattern")
.text("Explaining how to implement the Builder Pattern in Java")
.category("Programming")
.build();
assertEquals("Java Builder Pattern", post.getTitle());
assertEquals("Explaining how to implement the Builder Pattern in Java", post.getText());
assertEquals("Programming", post.getCategory());
}
@Test
void givenGenericBuilder_whenBuild_thenReturnObject() {
Post post = GenericBuilder.of(Post::new)
.with(Post::setTitle, "Java Builder Pattern")
.with(Post::setText, "Explaining how to implement the Builder Pattern in Java")
.with(Post::setCategory, "Programming")
.build();
assertEquals("Java Builder Pattern", post.getTitle());
assertEquals("Explaining how to implement the Builder Pattern in Java", post.getText());
assertEquals("Programming", post.getCategory());
}
@Test
void givenLombokBuilder_whenBuild_thenReturnObject() {
LombokPost post = LombokPost.builder()
.title("Java Builder Pattern")
.text("Explaining how to implement the Builder Pattern in Java")
.category("Programming")
.build();
assertEquals("Java Builder Pattern", post.getTitle());
assertEquals("Explaining how to implement the Builder Pattern in Java", post.getText());
assertEquals("Programming", post.getCategory());
}
}

View File

@ -69,6 +69,7 @@
<module>spring-boot-persistence-mongodb</module> <module>spring-boot-persistence-mongodb</module>
<module>spring-boot-persistence-mongodb-2</module> <module>spring-boot-persistence-mongodb-2</module>
<module>spring-boot-persistence-mongodb-3</module> <module>spring-boot-persistence-mongodb-3</module>
<module>spring-boot-persistence-mongodb-4</module>
<module>spring-data-arangodb</module> <module>spring-data-arangodb</module>
<!--<module>spring-data-cassandra</module>--> <!-- failing after upgrading to jdk17. JDK 17 compatibility in progress CASSANDRA-16895 --> <!--<module>spring-data-cassandra</module>--> <!-- failing after upgrading to jdk17. JDK 17 compatibility in progress CASSANDRA-16895 -->
<module>spring-data-cassandra-test</module> <module>spring-data-cassandra-test</module>

View File

@ -0,0 +1,5 @@
# Relevant Articles
- [ZonedDateTime with Spring Data MongoDB](https://www.baeldung.com/spring-data-mongodb-zoneddatetime)
- [A Guide to @DBRef in MongoDB](https://www.baeldung.com/spring-mongodb-dbref-annotation)
- More articles: [[<--prev]](../spring-boot-persistence-mongodb-3)

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-persistence-mongodb-4</artifactId>
<name>spring-boot-persistence-mongodb-4</name>
<description>This is simple boot application for Spring boot persistence mongodb test</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-2</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,5 +1,6 @@
package com.baeldung.mongodb.dbref; package com.baeldung.mongodb.dbref;
import com.baeldung.mongodb.dbref.repository.PersonRepository;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -7,8 +8,6 @@ import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner; import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.baeldung.mongodb.dbref.repository.PersonRepository;
@Component @Component
public class DbRefTester implements ApplicationRunner { public class DbRefTester implements ApplicationRunner {

View File

@ -1,11 +1,11 @@
package com.baeldung.mongodb.dbref.model; package com.baeldung.mongodb.dbref.model;
import java.util.List;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
@Document(collection = "Person") @Document(collection = "Person")
public class Person { public class Person {

View File

@ -1,8 +1,7 @@
package com.baeldung.mongodb.dbref.repository; package com.baeldung.mongodb.dbref.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.mongodb.dbref.model.Person; import com.baeldung.mongodb.dbref.model.Person;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface PersonRepository extends MongoRepository<Person, String> { public interface PersonRepository extends MongoRepository<Person, String> {

View File

@ -1,15 +1,14 @@
package com.baeldung.zoneddatetime.config; package com.baeldung.zoneddatetime.config;
import java.util.ArrayList; import com.baeldung.zoneddatetime.converter.ZonedDateTimeReadConverter;
import java.util.List; import com.baeldung.zoneddatetime.converter.ZonedDateTimeWriteConverter;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import com.baeldung.zoneddatetime.converter.ZonedDateTimeReadConverter; import java.util.ArrayList;
import com.baeldung.zoneddatetime.converter.ZonedDateTimeWriteConverter; import java.util.List;
@EnableMongoRepositories(basePackages = { "com.baeldung" }) @EnableMongoRepositories(basePackages = { "com.baeldung" })
public class MongoConfig extends AbstractMongoClientConfiguration { public class MongoConfig extends AbstractMongoClientConfiguration {

View File

@ -1,7 +1,6 @@
package com.baeldung.zoneddatetime.repository; package com.baeldung.zoneddatetime.repository;
import com.baeldung.zoneddatetime.model.Action;
import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.zoneddatetime.model.Action;
public interface ActionRepository extends MongoRepository<Action, String> { } public interface ActionRepository extends MongoRepository<Action, String> { }

View File

@ -0,0 +1 @@
spring.application.name=spring-boot-persistence-mongodb-4

View File

@ -1,11 +1,11 @@
package com.baeldung.mongodb.dbref; package com.baeldung.mongodb.dbref;
import static org.assertj.core.api.Assertions.assertThat; import com.baeldung.mongodb.dbref.model.Person;
import static org.junit.jupiter.api.Assertions.assertEquals; import com.baeldung.mongodb.dbref.model.Pet;
import com.baeldung.mongodb.dbref.repository.PersonRepository;
import java.util.ArrayList; import com.mongodb.BasicDBObjectBuilder;
import java.util.List; import com.mongodb.DBObject;
import com.mongodb.DBRef;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -14,12 +14,11 @@ import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.mongodb.dbref.model.Person; import java.util.ArrayList;
import com.baeldung.mongodb.dbref.model.Pet; import java.util.List;
import com.baeldung.mongodb.dbref.repository.PersonRepository;
import com.mongodb.BasicDBObjectBuilder; import static org.assertj.core.api.Assertions.assertThat;
import com.mongodb.DBObject; import static org.junit.jupiter.api.Assertions.assertEquals;
import com.mongodb.DBRef;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest

View File

@ -1,5 +1,8 @@
package com.baeldung.zoneddatetime; package com.baeldung.zoneddatetime;
import com.baeldung.zoneddatetime.config.MongoConfig;
import com.baeldung.zoneddatetime.model.Action;
import com.baeldung.zoneddatetime.repository.ActionRepository;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -10,10 +13,6 @@ import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.baeldung.zoneddatetime.config.MongoConfig;
import com.baeldung.zoneddatetime.model.Action;
import com.baeldung.zoneddatetime.repository.ActionRepository;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;

View File

@ -0,0 +1 @@
spring.mongodb.embedded.version=4.4.9

View File

@ -4,8 +4,6 @@
- [Spring Boot Integration Testing with Embedded MongoDB](http://www.baeldung.com/spring-boot-embedded-mongodb) - [Spring Boot Integration Testing with Embedded MongoDB](http://www.baeldung.com/spring-boot-embedded-mongodb)
- [Upload and Retrieve Files Using MongoDB and Spring Boot](https://www.baeldung.com/spring-boot-mongodb-upload-file) - [Upload and Retrieve Files Using MongoDB and Spring Boot](https://www.baeldung.com/spring-boot-mongodb-upload-file)
- [GridFS in Spring Data MongoDB](http://www.baeldung.com/spring-data-mongodb-gridfs) - [GridFS in Spring Data MongoDB](http://www.baeldung.com/spring-data-mongodb-gridfs)
- [ZonedDateTime with Spring Data MongoDB](https://www.baeldung.com/spring-data-mongodb-zoneddatetime)
- [A Guide to @DBRef in MongoDB](https://www.baeldung.com/spring-mongodb-dbref-annotation)
- [Import Data to MongoDB From JSON File Using Java](https://www.baeldung.com/java-import-json-mongodb) - [Import Data to MongoDB From JSON File Using Java](https://www.baeldung.com/java-import-json-mongodb)
- [Spring Data MongoDB Configure Connection](https://www.baeldung.com/spring-data-mongodb-connection) - [Spring Data MongoDB Configure Connection](https://www.baeldung.com/spring-data-mongodb-connection)
- More articles: [[next-->]](../spring-boot-persistence-mongodb-2) - More articles: [[next-->]](../spring-boot-persistence-mongodb-2)

View File

@ -0,0 +1,36 @@
package com.baeldung.projection.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "student")
public class Student {
@Id
private String id;
private String name;
private Long age;
public Student(String id, String name, Long age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getAge() {
return age;
}
public void setAge(Long age) {
this.age = age;
}
}

View File

@ -0,0 +1,32 @@
package com.baeldung.projection.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.Aggregation;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.projection.model.Student;
import java.util.List;
public interface StudentRepository extends MongoRepository<Student, String> {
@Aggregation(pipeline = {
"{ '$skip': ?0 }",
"{ '$limit': ?1 }"
})
List<Student> findAll(Long skip, Long limit);
@Aggregation(pipeline = {
"{ '$match': { 'age': ?0 } }",
"{ $skip: ?1 }",
"{ $limit: ?2 }"
})
List<Student> findAllByAgeCriteria(Long age, Long skip, Long limit);
@Aggregation(pipeline = {
"{ '$match': { 'id' : ?0 } }",
"{ '$sort' : { 'id' : 1 } }",
"{ '$skip' : ?1 }",
"{ '$limit' : ?2 }"
})
List<Student> findByStudentId(final String studentId, Long skip, Long limit);
Page<Student> findAll(Pageable pageable);
}

View File

@ -0,0 +1,90 @@
package com.baeldung.projection;
import com.baeldung.projection.config.ProjectionConfig;
import com.baeldung.projection.model.Student;
import com.baeldung.projection.repository.StudentRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ProjectionConfig.class)
public class StudentIntegrationTest {
@Autowired
private StudentRepository studentRepository;
private List<Student> studentList;
@Before
public void setUp() {
Student student1 = new Student("A", "Abraham", 15L);
Student student2 = new Student("B", "Usman", 30L);
Student student3 = new Student("C", "David", 20L);
Student student4 = new Student("D", "Tina", 45L);
Student student5 = new Student("E", "Maria", 33L);
studentList = Arrays.asList(student1, student2, student3, student4, student5);
studentRepository.saveAll(studentList);
}
@Test
public void whenRetrievingAllStudents_thenReturnsCorrectNumberOfRecords() {
// WHEN
List<Student> result = studentRepository.findAll(0L, 5L);
// THEN
assertEquals(5, result.size());
}
@Test
public void whenLimitingAndSkipping_thenReturnsLimitedStudents() {
// WHEN
List<Student> result = studentRepository.findAll(3L, 2L);
// THEN
assertEquals(2, result.size());
assertEquals("Tina", result.get(0).getName());
assertEquals("Maria", result.get(1).getName());
}
@Test
public void whenFilteringByAge_thenReturnsStudentsMatchingCriteria() {
// WHEN
List<Student> result = studentRepository.findAllByAgeCriteria(30L, 0L, 10L);
// THEN
assertEquals(1, result.size());
assertEquals("Usman", result.get(0).getName());
}
@Test
public void whenFindingById_thenReturnsMatchingStudent() {
// WHEN
List<Student> result = studentRepository.findByStudentId("A", 0L, 5L);
// THEN
assertEquals(1, result.size());
assertEquals("Abraham", result.get(0).getName());
}
@Test
public void whenFindByStudentIdUsingPageable_thenReturnsPageOfStudents() {
// GIVEN
Sort sort = Sort.by(Sort.Direction.DESC, "id");
Pageable pageable = PageRequest.of(0, 5, sort);
// WHEN
Page<Student> resultPage = studentRepository.findAll(pageable);
// THEN
assertEquals(5, resultPage.getTotalElements());
assertEquals("Maria", resultPage.getContent().get(0).getName());
}
}

View File

@ -20,6 +20,7 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId> <artifactId>spring-boot-starter-jdbc</artifactId>
<version>${spring-boot.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@ -30,7 +31,7 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
@ -46,47 +47,11 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<properties> <properties>
<spring-boot.version>3.2.0-SNAPSHOT</spring-boot.version>
<junit-jupiter.version>5.10.0</junit-jupiter.version> <junit-jupiter.version>5.10.0</junit-jupiter.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>3.2.4</spring-boot.version> <!-- Requires at least spring framework version 6.1.X to work -->
</properties> </properties>
</project> </project>

View File

@ -4,8 +4,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication @SpringBootApplication(scanBasePackages = "com.baeldung")
@ComponentScan(basePackages = "com.baledung.jdbcclient")
public class JdbcClientDemoApplication { public class JdbcClientDemoApplication {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -737,7 +737,7 @@
<module>libraries-security</module> <module>libraries-security</module>
<module>libraries-server-2</module> <module>libraries-server-2</module>
<module>libraries-server</module> <module>libraries-server</module>
<module>libraries-stream</module> <!-- <module>libraries-stream</module> JDK21-->
<module>libraries-testing-2</module> <module>libraries-testing-2</module>
<module>libraries-transform</module> <module>libraries-transform</module>
<module>libraries</module> <!-- very long running --> <module>libraries</module> <!-- very long running -->

View File

@ -18,7 +18,7 @@
<repository> <repository>
<id>spring-snapshots</id> <id>spring-snapshots</id>
<name>Spring Snapshots</name> <name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url> <url>https://repo.spring.io/snapshot</url> <!-- We need this for experimental dependencies not available on Maven Central -->
<releases> <releases>
<enabled>false</enabled> <enabled>false</enabled>
</releases> </releases>

View File

@ -14,14 +14,6 @@
<version>1.0.0-SNAPSHOT</version> <version>1.0.0-SNAPSHOT</version>
</parent> </parent>
<repositories>
<repository>
<id>repository.spring.release</id>
<name>Spring GA Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -73,7 +65,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version> <version>${springdoc-openapi-webmvc-ui.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
@ -102,7 +94,6 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<version>3.2.0-M2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
@ -202,18 +193,6 @@
</dependency> </dependency>
</dependencies> </dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.0-M2</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<profiles> <profiles>
<profile> <profile>
@ -300,6 +279,8 @@
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version> <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<jedis.version>5.0.2</jedis.version> <jedis.version>5.0.2</jedis.version>
<spring-kafka.version>3.1.2</spring-kafka.version> <spring-kafka.version>3.1.2</spring-kafka.version>
<spring-boot.version>3.2.4</spring-boot.version>
<springdoc-openapi-webmvc-ui.version>2.5.0</springdoc-openapi-webmvc-ui.version>
</properties> </properties>
</project> </project>

View File

@ -22,7 +22,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version> <version>${springdoc-openapi-webmvc-ui.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -100,6 +100,7 @@
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version> <maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
<native.maven.plugin.version>0.9.17</native.maven.plugin.version> <native.maven.plugin.version>0.9.17</native.maven.plugin.version>
<java.version>17</java.version> <java.version>17</java.version>
<springdoc-openapi-webmvc-ui.version>2.5.0</springdoc-openapi-webmvc-ui.version>
</properties> </properties>
</project> </project>

View File

@ -89,7 +89,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version> <version>${springdoc-openapi-webmvc-ui.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
@ -211,46 +211,10 @@
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version> <maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
<start-class>com.baeldung.sample.TodoApplication</start-class> <start-class>com.baeldung.sample.TodoApplication</start-class>
<mockserver.version>5.14.0</mockserver.version> <mockserver.version>5.14.0</mockserver.version>
<spring-boot.version>3.2.0-SNAPSHOT</spring-boot.version> <spring-boot.version>3.2.4</spring-boot.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version> <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<jupiter.version>5.10.0</jupiter.version> <jupiter.version>5.10.0</jupiter.version>
<springdoc-openapi-webmvc-ui.version>2.5.0</springdoc-openapi-webmvc-ui.version>
</properties> </properties>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
</project> </project>

View File

@ -40,7 +40,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version> <version>${springdoc-openapi-webmvc-ui.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
@ -119,6 +119,7 @@
<properties> <properties>
<springdoc.version>2.2.0</springdoc.version> <springdoc.version>2.2.0</springdoc.version>
<springdoc-openapi-webmvc-ui.version>2.5.0</springdoc-openapi-webmvc-ui.version>
<springdoc-openapi-maven-plugin.version>1.4</springdoc-openapi-maven-plugin.version> <springdoc-openapi-maven-plugin.version>1.4</springdoc-openapi-maven-plugin.version>
<java.version>17</java.version> <java.version>17</java.version>
</properties> </properties>

View File

@ -32,7 +32,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version> <version>${springdoc-openapi-webmvc-ui.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.annotation</groupId> <groupId>javax.annotation</groupId>
@ -53,6 +53,7 @@
<properties> <properties>
<springdoc.version>2.1.0</springdoc.version> <springdoc.version>2.1.0</springdoc.version>
<javax.version>1.3.2</javax.version> <javax.version>1.3.2</javax.version>
<springdoc-openapi-webmvc-ui.version>2.5.0</springdoc-openapi-webmvc-ui.version>
</properties> </properties>
</project> </project>

View File

@ -14,14 +14,16 @@ import org.springframework.stereotype.Component;
matchIfMissing = true) matchIfMissing = true)
@Component @Component
public class ApplicationRunnerTaskExecutor implements ApplicationRunner { public class ApplicationRunnerTaskExecutor implements ApplicationRunner {
private TaskService taskService;
private final TaskService taskService;
public ApplicationRunnerTaskExecutor(TaskService taskService) { public ApplicationRunnerTaskExecutor(TaskService taskService) {
this.taskService = taskService; this.taskService = taskService;
} }
@Override @Override
public void run(ApplicationArguments args) throws Exception { public void run(ApplicationArguments args) {
taskService.execute("application runner task"); taskService.execute("application runner task");
} }
} }

View File

@ -13,14 +13,14 @@ import org.springframework.stereotype.Component;
matchIfMissing = true) matchIfMissing = true)
@Component @Component
public class CommandLineTaskExecutor implements CommandLineRunner { public class CommandLineTaskExecutor implements CommandLineRunner {
private TaskService taskService; private final TaskService taskService;
public CommandLineTaskExecutor(TaskService taskService) { public CommandLineTaskExecutor(TaskService taskService) {
this.taskService = taskService; this.taskService = taskService;
} }
@Override @Override
public void run(String... args) throws Exception { public void run(String... args) {
taskService.execute("command line runner task"); taskService.execute("command line runner task");
} }
} }

View File

@ -6,9 +6,9 @@ import org.springframework.stereotype.Service;
@Service @Service
public class TaskService { public class TaskService {
private static Logger logger = LoggerFactory.getLogger(TaskService.class); private static final Logger logger = LoggerFactory.getLogger(TaskService.class);
public void execute(String task) { public void execute(String task) {
logger.info("do " + task); logger.info("do {}", task);
} }
} }

View File

@ -1,16 +1,13 @@
package com.baeldung.prevent.commandline.application.runner.execution; package com.baeldung.prevent.commandline.application.runner.execution;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import com.baeldung.prevent.commandline.application.runner.execution.ApplicationRunnerTaskExecutor;
import com.baeldung.prevent.commandline.application.runner.execution.CommandLineTaskExecutor;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
@SpringBootTest @SpringBootTest
class RunApplicationIntegrationTest { class RunApplicationIntegrationTest {
@SpyBean @SpyBean
@ -21,6 +18,6 @@ class RunApplicationIntegrationTest {
@Test @Test
void whenContextLoads_thenRunnersRun() throws Exception { void whenContextLoads_thenRunnersRun() throws Exception {
verify(applicationRunnerTaskExecutor, times(1)).run(any()); verify(applicationRunnerTaskExecutor, times(1)).run(any());
verify(commandLineTaskExecutor, times(1)).run(any()); verify(commandLineTaskExecutor, times(1)).run(any(String[].class));
} }
} }

View File

@ -90,14 +90,6 @@
</dependency> </dependency>
</dependencies> </dependencies>
<repositories>
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/release</url>
</repository>
</repositories>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -44,8 +44,18 @@
<version>${spring-boot.version}</version> <version>${spring-boot.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${byte-buddy.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<properties>
<byte-buddy.version>1.14.13</byte-buddy.version>
</properties>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -44,8 +44,18 @@
<version>${spring-boot.version}</version> <version>${spring-boot.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${byte-buddy.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<properties>
<byte-buddy.version>1.14.13</byte-buddy.version>
</properties>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -52,8 +52,19 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${byte-buddy.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<properties>
<byte-buddy.version>1.14.13</byte-buddy.version>
</properties>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -60,6 +60,12 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${byte-buddy.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -82,6 +88,7 @@
<properties> <properties>
<spring-cloud-dependencies.version>2021.0.3</spring-cloud-dependencies.version> <spring-cloud-dependencies.version>2021.0.3</spring-cloud-dependencies.version>
<spring-cloud-netflix-zuul.version>2.2.7.RELEASE</spring-cloud-netflix-zuul.version> <spring-cloud-netflix-zuul.version>2.2.7.RELEASE</spring-cloud-netflix-zuul.version>
<byte-buddy.version>1.14.13</byte-buddy.version>
</properties> </properties>
</project> </project>

View File

@ -15,27 +15,6 @@
<relativePath>../parent-boot-3</relativePath> <relativePath>../parent-boot-3</relativePath>
</parent> </parent>
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-snapshot</id>
<name>Spring Plugins Snapshot</name>
<url>https://repo.spring.io/plugins-snapshot</url>
</pluginRepository>
</pluginRepositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -15,32 +15,6 @@
<relativePath>../parent-boot-2</relativePath> <relativePath>../parent-boot-2</relativePath>
</parent> </parent>
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-snapshot</id>
<name>Spring Plugins Snapshot</name>
<url>https://repo.spring.io/plugins-snapshot</url>
</pluginRepository>
<pluginRepository>
<id>spring-plugins-milestone</id>
<name>Spring Plugins Milestone</name>
<url>https://repo.spring.io/plugins-milestone</url>
</pluginRepository>
</pluginRepositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -84,5 +58,31 @@
<spring-native.version>0.12.1</spring-native.version> <spring-native.version>0.12.1</spring-native.version>
<log4j2.version>2.17.1</log4j2.version> <log4j2.version>2.17.1</log4j2.version>
</properties> </properties>
<!-- Experimental dependency in this repository cannot rely on Maven Central alone -->
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-snapshot</id>
<name>Spring Plugins Snapshot</name>
<url>https://repo.spring.io/plugins-snapshot</url>
</pluginRepository>
<pluginRepository>
<id>spring-plugins-milestone</id>
<name>Spring Plugins Milestone</name>
<url>https://repo.spring.io/plugins-milestone</url>
</pluginRepository>
</pluginRepositories>
</project> </project>

View File

@ -20,6 +20,7 @@
<module>spring-reactive-data</module> <module>spring-reactive-data</module>
<module>spring-reactive-2</module> <module>spring-reactive-2</module>
<module>spring-reactive-3</module> <module>spring-reactive-3</module>
<module>spring-reactive-4</module>
<module>spring-reactive-client</module> <module>spring-reactive-client</module>
<module>spring-reactive-client-2</module> <module>spring-reactive-client-2</module>
<module>spring-reactive-filters</module> <module>spring-reactive-filters</module>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-reactive-4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-reactive-4</name>
<packaging>jar</packaging>
<description>spring sample project about new features</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent>
<properties>
<wiremock-standalone.version>3.4.2</wiremock-standalone.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>${wiremock-standalone.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,13 @@
package com.baeldung;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringReactiveApplication {
public static void main(String[] args) {
SpringApplication.run(SpringReactiveApplication.class, args);
}
}

View File

@ -0,0 +1,21 @@
package com.baeldung.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class TraceController {
@GetMapping(value = "/trace-annotated")
public Mono<String> trace(@RequestHeader(name = "traceId") final String traceId) {
return Mono.just("TraceId: ".concat(traceId));
}
@GetMapping(value = "/trace-exceptional")
public Mono<String> traceExceptional() {
return Mono.just("Traced");
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.filters;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Component
public class ExceptionalTraceFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
if (exchange.getRequest()
.getPath()
.toString()
.equals("/trace-exceptional")) {
exchange.getRequest()
.getHeaders()
.add("traceId", "TRACE-ID");
}
return chain.filter(exchange);
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.filters;
import org.springframework.web.reactive.function.server.HandlerFilterFunction;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
public class TraceHandlerFilterFunction implements HandlerFilterFunction<ServerResponse, ServerResponse> {
@Override
public Mono<ServerResponse> filter(ServerRequest request, HandlerFunction<ServerResponse> handlerFunction) {
ServerRequest serverRequest = ServerRequest.from(request)
.header("traceId", "FUNCTIONAL-TRACE-ID")
.build();
return handlerFunction.handle(serverRequest);
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.filters;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Component
public class TraceWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
exchange.getRequest()
.mutate()
.header("traceId", "ANNOTATED-TRACE-ID");
return chain.filter(exchange);
}
}

View File

@ -0,0 +1,18 @@
package com.baeldung.filters;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
public final class WebClientFilters {
public static ExchangeFilterFunction modifyRequestHeaders(final MultiValueMap<String, String> changedMap) {
return (request, next) -> {
final ClientRequest clientRequest = ClientRequest.from(request)
.header("traceId", "TRACE-ID")
.build();
changedMap.addAll(clientRequest.headers());
return next.exchange(clientRequest);
};
}
}

View File

@ -0,0 +1,22 @@
package com.baeldung.handler;
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;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
@Component
public class TraceRouterHandler {
public Mono<ServerResponse> handle(final ServerRequest serverRequest) {
final String traceId = serverRequest.headers()
.firstHeader("traceId");
assert traceId != null;
final Mono<String> body = Mono.just("TraceId: ".concat(traceId));
return ok().body(body, String.class);
}
}

View File

@ -0,0 +1,27 @@
package com.baeldung.router;
import com.baeldung.filters.TraceHandlerFilterFunction;
import com.baeldung.handler.TraceRouterHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@Configuration
public class TraceRouter {
@Bean
public RouterFunction<ServerResponse> routes(TraceRouterHandler routerHandler) {
return route(GET("/trace-functional-filter"), routerHandler::handle).filter(new TraceHandlerFilterFunction())
.and(route().GET("/trace-functional-before", routerHandler::handle)
.before(request -> ServerRequest.from(request)
.header("traceId", "FUNCTIONAL-TRACE-ID")
.build())
.build());
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

View File

@ -0,0 +1,50 @@
package com.baeldung.controller;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
@ExtendWith(SpringExtension.class)
@WebFluxTest(controllers = TraceController.class)
public class TraceControllerIntegrationTest {
@Autowired
private WebTestClient webTestClient;
@Test
void whenCallTraceAnnotatedEndpoint_thenResponseContainsTraceId() {
EntityExchangeResult<String> result = webTestClient.get()
.uri("/trace-annotated")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.returnResult();
final String body = "TraceId: ANNOTATED-TRACE-ID";
assertEquals(result.getResponseBody(), body);
}
@Test
void whenCallTraceExceptionalEndpoint_thenThrowsException() {
EntityExchangeResult<Map> result = webTestClient.get()
.uri("/trace-exceptional")
.exchange()
.expectStatus()
.is5xxServerError()
.expectBody(Map.class)
.returnResult();
assertNotNull(result.getResponseBody());
}
}

View File

@ -0,0 +1,55 @@
package com.baeldung.filters;
import static com.baeldung.filters.WebClientFilters.modifyRequestHeaders;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
@WireMockTest
public class WebClientFilterUnitTest {
@RegisterExtension
static WireMockExtension extension = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort()
.dynamicHttpsPort())
.build();
@Test
void whenCallEndpoint_thenRequestHeadersModified() {
extension.stubFor(get("/test").willReturn(aResponse().withStatus(200)
.withBody("SUCCESS")));
final MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
WebClient webClient = WebClient.builder()
.filter(modifyRequestHeaders(map))
.build();
String actual = sendGetRequest(webClient);
final String body = "SUCCESS";
Assertions.assertEquals(actual, body);
Assertions.assertEquals("TRACE-ID", map.getFirst("traceId"));
}
private String sendGetRequest(final WebClient webClient) {
return webClient.get()
.uri(getUrl())
.retrieve()
.bodyToMono(String.class)
.block();
}
private String getUrl() {
return "http://localhost:" + extension.getPort() + "/test";
}
}

View File

@ -0,0 +1,60 @@
package com.baeldung.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
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.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import com.baeldung.router.TraceRouter;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { TraceRouter.class, TraceRouterHandler.class })
@WebFluxTest
public class TraceRouteHandlerIntegrationTest {
@Autowired
private ApplicationContext context;
private WebTestClient webTestClient;
@BeforeEach
void setUp() {
webTestClient = WebTestClient.bindToApplicationContext(context)
.build();
}
@Test
void whenCallTraceFunctionalFilterEndpoint_thenResponseContainsTraceId() {
EntityExchangeResult<String> result = webTestClient.get()
.uri("/trace-functional-filter")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.returnResult();
final String body = "TraceId: FUNCTIONAL-TRACE-ID";
assertEquals(result.getResponseBody(), body);
}
@Test
void whenCallTraceFunctionalBeforeEndpoint_thenResponseContainsTraceId() {
EntityExchangeResult<String> result = webTestClient.get()
.uri("/trace-functional-before")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.returnResult();
final String body = "TraceId: FUNCTIONAL-TRACE-ID";
assertEquals(result.getResponseBody(), body);
}
}

View File

@ -65,6 +65,12 @@
<version>${couchbaseMock.version}</version> <version>${couchbaseMock.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${byte-buddy.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -129,6 +135,7 @@
<maven.compiler.source>11</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target> <maven.compiler.target>11</maven.compiler.target>
<java.version>11</java.version> <java.version>11</java.version>
<byte-buddy.version>1.14.13</byte-buddy.version>
</properties> </properties>
</project> </project>

View File

@ -11,9 +11,10 @@
<description>WebFlux and Spring Security OAuth</description> <description>WebFlux and Spring Security OAuth</description>
<parent> <parent>
<groupId>com.baeldung.spring.reactive</groupId> <groupId>com.baeldung</groupId>
<artifactId>spring-reactive-modules</artifactId> <artifactId>parent-boot-3</artifactId>
<version>1.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent> </parent>
<dependencies> <dependencies>
@ -62,4 +63,8 @@
</plugins> </plugins>
</build> </build>
<properties>
<start-class>com.baeldung.reactive.oauth.Spring5ReactiveOauthApplication</start-class>
</properties>
</project> </project>

View File

@ -1,6 +1,7 @@
package com.baeldung.reactive.oauth; package com.baeldung.reactive.oauth;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain;
@ -10,10 +11,11 @@ public class SecurityConfig {
@Bean @Bean
public SecurityWebFilterChain configure(ServerHttpSecurity http) throws Exception { public SecurityWebFilterChain configure(ServerHttpSecurity http) throws Exception {
return http.authorizeExchange() return http.authorizeExchange(auth -> auth
.pathMatchers("/about").permitAll() .pathMatchers("/about").permitAll()
.anyExchange().authenticated() .anyExchange().authenticated())
.and().oauth2Login() .oauth2Login(Customizer.withDefaults())
.and().build(); .build();
} }
} }

View File

@ -4,9 +4,12 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
@PropertySource("classpath:default-application.yml") @PropertySource("classpath:default-application.yml")
@ -24,4 +27,13 @@ public class Spring5ReactiveOauthApplication {
.filter(filter) .filter(filter)
.build(); .build();
} }
@Bean
public ReactiveClientRegistrationRepository clientRegistrations() {
ClientRegistration registration = ClientRegistration.withRegistrationId("bael").authorizationGrantType(
AuthorizationGrantType.CLIENT_CREDENTIALS).clientId("bael").tokenUri("default").build();
return new InMemoryReactiveClientRegistrationRepository(registration);
}
} }

View File

@ -2,6 +2,7 @@ package com.baeldung.webclient.authorizationcodeclient.configuration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain;
@ -9,13 +10,9 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
public class WebSecurityConfig { public class WebSecurityConfig {
@Bean @Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange() http.authorizeExchange(s-> s.anyExchange().authenticated())
.anyExchange() .oauth2Client(Customizer.withDefaults())
.authenticated() .formLogin(Customizer.withDefaults());
.and()
.oauth2Client()
.and()
.formLogin();
return http.build(); return http.build();
} }

View File

@ -2,6 +2,7 @@ package com.baeldung.webclient.authorizationcodelogin.configuration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain;
@ -9,11 +10,8 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
public class WebSecurityConfig { public class WebSecurityConfig {
@Bean @Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange() http.authorizeExchange(s-> s.anyExchange().authenticated())
.anyExchange() .oauth2Login(Customizer.withDefaults());
.authenticated()
.and()
.oauth2Login();
return http.build(); return http.build();
} }

View File

@ -1,22 +1,60 @@
package com.baeldung.webclient.clientcredentials.configuration; package com.baeldung.webclient.clientcredentials.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.security.oauth2.client.web.server.UnAuthenticatedServerOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
@Configuration @Configuration
public class WebClientConfig { public class WebClientConfig {
@Bean @Bean
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) { ReactiveClientRegistrationRepository clientRegistrations(
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository()); @Value("${spring.security.oauth2.client.provider.bael.token-uri}") String token_uri,
oauth.setDefaultClientRegistrationId("bael"); @Value("${spring.security.oauth2.client.registration.bael.client-id}") String client_id,
return WebClient.builder() @Value("${spring.security.oauth2.client.registration.bael.client-secret}") String client_secret,
.filter(oauth) @Value("${spring.security.oauth2.client.registration.bael.authorization-grant-type}") String authorizationGrantType
) {
ClientRegistration registration = ClientRegistration
.withRegistrationId("keycloak")
.tokenUri(token_uri)
.clientId(client_id)
.clientSecret(client_secret)
.authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))
.build(); .build();
return new InMemoryReactiveClientRegistrationRepository(registration);
}
@Bean
public AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository) {
InMemoryReactiveOAuth2AuthorizedClientService clientService =
new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
} }
@Bean
WebClient webClient(AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager auth2AuthorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(auth2AuthorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("bael");
return WebClient.builder()
.filter(oauth2Client)
.build();
}
} }

View File

@ -11,6 +11,6 @@ public class WebClientConfig {
public WebClient configureWebClient() { public WebClient configureWebClient() {
return WebClient.builder() return WebClient.builder()
.build(); .build();
}; }
} }

View File

@ -9,9 +9,7 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
public class WebSecurityConfig { public class WebSecurityConfig {
@Bean @Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange() http.authorizeExchange(s -> s.anyExchange().permitAll());
.anyExchange()
.permitAll();
return http.build(); return http.build();
} }
} }

View File

@ -1,12 +1,13 @@
package com.baeldung.reactive.oauth; package com.baeldung.reactive.oauth;
import com.baeldung.webclient.clientcredentials.configuration.WebClientConfig;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest(classes = WebClientConfig.class)
public class Spring5ReactiveOauthIntegrationTest { public class Spring5ReactiveOauthIntegrationTest {
@Test @Test

View File

@ -28,7 +28,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version> <version>${springdoc-openapi-webmvc-ui.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
@ -45,6 +45,7 @@
<properties> <properties>
<springdoc.version>2.4.0</springdoc.version> <springdoc.version>2.4.0</springdoc.version>
<start-class>com.baeldung.basicauth.SpringBootSpringdocBasicAuth</start-class> <start-class>com.baeldung.basicauth.SpringBootSpringdocBasicAuth</start-class>
<springdoc-openapi-webmvc-ui.version>2.5.0</springdoc-openapi-webmvc-ui.version>
</properties> </properties>
</project> </project>