Merge branch 'eugenp:master' into JAVA-18149

This commit is contained in:
timis1 2023-02-15 23:22:00 +02:00 committed by GitHub
commit 3eb4c1f078
69 changed files with 1808 additions and 125 deletions

2
.gitignore vendored
View File

@ -67,6 +67,8 @@ ethereum/logs/
jmeter/src/main/resources/*-JMeter.csv jmeter/src/main/resources/*-JMeter.csv
jmeter/src/main/resources/*-Basic*.csv jmeter/src/main/resources/*-Basic*.csv
jmeter/src/main/resources/*-JMeter*.csv jmeter/src/main/resources/*-JMeter*.csv
jmeter/src/main/resources/*ReportsDashboard*.csv
jmeter/src/main/resources/dashboard/*ReportsDashboard*.csv
ninja/devDb.mv.db ninja/devDb.mv.db

View File

@ -80,16 +80,20 @@ public class BaeldungIntegrationTest {
private void marshalCourseRepo(CourseRepo courseRepo) throws Exception { private void marshalCourseRepo(CourseRepo courseRepo) throws Exception {
AegisWriter<XMLStreamWriter> writer = context.createXMLStreamWriter(); AegisWriter<XMLStreamWriter> writer = context.createXMLStreamWriter();
AegisType aegisType = context.getTypeMapping().getType(CourseRepo.class); AegisType aegisType = context.getTypeMapping().getType(CourseRepo.class);
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(new FileOutputStream(fileName)); final FileOutputStream stream = new FileOutputStream(fileName);
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(stream);
writer.write(courseRepo, new QName("http://aegis.cxf.baeldung.com", "baeldung"), false, xmlWriter, aegisType); writer.write(courseRepo, new QName("http://aegis.cxf.baeldung.com", "baeldung"), false, xmlWriter, aegisType);
xmlWriter.close(); xmlWriter.close();
stream.close();
} }
private CourseRepo unmarshalCourseRepo() throws Exception { private CourseRepo unmarshalCourseRepo() throws Exception {
AegisReader<XMLStreamReader> reader = context.createXMLStreamReader(); AegisReader<XMLStreamReader> reader = context.createXMLStreamReader();
XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(new FileInputStream(fileName)); final FileInputStream stream = new FileInputStream(fileName);
XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(stream);
CourseRepo courseRepo = (CourseRepo) reader.read(xmlReader, context.getTypeMapping().getType(CourseRepo.class)); CourseRepo courseRepo = (CourseRepo) reader.read(xmlReader, context.getTypeMapping().getType(CourseRepo.class));
xmlReader.close(); xmlReader.close();
stream.close();
return courseRepo; return courseRepo;
} }
@ -97,7 +101,7 @@ public class BaeldungIntegrationTest {
public void cleanup(){ public void cleanup(){
File testFile = new File(fileName); File testFile = new File(fileName);
if (testFile.exists()) { if (testFile.exists()) {
testFile.delete(); testFile.deleteOnExit();
} }
} }
} }

View File

@ -63,6 +63,7 @@
<org.apache.httpcomponents.version>4.5.2</org.apache.httpcomponents.version> <org.apache.httpcomponents.version>4.5.2</org.apache.httpcomponents.version>
<velocity-version>1.7</velocity-version> <velocity-version>1.7</velocity-version>
<velocity-tools-version>2.0</velocity-tools-version> <velocity-tools-version>2.0</velocity-tools-version>
<maven-war-plugin.version>3.3.2</maven-war-plugin.version>
</properties> </properties>
</project> </project>

View File

@ -62,10 +62,10 @@
</build> </build>
<properties> <properties>
<asciidoctor-maven-plugin.version>1.5.6</asciidoctor-maven-plugin.version> <asciidoctor-maven-plugin.version>2.2.2</asciidoctor-maven-plugin.version>
<asciidoctorj.version>1.5.6</asciidoctorj.version> <asciidoctorj.version>2.5.7</asciidoctorj.version>
<asciidoctorj-pdf.version>1.5.0-alpha.15</asciidoctorj-pdf.version> <asciidoctorj-pdf.version>2.3.4</asciidoctorj-pdf.version>
<asciidoctorj-pdf.plugin.version>1.5.0-alpha.15</asciidoctorj-pdf.plugin.version> <asciidoctorj-pdf.plugin.version>2.3.4</asciidoctorj-pdf.plugin.version>
</properties> </properties>
</project> </project>

View File

@ -77,6 +77,7 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
@ -92,6 +93,7 @@
<properties> <properties>
<spring.version>2.2.1.RELEASE</spring.version> <spring.version>2.2.1.RELEASE</spring.version>
<awssdk.version>2.17.283</awssdk.version> <awssdk.version>2.17.283</awssdk.version>
<lombok.version>1.18.20</lombok.version>
</properties> </properties>
</project> </project>

View File

@ -0,0 +1,19 @@
package com.baeldung.argsVsvarargs;
public class StringArrayAndVarargs {
public static void capitalizeNames(String[] args) {
for(int i = 0; i < args.length; i++){
args[i] = args[i].toUpperCase();
}
}
public static String[] firstLetterOfWords(String... args) {
String[] firstLetter = new String[args.length];
for(int i = 0; i < args.length; i++){
firstLetter[i] = String.valueOf(args[i].charAt(0));
}
return firstLetter;
}
}

View File

@ -0,0 +1,24 @@
package com.baeldung.argsVsvarargs;
import static com.baeldung.argsVsvarargs.StringArrayAndVarargs.capitalizeNames;
import static com.baeldung.argsVsvarargs.StringArrayAndVarargs.firstLetterOfWords;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class StringArrayAndVarargsUnitTest {
@Test
void whenCheckingArgumentClassName_thenNameShouldBeStringArray() {
String[] names = {"john", "ade", "kofi", "imo"};
assertNotNull(names);
assertEquals("java.lang.String[]", names.getClass().getTypeName());
capitalizeNames(names);
}
@Test
void whenCheckingReturnedObjectClass_thenClassShouldBeStringArray() {
assertEquals(String[].class, firstLetterOfWords("football", "basketball", "volleyball").getClass());
assertEquals(3, firstLetterOfWords("football", "basketball", "volleyball").length);
}
}

View File

@ -0,0 +1,78 @@
package com.baeldung.array.isobjectarray;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Array;
import org.junit.jupiter.api.Test;
public class CheckObjectIsArrayUnitTest {
private static final Object ARRAY_INT = new int[] { 1, 2, 3, 4, 5 };
private static final Object ARRAY_PERSON = new Person[] { new Person("Jackie Chan", "Hong Kong"), new Person("Tom Hanks", "United States") };
boolean isArray(Object obj) {
return obj instanceof Object[] || obj instanceof boolean[] || obj instanceof byte[] || obj instanceof short[] || obj instanceof char[] || obj instanceof int[] || obj instanceof long[] || obj instanceof float[] || obj instanceof double[];
}
@Test
void givenAnArrayObject_whenUsingInstanceof_getExpectedResult() {
assertTrue(ARRAY_PERSON instanceof Object[]);
assertFalse(ARRAY_INT instanceof Object[]);
assertTrue(ARRAY_INT instanceof int[]);
}
@Test
void givenAnArrayObject_whenUsingOurIsArray_getExpectedResult() {
assertTrue(isArray(ARRAY_PERSON));
assertTrue(isArray(ARRAY_INT));
}
@Test
void givenAnArrayObject_whenUsingClassIsArray_getExpectedResult() {
assertTrue(ARRAY_INT.getClass()
.isArray());
assertTrue(ARRAY_PERSON.getClass()
.isArray());
assertEquals(Person.class, ARRAY_PERSON.getClass()
.getComponentType());
assertEquals(int.class, ARRAY_INT.getClass()
.getComponentType());
}
@Test
void givenAnArrayObject_whenUsingArrayGet_getExpectedElement() {
if (ARRAY_PERSON.getClass()
.isArray() && ARRAY_PERSON.getClass()
.getComponentType() == Person.class) {
Person person = (Person) Array.get(ARRAY_PERSON, 1);
assertEquals("Tom Hanks", person.getName());
}
if (ARRAY_INT.getClass()
.isArray() && ARRAY_INT.getClass()
.getComponentType() == int.class) {
assertEquals(2, ((int) Array.get(ARRAY_INT, 1)));
}
}
}
class Person {
private String name;
private String Location;
public Person(String name, String location) {
this.name = name;
this.Location = location;
}
public String getName() {
return name;
}
public String getLocation() {
return Location;
}
}

View File

@ -1,7 +1,7 @@
package com.baeldung.concurrent.atomic; package com.baeldung.concurrent.atomic;
public class SafeCounterWithLock { public class SafeCounterWithLock {
private volatile int counter; private int counter;
int getValue() { int getValue() {
return counter; return counter;

View File

@ -10,12 +10,6 @@ public class SafeCounterWithoutLock {
} }
void increment() { void increment() {
while(true) { counter.incrementAndGet();
int existingValue = getValue();
int newValue = existingValue + 1;
if(counter.compareAndSet(existingValue, newValue)) {
return;
}
}
} }
} }

View File

@ -17,8 +17,8 @@ public class ThreadSafeCounterIntegrationTest {
SafeCounterWithLock safeCounter = new SafeCounterWithLock(); SafeCounterWithLock safeCounter = new SafeCounterWithLock();
IntStream.range(0, 1000) IntStream.range(0, 1000)
.forEach(count -> service.submit(safeCounter::increment)); .forEach(count -> service.execute(safeCounter::increment));
service.awaitTermination(100, TimeUnit.MILLISECONDS); shutdownAndAwaitTermination(service);
assertEquals(1000, safeCounter.getValue()); assertEquals(1000, safeCounter.getValue());
} }
@ -29,10 +29,30 @@ public class ThreadSafeCounterIntegrationTest {
SafeCounterWithoutLock safeCounter = new SafeCounterWithoutLock(); SafeCounterWithoutLock safeCounter = new SafeCounterWithoutLock();
IntStream.range(0, 1000) IntStream.range(0, 1000)
.forEach(count -> service.submit(safeCounter::increment)); .forEach(count -> service.execute(safeCounter::increment));
service.awaitTermination(100, TimeUnit.MILLISECONDS); shutdownAndAwaitTermination(service);
assertEquals(1000, safeCounter.getValue()); assertEquals(1000, safeCounter.getValue());
} }
private void shutdownAndAwaitTermination(ExecutorService pool) {
// Disable new tasks from being submitted
pool.shutdown();
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS)) {
// Cancel currently executing tasks forcefully
pool.shutdownNow();
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ex) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
} }

View File

@ -0,0 +1,25 @@
<?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-hex</artifactId>
<version>0.1.0-SNAPSHOT</version>
<name>core-java-hex</name>
<packaging>jar</packaging>
<parent>
<groupId>com.baeldung.core-java-modules</groupId>
<artifactId>core-java-modules</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
</dependencies>
<properties>
</properties>
</project>

View File

@ -0,0 +1,25 @@
package com.baeldung.hextorgb;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class HexToRgbUnitTest {
@Test
public void givenHexCode_whenConvertedToRgb_thenCorrectRgbValuesAreReturned() {
String hexCode = "FF9933";
int red = 255;
int green = 153;
int blue = 51;
int resultRed = Integer.valueOf(hexCode.substring(0, 2), 16);
int resultGreen = Integer.valueOf(hexCode.substring(2, 4), 16);
int resultBlue = Integer.valueOf(hexCode.substring(4, 6), 16);
assertEquals(red, resultRed);
assertEquals(green, resultGreen);
assertEquals(blue, resultBlue);
}
}

View File

@ -0,0 +1,18 @@
package com.baeldung.firstocurrenceofaninteger;
public class FirstOccurrenceOfAnInteger {
static Integer findFirstInteger(String s) {
int i = 0;
while (i < s.length() && !Character.isDigit(s.charAt(i))) {
i++;
}
int j = i;
while (j < s.length() && Character.isDigit(s.charAt(j))) {
j++;
}
return Integer.parseInt(s.substring(i, j));
}
}

View File

@ -0,0 +1,46 @@
package com.baeldung.firstocurrenceofaninteger;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
class FirstOccurrenceOfAnIntegerUnitTest {
@Test
void whenUsingPatternMatcher_findFirstInteger() {
String s = "ba31dung123";
Matcher matcher = Pattern.compile("\\d+").matcher(s);
matcher.find();
int i = Integer.parseInt(matcher.group());
Assertions.assertEquals(31, i);
}
@Test
void whenUsingScanner_findFirstInteger() {
int i = new Scanner("ba31dung123").useDelimiter("\\D+").nextInt();
Assertions.assertEquals(31, i);
}
@Test
void whenUsingSplit_findFirstInteger() {
String str = "ba31dung123";
List<String> tokens = Arrays.stream(str.split("\\D+"))
.filter(s -> s.length() > 0).collect(Collectors.toList());
Assertions.assertEquals(31, Integer.parseInt(tokens.get(0)));
}
@Test
void whenUsingCustomMethod_findFirstInteger() {
String str = "ba31dung123";
Integer i = FirstOccurrenceOfAnInteger.findFirstInteger(str);
Assertions.assertEquals(31, i);
}
}

View File

@ -62,6 +62,7 @@
<module>core-java-exceptions-4</module> <module>core-java-exceptions-4</module>
<module>core-java-function</module> <module>core-java-function</module>
<module>core-java-functional</module> <module>core-java-functional</module>
<module>core-java-hex</module>
<module>core-java-io</module> <module>core-java-io</module>
<module>core-java-io-2</module> <module>core-java-io-2</module>
<module>core-java-io-3</module> <module>core-java-io-3</module>

View File

@ -23,7 +23,7 @@
</dependencies> </dependencies>
<properties> <properties>
<guice.version>4.1.0</guice.version> <guice.version>5.0.1</guice.version>
</properties> </properties>
</project> </project>

View File

@ -136,7 +136,7 @@
<resultsDirectory>${project.basedir}/src/main/resources/dashboard</resultsDirectory> <resultsDirectory>${project.basedir}/src/main/resources/dashboard</resultsDirectory>
<generateReports>true</generateReports> <generateReports>true</generateReports>
<ignoreResultFailures>true</ignoreResultFailures> <ignoreResultFailures>true</ignoreResultFailures>
<testResultsTimestamp>false</testResultsTimestamp> <testResultsTimestamp>true</testResultsTimestamp>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>

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>rethinkdb</artifactId>
<name>rethinkdb</name>
<description>Code snippets for RethinkDB articles</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.rethinkdb</groupId>
<artifactId>rethinkdb-driver</artifactId>
<version>2.4.4</version>
</dependency>
<!-- utils -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<properties>
<java.version>17</java.version>
</properties>
</project>

View File

@ -0,0 +1,62 @@
package com.baeldung.rethinkdb;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static com.rethinkdb.RethinkDB.r;
/**
* Some tests demonstrating inserting data.
*/
public class InsertIntegrationTest extends TestBase {
/**
* Create a table for the tests.
*/
@BeforeEach
public void createTable() {
r.db(DB_NAME).tableCreate(tableName).run(conn);
}
/**
* Insert a single simple record into the database.
*/
@Test
public void insertSimpleRecord() {
r.db(DB_NAME).table(tableName)
.insert(
r.hashMap()
.with("name", "Baeldung")
)
.run(conn);
}
@Test
public void insertMap() {
r.db(DB_NAME).table(tableName)
.insert(
Map.of("name", "Baeldung")
)
.run(conn);
}
@Test
public void insertComplex() {
r.db(DB_NAME).table(tableName)
.insert(
r.hashMap()
.with("name", "Baeldung")
.with("articles", r.array(
r.hashMap()
.with("name", "String Interpolation in Java")
.with("url", "https://www.baeldung.com/java-string-interpolation"),
r.hashMap()
.with("name", "Access HTTPS REST Service Using Spring RestTemplate")
.with("url", "https://www.baeldung.com/spring-resttemplate-secure-https-service")
)
)
)
.run(conn);
}
}

View File

@ -0,0 +1,81 @@
package com.baeldung.rethinkdb;
import com.rethinkdb.net.Result;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.rethinkdb.RethinkDB.r;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Some tests demonstrating querying data.
*/
public class QueryIntegrationTest extends TestBase {
/**
* Create a table for the tests.
*/
@BeforeEach
public void createTable() {
r.db(DB_NAME).tableCreate(tableName).run(conn);
r.db(DB_NAME).table(tableName)
.insert(
r.hashMap()
.with("id", "article1")
.with("name", "String Interpolation in Java")
.with("url", "https://www.baeldung.com/java-string-interpolation")
).run(conn);
r.db(DB_NAME).table(tableName)
.insert(
r.hashMap()
.with("id", "article2")
.with("name", "Access HTTPS REST Service Using Spring RestTemplate")
.with("url", "https://www.baeldung.com/spring-resttemplate-secure-https-service")
).run(conn);
}
@Test
public void listAll() {
Result<Map> results = r.db(DB_NAME).table(tableName).run(conn, Map.class);
// We can't ensure the order the results come back in.
Set<String> expected = new HashSet<>(Set.of(
"String Interpolation in Java",
"Access HTTPS REST Service Using Spring RestTemplate"
));
for (Map result : results) {
assertTrue(expected.remove(result.get("name")));
}
assertTrue(expected.isEmpty());
}
@Test
public void listSome() {
Result<Map> results = r.db(DB_NAME)
.table(tableName)
.filter(r -> r.g("name").eq("String Interpolation in Java"))
.run(conn, Map.class);
// We can't ensure the order the results come back in.
Set<String> expected = Set.of("https://www.baeldung.com/java-string-interpolation");
assertEquals(expected, results.stream()
.map(r -> r.get("url"))
.collect(Collectors.toSet()));
}
@Test
public void getByKey() {
Result<Map> results = r.db(DB_NAME).table(tableName).get("article1").run(conn, Map.class);
assertEquals("String Interpolation in Java", results.first().get("name"));
}
}

View File

@ -0,0 +1,117 @@
package com.baeldung.rethinkdb;
import com.rethinkdb.net.Result;
import org.junit.jupiter.api.Test;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static com.rethinkdb.RethinkDB.r;
/**
* Some tests demonstrating streaming live changes to data.
*/
public class StreamingIntegrationTest extends TestBase {
@Test
public void getLiveInserts() throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
r.db(DB_NAME).tableCreate(tableName).run(conn);
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", 0)).run(conn);
executorService.submit(() -> {
Result<Map> cursor = r.db(DB_NAME).table(tableName).changes().run(conn, Map.class);
cursor.stream().forEach(record -> System.out.println("Record: " + record));
});
for (int i = 0; i < 10; ++i) {
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", i)).run(conn);
TimeUnit.MILLISECONDS.sleep(100);
}
executorService.shutdownNow();
}
@Test
public void getSomeLiveInserts() throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
r.db(DB_NAME).tableCreate(tableName).run(conn);
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", 0)).run(conn);
executorService.submit(() -> {
Result<Map> cursor = r.db(DB_NAME).table(tableName)
.filter(r -> r.g("index").eq(5))
.changes()
.run(conn, Map.class);
cursor.stream().forEach(record -> System.out.println("Record: " + record));
});
for (int i = 0; i < 10; ++i) {
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", i)).run(conn);
TimeUnit.MILLISECONDS.sleep(100);
}
executorService.shutdownNow();
}
@Test
public void getLiveUpdates() throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
r.db(DB_NAME).tableCreate(tableName).run(conn);
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", 0)).run(conn);
executorService.submit(() -> {
Result<Map> cursor = r.db(DB_NAME).table(tableName).changes().run(conn, Map.class);
cursor.stream().forEach(record -> System.out.println("Record: " + record));
});
for (int i = 0; i < 10; ++i) {
r.db(DB_NAME).table(tableName).update(r.hashMap().with("index", i)).run(conn);
TimeUnit.MILLISECONDS.sleep(100);
}
executorService.shutdownNow();
}
@Test
public void getLiveDeletes() throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
r.db(DB_NAME).tableCreate(tableName).run(conn);
for (int i = 0; i < 10; ++i) {
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", i)).run(conn);
}
executorService.submit(() -> {
Result<Map> cursor = r.db(DB_NAME).table(tableName).changes().run(conn, Map.class);
cursor.stream().forEach(record -> System.out.println("Record: " + record));
});
r.db(DB_NAME).table(tableName)
.filter(r -> r.g("index").eq(1))
.delete()
.run(conn);
r.db(DB_NAME).table(tableName)
.filter(r -> r.g("index").eq(3))
.delete()
.run(conn);
r.db(DB_NAME).table(tableName)
.filter(r -> r.g("index").eq(5))
.delete()
.run(conn);
executorService.shutdownNow();
}
}

View File

@ -0,0 +1,39 @@
package com.baeldung.rethinkdb;
import com.rethinkdb.gen.exc.ReqlOpFailedError;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
import static com.rethinkdb.RethinkDB.r;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Some tests demonstrating working with tables.
*/
public class TablesIntegrationTest extends TestBase {
@Test
public void createTable() {
r.db(DB_NAME).tableCreate(tableName).run(conn);
}
@Test
public void createTableTwice() {
r.db(DB_NAME).tableCreate(tableName).run(conn);
Assertions.assertThrows(ReqlOpFailedError.class, () -> {
r.db(DB_NAME).tableCreate(tableName).run(conn);
});
}
@Test
public void listTables() {
r.db(DB_NAME).tableCreate(tableName).run(conn);
List<String> tables = r.db(DB_NAME).tableList().run(conn, List.class).first();
assertTrue(tables.contains(tableName));
}
}

View File

@ -0,0 +1,44 @@
package com.baeldung.rethinkdb;
import com.rethinkdb.net.Connection;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import java.util.UUID;
import static com.rethinkdb.RethinkDB.r;
/**
* Base class for RethinkDB tests.
*/
public class TestBase {
/** The database name to work with */
protected static final String DB_NAME = "test";
/** A randomly generated table name so they never collide */
protected final String tableName = UUID.randomUUID().toString().replaceAll("-","");
/** A database connection */
protected Connection conn;
/**
* Connect to the database for each test
*/
@BeforeEach
public void connect() {
conn = r.connection()
.hostname("localhost")
.port(28015)
.connect();
}
/**
* Disconnect from the database after each test
*/
@AfterEach
public void disconnect() {
if (this.conn != null) {
conn.close();
}
}
}

View File

@ -0,0 +1,55 @@
package com.baeldung.rethinkdb;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static com.rethinkdb.RethinkDB.r;
/**
* Some tests demonstrating updating data.
*/
public class UpdateIntegrationTest extends TestBase {
/**
* Create a table for the tests.
*/
@BeforeEach
public void createTable() {
r.db(DB_NAME).tableCreate(tableName).run(conn);
r.db(DB_NAME).table(tableName)
.insert(
r.hashMap()
.with("id", "article1")
.with("name", "String Interpolation in Java")
.with("url", "https://www.baeldung.com/java-string-interpolation")
).run(conn);
r.db(DB_NAME).table(tableName)
.insert(
r.hashMap()
.with("id", "article2")
.with("name", "Access HTTPS REST Service Using Spring RestTemplate")
.with("url", "https://www.baeldung.com/spring-resttemplate-secure-https-service")
).run(conn);
}
@Test
public void updateAll() {
r.db(DB_NAME).table(tableName).update(r.hashMap().with("site", "Baeldung")).run(conn);
}
@Test
public void updateSome() {
r.db(DB_NAME).table(tableName)
.filter(r -> r.g("name").eq("String Interpolation in Java"))
.update(r.hashMap().with("category", "java"))
.run(conn);
}
@Test
public void delete() {
r.db(DB_NAME).table(tableName)
.filter(r -> r.g("name").eq("String Interpolation in Java"))
.delete()
.run(conn);
}
}

View File

@ -4,8 +4,6 @@ import org.bson.BsonBinary;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import com.mongodb.client.vault.ClientEncryption;
@Configuration @Configuration
public class EncryptionConfig { public class EncryptionConfig {
@ -21,18 +19,8 @@ public class EncryptionConfig {
@Value("${com.baeldung.csfle.auto-decryption:false}") @Value("${com.baeldung.csfle.auto-decryption:false}")
private Boolean autoDecryption; private Boolean autoDecryption;
private ClientEncryption encryption;
private BsonBinary dataKeyId; private BsonBinary dataKeyId;
public void setEncryption(ClientEncryption encryption) {
this.encryption = encryption;
}
public ClientEncryption getEncryption() {
return encryption;
}
public void setDataKeyId(BsonBinary dataKeyId) { public void setDataKeyId(BsonBinary dataKeyId) {
this.dataKeyId = dataKeyId; this.dataKeyId = dataKeyId;
} }

View File

@ -11,12 +11,12 @@ import org.bson.Document;
import org.bson.conversions.Bson; import org.bson.conversions.Bson;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
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 com.baeldung.boot.csfle.config.converter.IntegerConverter; import com.baeldung.boot.csfle.config.converter.BinaryConverter;
import com.baeldung.boot.csfle.config.converter.StringConverter;
import com.mongodb.AutoEncryptionSettings; import com.mongodb.AutoEncryptionSettings;
import com.mongodb.ClientEncryptionSettings; import com.mongodb.ClientEncryptionSettings;
import com.mongodb.ConnectionString; import com.mongodb.ConnectionString;
@ -52,16 +52,17 @@ public class MongoClientConfig extends AbstractMongoClientConfiguration {
@Override @Override
public MongoCustomConversions customConversions() { public MongoCustomConversions customConversions() {
return new MongoCustomConversions(Arrays.asList(new StringConverter(encryptionConfig), new IntegerConverter(encryptionConfig))); return new MongoCustomConversions(Arrays.asList(new BinaryConverter()));
} }
@Bean
@Override @Override
public MongoClient mongoClient() { public MongoClient mongoClient() {
MongoClient client; MongoClient client;
try { try {
client = MongoClients.create(clientSettings()); client = MongoClients.create(clientSettings());
ClientEncryption encryption = createClientEncryption(); ClientEncryption encryption = clientEncryption();
encryptionConfig.setDataKeyId(createOrRetrieveDataKey(client, encryption)); encryptionConfig.setDataKeyId(createOrRetrieveDataKey(client, encryption));
return client; return client;
@ -70,6 +71,19 @@ public class MongoClientConfig extends AbstractMongoClientConfiguration {
} }
} }
@Bean
public ClientEncryption clientEncryption() throws FileNotFoundException, IOException {
Map<String, Map<String, Object>> kmsProviders = LocalKmsUtils.providersMap(encryptionConfig.getMasterKeyPath());
ClientEncryptionSettings encryptionSettings = ClientEncryptionSettings.builder()
.keyVaultMongoClientSettings(clientSettings())
.keyVaultNamespace(encryptionConfig.getKeyVaultNamespace())
.kmsProviders(kmsProviders)
.build();
return ClientEncryptions.create(encryptionSettings);
}
private BsonBinary createOrRetrieveDataKey(MongoClient client, ClientEncryption encryption) { private BsonBinary createOrRetrieveDataKey(MongoClient client, ClientEncryption encryption) {
MongoNamespace namespace = new MongoNamespace(encryptionConfig.getKeyVaultNamespace()); MongoNamespace namespace = new MongoNamespace(encryptionConfig.getKeyVaultNamespace());
MongoCollection<Document> keyVault = client.getDatabase(namespace.getDatabaseName()) MongoCollection<Document> keyVault = client.getDatabase(namespace.getDatabaseName())
@ -92,19 +106,6 @@ public class MongoClientConfig extends AbstractMongoClientConfiguration {
} }
} }
private ClientEncryption createClientEncryption() throws FileNotFoundException, IOException {
Map<String, Map<String, Object>> kmsProviders = LocalKmsUtils.providersMap(encryptionConfig.getMasterKeyPath());
ClientEncryptionSettings encryptionSettings = ClientEncryptionSettings.builder()
.keyVaultMongoClientSettings(clientSettings())
.keyVaultNamespace(encryptionConfig.getKeyVaultNamespace())
.kmsProviders(kmsProviders)
.build();
encryptionConfig.setEncryption(ClientEncryptions.create(encryptionSettings));
return encryptionConfig.getEncryption();
}
private MongoClientSettings clientSettings() throws FileNotFoundException, IOException { private MongoClientSettings clientSettings() throws FileNotFoundException, IOException {
Builder settings = MongoClientSettings.builder() Builder settings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(uri)); .applyConnectionString(new ConnectionString(uri));

View File

@ -0,0 +1,13 @@
package com.baeldung.boot.csfle.config.converter;
import org.bson.BsonBinary;
import org.bson.types.Binary;
import org.springframework.core.convert.converter.Converter;
public class BinaryConverter implements Converter<Binary, BsonBinary> {
@Override
public BsonBinary convert(Binary source) {
return new BsonBinary(source.getType(), source.getData());
}
}

View File

@ -1,27 +0,0 @@
package com.baeldung.boot.csfle.config.converter;
import org.bson.BsonBinary;
import org.bson.BsonValue;
import org.bson.types.Binary;
import org.springframework.core.convert.converter.Converter;
import com.baeldung.boot.csfle.config.EncryptionConfig;
public class IntegerConverter implements Converter<Binary, Integer> {
private EncryptionConfig encryptionConfig;
public IntegerConverter(EncryptionConfig config) {
this.encryptionConfig = config;
}
@Override
public Integer convert(Binary source) {
BsonBinary bin = new BsonBinary(source.getType(), source.getData());
BsonValue value = encryptionConfig.getEncryption()
.decrypt(bin);
return value.asInt32()
.getValue();
}
}

View File

@ -1,27 +0,0 @@
package com.baeldung.boot.csfle.config.converter;
import org.bson.BsonBinary;
import org.bson.BsonValue;
import org.bson.types.Binary;
import org.springframework.core.convert.converter.Converter;
import com.baeldung.boot.csfle.config.EncryptionConfig;
public class StringConverter implements Converter<Binary, String> {
private EncryptionConfig encryptionConfig;
public StringConverter(EncryptionConfig config) {
this.encryptionConfig = config;
}
@Override
public String convert(Binary source) {
BsonBinary bin = new BsonBinary(source.getType(), source.getData());
BsonValue value = encryptionConfig.getEncryption()
.decrypt(bin);
return value.asString()
.getValue();
}
}

View File

@ -1,6 +1,7 @@
package com.baeldung.boot.csfle.service; package com.baeldung.boot.csfle.service;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.bson.BsonBinary; import org.bson.BsonBinary;
import org.bson.BsonInt32; import org.bson.BsonInt32;
@ -16,6 +17,7 @@ import com.baeldung.boot.csfle.config.EncryptionConfig;
import com.baeldung.boot.csfle.data.Citizen; import com.baeldung.boot.csfle.data.Citizen;
import com.baeldung.boot.csfle.data.EncryptedCitizen; import com.baeldung.boot.csfle.data.EncryptedCitizen;
import com.mongodb.client.model.vault.EncryptOptions; import com.mongodb.client.model.vault.EncryptOptions;
import com.mongodb.client.vault.ClientEncryption;
@Service @Service
public class CitizenService { public class CitizenService {
@ -29,6 +31,9 @@ public class CitizenService {
@Autowired @Autowired
private EncryptionConfig encryptionConfig; private EncryptionConfig encryptionConfig;
@Autowired
private ClientEncryption clientEncryption;
public EncryptedCitizen save(Citizen citizen) { public EncryptedCitizen save(Citizen citizen) {
EncryptedCitizen encryptedCitizen = new EncryptedCitizen(citizen); EncryptedCitizen encryptedCitizen = new EncryptedCitizen(citizen);
encryptedCitizen.setEmail(encrypt(citizen.getEmail(), DETERMINISTIC_ALGORITHM)); encryptedCitizen.setEmail(encrypt(citizen.getEmail(), DETERMINISTIC_ALGORITHM));
@ -38,26 +43,68 @@ public class CitizenService {
} }
public List<Citizen> findAll() { public List<Citizen> findAll() {
return mongo.findAll(Citizen.class); if (!encryptionConfig.getAutoDecryption()) {
List<EncryptedCitizen> allEncrypted = mongo.findAll(EncryptedCitizen.class);
return allEncrypted.stream()
.map(this::decrypt)
.collect(Collectors.toList());
} else {
return mongo.findAll(Citizen.class);
}
} }
public Citizen findByEmail(String email) { public Citizen findByEmail(String email) {
Query byEmail = new Query(Criteria.where("email") Query byEmail = new Query(Criteria.where("email")
.is(encrypt(email, DETERMINISTIC_ALGORITHM))); .is(encrypt(email, DETERMINISTIC_ALGORITHM)));
return mongo.findOne(byEmail, Citizen.class); if (!encryptionConfig.getAutoDecryption()) {
EncryptedCitizen encryptedCitizen = mongo.findOne(byEmail, EncryptedCitizen.class);
return decrypt(encryptedCitizen);
} else {
return mongo.findOne(byEmail, Citizen.class);
}
} }
public BsonBinary encrypt(Object value, String algorithm) { public BsonBinary encrypt(Object value, String algorithm) {
if (value == null) if (value == null)
return null; return null;
BsonValue bsonValue = value instanceof Integer BsonValue bsonValue;
? new BsonInt32((Integer) value) if (value instanceof Integer) {
: new BsonString(value.toString()); bsonValue = new BsonInt32((Integer) value);
} else if (value instanceof String) {
bsonValue = new BsonString((String) value);
} else {
throw new IllegalArgumentException("unsupported type: " + value.getClass());
}
EncryptOptions options = new EncryptOptions(algorithm); EncryptOptions options = new EncryptOptions(algorithm);
options.keyId(encryptionConfig.getDataKeyId()); options.keyId(encryptionConfig.getDataKeyId());
return encryptionConfig.getEncryption() return clientEncryption.encrypt(bsonValue, options);
.encrypt(bsonValue, options); }
public BsonValue decryptProperty(BsonBinary value) {
if (value == null)
return null;
return clientEncryption.decrypt(value);
}
private Citizen decrypt(EncryptedCitizen encrypted) {
Citizen citizen = new Citizen(encrypted);
BsonValue decryptedBirthYear = decryptProperty(encrypted.getBirthYear());
if (decryptedBirthYear != null) {
citizen.setBirthYear(decryptedBirthYear.asInt32()
.intValue());
}
BsonValue decryptedEmail = decryptProperty(encrypted.getEmail());
if (decryptedEmail != null) {
citizen.setEmail(decryptedEmail.asString()
.getValue());
}
return citizen;
} }
} }

View File

@ -5,6 +5,9 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-jpa-query-3</artifactId> <artifactId>spring-data-jpa-query-3</artifactId>
<name>spring-data-jpa-query-3</name> <name>spring-data-jpa-query-3</name>
<properties>
<javafaker.version>0.15</javafaker.version>
</properties>
<parent> <parent>
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
@ -22,6 +25,11 @@
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>${javafaker.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>

View File

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

View File

@ -0,0 +1,61 @@
package com.baeldung.spring.data.jpa.collectionsvsstream;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "_user")
public class User {
private String firstName;
private String lastName;
private int age;
@Id
private int id;
public User() {
}
public User(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public User(String firstName, String lastName, int age, int id) {
this(firstName, lastName, age);
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.spring.data.jpa.collectionsvsstream;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, String> {
Stream<User> findAllByAgeGreaterThan(int age);
List<User> findByAgeGreaterThan(int age);
}

View File

@ -0,0 +1,58 @@
package com.baeldung.spring.data.jpa.collectionsvsstream;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import com.github.javafaker.Faker;
@DataJpaTest
class UserRepositoryUnitTest {
@Autowired
private UserRepository userRepository;
@BeforeEach
public void setup() {
Faker faker = new Faker();
List<User> people = IntStream.range(1, 100)
.parallel()
.mapToObj(i -> new User(faker.name()
.firstName(), faker.name()
.lastName(), faker.number()
.numberBetween(1, 100), i))
.collect(Collectors.toList());
userRepository.saveAll(people);
}
@AfterEach
public void tearDown() {
userRepository.deleteAll();
}
@Test
public void whenAgeIs20_thenItShouldReturnAllUsersWhoseAgeIsGreaterThan20InAList() {
List<User> users = userRepository.findByAgeGreaterThan(20);
assertThat(users).isNotEmpty();
assertThat(users.stream()
.map(User::getAge)
.allMatch(age -> age > 20)).isTrue();
}
@Test
public void whenAgeIs20_thenItShouldReturnAllUsersWhoseAgeIsGreaterThan20InAStream() {
Stream<User> users = userRepository.findAllByAgeGreaterThan(20);
assertThat(users).isNotNull();
assertThat(users.map(User::getAge)
.allMatch(age -> age > 20)).isTrue();
}
}

25
pom.xml
View File

@ -332,12 +332,6 @@
<module>apache-cxf-modules</module> <module>apache-cxf-modules</module>
<module>apache-libraries</module> <module>apache-libraries</module>
<module>apache-poi</module>
<module>apache-velocity</module>
<module>di-modules</module>
<module>asciidoctor</module>
<module>aws-modules</module>
<module>azure</module> <module>azure</module>
<module>checker-plugin</module> <module>checker-plugin</module>
@ -614,12 +608,6 @@
<module>apache-cxf-modules</module> <module>apache-cxf-modules</module>
<module>apache-libraries</module> <module>apache-libraries</module>
<module>apache-poi</module>
<module>apache-velocity</module>
<module>di-modules</module>
<module>asciidoctor</module>
<module>aws-modules</module>
<module>azure</module> <module>azure</module>
<module>checker-plugin</module> <module>checker-plugin</module>
@ -907,6 +895,11 @@
<modules> <modules>
<module>algorithms-modules</module> <module>algorithms-modules</module>
<module>apache-poi</module>
<module>apache-velocity</module>
<module>di-modules</module>
<module>asciidoctor</module>
<module>aws-modules</module>
<module>core-java-modules/core-java-9</module> <module>core-java-modules/core-java-9</module>
<module>core-java-modules/core-java-9-improvements</module> <module>core-java-modules/core-java-9-improvements</module>
<module>core-java-modules/core-java-9-jigsaw</module> <module>core-java-modules/core-java-9-jigsaw</module>
@ -963,6 +956,7 @@
<module>spring-boot-modules/spring-boot-3</module> <module>spring-boot-modules/spring-boot-3</module>
<module>spring-boot-modules/spring-boot-3-native</module> <module>spring-boot-modules/spring-boot-3-native</module>
<module>spring-boot-modules/spring-boot-3-observation</module> <module>spring-boot-modules/spring-boot-3-observation</module>
<module>spring-boot-modules/spring-boot-3-test-pitfalls</module>
<module>spring-swagger-codegen/custom-validations-opeanpi-codegen</module> <module>spring-swagger-codegen/custom-validations-opeanpi-codegen</module>
<module>testing-modules/testing-assertions</module> <module>testing-modules/testing-assertions</module>
<module>persistence-modules/fauna</module> <module>persistence-modules/fauna</module>
@ -1106,6 +1100,12 @@
<modules> <modules>
<module>algorithms-modules</module> <module>algorithms-modules</module>
<module>apache-poi</module>
<module>apache-velocity</module>
<module>di-modules</module>
<module>asciidoctor</module>
<module>aws-modules</module>
<module>core-java-modules/core-java-9</module> <module>core-java-modules/core-java-9</module>
<module>core-java-modules/core-java-9-improvements</module> <module>core-java-modules/core-java-9-improvements</module>
<module>core-java-modules/core-java-9-jigsaw</module> <module>core-java-modules/core-java-9-jigsaw</module>
@ -1162,6 +1162,7 @@
<module>spring-boot-modules/spring-boot-3</module> <module>spring-boot-modules/spring-boot-3</module>
<module>spring-boot-modules/spring-boot-3-native</module> <module>spring-boot-modules/spring-boot-3-native</module>
<module>spring-boot-modules/spring-boot-3-observation</module> <module>spring-boot-modules/spring-boot-3-observation</module>
<module>spring-boot-modules/spring-boot-3-test-pitfalls</module>
<module>spring-swagger-codegen/custom-validations-opeanpi-codegen</module> <module>spring-swagger-codegen/custom-validations-opeanpi-codegen</module>
<module>testing-modules/testing-assertions</module> <module>testing-modules/testing-assertions</module>
<module>persistence-modules/fauna</module> <module>persistence-modules/fauna</module>

View File

@ -16,6 +16,11 @@
<quarkus.platform.version>2.16.0.Final</quarkus.platform.version> <quarkus.platform.version>2.16.0.Final</quarkus.platform.version>
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version> <surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
</properties> </properties>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>quarkus-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>

View File

@ -0,0 +1,87 @@
<?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-boot-3-test-pitfalls</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-3-test-pitfalls</name>
<description>Demo project for Spring Boot Testing Pitfalls</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<properties>
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
</properties>
</project>

View File

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

View File

@ -0,0 +1,10 @@
package com.baeldung.sample.pets.boundary;
import lombok.Data;
@Data
public class PetDto {
private String name;
}

View File

@ -0,0 +1,13 @@
package com.baeldung.sample.pets.boundary;
import com.baeldung.sample.pets.domain.Pet;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public interface PetDtoMapper {
PetDto map(Pet source);
Pet map(PetDto source);
}

View File

@ -0,0 +1,28 @@
package com.baeldung.sample.pets.boundary;
import com.baeldung.sample.pets.domain.PetService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/pets")
@RequiredArgsConstructor
public class PetsController {
private final PetService service;
private final PetDtoMapper mapper;
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public Collection<PetDto> readAll() {
return service.getPets().stream()
.map(mapper::map)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,4 @@
package com.baeldung.sample.pets.domain;
public record Pet(String name) {
}

View File

@ -0,0 +1,14 @@
package com.baeldung.sample.pets.domain;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Delegate;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class PetService {
@Delegate
private final PetServiceRepository repo;
}

View File

@ -0,0 +1,13 @@
package com.baeldung.sample.pets.domain;
import java.util.Collection;
public interface PetServiceRepository {
boolean add(Pet pet);
void clear();
Collection<Pet> getPets();
}

View File

@ -0,0 +1,29 @@
package com.baeldung.sample.pets.domain;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@Component
public class PetServiceRepositoryImpl implements PetServiceRepository {
private final Set<Pet> pets = new HashSet<>();
@Override
public Set<Pet> getPets() {
return Collections.unmodifiableSet(pets);
}
@Override
public boolean add(Pet pet) {
return this.pets.add(pet);
}
@Override
public void clear() {
this.pets.clear();
}
}

View File

@ -0,0 +1,47 @@
package com.baeldung.sample.pets.boundary;
import com.baeldung.sample.pets.domain.PetService;
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.mock.mockito.MockReset;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@ExtendWith(SpringExtension.class)
public class PetDtoMapperIntegrationTest {
@Configuration
@ComponentScan(basePackageClasses = PetDtoMapper.class)
static class PetDtoMapperTestConfig {
/*
* This would be necessary because the controller is also initialized
* and needs the service, although we do not want to test it here.
*
* Solutions:
* - place the mapper into a separate sub package
* - do not test the mapper separately, test it integrated within the controller
* (recommended)
*/
@Bean
PetService createServiceMock() {
return mock(PetService.class, MockReset.withSettings(MockReset.AFTER));
}
}
@Autowired
PetDtoMapper mapper;
@Test
void shouldExist() { // simply test correct test setup
assertThat(mapper).isNotNull();
}
}

View File

@ -0,0 +1,10 @@
package com.baeldung.sample.pets.boundary;
import org.springframework.context.annotation.ComponentScan;
/**
* Just an interface to use for compiler-checked component scanning during tests.
* @see ComponentScan#basePackageClasses()
*/
public interface PetsBoundaryLayer {
}

View File

@ -0,0 +1,36 @@
package com.baeldung.sample.pets.boundary;
import com.baeldung.sample.pets.domain.PetService;
import com.baeldung.sample.test.slices.PetsBoundaryTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Collections;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@PetsBoundaryTest
class PetsControllerMvcIntegrationTest {
@Autowired
MockMvc mvc;
@Autowired
PetService service;
@Test
void shouldReturnEmptyArrayWhenGetPets() throws Exception {
when(service.getPets()).thenReturn(Collections.emptyList());
mvc.perform(
get("/pets")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(content().string("[]"));
}
}

View File

@ -0,0 +1,26 @@
package com.baeldung.sample.pets.domain;
import com.baeldung.sample.test.slices.PetsDomainTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@PetsDomainTest
class PetServiceIntegrationTest {
@Autowired
PetService service;
@Autowired // Mock
PetServiceRepository repository;
@Test
void shouldAddPetWhenNotAlreadyExisting() {
var pet = new Pet("Dog");
when(repository.add(pet)).thenReturn(true);
var result = service.add(pet);
assertThat(result).isTrue();
}
}

View File

@ -0,0 +1,31 @@
package com.baeldung.sample.pets.domain;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class PetServiceUnitTest {
PetService service = new PetService(new PetServiceRepositoryImpl());
@Test
void shouldAddPetWhenNotAlreadyExisting() {
var pet = new Pet("Dog");
var result = service.add(pet);
assertThat(result).isTrue();
assertThat(service.getPets()).hasSize(1);
}
@Test
void shouldNotAddPetWhenAlreadyExisting() {
var pet = new Pet("Cat");
var result = service.add(pet);
assertThat(result).isTrue();
// try a second time
result = service.add(pet);
assertThat(result).isFalse();
assertThat(service.getPets()).hasSize(1);
}
}

View File

@ -0,0 +1,10 @@
package com.baeldung.sample.pets.domain;
import org.springframework.context.annotation.ComponentScan;
/**
* Just an interface to use for compiler-checked component scanning during tests.
* @see ComponentScan#basePackageClasses()
*/
public interface PetsDomainLayer {
}

View File

@ -0,0 +1,52 @@
package com.baeldung.sample.test.slices;
import com.baeldung.sample.pets.boundary.PetsBoundaryLayer;
import com.baeldung.sample.pets.boundary.PetsController;
import com.baeldung.sample.pets.domain.PetService;
import org.junit.jupiter.api.Tag;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockReset;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ActiveProfiles;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static org.mockito.Mockito.mock;
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@WebMvcTest(controllers = PetsController.class)
@ComponentScan(basePackageClasses = PetsBoundaryLayer.class)
@Import(PetsBoundaryTest.PetBoundaryTestConfiguration.class)
// further features that can help to configure and execute tests
@ActiveProfiles({ "test", "boundary-test" })
@Tag("integration-test")
@Tag("boundary-test")
public @interface PetsBoundaryTest {
@TestConfiguration
class PetBoundaryTestConfiguration {
@Primary
@Bean
PetService createPetServiceMock() {
return mock(
PetService.class,
MockReset.withSettings(MockReset.AFTER)
);
}
}
}

View File

@ -0,0 +1,52 @@
package com.baeldung.sample.test.slices;
import com.baeldung.sample.pets.domain.PetServiceRepository;
import com.baeldung.sample.pets.domain.PetsDomainLayer;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockReset;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static org.mockito.Mockito.mock;
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ExtendWith(SpringExtension.class)
@ComponentScan(basePackageClasses = PetsDomainLayer.class)
@Import(PetsDomainTest.PetServiceTestConfiguration.class)
// further features that can help to configure and execute tests
@ActiveProfiles({"test", "domain-test"})
@Tag("integration-test")
@Tag("domain-test")
public @interface PetsDomainTest {
@TestConfiguration
class PetServiceTestConfiguration {
@Primary
@Bean
PetServiceRepository createPetsRepositoryMock() {
return mock(
PetServiceRepository.class,
MockReset.withSettings(MockReset.AFTER)
);
}
}
}

View File

@ -0,0 +1,11 @@
logging:
level:
root: info
org:
springframework:
test:
context:
cache: DEBUG
spring:
main:
allow-bean-definition-overriding: true

View File

@ -0,0 +1,29 @@
package com.baeldung.sample.singleton;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class SingletonBeanConfig {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public SingletonBean singletonBean() {
return new SingletonBean();
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public SingletonBean anotherSingletonBean() {
return new SingletonBean();
}
static class SingletonBean {
public String getValue() {
return "test";
}
}
}

View File

@ -0,0 +1,24 @@
package com.baeldung.sample.singleton;
public final class ThreadSafeSingleInstance {
private static volatile ThreadSafeSingleInstance instance = null;
private ThreadSafeSingleInstance() {}
public static ThreadSafeSingleInstance getInstance() {
if (instance == null) {
synchronized(ThreadSafeSingleInstance.class) {
if (instance == null) {
instance = new ThreadSafeSingleInstance();
}
}
}
return instance;
}
public String getValue() {
return "test";
}
}

View File

@ -0,0 +1,35 @@
package com.baeldung.sample.singleton;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SingletonBeanUnitTest {
@Autowired
@Qualifier("singletonBean")
private SingletonBeanConfig.SingletonBean beanOne;
@Autowired
@Qualifier("singletonBean")
private SingletonBeanConfig.SingletonBean beanTwo;
@Autowired
@Qualifier("anotherSingletonBean")
private SingletonBeanConfig.SingletonBean beanThree;
@Test
void givenTwoBeansWithSameId_whenInjectingThem_thenSameInstancesAreReturned() {
assertSame(beanOne, beanTwo);
}
@Test
void givenTwoBeansWithDifferentId_whenInjectingThem_thenDifferentInstancesAreReturned() {
assertNotSame(beanOne, beanThree);
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.sample.singleton;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertSame;
class ThreadSafeSingleInstanceUnitTest {
@Test
void givenTwoSingletonInstances_whenGettingThem_thenSameInstancesAreReturned() {
ThreadSafeSingleInstance instanceOne = ThreadSafeSingleInstance.getInstance();
ThreadSafeSingleInstance instanceTwo = ThreadSafeSingleInstance.getInstance();
assertSame(instanceOne, instanceTwo);
}
}

View File

@ -1,3 +1,7 @@
### Spring Reactive Articles that are also part of the e-book
This module contains articles about Spring Reactive that are also part of an Ebook.
## Spring Reactive ## Spring Reactive
This module contains articles describing reactive processing in Spring. This module contains articles describing reactive processing in Spring.
@ -14,3 +18,7 @@ This module contains articles describing reactive processing in Spring.
- [Handling Errors in Spring WebFlux](https://www.baeldung.com/spring-webflux-errors) - [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) - [Spring Security 5 for Reactive Applications](https://www.baeldung.com/spring-security-5-reactive)
- [Concurrency in Spring WebFlux](https://www.baeldung.com/spring-webflux-concurrency) - [Concurrency in Spring WebFlux](https://www.baeldung.com/spring-webflux-concurrency)
### NOTE:
Since this is a module tied to an e-book, it should **not** be moved or used to store the code for any further article.

View File

@ -0,0 +1,19 @@
package com.baeldung.junit5.nested;
public class Article {
private String name;
private Membership articleLevel;
public Article(String name, Membership articleLevel) {
this.name = name;
this.articleLevel = articleLevel;
}
public String getName() {
return name;
}
public Membership getArticleLevel() {
return articleLevel;
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.junit5.nested;
public enum Membership {
FREE(0), SILVER(10), GOLD(20);
private final int level;
Membership(int level) {
this.level = level;
}
public int compare(Membership other) {
return level - other.level;
}
}

View File

@ -0,0 +1,32 @@
package com.baeldung.junit5.nested;
import java.util.List;
import java.util.stream.Collectors;
public class Publication {
private final List<Article> articles;
public Publication(List<Article> articles) {
this.articles = articles;
}
public List<String> getReadableArticles(User user) {
return articles.stream()
.filter(a -> a.getArticleLevel()
.compare(user.getMembership()) <= 0)
.map(Article::getName)
.collect(Collectors.toList());
}
public List<String> getLockedArticles(User user) {
return articles.stream()
.filter(a -> a.getArticleLevel()
.compare(user.getMembership()) > 0)
.map(Article::getName)
.collect(Collectors.toList());
}
public List<Article> getArticles() {
return articles;
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.junit5.nested;
public class User {
private String name;
private Membership membership;
public User(String name, Membership membership) {
this.name = name;
this.membership = membership;
}
public String getName() {
return name;
}
public Membership getMembership() {
return membership;
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.junit5.nested;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
public class NestedUnitTest {
@BeforeEach
void beforeEach() {
System.out.println("NestedUnitTest.beforeEach()");
}
@Nested
class FirstNestedClass {
@BeforeEach
void beforeEach() {
System.out.println("FirstNestedClass.beforeEach()");
}
@Test
void test() {
System.out.println("FirstNestedClass.test()");
}
}
@Nested
class SecondNestedClass {
@BeforeEach
void beforeEach() {
System.out.println("SecondNestedClass.beforeEach()");
}
@Test
void test() {
System.out.println("SecondNestedClass.test()");
}
}
}

View File

@ -0,0 +1,93 @@
package com.baeldung.junit5.nested;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@DisplayName("given a article publication with three articles")
class OnlinePublicationUnitTest {
private Publication publication;
@BeforeEach
void setupArticlesAndPublication() {
Article freeArticle = new Article("free article", Membership.FREE);
Article silverArticle = new Article("silver level article", Membership.SILVER);
Article goldArticle = new Article("gold level article", Membership.GOLD);
publication = new Publication(Arrays.asList(freeArticle, silverArticle, goldArticle));
}
@Test
@DisplayName("then 3 articles are available")
void shouldHaveThreeArticlesInTotal() {
List<Article> allArticles = publication.getArticles();
assertThat(allArticles).hasSize(3);
}
@Nested
@DisplayName("when a user with a 'free' membership logs in")
class UserWithAFreeMembership {
User freeFreya = new User("Freya", Membership.FREE);
@Test
@DisplayName("then he should be able to read the 'free' articles")
void shouldOnlyReadFreeArticles() {
List<String> articles = publication.getReadableArticles(freeFreya);
assertThat(articles).containsExactly("free article");
}
@Test
@DisplayName("then he shouldn't be able to read the 'silver' and 'gold' articles")
void shouldSeeSilverAndGoldLevelArticlesAsLocked() {
List<String> articles = publication.getLockedArticles(freeFreya);
assertThat(articles).containsExactlyInAnyOrder("silver level article", "gold level article");
}
}
@Nested
@DisplayName("when a user with a 'silver' membership logs in")
class UserWithSilverMembership {
User silverSilvester = new User("Silvester", Membership.SILVER);
@Test
@DisplayName("then he should be able to read the 'free' and 'silver' level articles")
void shouldOnlyReadFreeAndSilverLevelArticles() {
List<String> articles = publication.getReadableArticles(silverSilvester);
assertThat(articles).containsExactlyInAnyOrder("free article", "silver level article");
}
@Test
@DisplayName("then he should see the 'gold' level articles as locked")
void shouldSeeGoldLevelArticlesAsLocked() {
List<String> articles = publication.getLockedArticles(silverSilvester);
assertThat(articles).containsExactlyInAnyOrder("gold level article");
}
}
@Nested
@DisplayName("when a user with a 'gold' membership logs in")
class UserWithGoldMembership {
User goldenGeorge = new User("George", Membership.GOLD);
@Test
@DisplayName("then he should be able to read all the articles")
void shouldSeeAllArticles() {
List<String> articles = publication.getReadableArticles(goldenGeorge);
assertThat(articles).containsExactlyInAnyOrder("free article", "silver level article", "gold level article");
}
@Test
@DisplayName("then he should not see any article as locked")
void shouldNotHaveHiddenArticles() {
List<String> articles = publication.getLockedArticles(goldenGeorge);
assertThat(articles).isEmpty();
}
}
}