Merge branch 'master' of https://github.com/eugenp/tutorials into task/JAVA-13629

This commit is contained in:
Dhawal Kapil 2022-07-31 22:00:29 +05:30
commit 17d2d5a999
173 changed files with 5142 additions and 386 deletions

View File

@ -12,5 +12,4 @@ This module contains articles about Java 11 core features
- [Java HTTPS Client Certificate Authentication](https://www.baeldung.com/java-https-client-certificate-authentication) - [Java HTTPS Client Certificate Authentication](https://www.baeldung.com/java-https-client-certificate-authentication)
- [Call Methods at Runtime Using Java Reflection](https://www.baeldung.com/java-method-reflection) - [Call Methods at Runtime Using Java Reflection](https://www.baeldung.com/java-method-reflection)
- [Java HttpClient Basic Authentication](https://www.baeldung.com/java-httpclient-basic-auth) - [Java HttpClient Basic Authentication](https://www.baeldung.com/java-httpclient-basic-auth)
- [Java HttpClient With SSL](https://www.baeldung.com/java-httpclient-ssl) - [Java HttpClient With SSL](https://www.baeldung.com/java-httpclient-ssl)
- [Adding Parameters to Java HttpClient Requests](https://www.baeldung.com/java-httpclient-request-parameters)

View File

@ -0,0 +1,6 @@
## Core Java 11
This module contains articles about Java 11 core features
### Relevant articles
- [Adding Parameters to Java HttpClient Requests](https://www.baeldung.com/java-httpclient-request-parameters)

View File

@ -0,0 +1,37 @@
<?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-11-3</artifactId>
<version>0.1.0-SNAPSHOT</version>
<name>core-java-11-3</name>
<packaging>jar</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source.version}</source>
<target>${maven.compiler.target.version}</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.source.version>11</maven.compiler.source.version>
<maven.compiler.target.version>11</maven.compiler.target.version>
</properties>
</project>

View File

@ -5,4 +5,4 @@
- [Introduction to HexFormat in Java 17](https://www.baeldung.com/java-hexformat) - [Introduction to HexFormat in Java 17](https://www.baeldung.com/java-hexformat)
- [New Features in Java 17](https://www.baeldung.com/java-17-new-features) - [New Features in Java 17](https://www.baeldung.com/java-17-new-features)
- [Random Number Generators in Java 17](https://www.baeldung.com/java-17-random-number-generators) - [Random Number Generators in Java 17](https://www.baeldung.com/java-17-random-number-generators)
- [Sealed Classes and Interfaces in Java 17](https://www.baeldung.com/java-sealed-classes-interfaces) - [Sealed Classes and Interfaces in Java](https://www.baeldung.com/java-sealed-classes-interfaces)

View File

@ -0,0 +1,21 @@
package com.baeldung.list.replace;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class ReplaceUsingIndexInArrayListUnitTest {
private static final List<Integer> EXPECTED = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
@Test void givenArrayList_updateUsingSet() {
List<Integer> aList = new ArrayList<>(Arrays.asList(1, 2, 7, 4, 5));
aList.set(2, 3);
assertThat(aList).isEqualTo(EXPECTED);
}
}

View File

@ -0,0 +1,24 @@
package com.baeldung.map.multikey;
import java.util.HashMap;
import java.util.Map;
public class BaseClassUserCache {
private final Map<Object, User> cache = new HashMap<>();
public User getById(String id) {
return cache.get(id);
}
public User getById(Long id) {
return cache.get(id);
}
public void storeById(String id, User user) {
cache.put(id, user);
}
public void storeById(Long id, User user) {
cache.put(id, user);
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.map.multikey;
import java.util.HashMap;
import java.util.Map;
public class MultipleMapsUserCache {
private final Map<String, User> stringCache = new HashMap<>();
private final Map<Long, User> longCache = new HashMap<>();
public User getById(String id) {
return stringCache.get(id);
}
public User getById(Long id) {
return longCache.get(id);
}
public void storeById(String id, User user) {
stringCache.put(id, user);
}
public void storeById(Long id, User user) {
longCache.put(id, user);
}
}

View File

@ -0,0 +1,13 @@
package com.baeldung.map.multikey;
public class User {
private final String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,42 @@
package com.baeldung.map.multikey;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class WrapperClassUserCache {
private Map<CacheKey, User> cache = new HashMap<>();
public User getById(CacheKey key) {
return cache.get(key);
}
public void storeById(CacheKey key, User user) {
cache.put(key, user);
}
public static class CacheKey {
private final Object value;
public CacheKey(String value) {
this.value = value;
}
public CacheKey(Long value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CacheKey cacheKey = (CacheKey) o;
return value.equals(cacheKey.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
}

View File

@ -0,0 +1,62 @@
package com.baeldung.map.multikey;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class WrapperInterfaceUserCache {
private Map<CacheKey, User> cache = new HashMap<>();
public User getById(CacheKey key) {
return cache.get(key);
}
public void storeById(CacheKey key, User user) {
cache.put(key, user);
}
public interface CacheKey {
}
public static class StringCacheKey implements CacheKey{
private final String value;
public StringCacheKey(String value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StringCacheKey that = (StringCacheKey) o;
return value.equals(that.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
public static class LongCacheKey implements CacheKey {
private final Long value;
public LongCacheKey(Long value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LongCacheKey that = (LongCacheKey) o;
return value.equals(that.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
}

View File

@ -0,0 +1,32 @@
package com.baeldung.map.multikey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class BaseClassUserCacheUnitTest {
private BaseClassUserCache cache = new BaseClassUserCache();
@BeforeEach
public void setup() {
cache.storeById("a", new User("User A"));
cache.storeById("b", new User("User B"));
cache.storeById(3L, new User("User 3"));
cache.storeById(4L, new User("User 4"));
}
@Test
public void getByString() {
User user = cache.getById("b");
assertNotNull(user);
assertEquals("User B", user.getName());
}
@Test
public void getByLong() {
User user = cache.getById(4L);
assertNotNull(user);
assertEquals("User 4", user.getName());
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.map.multikey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class MultipleMapsUserCacheUnitTest {
private MultipleMapsUserCache cache = new MultipleMapsUserCache();
@BeforeEach
public void setup() {
cache.storeById("a", new User("User A"));
cache.storeById("b", new User("User B"));
cache.storeById(3L, new User("User 3"));
cache.storeById(4L, new User("User 4"));
}
@Test
public void getByString() {
User user = cache.getById("b");
assertNotNull(user);
assertEquals("User B", user.getName());
}
@Test
public void getByLong() {
User user = cache.getById(4L);
assertNotNull(user);
assertEquals("User 4", user.getName());
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.map.multikey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class WrapperClassUserCacheUnitTest {
private WrapperClassUserCache cache = new WrapperClassUserCache();
@BeforeEach
public void setup() {
cache.storeById(new WrapperClassUserCache.CacheKey("a"), new User("User A"));
cache.storeById(new WrapperClassUserCache.CacheKey("b"), new User("User B"));
cache.storeById(new WrapperClassUserCache.CacheKey(3L), new User("User 3"));
cache.storeById(new WrapperClassUserCache.CacheKey(4L), new User("User 4"));
}
@Test
public void getByString() {
User user = cache.getById(new WrapperClassUserCache.CacheKey("b"));
assertNotNull(user);
assertEquals("User B", user.getName());
}
@Test
public void getByLong() {
User user = cache.getById(new WrapperClassUserCache.CacheKey(4L));
assertNotNull(user);
assertEquals("User 4", user.getName());
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.map.multikey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class WrapperInterfaceUserCacheUnitTest {
private WrapperInterfaceUserCache cache = new WrapperInterfaceUserCache();
@BeforeEach
public void setup() {
cache.storeById(new WrapperInterfaceUserCache.StringCacheKey("a"), new User("User A"));
cache.storeById(new WrapperInterfaceUserCache.StringCacheKey("b"), new User("User B"));
cache.storeById(new WrapperInterfaceUserCache.LongCacheKey(3L), new User("User 3"));
cache.storeById(new WrapperInterfaceUserCache.LongCacheKey(4L), new User("User 4"));
}
@Test
public void getByString() {
User user = cache.getById(new WrapperInterfaceUserCache.StringCacheKey("b"));
assertNotNull(user);
assertEquals("User B", user.getName());
}
@Test
public void getByLong() {
User user = cache.getById(new WrapperInterfaceUserCache.LongCacheKey(4L));
assertNotNull(user);
assertEquals("User 4", user.getName());
}
}

View File

@ -0,0 +1,22 @@
package com.baeldung.initializerblock.instanceblock;
public class InstanceBlockExample {
{
System.out.println("Instance initializer block 1");
}
{
System.out.println("Instance initializer block 2");
}
public InstanceBlockExample() {
System.out.println("Class constructor");
}
public static void main(String[] args) {
InstanceBlockExample iib = new InstanceBlockExample();
System.out.println("Main Method");
}
}

View File

@ -0,0 +1,17 @@
package com.baeldung.initializerblock.staticblock;
public class StaticBlockExample {
static {
System.out.println("static block 1");
}
static {
System.out.println("static block 2");
}
public static void main(String[] args) {
System.out.println("Main Method");
}
}

View File

@ -22,4 +22,22 @@
</resources> </resources>
</build> </build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
</dependencies>
</project> </project>

View File

@ -0,0 +1,26 @@
package com.baeldung.intrange;
import org.apache.commons.lang3.Range;
public class IntRangeApacheCommons {
public static boolean isInClosedRange(Integer number, Integer lowerBound, Integer upperBound) {
final Range<Integer> range = Range.between(lowerBound, upperBound);
return range.contains(number);
}
public static boolean isInOpenRange(Integer number, Integer lowerBound, Integer upperBound) {
final Range<Integer> range = Range.between(lowerBound + 1, upperBound - 1);
return range.contains(number);
}
public static boolean isInOpenClosedRange(Integer number, Integer lowerBound, Integer upperBound) {
final Range<Integer> range = Range.between(lowerBound + 1, upperBound);
return range.contains(number);
}
public static boolean isInClosedOpenRange(Integer number, Integer lowerBound, Integer upperBound) {
final Range<Integer> range = Range.between(lowerBound, upperBound - 1);
return range.contains(number);
}
}

View File

@ -0,0 +1,26 @@
package com.baeldung.intrange;
import com.google.common.collect.Range;
public class IntRangeGoogleGuava {
public static boolean isInClosedRange(Integer number, Integer lowerBound, Integer upperBound) {
final Range<Integer> range = Range.closed(lowerBound, upperBound);
return range.contains(number);
}
public static boolean isInOpenRange(Integer number, Integer lowerBound, Integer upperBound) {
final Range<Integer> range = Range.open(lowerBound, upperBound);
return range.contains(number);
}
public static boolean isInOpenClosedRange(Integer number, Integer lowerBound, Integer upperBound) {
final Range<Integer> range = Range.openClosed(lowerBound, upperBound);
return range.contains(number);
}
public static boolean isInClosedOpenRange(Integer number, Integer lowerBound, Integer upperBound) {
final Range<Integer> range = Range.closedOpen(lowerBound, upperBound);
return range.contains(number);
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.intrange;
public class IntRangeOperators {
public static boolean isInClosedRange(Integer number, Integer lowerBound, Integer upperBound) {
return (lowerBound <= number && number <= upperBound);
}
public static boolean isInOpenRange(Integer number, Integer lowerBound, Integer upperBound) {
return (lowerBound < number && number < upperBound);
}
public static boolean isInOpenClosedRange(Integer number, Integer lowerBound, Integer upperBound) {
return (lowerBound < number && number <= upperBound);
}
public static boolean isInClosedOpenRange(Integer number, Integer lowerBound, Integer upperBound) {
return (lowerBound <= number && number < upperBound);
}
}

View File

@ -0,0 +1,26 @@
package com.baeldung.intrange;
import java.time.temporal.ValueRange;
public class IntRangeValueRange {
public static boolean isInClosedRange(Integer number, Integer lowerBound, Integer upperBound) {
final ValueRange range = ValueRange.of(lowerBound, upperBound);
return range.isValidIntValue(number);
}
public static boolean isInOpenRange(Integer number, Integer lowerBound, Integer upperBound) {
final ValueRange range = ValueRange.of(lowerBound + 1, upperBound - 1);
return range.isValidIntValue(number);
}
public static boolean isInOpenClosedRange(Integer number, Integer lowerBound, Integer upperBound) {
final ValueRange range = ValueRange.of(lowerBound + 1, upperBound);
return range.isValidIntValue(number);
}
public static boolean isInClosedOpenRange(Integer number, Integer lowerBound, Integer upperBound) {
final ValueRange range = ValueRange.of(lowerBound, upperBound - 1);
return range.isValidIntValue(number);
}
}

View File

@ -0,0 +1,97 @@
package com.baeldung.intrange;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class IntRangeApacheCommonsUnitTest {
@Test
void givenIntRangeApacheCommons_whenIsInClosedRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeApacheCommons.isInClosedRange(10, 10, 20);
boolean resultUpperBound = IntRangeApacheCommons.isInClosedRange(20, 10, 20);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeApacheCommons_whenIsNotInClosedRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeApacheCommons.isInClosedRange(8, 10, 20);
boolean resultUpperBound = IntRangeApacheCommons.isInClosedRange(22, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeApacheCommons_whenIsInOpenRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeApacheCommons.isInOpenRange(11, 10, 20);
boolean resultUpperBound = IntRangeApacheCommons.isInOpenRange(19, 10, 20);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeApacheCommons_whenIsNotInOpenRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeApacheCommons.isInOpenRange(10, 10, 20);
boolean resultUpperBound = IntRangeApacheCommons.isInOpenRange(20, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeApacheCommons_whenIsInOpenClosedRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeApacheCommons.isInOpenClosedRange(11, 10, 20);
boolean resultUpperBound = IntRangeApacheCommons.isInOpenClosedRange(20, 10, 20);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeApacheCommons_whenIsNotInOpenClosedRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeApacheCommons.isInOpenClosedRange(10, 10, 20);
boolean resultUpperBound = IntRangeApacheCommons.isInOpenClosedRange(21, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeApacheCommons_whenIsInClosedOpenRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeApacheCommons.isInClosedOpenRange(10, 10, 20);
boolean resultUpperBound = IntRangeApacheCommons.isInClosedOpenRange(19, 10, 20);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeApacheCommons_whenIsNotInClosedOpenRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeApacheCommons.isInClosedOpenRange(9, 10, 20);
boolean resultUpperBound = IntRangeApacheCommons.isInClosedOpenRange(20, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
}

View File

@ -0,0 +1,87 @@
package com.baeldung.intrange;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class IntRangeGoogleGuavaUnitTest {
@Test
void givenIntRangeGoogleGuava_whenIsInOpenRange_thenSuccess() {
// when
boolean result = IntRangeGoogleGuava.isInOpenRange(14, 10, 20);
//then
assertTrue(result);
}
@Test
void givenIntRangeGoogleGuava_whenIsNotInOpenRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeGoogleGuava.isInOpenRange(10, 10, 20);
boolean resultUpperBound = IntRangeGoogleGuava.isInOpenRange(20, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeGoogleGuava_whenIsInClosedRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeGoogleGuava.isInClosedRange(-10, -10, 5);
boolean resultUpperBound = IntRangeGoogleGuava.isInClosedRange(5, -10, 5);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeGoogleGuava_whenIsNotInClosedRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeGoogleGuava.isInClosedRange(-11, -10, 5);
boolean resultUpperBound = IntRangeGoogleGuava.isInClosedRange(6, -10, 5);
//then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeGoogleGuava_whenIsInOpenClosedRange_thenSuccess() {
// when
boolean result = IntRangeGoogleGuava.isInOpenClosedRange(20, 10, 20);
// then
assertTrue(result);
}
@Test
void givenIntRangeGoogleGuava_whenIsNotInOpenClosedRange_thenFailure() {
// when
boolean result = IntRangeGoogleGuava.isInOpenClosedRange(10, 10, 20);
// then
assertFalse(result);
}
@Test
void givenIntRangeGoogleGuava_whenIsInClosedOpenRange_thenSuccess() {
// when
boolean result = IntRangeGoogleGuava.isInClosedOpenRange(10, 10, 20);
// then
assertTrue(result);
}
@Test
void givenIntRangeGoogleGuava_whenIsNotInClosedOpenRange_thenFailure() {
// when
boolean result = IntRangeGoogleGuava.isInClosedOpenRange(20, 10, 20);
// then
assertFalse(result);
}
}

View File

@ -0,0 +1,87 @@
package com.baeldung.intrange;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class IntRangeOperatorsUnitTest {
@Test
void givenIntRangeOperators_whenIsInOpenRange_thenSuccess() {
// when
boolean result = IntRangeOperators.isInOpenRange(11, 10, 20);
//then
assertTrue(result);
}
@Test
void givenIntRangeOperators_whenIsNotInOpenRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeOperators.isInOpenRange(10, 10, 20);
boolean resultUpperBound = IntRangeOperators.isInOpenRange(20, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeOperators_whenIsInClosedRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeOperators.isInClosedRange(-10, -10, 5);
boolean resultUpperBound = IntRangeOperators.isInClosedRange(5, -10, 5);
// then
assertTrue(resultUpperBound);
assertTrue(resultLowerBound);
}
@Test
void givenIntRangeOperators_whenIsNotInClosedRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeOperators.isInClosedRange(-11, -10, 5);
boolean resultUpperBound = IntRangeOperators.isInClosedRange(6, -10, 5);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeOperators_whenIsInOpenClosedRange_thenSuccess() {
// when
boolean result = IntRangeOperators.isInOpenClosedRange(20, 10, 20);
// then
assertTrue(result);
}
@Test
void givenIntRangeOperators_whenIsNotInOpenClosedRange_thenFailure() {
// when
boolean result = IntRangeOperators.isInOpenClosedRange(10, 10, 20);
// then
assertFalse(result);
}
@Test
void givenIntRangeOperators_whenIsInClosedOpenRange_thenSuccess() {
// when
boolean result = IntRangeOperators.isInClosedOpenRange(10, 10, 20);
// then
assertTrue(result);
}
@Test
void givenIntRangeOperators_whenIsNotInClosedOpenRange_thenFailure() {
// when
boolean result = IntRangeOperators.isInClosedOpenRange(20, 10, 20);
// then
assertFalse(result);
}
}

View File

@ -0,0 +1,97 @@
package com.baeldung.intrange;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class IntRangeValueRangeUnitTest {
@Test
void givenIntRangeValueRange_whenIsInClosedRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeValueRange.isInClosedRange(10, 10, 20);
boolean resultUpperBound = IntRangeValueRange.isInClosedRange(20, 10, 20);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeValueRange_whenIsNotInClosedRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeValueRange.isInClosedRange(9, 10, 20);
boolean resultUpperBound = IntRangeValueRange.isInClosedRange(21, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeValueRange_whenIsInOpenRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeValueRange.isInOpenRange(11, 10, 20);
boolean resultUpperBound = IntRangeValueRange.isInOpenRange(19, 10, 20);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeValueRange_whenIsNotInOpenRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeValueRange.isInOpenRange(10, 10, 20);
boolean resultUpperBound = IntRangeValueRange.isInOpenRange(20, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeValueRange_whenIsInOpenClosedRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeValueRange.isInOpenClosedRange(11, 10, 20);
boolean resultUpperBound = IntRangeValueRange.isInOpenClosedRange(20, 10, 20);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeValueRange_whenIsNotInOpenClosedRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeValueRange.isInOpenClosedRange(10, 10, 20);
boolean resultUpperBound = IntRangeValueRange.isInOpenClosedRange(21, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
@Test
void givenIntRangeValueRange_whenIsInClosedOpenRange_thenSuccess() {
// when
boolean resultLowerBound = IntRangeValueRange.isInClosedOpenRange(10, 10, 20);
boolean resultUpperBound = IntRangeValueRange.isInClosedOpenRange(19, 10, 20);
// then
assertTrue(resultLowerBound);
assertTrue(resultUpperBound);
}
@Test
void givenIntRangeValueRange_whenIsNotInClosedOpenRange_thenFailure() {
// when
boolean resultLowerBound = IntRangeValueRange.isInClosedOpenRange(9, 10, 20);
boolean resultUpperBound = IntRangeValueRange.isInClosedOpenRange(20, 10, 20);
// then
assertFalse(resultLowerBound);
assertFalse(resultUpperBound);
}
}

View File

@ -43,6 +43,16 @@
<version>3.23.1</version> <version>3.23.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh-core.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh-generator.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,56 @@
package com.baeldung.streams.filteronlyoneelement;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
public class BenchmarkRunner {
public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(args);
}
@State(Scope.Benchmark)
public static class MyState {
final Stream<Integer> getIntegers() {
return IntStream.range(1, 1000000)
.boxed();
}
final Predicate<Integer> PREDICATE = i -> i == 751879;
}
@Benchmark
public void evaluateFindUniqueElementMatchingPredicate_WithReduction(Blackhole blackhole, MyState state) {
blackhole.consume(FilterUtils.findUniqueElementMatchingPredicate_WithReduction(state.getIntegers(), state.PREDICATE));
}
@Benchmark
public void evaluateFindUniqueElementMatchingPredicate_WithCollectingAndThen(Blackhole blackhole, MyState state) {
blackhole.consume(FilterUtils.findUniqueElementMatchingPredicate_WithCollectingAndThen(state.getIntegers(), state.PREDICATE));
}
@Benchmark
public void evaluateGetUniqueElementMatchingPredicate_WithReduction(Blackhole blackhole, MyState state) {
try {
FilterUtils.getUniqueElementMatchingPredicate_WithReduction(state.getIntegers(), state.PREDICATE);
} catch (IllegalStateException exception) {
blackhole.consume(exception);
}
}
@Benchmark
public void evaluateGetUniqueElementMatchingPredicate_WithCollectingAndThen(Blackhole blackhole, MyState state) {
try {
FilterUtils.getUniqueElementMatchingPredicate_WithCollectingAndThen(state.getIntegers(), state.PREDICATE);
} catch (IllegalStateException exception) {
blackhole.consume(exception);
}
}
}

View File

@ -0,0 +1,50 @@
package com.baeldung.streams.filteronlyoneelement;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FilterUtils {
public static <T> Optional<T> findUniqueElementMatchingPredicate_WithReduction(Stream<T> elements, Predicate<T> predicate) {
return elements.filter(predicate)
.collect(Collectors.reducing((a, b) -> null));
}
public static <T> T getUniqueElementMatchingPredicate_WithReduction(Stream<T> elements, Predicate<T> predicate) {
return elements.filter(predicate)
.reduce((a, b) -> {
throw new IllegalStateException("Too many elements match the predicate");
})
.orElseThrow(() -> new IllegalStateException("No element matches the predicate"));
}
public static <T> Optional<T> findUniqueElementMatchingPredicate_WithCollectingAndThen(Stream<T> elements, Predicate<T> predicate) {
return elements.filter(predicate)
.collect(Collectors.collectingAndThen(Collectors.toList(), list -> Optional.ofNullable(findUniqueElement(list))));
}
private static <T> T findUniqueElement(List<T> elements) {
if (elements.size() == 1) {
return elements.get(0);
}
return null;
}
public static <T> T getUniqueElementMatchingPredicate_WithCollectingAndThen(Stream<T> elements, Predicate<T> predicate) {
return elements.filter(predicate)
.collect(Collectors.collectingAndThen(Collectors.toList(), FilterUtils::getUniqueElement));
}
private static <T> T getUniqueElement(List<T> elements) {
if (elements.size() > 1) {
throw new IllegalStateException("Too many elements match the predicate");
} else if (elements.size() == 0) {
throw new IllegalStateException("No element matches the predicate");
}
return elements.get(0);
}
}

View File

@ -0,0 +1,88 @@
package com.baeldung.streams.filteronlyoneelement;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
public class FilterUtilsUnitTest {
private static final Predicate<Integer> IS_STRICTLY_GREATER_THAN5 = i -> i > 5;
private static final Predicate<Integer> IS_STRICTLY_GREATER_THAN4 = i -> i > 4;
private static final Predicate<Integer> IS_STRICTLY_GREATER_THAN3 = i -> i > 3;
private Stream getIntegers() {
return Stream.of(1, 2, 3, 4, 5);
}
@Test
void givenNoElementMatchingPredicate_WhenFindUniqueElementMatchingPredicateWithReduction_ThenNoneFound() {
assertTrue(FilterUtils.findUniqueElementMatchingPredicate_WithReduction(getIntegers(), IS_STRICTLY_GREATER_THAN5)
.isEmpty());
}
@Test
void givenTwoElementsMatchingPredicate_WhenFindUniqueElementMatchingPredicateWithReduction_ThenEmpty() {
assertTrue(FilterUtils.findUniqueElementMatchingPredicate_WithReduction(getIntegers(), IS_STRICTLY_GREATER_THAN3)
.isEmpty());
}
@Test
void givenOnlyOneElementMatchingPredicate_WhenFindUniqueElementMatchingPredicateWithReduction_ThenFindsIt() {
assertEquals(5, FilterUtils.findUniqueElementMatchingPredicate_WithReduction(getIntegers(), IS_STRICTLY_GREATER_THAN4)
.get());
}
@Test
void givenNoElementMatchingPredicate_WhenGetUniqueElementMatchingPredicateWithReduction_ThenThrows() {
assertThrows(IllegalStateException.class, () -> FilterUtils.getUniqueElementMatchingPredicate_WithReduction(getIntegers(), IS_STRICTLY_GREATER_THAN5));
}
@Test
void givenTwoElementsMatchingPredicate_WhenGetUniqueElementMatchingPredicateWithReduction_ThenThrows() {
assertThrows(IllegalStateException.class, () -> FilterUtils.getUniqueElementMatchingPredicate_WithReduction(getIntegers(), IS_STRICTLY_GREATER_THAN3));
}
@Test
void givenOnlyOneElementMatchingPredicate_WhenFindUniqueElementMatchingPredicateWithReduction_ThenGetIt() {
assertEquals(5, FilterUtils.getUniqueElementMatchingPredicate_WithReduction(getIntegers(), IS_STRICTLY_GREATER_THAN4));
}
@Test
void givenNoElementMatchingPredicate_WhenFindUniqueElementMatchingPredicateWithCollectingAndThen_ThenEmpty() {
assertTrue(FilterUtils.findUniqueElementMatchingPredicate_WithCollectingAndThen(getIntegers(), IS_STRICTLY_GREATER_THAN5)
.isEmpty());
}
@Test
void givenTwoElementsMatchingPredicate_WhenFindUniqueElementMatchingPredicateWithCollectingAndThen_ThenEmpty() {
assertTrue(FilterUtils.findUniqueElementMatchingPredicate_WithCollectingAndThen(getIntegers(), IS_STRICTLY_GREATER_THAN3)
.isEmpty());
}
@Test
void givenOnlyOneElementMatchingPredicate_WhenFindUniqueElementMatchingPredicateWithCollectingAndThen_ThenFindsIt() {
assertEquals(5, FilterUtils.findUniqueElementMatchingPredicate_WithCollectingAndThen(getIntegers(), IS_STRICTLY_GREATER_THAN4)
.get());
}
@Test
void givenNoElementMatchingPredicate_WhenGetUniqueElementMatchingPredicateWithCollectingAndThen_ThenThrows() {
assertThrows(IllegalStateException.class, () -> FilterUtils.getUniqueElementMatchingPredicate_WithCollectingAndThen(getIntegers(), IS_STRICTLY_GREATER_THAN5));
}
@Test
void givenTwoElementsMatchingPredicate_WhenGetUniqueElementMatchingPredicateWithCollectingAndThen_ThenThrows() {
assertThrows(IllegalStateException.class, () -> FilterUtils.getUniqueElementMatchingPredicate_WithCollectingAndThen(getIntegers(), IS_STRICTLY_GREATER_THAN3));
}
@Test
void givenOnlyOneElementMatchingPredicate_WhenFindUniqueElementMatchingPredicateWithCollectingAndThen_ThenGetIt() {
assertEquals(5, FilterUtils.getUniqueElementMatchingPredicate_WithCollectingAndThen(getIntegers(), IS_STRICTLY_GREATER_THAN4));
}
}

View File

@ -8,3 +8,4 @@
- [Check if a String Ends with a Certain Pattern in Java](https://www.baeldung.com/java-string-ends-pattern) - [Check if a String Ends with a Certain Pattern in Java](https://www.baeldung.com/java-string-ends-pattern)
- [Check if a Character is a Vowel in Java](https://www.baeldung.com/java-check-character-vowel) - [Check if a Character is a Vowel in Java](https://www.baeldung.com/java-check-character-vowel)
- [How to Truncate a String in Java](https://www.baeldung.com/java-truncating-strings) - [How to Truncate a String in Java](https://www.baeldung.com/java-truncating-strings)
- [Remove Whitespace From a String in Java](https://www.baeldung.com/java-string-remove-whitespace)

View File

@ -6,6 +6,10 @@
</encoder> </encoder>
</appender> </appender>
<logger name="org.xacml4j.v30.XacmlPolicyTestSupport" level="warn">
<appender-ref ref="STDOUT" />
</logger>
<root level="INFO"> <root level="INFO">
<appender-ref ref="STDOUT" /> <appender-ref ref="STDOUT" />
</root> </root>

View File

@ -10,10 +10,9 @@
<description>Aggregator Service for LightRun Article</description> <description>Aggregator Service for LightRun Article</description>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>com.baelduung</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>lightrun</artifactId>
<version>2.6.7</version> <version>0.0.1-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository -->
</parent> </parent>
<dependencies> <dependencies>
@ -43,6 +42,7 @@
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<maven-pmd-plugin.version>3.17.0</maven-pmd-plugin.version>
</properties> </properties>
</project> </project>

View File

@ -9,6 +9,13 @@
<name>lightrun</name> <name>lightrun</name>
<description>Services for LightRun Article</description> <description>Services for LightRun Article</description>
<packaging>pom</packaging> <packaging>pom</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-boot-2</relativePath>
</parent>
<modules> <modules>
<module>tasks-service</module> <module>tasks-service</module>

View File

@ -10,10 +10,9 @@
<description>Tasks Service for LightRun Article</description> <description>Tasks Service for LightRun Article</description>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>com.baelduung</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>lightrun</artifactId>
<version>2.6.7</version> <version>0.0.1-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository -->
</parent> </parent>
<dependencies> <dependencies>
@ -64,6 +63,7 @@
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<maven-pmd-plugin.version>3.17.0</maven-pmd-plugin.version>
</properties> </properties>
</project> </project>

View File

@ -10,10 +10,9 @@
<description>Users Service for LightRun Article</description> <description>Users Service for LightRun Article</description>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>com.baelduung</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>lightrun</artifactId>
<version>2.6.7</version> <version>0.0.1-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository -->
</parent> </parent>
<dependencies> <dependencies>
@ -60,6 +59,7 @@
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<maven-pmd-plugin.version>3.17.0</maven-pmd-plugin.version>
</properties> </properties>
</project> </project>

View File

@ -6,17 +6,20 @@ import com.baeldung.entity.Division;
import com.baeldung.entity.Employee; import com.baeldung.entity.Employee;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import java.util.List; import java.util.List;
@Mapper @Mapper
public interface EmployeeMapper { public interface EmployeeMapper {
@Mappings({ @Mapping(target = "employeeId", source = "entity.id"), @Mapping(target = "employeeName", source = "entity.name"), @Mapping(target = "employeeStartDt", source = "entity.startDt", dateFormat = "dd-MM-yyyy HH:mm:ss") }) @Mapping(target = "employeeId", source = "entity.id")
@Mapping(target = "employeeName", source = "entity.name")
@Mapping(target = "employeeStartDt", source = "entity.startDt", dateFormat = "dd-MM-yyyy HH:mm:ss")
EmployeeDTO employeeToEmployeeDTO(Employee entity); EmployeeDTO employeeToEmployeeDTO(Employee entity);
@Mappings({ @Mapping(target = "id", source = "dto.employeeId"), @Mapping(target = "name", source = "dto.employeeName"), @Mapping(target = "startDt", source = "dto.employeeStartDt", dateFormat = "dd-MM-yyyy HH:mm:ss") }) @Mapping(target = "id", source = "dto.employeeId")
@Mapping(target = "name", source = "dto.employeeName")
@Mapping(target = "startDt", source = "dto.employeeStartDt", dateFormat = "dd-MM-yyyy HH:mm:ss")
Employee employeeDTOtoEmployee(EmployeeDTO dto); Employee employeeDTOtoEmployee(EmployeeDTO dto);
DivisionDTO divisionToDivisionDTO(Division entity); DivisionDTO divisionToDivisionDTO(Division entity);

View File

@ -8,3 +8,4 @@ This module contains articles about Apache Maven. Please refer to its submodules
- [Apache Maven Standard Directory Layout](https://www.baeldung.com/maven-directory-structure) - [Apache Maven Standard Directory Layout](https://www.baeldung.com/maven-directory-structure)
- [Multi-Module Project with Maven](https://www.baeldung.com/maven-multi-module) - [Multi-Module Project with Maven](https://www.baeldung.com/maven-multi-module)
- [Maven Packaging Types](https://www.baeldung.com/maven-packaging-types) - [Maven Packaging Types](https://www.baeldung.com/maven-packaging-types)
- [Maven Snapshot Repository vs Release Repository](https://www.baeldung.com/maven-snapshot-release-repository)

View File

@ -0,0 +1,52 @@
<?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>maven-repository-download</artifactId>
<name>Maven Release Repository</name>
<version>1.0.0-SNAPSHOT</version>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>maven-repositories</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<repositories>
<repository>
<id>nexus-snapshot</id>
<name>nexus-snapshot</name>
<url>http://localhost:8081/repository/maven-snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>nexus-release</id>
<name>nexus-release</name>
<url>http://localhost:8081/repository/maven-releases/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.baeldung</groupId>
<artifactId>maven-release-repository</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.baeldung</groupId>
<artifactId>maven-snapshot-repository</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>maven-release-repository</artifactId>
<name>Maven Release Repository</name>
<version>1.0.0</version>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>maven-repositories</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<repositories>
<repository>
<id>nexus</id>
<name>nexus-release</name>
<url>http://localhost:8081/repository/maven-releases/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>nexus</id>
<name>nexus-release</name>
<url>http://localhost:8081/repository/maven-releases/</url>
</repository>
</distributionManagement>
</project>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>maven-snapshot-repository</artifactId>
<name>Maven Snapshot Repository</name>
<version>1.0.0-SNAPSHOT</version>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>maven-repositories</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<repositories>
<repository>
<id>nexus</id>
<name>nexus-snapshot</name>
<url>http://localhost:8081/repository/maven-snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<distributionManagement>
<snapshotRepository>
<id>nexus</id>
<name>nexus-snapshot</name>
<url>http://localhost:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>

View File

@ -0,0 +1,23 @@
<?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>maven-repositories</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>Maven Repositories</name>
<packaging>pom</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>maven-modules</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modules>
<module>maven-release-repository</module>
<module>maven-snapshot-repository</module>
<module>maven-download-artifacts</module>
</modules>
</project>

View File

@ -41,6 +41,7 @@
<module>maven-parent-pom-resolution</module> <module>maven-parent-pom-resolution</module>
<module>maven-simple</module> <module>maven-simple</module>
<module>maven-classifier</module> <module>maven-classifier</module>
<module>maven-repositories</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>

3
parent-boot-3/README.md Normal file
View File

@ -0,0 +1,3 @@
## Parent Boot 2
This is a parent module for all projects using Spring Boot 3.

97
parent-boot-3/pom.xml Normal file
View File

@ -0,0 +1,97 @@
<?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>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>parent-boot-3</name>
<packaging>pom</packaging>
<description>Parent for all Spring Boot 3 modules</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>${start-class}</mainClass>
<!-- this is necessary as we're not using the Boot parent -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</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>
</pluginRepositories>
<properties>
<spring-boot.version>3.0.0-M3</spring-boot.version>
<junit-jupiter.version>5.8.2</junit-jupiter.version>
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
<lombok.version>1.18.22</lombok.version>
<java.version>17</java.version>
<maven-pmd-plugin.version>3.17.0</maven-pmd-plugin.version>
</properties>
</project>

View File

@ -9,11 +9,11 @@
<name>fauna</name> <name>fauna</name>
<description>Blogging Service built with FaunaDB</description> <description>Blogging Service built with FaunaDB</description>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>com.baeldung</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>parent-boot-2</artifactId>
<version>2.6.2</version> <version>0.0.1-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository --> <relativePath>../../parent-boot-2</relativePath>
</parent> </parent>
<dependencies> <dependencies>
@ -54,6 +54,7 @@
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<maven-pmd-plugin.version>3.17.0</maven-pmd-plugin.version>
</properties> </properties>
</project> </project>

View File

@ -6,4 +6,4 @@
- [Hibernates “Object References an Unsaved Transient Instance” Error](https://www.baeldung.com/hibernate-unsaved-transient-instance-error) - [Hibernates “Object References an Unsaved Transient Instance” Error](https://www.baeldung.com/hibernate-unsaved-transient-instance-error)
- [EntityNotFoundException in Hibernate](https://www.baeldung.com/hibernate-entitynotfoundexception) - [EntityNotFoundException in Hibernate](https://www.baeldung.com/hibernate-entitynotfoundexception)
- [Hibernates “Not-Null Property References a Null or Transient Value” Error](https://www.baeldung.com/hibernate-not-null-error) - [Hibernates “Not-Null Property References a Null or Transient Value” Error](https://www.baeldung.com/hibernate-not-null-error)
- [Hibernate's “Detached Entity Passed to Persist” Error](https://www.baeldung.com/hibernate-detached-entity-passed-to-persist) - [Hibernates “Detached Entity Passed to Persist” Error](https://www.baeldung.com/hibernate-detached-entity-passed-to-persist)

View File

@ -24,7 +24,8 @@ public class HibernateUtil {
settings.put(Environment.USER, "sa"); settings.put(Environment.USER, "sa");
settings.put(Environment.PASS, ""); settings.put(Environment.PASS, "");
settings.put(Environment.DIALECT, "org.hibernate.dialect.HSQLDialect"); settings.put(Environment.DIALECT, "org.hibernate.dialect.HSQLDialect");
settings.put(Environment.SHOW_SQL, "true"); // enable to show Hibernate generated SQL
settings.put(Environment.SHOW_SQL, "false");
settings.put(Environment.FORMAT_SQL, "true"); settings.put(Environment.FORMAT_SQL, "true");
settings.put(Environment.USE_SQL_COMMENTS, "true"); settings.put(Environment.USE_SQL_COMMENTS, "true");
settings.put(Environment.HBM2DDL_AUTO, "update"); settings.put(Environment.HBM2DDL_AUTO, "update");

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{ISO8601}]-[%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.hibernate" level="error">
<appender-ref ref="STDOUT" />
</logger>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -87,7 +87,7 @@
<properties> <properties>
<mapstruct.version>1.3.1.Final</mapstruct.version> <mapstruct.version>1.3.1.Final</mapstruct.version>
<testcontainers.version>1.12.2</testcontainers.version> <testcontainers.version>1.17.3</testcontainers.version>
</properties> </properties>
</project> </project>

View File

@ -1,6 +1,7 @@
spring: spring:
datasource: datasource:
url: jdbc:tc:postgresql:11.1:///integration-tests-db url: jdbc:tc:postgresql:11.1:///integration-tests-db
driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
jpa: jpa:
hibernate: hibernate:
ddl-auto: create ddl-auto: create

View File

@ -1252,6 +1252,7 @@
<module>quarkus-modules/quarkus-jandex</module> <module>quarkus-modules/quarkus-jandex</module>
<module>spring-boot-modules/spring-boot-cassandre</module> <module>spring-boot-modules/spring-boot-cassandre</module>
<module>spring-boot-modules/spring-boot-camel</module> <module>spring-boot-modules/spring-boot-camel</module>
<module>spring-boot-modules/spring-boot-3</module>
<module>testing-modules/testing-assertions</module> <module>testing-modules/testing-assertions</module>
<module>persistence-modules/fauna</module> <module>persistence-modules/fauna</module>
<module>lightrun</module> <module>lightrun</module>

View File

@ -6,6 +6,9 @@ To follow this tutorial, you will need the following things:
- Maven (Embedded, IDE, or local installation) - Maven (Embedded, IDE, or local installation)
- Docker (https://www.docker.com/) - Docker (https://www.docker.com/)
- Jmeter (https://jmeter.apache.org/) - Jmeter (https://jmeter.apache.org/)
- wrk (https://github.com/wg/wrk)
- hyperfoil (https://hyperfoil.io/)
- lua (https://www.lua.org/)
To create this test, I used some custom features from Jmeter. You can install the Jmeter plugin manager here: To create this test, I used some custom features from Jmeter. You can install the Jmeter plugin manager here:
https://loadium.com/blog/how-to-install-use-jmeter-plugin. After that, please install the following plugins: https://loadium.com/blog/how-to-install-use-jmeter-plugin. After that, please install the following plugins:
@ -17,31 +20,32 @@ The test file is `load_test.jmx` in case of any change need. You can open it wit
$jmeter_home/bin/jmeter -n -t load_test.jmx -l log.csv -e -o ./report $jmeter_home/bin/jmeter -n -t load_test.jmx -l log.csv -e -o ./report
``` ```
Just remember to change the variable `jmeter_home` with the path to the JMeter folder. The path to the data files is relative, so either keep them in the same folder as the test or use Jmeter GUI to change it. Just remember to change the variable `jmeter_home` with the path to the JMeter folder. The path to the data files is relative, so either keep them in the same folder as the test or use Jmeter GUI to change it. Rememeber that as mentioned in the article, we cannot consider the response times recorded by Jmeter due to the Coordinated Omission Problem.
Open the VisualVM application and select your application to start monitoring before running the test, and of course, start the sample application first. Open the VisualVM application and select your application to start monitoring before running the test, and of course, start the sample application first.
## Spring Boot ## Spring Boot
To build the application, you only need to run the following command in the Spring project root: To build the application, you only need to run the following command in the Spring project root:
``` ```
./mvnw package -f pom.xml ./mvnw clean package -f pom.xml
``` ```
Or this one in case you want to build the native one: Or this one in case you want to build the native one:
``` ```
./mvnw -DskipTests package -Pnative -f pom.xml ./mvnw clean package -Pnative -f pom.xml
``` ```
In this case, you will need to have the `GRAALVM_HOME` env variable defined. You only need this if you want to build the image locally. Otherwise, you can build it using docker by leveraging the Spring Boot maven plugin. It will pull a docker image of the GraalVM, and with that, it will create the native image of the app. To do that, run: In this case, you will need to have the `GRAALVM_HOME` env variable defined. You only need this if you want to build the image locally. Otherwise, you can build it using docker by leveraging the Spring Boot maven plugin. It will pull a docker image of the GraalVM, and with that, it will create the native image of the app. To do that, run:
``` ```
./mvnw spring-boot:build-image ./mvnw clean package spring-boot:build-image -Pnative -f pom.xml
``` ```
You can also create a docker image with the JVM version of the app running the script `build_jvm_docker.sh` or: You can also create a docker image with the JVM version one of the app running the script `build.sh` or:
``` ```
docker build -f src/main/docker/Dockerfile.jvm -t spring-project:0.1-SNAPSHOT . ./mvnw clean package spring-boot:build-image -f pom.xml
``` ```
You can execute the script `start_app.sh` or `start_jvm.sh` to run the application locally. In this case, you will need the Postgres DB. You can run it in docker with the command: You can execute the script `start_app.sh` or `start_jvm.sh` to run the application locally. In this case, you will need the Mysql DB. You can run it in docker with the command:
``` ```
docker run -e POSTGRES_PASSWORD=example -p 5432:5432 postgres docker run --name mysqldb --network=host -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=baeldung -d mysql:5.7.38 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
``` ```
You can also run both application and DB from docker, using: You can also run both application and DB from docker, using:
``` ```
@ -67,7 +71,7 @@ And to the JVM version:
To start the application locally, use either the scripts `start_app.sh` and `start_jvm.sh` with the docker DB: To start the application locally, use either the scripts `start_app.sh` and `start_jvm.sh` with the docker DB:
``` ```
docker run -e POSTGRES_PASSWORD=example -p 5432:5432 postgres docker run --name mysqldb --network=host -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=baeldung -d mysql:5.7.38 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
``` ```
Or use the script to build the docker image of the application, running: Or use the script to build the docker image of the application, running:
```bash ```bash
@ -94,6 +98,38 @@ docker-compose -f src/main/docker/quarkus.yml up
Now you have all you need to reproduce the tests with your machine. Now you have all you need to reproduce the tests with your machine.
## Wrk
Another option to execute the load test is to use the wrk. This library is capable of generation a pretty high load only using a single core. To install it you only have to checkout the project compile it (using make) and define the `wrk_home` envvar. To run the test use:
```
./run_test_wrk.sh
```
You will need to have installed lua in your machine.
### Tips
If you want to run the applications in your machine you can use the following command to restrict the CPUs available to the app:
```
cpulimit -l 300 -p ## 300 means at most 3 cores.
```
This will make sure the load is on the application and not in the DB.
## Hyperfoil
To the hyperfoil test to get a report regarding the performance of the application, its throughput and response time. You can run the `docker_run.sh` from the hyperfoil folder, or the following:
```
docker run -it -v volume:/benchmarks:Z -v tmp/reports:/tmp/reports:Z --network=host quay.io/hyperfoil/hyperfoil cli
```
And then:
```
start-local && upload /benchmarks/benchmark.hf.yaml && run benchmark
```
Optionally, we can extract a html report from it, by running:
```
report --destination=/tmp/reports
```
### Relevant Articles: ### Relevant Articles:
- [Spring Boot vs Quarkus](https://www.baeldung.com/spring-boot-vs-quarkus) - [Spring Boot vs Quarkus](https://www.baeldung.com/spring-boot-vs-quarkus)

View File

@ -0,0 +1,13 @@
#!/bin/bash
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
docker run -it -v $SCRIPTPATH/volume:/benchmarks:Z -v $SCRIPTPATH/tmp/reports:/tmp/reports:Z --network=host quay.io/hyperfoil/hyperfoil cli
#start-local && upload /benchmarks/benchmark.hf.yaml && run benchmark
# step 1 run: start-local
# step 2 run (Run this every time the file is modified): upload /benchmarks/benchmark.hf.yaml
# step 3 run: run benchmark
# step 4 run: stats
# step 5 run: report --destination=/tmp/reports

View File

@ -0,0 +1,86 @@
name: benchmark
http:
host: http://localhost:8080
sharedConnections: 100
phases:
- main:
constantRate:
startAfter: rampup
usersPerSec: 3300
maxSessions: 6000
duration: 5m
forks:
- post_zipcode: &post_zipcode
scenario:
- fetchIndex:
- randomCsvRow:
file: /benchmarks/zip_code_database.csv
removeQuotes: true
columns:
0: zip
1: type
3: city
6: state
7: county
8: timezone
- httpRequest:
sla:
- blockedRatio: 500
POST: /zipcode
headers:
Content-Type: application/json;charset=UTF-8
Accept: application/json
body: |
{
"zip" : "${zip}",
"type" : "${type}",
"city" : "${city}",
"state" : "${state}",
"county" : "${county}",
"timezone" : "${timezone}"
}
- get_zipcode: &get_zipcode
scenario:
- fetchIndex:
- randomCsvRow:
file: /benchmarks/zip_code_database.csv
removeQuotes: true
columns:
0: zipcode
- httpRequest:
sla:
- blockedRatio: 500
headers:
accept: application/json
GET: /zipcode/${zipcode}
- get_zipcode_by_city: &get_zipcode_by_city
scenario:
- fetchDetails:
- randomCsvRow:
file: /benchmarks/cities.csv
removeQuotes: true
columns:
0: city
- httpRequest:
sla:
- blockedRatio: 500
headers:
accept: application/json
GET: /zipcode/by_city?city=${city}
- spike:
constantRate:
startAfter: main
usersPerSec: 4400
duration: 2m
forks:
- get_zipcode_by_city: *get_zipcode_by_city
- get_zipcode: *get_zipcode
- rampup:
increasingRate:
initialUsersPerSec: 3
targetUsersPerSec: 2500
duration: 1m
forks:
- post_zipcode: *post_zipcode
- get_zipcode: *get_zipcode

View File

@ -0,0 +1,136 @@
Holtsville
Adjuntas
Aguada
Aguadilla
Maricao
Anasco
Angeles
Arecibo
Bajadero
Barceloneta
Boqueron
Cabo Rojo
Penuelas
Camuy
Castaner
Rosario
Sabana Grande
Ciales
Utuado
Dorado
Ensenada
Florida
Garrochales
Guanica
Guayanilla
Hatillo
Hormigueros
Isabela
Jayuya
Lajas
Lares
Las Marias
Manati
Moca
Rincon
Quebradillas
Mayaguez
San German
San Sebastian
Morovis
Sabana Hoyos
San Antonio
Vega Alta
Vega Baja
Yauco
Aguas Buenas
Aguirre
Aibonito
Maunabo
Arroyo
Mercedita
Ponce
Naguabo
Naranjito
Orocovis
Palmer
Patillas
Caguas
Canovanas
Ceiba
Cayey
Fajardo
Cidra
Puerto Real
Punta Santiago
Roosevelt Roads
Rio Blanco
Rio Grande
Salinas
San Lorenzo
Santa Isabel
Vieques
Villalba
Yabucoa
Coamo
Las Piedras
Loiza
Luquillo
Culebra
Juncos
Gurabo
Coto Laurel
Comerio
Corozal
Guayama
La Plata
Humacao
Barranquitas
Juana Diaz
St Thomas
Christiansted
St John
Frederiksted
Kingshill
San Juan
Fort Buchanan
Toa Baja
Sabana Seca
Toa Alta
Bayamon
Catano
Guaynabo
Trujillo Alto
Saint Just
Carolina
Agawam
Amherst
Barre
Belchertown
Blandford
Bondsville
Brimfield
Chester
Chesterfield
Chicopee
Cummington
Easthampton
East Longmeadow
East Otis
Feeding Hills
Gilbertville
Goshen
Granby
Granville
Hadley
Hampden
Hardwick
Hatfield
Haydenville
Holyoke
Huntington
Leeds
Leverett
Ludlow
Monson
North Amherst
1 Holtsville
2 Adjuntas
3 Aguada
4 Aguadilla
5 Maricao
6 Anasco
7 Angeles
8 Arecibo
9 Bajadero
10 Barceloneta
11 Boqueron
12 Cabo Rojo
13 Penuelas
14 Camuy
15 Castaner
16 Rosario
17 Sabana Grande
18 Ciales
19 Utuado
20 Dorado
21 Ensenada
22 Florida
23 Garrochales
24 Guanica
25 Guayanilla
26 Hatillo
27 Hormigueros
28 Isabela
29 Jayuya
30 Lajas
31 Lares
32 Las Marias
33 Manati
34 Moca
35 Rincon
36 Quebradillas
37 Mayaguez
38 San German
39 San Sebastian
40 Morovis
41 Sabana Hoyos
42 San Antonio
43 Vega Alta
44 Vega Baja
45 Yauco
46 Aguas Buenas
47 Aguirre
48 Aibonito
49 Maunabo
50 Arroyo
51 Mercedita
52 Ponce
53 Naguabo
54 Naranjito
55 Orocovis
56 Palmer
57 Patillas
58 Caguas
59 Canovanas
60 Ceiba
61 Cayey
62 Fajardo
63 Cidra
64 Puerto Real
65 Punta Santiago
66 Roosevelt Roads
67 Rio Blanco
68 Rio Grande
69 Salinas
70 San Lorenzo
71 Santa Isabel
72 Vieques
73 Villalba
74 Yabucoa
75 Coamo
76 Las Piedras
77 Loiza
78 Luquillo
79 Culebra
80 Juncos
81 Gurabo
82 Coto Laurel
83 Comerio
84 Corozal
85 Guayama
86 La Plata
87 Humacao
88 Barranquitas
89 Juana Diaz
90 St Thomas
91 Christiansted
92 St John
93 Frederiksted
94 Kingshill
95 San Juan
96 Fort Buchanan
97 Toa Baja
98 Sabana Seca
99 Toa Alta
100 Bayamon
101 Catano
102 Guaynabo
103 Trujillo Alto
104 Saint Just
105 Carolina
106 Agawam
107 Amherst
108 Barre
109 Belchertown
110 Blandford
111 Bondsville
112 Brimfield
113 Chester
114 Chesterfield
115 Chicopee
116 Cummington
117 Easthampton
118 East Longmeadow
119 East Otis
120 Feeding Hills
121 Gilbertville
122 Goshen
123 Granby
124 Granville
125 Hadley
126 Hampden
127 Hardwick
128 Hatfield
129 Haydenville
130 Holyoke
131 Huntington
132 Leeds
133 Leverett
134 Ludlow
135 Monson
136 North Amherst

View File

@ -2,12 +2,14 @@
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
./mvnw quarkus:add-extension -Dextensions=container-image-docker mvn quarkus:add-extension -Dextensions=container-image-docker
if [ "$1" = "native" ]; then if [ "$1" = "native" ]; then
./mvnw package -Pnative -Dquarkus.native.container-build=true -f $SCRIPTPATH/pom.xml && mvn clean package -Pnative -Dquarkus.native.container-build=true -f $SCRIPTPATH/pom.xml &&
docker build -f $SCRIPTPATH/src/main/docker/Dockerfile.native -t quarkus-project:0.1-SNAPSHOT $SCRIPTPATH/. docker build -f $SCRIPTPATH/src/main/docker/Dockerfile.native -t quarkus-project:0.1-SNAPSHOT $SCRIPTPATH/.
elif [ "$1" = "local-native" ]; then
mvn clean package -DskipTests -Pnative -f $SCRIPTPATH/pom.xml
else else
./mvnw package -Dquarkus.container-build=true -f $SCRIPTPATH/pom.xml && mvn clean package -Dquarkus.container-build=true -f $SCRIPTPATH/pom.xml &&
docker build -f $SCRIPTPATH/src/main/docker/Dockerfile.jvm -t quarkus-project:0.1-SNAPSHOT $SCRIPTPATH/. docker build -f $SCRIPTPATH/src/main/docker/Dockerfile.jvm -t quarkus-project:0.1-SNAPSHOT $SCRIPTPATH/.
fi fi

View File

@ -1,150 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>quarkus-project</artifactId> <parent>
<version>0.1-SNAPSHOT</version> <groupId>com.baeldung</groupId>
<artifactId>quarkus-vs-springboot</artifactId>
<parent> <version>1.0-SNAPSHOT</version>
<groupId>com.baeldung</groupId> </parent>
<artifactId>quarkus-vs-springboot</artifactId> <artifactId>quarkus-project</artifactId>
<version>1.0-SNAPSHOT</version> <version>0.1-SNAPSHOT</version>
</parent> <properties>
<compiler-plugin.version>3.10.1</compiler-plugin.version>
<dependencyManagement> <maven.compiler.parameters>true</maven.compiler.parameters>
<dependencies> <maven.compiler.source>11</maven.compiler.source>
<dependency> <maven.compiler.target>11</maven.compiler.target>
<groupId>${quarkus.platform.group-id}</groupId> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<artifactId>${quarkus.platform.artifact-id}</artifactId> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<version>${quarkus.platform.version}</version> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<type>pom</type> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<scope>import</scope> <quarkus.platform.version>2.9.2.Final</quarkus.platform.version>
</dependency> <surefire-plugin.version>3.0.0-M6</surefire-plugin.version>
</dependencies> </properties>
</dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId> <artifactId>${quarkus.platform.artifact-id}</artifactId>
</dependency> <version>${quarkus.platform.version}</version>
<dependency> <type>pom</type>
<groupId>io.quarkus</groupId> <scope>import</scope>
<artifactId>quarkus-resteasy-reactive</artifactId> </dependency>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-docker</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</dependencyManagement>
<build> <dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-mysql-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-docker</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<parameters>${maven.compiler.parameters}</parameters>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>${quarkus.platform.group-id}</groupId> <artifactId>maven-failsafe-plugin</artifactId>
<artifactId>quarkus-maven-plugin</artifactId> <version>${surefire-plugin.version}</version>
<version>${quarkus.platform.version}</version> <executions>
<extensions>true</extensions> <execution>
<executions> <goals>
<execution> <goal>integration-test</goal>
<goals> <goal>verify</goal>
<goal>build</goal> </goals>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration> <configuration>
<parameters>${maven.compiler.parameters}</parameters> <systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration> </configuration>
</plugin> </execution>
<plugin> </executions>
<artifactId>maven-surefire-plugin</artifactId> </plugin>
<version>${surefire-plugin.version}</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<profiles> <properties>
<profile> <quarkus.native.additional-build-args>-H:+AllowVMInspection</quarkus.native.additional-build-args>
<id>native</id> <quarkus.package.type>native</quarkus.package.type>
<activation> </properties>
<property> </profile>
<name>native</name> </profiles>
</property> </project>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.native.additional-build-args>-H:+AllowVMInspection</quarkus.native.additional-build-args>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
<properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>2.2.2.Final</quarkus.platform.version>
<surefire-plugin.version>3.0.0-M4</surefire-plugin.version>
</properties>
</project>

View File

@ -41,7 +41,8 @@ RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
&& echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. # Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=5000 -Dcom.sun.management.jmxremote.rmi.port=5001 -Dcom.sun.management.jmxremote.host=0.0.0.0 -Djava.rmi.server.hostname=0.0.0.0"
# We make four distinct layers so if there are application changes the library layers can be re-used # We make four distinct layers so if there are application changes the library layers can be re-used
COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/ COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=1001 target/quarkus-app/*.jar /deployments/ COPY --chown=1001 target/quarkus-app/*.jar /deployments/

View File

@ -1,23 +1,25 @@
version: '3.1' version: '3.1'
services: services:
db: db:
image: postgres image: mysql:5.7.38
ports: ports:
- '5432:5432' - '3306:3306'
environment: environment:
POSTGRES_PASSWORD: example MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: baeldung
command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ]
healthcheck:
test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
app: app:
image: quarkus-project:0.1-SNAPSHOT image: quarkus-project:0.1-SNAPSHOT
ports: network_mode: "host"
- '8080:8080'
environment: environment:
DB_URL: postgresql://db:5432/postgres DB_URL: mysql://localhost:3306/baeldung?useSSL=true&requireSSL=true
links: HOST_HOSTNAME: ${EXTERNAL_IP}
- "db"
depends_on: depends_on:
- "db" db:
networks: condition: service_healthy
default: deploy:
driver: bridge resources:
limits:
cpus: '3.00'

View File

@ -1,6 +1,7 @@
package com.baeldung.quarkus_project; package com.baeldung.quarkus_project;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import io.quarkus.hibernate.reactive.panache.common.runtime.ReactiveTransactional;
import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
@ -13,7 +14,8 @@ public class ZipCodeRepo implements PanacheRepositoryBase<ZipCode, String> {
return find("city = ?1", city).stream(); return find("city = ?1", city).stream();
} }
@ReactiveTransactional
public Uni<ZipCode> save(ZipCode zipCode) { public Uni<ZipCode> save(ZipCode zipCode) {
return zipCode.persistAndFlush(); return zipCode.persist();
} }
} }

View File

@ -2,9 +2,8 @@ package com.baeldung.quarkus_project;
import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import org.jboss.logging.Logger;
import javax.transaction.Transactional; import javax.persistence.PersistenceException;
import javax.ws.rs.*; import javax.ws.rs.*;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
@ -22,7 +21,7 @@ public class ZipCodeResource {
@GET @GET
@Path("/{zipcode}") @Path("/{zipcode}")
public Uni<ZipCode> findById(@PathParam("zipcode") String zipcode) { public Uni<ZipCode> findById(@PathParam("zipcode") String zipcode) {
return zipRepo.findById(zipcode); return getById(zipcode);
} }
@GET @GET
@ -32,12 +31,17 @@ public class ZipCodeResource {
} }
@POST @POST
@Transactional
public Uni<ZipCode> create(ZipCode zipCode) { public Uni<ZipCode> create(ZipCode zipCode) {
return zipRepo.findById(zipCode.getZip()) return getById(zipCode.getZip())
.onItem() .onItem()
.ifNull() .ifNull()
.switchTo(createZipCode(zipCode)); .switchTo(createZipCode(zipCode))
.onFailure(PersistenceException.class)
.recoverWithUni(() -> getById(zipCode.getZip()));
}
private Uni<ZipCode> getById(String zipCode) {
return zipRepo.findById(zipCode);
} }
private Uni<ZipCode> createZipCode(ZipCode zipCode) { private Uni<ZipCode> createZipCode(ZipCode zipCode) {

View File

@ -1,9 +1,12 @@
quarkus.datasource.db-kind=postgresql quarkus.datasource.db-kind=mysql
quarkus.datasource.username=postgres quarkus.datasource.username=root
quarkus.datasource.password=example quarkus.datasource.password=root
quarkus.datasource.reactive.url=${DB_URL:postgresql://localhost:5432/postgres} quarkus.datasource.reactive.url=${DB_URL:mysql://localhost:3306/baeldung?useSSL=true&requireSSL=true}
quarkus.datasource.reactive.max-size=20 quarkus.datasource.reactive.max-size=95
quarkus.datasource.reactive.mysql.ssl-mode=required
#quarkus.hibernate-orm.log.sql=true #quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.native.enable-vm-inspection=true
quarkus.datasource.reactive.trust-all=true

View File

@ -0,0 +1,11 @@
#!/bin/bash
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
if [ "$1" = "native" ]; then
mvn clean package -DskipTests spring-boot:build-image -Pnative -f $SCRIPTPATH/pom.xml
elif [ "$1" = "local-native" ]; then
mvn clean package -DskipTests -Plocal-native -f $SCRIPTPATH/pom.xml
else
mvn clean package -DskipTests spring-boot:build-image -f $SCRIPTPATH/pom.xml
fi

View File

@ -1,6 +0,0 @@
#!/bin/bash
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
docker build -f $SCRIPTPATH/src/main/docker/Dockerfile.jvm -t spring-project:0.1-SNAPSHOT $SCRIPTPATH/.

View File

@ -10,8 +10,8 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version> <version>2.6.9</version>
<relativePath /> <relativePath/>
</parent> </parent>
<dependencies> <dependencies>
@ -29,14 +29,9 @@
<version>${spring-native.version}</version> <version>${spring-native.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.r2dbc</groupId> <groupId>com.github.jasync-sql</groupId>
<artifactId>r2dbc-postgresql</artifactId> <artifactId>jasync-r2dbc-mysql</artifactId>
<scope>runtime</scope> <version>2.0.8</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -48,131 +43,210 @@
<artifactId>reactor-test</artifactId> <artifactId>reactor-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>r2dbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>1.17.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <configuration>
<classifier>${repackage.classifier}</classifier> <classifier>exec</classifier>
<layers>
<enabled>true</enabled>
</layers>
<image> <image>
<builder>paketobuildpacks/builder:tiny</builder> <builder>paketobuildpacks/builder:tiny</builder>
<env> <env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE> <BP_NATIVE_IMAGE>false</BP_NATIVE_IMAGE>
<BPL_JFR_ENABLED>true</BPL_JFR_ENABLED>
</env> </env>
</image> </image>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.springframework.experimental</groupId> <artifactId>maven-surefire-plugin</artifactId>
<artifactId>spring-aot-maven-plugin</artifactId> <version>${surefire-plugin.version}</version>
<version>${spring-native.version}</version> <configuration>
<executions> <includes>
<execution> <include>**/*IT</include>
<id>test-generate</id> </includes>
<goals> </configuration>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<repositories> <repositories>
<repository> <repository>
<id>spring-releases</id> <id>spring-release</id>
<name>Spring Releases</name> <name>Spring release</name>
<url>https://repo.spring.io/release</url> <url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<!-- This can be removed once a stable version of org.springframework.experimental:spring-aot-maven-plugin,
compatible with latest JDK, is released. -->
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository> </repository>
</repositories> </repositories>
<pluginRepositories> <pluginRepositories>
<pluginRepository> <pluginRepository>
<id>spring-releases</id> <id>spring-release</id>
<name>Spring Releases</name> <name>Spring release</name>
<url>https://repo.spring.io/release</url> <url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<!-- This can be removed once a stable version of org.springframework.experimental:spring-aot-maven-plugin,
compatible with latest JDK, is released. -->
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository> </pluginRepository>
</pluginRepositories> </pluginRepositories>
<profiles> <profiles>
<profile> <profile>
<id>native</id> <id>native</id>
<properties>
<repackage.classifier>exec</repackage.classifier>
<native-buildtools.version>0.9.3</native-buildtools.version>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.graalvm.buildtools</groupId> <groupId>org.junit.platform</groupId>
<artifactId>junit-platform-native</artifactId> <artifactId>junit-platform-launcher</artifactId>
<version>${native-buildtools.version}</version> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
<image>
<builder>paketobuildpacks/builder:tiny</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
<BPL_JFR_ENABLED>true</BPL_JFR_ENABLED>
</env>
</image>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<executions>
<execution>
<id>test-generate</id>
<goals>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>local-native</id>
<properties>
<repackage.classifier>exec</repackage.classifier>
<native-buildtools.version>0.9.11</native-buildtools.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<executions>
<execution>
<id>test-generate</id>
<goals>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.graalvm.buildtools</groupId> <groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId> <artifactId>native-maven-plugin</artifactId>
<version>${native-buildtools.version}</version> <version>${native-buildtools.version}</version>
<extensions>true</extensions>
<configuration> <configuration>
<buildArgs combine.children="append"> <buildArgs combine.children="append">
<buildArgs>-H:+AllowVMInspection</buildArgs> <buildArgs>-H:+AllowVMInspection</buildArgs>
</buildArgs> </buildArgs>
</configuration> </configuration>
<executions> <executions>
<execution>
<id>test-native</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
<execution> <execution>
<id>build-native</id> <id>build-native</id>
<phase>package</phase>
<goals> <goals>
<goal>build</goal> <goal>build</goal>
</goals> </goals>
<phase>package</phase>
</execution>
<execution>
<id>test-native</id>
<goals>
<goal>test</goal>
</goals>
<phase>test</phase>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M6</version>
<configuration> <configuration>
<argLine>-DspringAot=true <argLine>-DspringAot=true
-agentlib:native-image-agent=access-filter-file=src/test/resources/access-filter.json,config-merge-dir=target/classes/META-INF/native-image</argLine> -agentlib:native-image-agent=access-filter-file=src/test/resources/access-filter.json,config-merge-dir=target/classes/META-INF/native-image</argLine>
@ -185,9 +259,8 @@
<properties> <properties>
<java.version>11</java.version> <java.version>11</java.version>
<repackage.classifier /> <spring-native.version>0.12.1</spring-native.version>
<spring-native.version>0.11.0-RC1</spring-native.version> <surefire-plugin.version>3.0.0-M6</surefire-plugin.version>
<log4j2.version>2.17.1</log4j2.version>
</properties> </properties>
</project> </project>

View File

@ -1,12 +0,0 @@
FROM openjdk:11
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
COPY --chown=1001 target/spring-project-0.1-SNAPSHOT-exec.jar /spring-app/
WORKDIR /spring-app
EXPOSE 8080
USER 1001
ENTRYPOINT ["java", "-jar", "spring-project-0.1-SNAPSHOT-exec.jar" ]

View File

@ -2,21 +2,25 @@ version: '3.1'
services: services:
db: db:
image: postgres image: mysql:5.7.38
ports: ports:
- '5432:5432' - '3306:3306'
environment: environment:
POSTGRES_PASSWORD: example MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: baeldung
command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ]
healthcheck:
test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
app: app:
image: spring-project:0.1-SNAPSHOT image: docker.io/library/spring-project:0.1-SNAPSHOT
ports: network_mode: "host"
- '8080:8080'
environment: environment:
DB_URL: r2dbc:postgresql://db:5432/postgres DB_URL: r2dbc:mysql://localhost:3306/baeldung?useSSL=true&requireSSL=true
links: HOST_HOSTNAME: ${EXTERNAL_IP}
- "db"
depends_on: depends_on:
- "db" db:
networks: condition: service_healthy
default: deploy:
driver: bridge resources:
limits:
cpus: '3.00'

View File

@ -1,21 +1,22 @@
package com.baeldung.spring_project; package com.baeldung.spring_project;
import com.baeldung.spring_project.domain.ZIPRepo;
import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.ConnectionFactory;
import org.springframework.boot.SpringApplication; 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.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
import org.springframework.r2dbc.connection.R2dbcTransactionManager; import org.springframework.r2dbc.connection.R2dbcTransactionManager;
import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer; import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator; import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
import org.springframework.transaction.ReactiveTransactionManager; import org.springframework.transaction.ReactiveTransactionManager;
@SpringBootApplication @SpringBootApplication
@EnableR2dbcRepositories
public class Startup { public class Startup {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(Startup.class, args).getBean(ZIPRepo.class).findById(""); SpringApplication.run(Startup.class, args);
} }
@Bean @Bean
@ -34,4 +35,5 @@ public class Startup {
@Bean ReactiveTransactionManager transactionManager(ConnectionFactory connectionFactory) { @Bean ReactiveTransactionManager transactionManager(ConnectionFactory connectionFactory) {
return new R2dbcTransactionManager(connectionFactory); return new R2dbcTransactionManager(connectionFactory);
} }
} }

View File

@ -2,11 +2,14 @@ package com.baeldung.spring_project;
import com.baeldung.spring_project.domain.ZIPRepo; import com.baeldung.spring_project.domain.ZIPRepo;
import com.baeldung.spring_project.domain.ZipCode; import com.baeldung.spring_project.domain.ZipCode;
import org.springframework.transaction.annotation.Transactional; import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.r2dbc.UncategorizedR2dbcException;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@RestController @RestController
@ -21,7 +24,7 @@ public class ZipCodeApi {
@GetMapping("/{zipcode}") @GetMapping("/{zipcode}")
public Mono<ZipCode> findById(@PathVariable String zipcode) { public Mono<ZipCode> findById(@PathVariable String zipcode) {
return zipRepo.findById(zipcode); return getById(zipcode);
} }
@GetMapping("/by_city") @GetMapping("/by_city")
@ -29,10 +32,23 @@ public class ZipCodeApi {
return zipRepo.findByCity(city); return zipRepo.findByCity(city);
} }
@Transactional
@PostMapping @PostMapping
public Mono<ZipCode> create(@RequestBody ZipCode zipCode) { public Mono<ZipCode> create(@RequestBody ZipCode zipCode) {
return zipRepo.findById(zipCode.getZip()).switchIfEmpty(Mono.defer(createZipCode(zipCode))); return getById(zipCode.getZip())
.switchIfEmpty(Mono.defer(createZipCode(zipCode)))
.onErrorResume(this::isKeyDuplicated, this.recoverWith(zipCode));
}
private Mono<ZipCode> getById(String zipCode) {
return zipRepo.findById(zipCode);
}
private boolean isKeyDuplicated(Throwable ex) {
return ex instanceof DataIntegrityViolationException || ex instanceof UncategorizedR2dbcException;
}
private Function<? super Throwable, ? extends Mono<ZipCode>> recoverWith(ZipCode zipCode) {
return throwable -> zipRepo.findById(zipCode.getZip());
} }
private Supplier<Mono<? extends ZipCode>> createZipCode(ZipCode zipCode) { private Supplier<Mono<? extends ZipCode>> createZipCode(ZipCode zipCode) {

View File

@ -1,5 +1,7 @@
spring.r2dbc.url=${DB_URL:r2dbc:postgresql://localhost:5432/postgres} spring.r2dbc.url=${DB_URL:r2dbc:mysql://localhost:3306/baeldung?useSSL=true&requireSSL=true}
spring.r2dbc.username=postgres spring.r2dbc.properties.sslMode=required
spring.r2dbc.password=example spring.r2dbc.username=root
spring.r2dbc.pool.enabled=true spring.r2dbc.password=root
spring.r2dbc.pool.maxSize=20 spring.r2dbc.pool.enabled=true
spring.r2dbc.pool.maxSize=95

View File

@ -1,9 +1,20 @@
package com.baeldung.spring_project; package com.baeldung.spring_project;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.testcontainers.junit.jupiter.Testcontainers;
@SpringBootTest import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "spring.r2dbc.url=r2dbc:tc:mysql:///baeldung?TC_IMAGE_TAG=5.7.34"}
)
@TestInstance(value = PER_CLASS)
@Testcontainers
@Disabled
class StartupIT { class StartupIT {
@Test @Test

View File

@ -0,0 +1,136 @@
Holtsville
Adjuntas
Aguada
Aguadilla
Maricao
Anasco
Angeles
Arecibo
Bajadero
Barceloneta
Boqueron
Cabo Rojo
Penuelas
Camuy
Castaner
Rosario
Sabana Grande
Ciales
Utuado
Dorado
Ensenada
Florida
Garrochales
Guanica
Guayanilla
Hatillo
Hormigueros
Isabela
Jayuya
Lajas
Lares
Las Marias
Manati
Moca
Rincon
Quebradillas
Mayaguez
San German
San Sebastian
Morovis
Sabana Hoyos
San Antonio
Vega Alta
Vega Baja
Yauco
Aguas Buenas
Aguirre
Aibonito
Maunabo
Arroyo
Mercedita
Ponce
Naguabo
Naranjito
Orocovis
Palmer
Patillas
Caguas
Canovanas
Ceiba
Cayey
Fajardo
Cidra
Puerto Real
Punta Santiago
Roosevelt Roads
Rio Blanco
Rio Grande
Salinas
San Lorenzo
Santa Isabel
Vieques
Villalba
Yabucoa
Coamo
Las Piedras
Loiza
Luquillo
Culebra
Juncos
Gurabo
Coto Laurel
Comerio
Corozal
Guayama
La Plata
Humacao
Barranquitas
Juana Diaz
St Thomas
Christiansted
St John
Frederiksted
Kingshill
San Juan
Fort Buchanan
Toa Baja
Sabana Seca
Toa Alta
Bayamon
Catano
Guaynabo
Trujillo Alto
Saint Just
Carolina
Agawam
Amherst
Barre
Belchertown
Blandford
Bondsville
Brimfield
Chester
Chesterfield
Chicopee
Cummington
Easthampton
East Longmeadow
East Otis
Feeding Hills
Gilbertville
Goshen
Granby
Granville
Hadley
Hampden
Hardwick
Hatfield
Haydenville
Holyoke
Huntington
Leeds
Leverett
Ludlow
Monson
North Amherst
1 Holtsville
2 Adjuntas
3 Aguada
4 Aguadilla
5 Maricao
6 Anasco
7 Angeles
8 Arecibo
9 Bajadero
10 Barceloneta
11 Boqueron
12 Cabo Rojo
13 Penuelas
14 Camuy
15 Castaner
16 Rosario
17 Sabana Grande
18 Ciales
19 Utuado
20 Dorado
21 Ensenada
22 Florida
23 Garrochales
24 Guanica
25 Guayanilla
26 Hatillo
27 Hormigueros
28 Isabela
29 Jayuya
30 Lajas
31 Lares
32 Las Marias
33 Manati
34 Moca
35 Rincon
36 Quebradillas
37 Mayaguez
38 San German
39 San Sebastian
40 Morovis
41 Sabana Hoyos
42 San Antonio
43 Vega Alta
44 Vega Baja
45 Yauco
46 Aguas Buenas
47 Aguirre
48 Aibonito
49 Maunabo
50 Arroyo
51 Mercedita
52 Ponce
53 Naguabo
54 Naranjito
55 Orocovis
56 Palmer
57 Patillas
58 Caguas
59 Canovanas
60 Ceiba
61 Cayey
62 Fajardo
63 Cidra
64 Puerto Real
65 Punta Santiago
66 Roosevelt Roads
67 Rio Blanco
68 Rio Grande
69 Salinas
70 San Lorenzo
71 Santa Isabel
72 Vieques
73 Villalba
74 Yabucoa
75 Coamo
76 Las Piedras
77 Loiza
78 Luquillo
79 Culebra
80 Juncos
81 Gurabo
82 Coto Laurel
83 Comerio
84 Corozal
85 Guayama
86 La Plata
87 Humacao
88 Barranquitas
89 Juana Diaz
90 St Thomas
91 Christiansted
92 St John
93 Frederiksted
94 Kingshill
95 San Juan
96 Fort Buchanan
97 Toa Baja
98 Sabana Seca
99 Toa Alta
100 Bayamon
101 Catano
102 Guaynabo
103 Trujillo Alto
104 Saint Just
105 Carolina
106 Agawam
107 Amherst
108 Barre
109 Belchertown
110 Blandford
111 Bondsville
112 Brimfield
113 Chester
114 Chesterfield
115 Chicopee
116 Cummington
117 Easthampton
118 East Longmeadow
119 East Otis
120 Feeding Hills
121 Gilbertville
122 Goshen
123 Granby
124 Granville
125 Hadley
126 Hampden
127 Hardwick
128 Hatfield
129 Haydenville
130 Holyoke
131 Huntington
132 Leeds
133 Leverett
134 Ludlow
135 Monson
136 North Amherst

View File

@ -0,0 +1,65 @@
local require = require
local json = require "json"
math.randomseed(os.time())
-- read csv lines
function ParseCSVLine(line,sep)
local res = {}
local pos = 1
sep = sep or ','
while true do
local c = string.sub(line,pos,pos)
if (c == "") then break end
if (c == '"') then
local txt = ""
repeat
local startp,endp = string.find(line,'^%b""',pos)
txt = txt..string.sub(line,startp+1,endp-1)
pos = endp + 1
c = string.sub(line,pos,pos)
if (c == '"') then txt = txt..'"' end
until (c ~= '"')
table.insert(res,txt)
assert(c == sep or c == "")
pos = pos + 1
else
local startp,endp = string.find(line,sep,pos)
if (startp) then
table.insert(res,string.sub(line,pos,startp-1))
pos = endp + 1
else
table.insert(res,string.sub(line,pos))
break
end
end
end
return res
end
loadFile = function()
local filename = "zip_code_database.csv"
local data = {}
local count = 0
local sep = ","
for line in io.lines(filename) do
local values = ParseCSVLine(line,sep)
data[count + 1] = { zip=values[1], type=values[2], city=values[4], state=values[7], county=values[8], timezone=values[9] }
count = count + 1
end
return data
end
generator = function()
local data = loadFile()
return coroutine.create(function()
for k,v in pairs(data) do
coroutine.yield(json.stringify(v))
end
end)
end
return generator()

View File

@ -0,0 +1,79 @@
local require = require
local json = require "json"
math.randomseed(os.clock()*100000000000)
function ParseCSVLine(line,sep)
local res = {}
local pos = 1
sep = sep or ','
while true do
local c = string.sub(line,pos,pos)
if (c == "") then break end
if (c == '"') then
local txt = ""
repeat
local startp,endp = string.find(line,'^%b""',pos)
txt = txt..string.sub(line,startp+1,endp-1)
pos = endp + 1
c = string.sub(line,pos,pos)
if (c == '"') then txt = txt..'"' end
until (c ~= '"')
table.insert(res,txt)
assert(c == sep or c == "")
pos = pos + 1
else
local startp,endp = string.find(line,sep,pos)
if (startp) then
table.insert(res,string.sub(line,pos,startp-1))
pos = endp + 1
else
table.insert(res,string.sub(line,pos))
break
end
end
end
return res
end
loadFile = function()
local filename = "cities.csv"
local data = {}
local count = 0
local sep = ","
for line in io.lines(filename) do
local values = ParseCSVLine(line,sep)
data[count + 1] = values[1]
count = count + 1
end
return data
end
local data = loadFile()
local urlencode = function (str)
str = string.gsub (str, "([^0-9a-zA-Z !'()*._~-])", -- locale independent
function (c) return string.format ("%%%02X", string.byte(c)) end)
str = string.gsub (str, " ", "+")
return str
end
request = function()
url_path = "/zipcode/by_city?city=" .. urlencode(data[math.random(1, 136)])
local headers = { ["Content-Type"] = "application/json;charset=UTF-8" }
return wrk.format("GET", url_path, headers, nil)
end
done = function(summary, latency, requests)
io.write("--------------GET CITY ZIPCODES----------------\n")
for _, p in pairs({ 50, 90, 99, 99.999 }) do
n = latency:percentile(p)
io.write(string.format("%g%%,%d\n", p, n))
end
io.write("-----------------------------------------------\n\n")
end

View File

@ -0,0 +1,77 @@
local require = require
local json = require "json"
math.randomseed(os.clock()*100000000000)
function ParseCSVLine(line,sep)
local res = {}
local pos = 1
sep = sep or ','
while true do
local c = string.sub(line,pos,pos)
if (c == "") then break end
if (c == '"') then
local txt = ""
repeat
local startp,endp = string.find(line,'^%b""',pos)
txt = txt..string.sub(line,startp+1,endp-1)
pos = endp + 1
c = string.sub(line,pos,pos)
if (c == '"') then txt = txt..'"' end
until (c ~= '"')
table.insert(res,txt)
assert(c == sep or c == "")
pos = pos + 1
else
local startp,endp = string.find(line,sep,pos)
if (startp) then
table.insert(res,string.sub(line,pos,startp-1))
pos = endp + 1
else
table.insert(res,string.sub(line,pos))
break
end
end
end
return res
end
loadFile = function()
local filename = "zip_code_database.csv"
local data = {}
local count = 0
local sep = ","
for line in io.lines(filename) do
local values = ParseCSVLine(line,sep)
data[count + 1] = values[1]
count = count + 1
end
return data
end
local data = loadFile()
request = function()
local value = data[math.random(1, 12079)]
url_path = "/zipcode/" .. value
local headers = { ["Content-Type"] = "application/json;charset=UTF-8" }
return wrk.format("GET", url_path, headers, nil)
end
done = function(summary, latency, requests)
io.write("--------------GET ZIPCODE----------------\n")
for _, p in pairs({ 50, 90, 99, 99.999 }) do
n = latency:percentile(p)
io.write(string.format("%g%%,%d\n", p, n))
end
io.write("-----------------------------------------\n\n")
end

View File

@ -0,0 +1,133 @@
local json = {}
local function kind_of(obj)
if type(obj) ~= 'table' then return type(obj) end
local i = 1
for _ in pairs(obj) do
if obj[i] ~= nil then i = i + 1 else return 'table' end
end
if i == 1 then return 'table' else return 'array' end
end
local function escape_str(s)
local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'}
for i, c in ipairs(in_char) do
s = s:gsub(c, '\\' .. out_char[i])
end
return s
end
local function skip_delim(str, pos, delim, err_if_missing)
pos = pos + #str:match('^%s*', pos)
if str:sub(pos, pos) ~= delim then
if err_if_missing then
error('Expected ' .. delim .. ' near position ' .. pos)
end
return pos, false
end
return pos + 1, true
end
local function parse_str_val(str, pos, val)
val = val or ''
local early_end_error = 'End of input found while parsing string.'
if pos > #str then error(early_end_error) end
local c = str:sub(pos, pos)
if c == '"' then return val, pos + 1 end
if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
local nextc = str:sub(pos + 1, pos + 1)
if not nextc then error(early_end_error) end
return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
end
local function parse_num_val(str, pos)
local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
local val = tonumber(num_str)
if not val then error('Error parsing number at position ' .. pos .. '.') end
return val, pos + #num_str
end
function json.stringify(obj, as_key)
local s = {}
local kind = kind_of(obj)
if kind == 'array' then
if as_key then error('Can\'t encode array as key.') end
s[#s + 1] = '['
for i, val in ipairs(obj) do
if i > 1 then s[#s + 1] = ', ' end
s[#s + 1] = json.stringify(val)
end
s[#s + 1] = ']'
elseif kind == 'table' then
if as_key then error('Can\'t encode table as key.') end
s[#s + 1] = '{'
for k, v in pairs(obj) do
if #s > 1 then s[#s + 1] = ', ' end
s[#s + 1] = json.stringify(k, true)
s[#s + 1] = ':'
s[#s + 1] = json.stringify(v)
end
s[#s + 1] = '}'
elseif kind == 'string' then
return '"' .. escape_str(obj) .. '"'
elseif kind == 'number' then
if as_key then return '"' .. tostring(obj) .. '"' end
return tostring(obj)
elseif kind == 'boolean' then
return tostring(obj)
elseif kind == 'nil' then
return 'null'
else
error('Unjsonifiable type: ' .. kind .. '.')
end
return table.concat(s)
end
json.null = {}
function json.parse(str, pos, end_delim)
pos = pos or 1
if pos > #str then error('Reached unexpected end of input.') end
local pos = pos + #str:match('^%s*', pos)
local first = str:sub(pos, pos)
if first == '{' then
local obj, key, delim_found = {}, true, true
pos = pos + 1
while true do
key, pos = json.parse(str, pos, '}')
if key == nil then return obj, pos end
if not delim_found then error('Comma missing between object items.') end
pos = skip_delim(str, pos, ':', true)
obj[key], pos = json.parse(str, pos)
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '[' then
local arr, val, delim_found = {}, true, true
pos = pos + 1
while true do
val, pos = json.parse(str, pos, ']')
if val == nil then return arr, pos end
if not delim_found then error('Comma missing between array items.') end
arr[#arr + 1] = val
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '"' then
return parse_str_val(str, pos + 1)
elseif first == '-' or first:match('%d') then
return parse_num_val(str, pos)
elseif first == end_delim then
return nil, pos + 1
else
local literals = {['true'] = true, ['false'] = false, ['null'] = json.null}
for lit_str, lit_val in pairs(literals) do
local lit_end = pos + #lit_str - 1
if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
end
local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
error('Invalid json syntax starting at ' .. pos_info_str)
end
end
return json

View File

@ -0,0 +1,73 @@
local require = require
local json = require "json"
math.randomseed(os.clock()*100000000000)
function ParseCSVLine(line,sep)
local res = {}
local pos = 1
sep = sep or ','
while true do
local c = string.sub(line,pos,pos)
if (c == "") then break end
if (c == '"') then
local txt = ""
repeat
local startp,endp = string.find(line,'^%b""',pos)
txt = txt..string.sub(line,startp+1,endp-1)
pos = endp + 1
c = string.sub(line,pos,pos)
if (c == '"') then txt = txt..'"' end
until (c ~= '"')
table.insert(res,txt)
assert(c == sep or c == "")
pos = pos + 1
else
local startp,endp = string.find(line,sep,pos)
if (startp) then
table.insert(res,string.sub(line,pos,startp-1))
pos = endp + 1
else
table.insert(res,string.sub(line,pos))
break
end
end
end
return res
end
loadFile = function()
local filename = "zip_code_database.csv"
local data = {}
local count = 0
local sep = ","
for line in io.lines(filename) do
local values = ParseCSVLine(line,sep)
data[count + 1] = { zip=values[1], type=values[2], city=values[4], state=values[7], county=values[8], timezone=values[9] }
count = count + 1
end
return data
end
local data = loadFile()
request = function()
local url_path = "/zipcode"
local val = data[math.random(1, 12079)]
local headers = { ["Content-Type"] = "application/json;charset=UTF-8" }
return wrk.format("POST", url_path, headers, json.stringify(val))
end
done = function(summary, latency, requests)
io.write("--------------POST ZIPCODE----------------\n")
for _, p in pairs({ 50, 75, 90, 99, 99.999 }) do
n = latency:percentile(p)
io.write(string.format("%g%%,%d\n", p, n))
end
io.write("------------------------------------------\n\n")
end

View File

@ -0,0 +1,14 @@
#!/bin/bash
$wrk_home/wrk -t1 -c5 -d1m -s ./post_zipcode.lua --timeout 2m -H 'Host: localhost' http://localhost:8080 & sleep 60
$wrk_home/wrk -t1 -c20 -d5m -s ./post_zipcode.lua --timeout 2m -H 'Host: localhost' http://localhost:8080 & sleep 60
$wrk_home/wrk -t1 -c20 -d5m -s ./get_by_city.lua --timeout 2m -H 'Host: localhost' http://localhost:8080 \ &
$wrk_home/wrk -t1 -c20 -d5m -s ./get_zipcode.lua --timeout 2m -H 'Host: localhost' http://localhost:8080 \ & sleep 120
$wrk_home/wrk -t2 -c10 -d3m -s ./get_by_city.lua --timeout 2m -H 'Host: localhost' http://localhost:8080 \ &
$wrk_home/wrk -t2 -c10 -d3m -s ./get_zipcode.lua --timeout 2m -H 'Host: localhost' http://localhost:8080 \ &
wait

View File

@ -1,4 +1,4 @@
## Relevant Articles: ## Relevant Articles:
- [Guide to QuarkusIO](https://www.baeldung.com/quarkus-io) - [Guide to Quarkus](https://www.baeldung.com/quarkus-io)
- [Testing Quarkus Applications](https://www.baeldung.com/java-quarkus-testing) - [Testing Quarkus Applications](https://www.baeldung.com/java-quarkus-testing)

View File

@ -3,4 +3,5 @@
This module contains articles about reactive Spring 5. This module contains articles about reactive Spring 5.
- [Logging a Reactive Sequence](https://www.baeldung.com/spring-reactive-sequence-logging) - [Logging a Reactive Sequence](https://www.baeldung.com/spring-reactive-sequence-logging)
- [Reading Flux Into a Single InputStream Using Spring Reactive WebClient](https://www.baeldung.com/spring-reactive-read-flux-into-inputstream)
- More articles: [[<-- prev]](../spring-5-reactive-2) - More articles: [[<-- prev]](../spring-5-reactive-2)

View File

@ -0,0 +1,94 @@
package com.baeldung.databuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class DataBufferToInputStream {
private static final Logger logger = LoggerFactory.getLogger(DataBufferToInputStream.class);
private static final String REQUEST_ENDPOINT = "https://gorest.co.in/public/v2/users";
private static WebClient getWebClient() {
WebClient.Builder webClientBuilder = WebClient.builder();
return webClientBuilder.build();
}
public static InputStream getResponseAsInputStream(WebClient client, String url) throws IOException, InterruptedException {
PipedOutputStream pipedOutputStream = new PipedOutputStream();
PipedInputStream pipedInputStream = new PipedInputStream(1024 * 10);
pipedInputStream.connect(pipedOutputStream);
Flux<DataBuffer> body = client.get()
.uri(url)
.exchangeToFlux(clientResponse -> {
return clientResponse.body(BodyExtractors.toDataBuffers());
})
.doOnError(error -> {
logger.error("error occurred while reading body", error);
})
.doFinally(s -> {
try {
pipedOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.doOnCancel(() -> {
logger.error("Get request is cancelled");
});
DataBufferUtils.write(body, pipedOutputStream)
.log("Writing to output buffer")
.subscribe();
return pipedInputStream;
}
private static String readContentFromPipedInputStream(PipedInputStream stream) throws IOException {
StringBuffer contentStringBuffer = new StringBuffer();
try {
Thread pipeReader = new Thread(() -> {
try {
contentStringBuffer.append(readContent(stream));
} catch (IOException e) {
throw new RuntimeException(e);
}
});
pipeReader.start();
pipeReader.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
stream.close();
}
return String.valueOf(contentStringBuffer);
}
private static String readContent(InputStream stream) throws IOException {
StringBuffer contentStringBuffer = new StringBuffer();
byte[] tmp = new byte[stream.available()];
int byteCount = stream.read(tmp, 0, tmp.length);
logger.info(String.format("read %d bytes from the stream\n", byteCount));
contentStringBuffer.append(new String(tmp));
return String.valueOf(contentStringBuffer);
}
public static void main(String[] args) throws IOException, InterruptedException {
WebClient webClient = getWebClient();
InputStream inputStream = getResponseAsInputStream(webClient, REQUEST_ENDPOINT);
Thread.sleep(3000);
String content = readContentFromPipedInputStream((PipedInputStream) inputStream);
logger.info("response content: \n{}", content.replace("}", "}\n"));
}
}

View File

@ -0,0 +1,77 @@
package databuffer;
import com.baeldung.databuffer.DataBufferToInputStream;
import io.restassured.internal.util.IOUtils;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
class DataBufferToInputStreamUnitTest {
private String getResponseStub() throws IOException {
InputStream inputStream = null;
BufferedReader reader = null;
String content = null;
try {
inputStream = this.getClass()
.getClassLoader()
.getResourceAsStream("user-response.json");
if (inputStream != null) {
reader = new BufferedReader(new InputStreamReader(inputStream));
content = reader.lines()
.collect(Collectors.joining(System.lineSeparator()));
}
} catch (Exception ex) {
throw new RuntimeException("exception caught while getting response stub");
} finally {
reader.close();
inputStream.close();
}
return content;
}
private InputStream getResponseStubAsInputStream() {
return this.getClass()
.getClassLoader()
.getResourceAsStream("user-response.json");
}
private WebClient getMockWebClient() throws IOException {
String content = getResponseStub();
ClientResponse clientResponse = ClientResponse.create(HttpStatus.OK)
.header("Content-Type", "application/json")
.body(content)
.build();
ExchangeFunction exchangeFunction = clientRequest -> Mono.just(clientResponse);
WebClient.Builder webClientBuilder = WebClient.builder()
.exchangeFunction(exchangeFunction);
WebClient webClient = webClientBuilder.build();
return webClient;
}
@Test
public void testResponseAsInputStream() throws IOException, InterruptedException {
String mockUrl = Mockito.anyString();
WebClient mockWebClient = getMockWebClient();
InputStream inputStream = DataBufferToInputStream.getResponseAsInputStream(mockWebClient, mockUrl);
byte[] expectedBytes = IOUtils.toByteArray(getResponseStubAsInputStream());
byte[] actualBytes = IOUtils.toByteArray(inputStream);
assertArrayEquals(expectedBytes, actualBytes);
}
}

View File

@ -0,0 +1,72 @@
[
{
"id": 2683,
"name": "Maheswar Kocchar",
"email": "maheswar_kocchar@kihn.info",
"gender": "male",
"status": "active"
},
{
"id": 2680,
"name": "Lakshminath Khan",
"email": "lakshminath_khan@barrows-cormier.biz",
"gender": "female",
"status": "inactive"
},
{
"id": 2679,
"name": "Tarun Arora",
"email": "tarun_arora@rolfson.net",
"gender": "female",
"status": "inactive"
},
{
"id": 2678,
"name": "Agnivesh Dubashi",
"email": "dubashi_agnivesh@senger.name",
"gender": "male",
"status": "inactive"
},
{
"id": 2677,
"name": "Dhanu Gowda",
"email": "gowda_dhanu@hayes.org",
"gender": "male",
"status": "active"
},
{
"id": 2675,
"name": "Harinakshi Pilla Jr.",
"email": "pilla_jr_harinakshi@rutherford-monahan.com",
"gender": "female",
"status": "inactive"
},
{
"id": 2673,
"name": "Kalpana Prajapat",
"email": "prajapat_kalpana@wilkinson-schaefer.net",
"gender": "female",
"status": "active"
},
{
"id": 2672,
"name": "Chakradhar Jha",
"email": "jha_chakradhar@baumbach.info",
"gender": "male",
"status": "active"
},
{
"id": 2670,
"name": "Divaakar Deshpande Jr.",
"email": "deshpande_jr_divaakar@mertz.info",
"gender": "female",
"status": "inactive"
},
{
"id": 2669,
"name": "Prasanna Mehra",
"email": "prasanna_mehra@ruecker-larkin.name",
"gender": "female",
"status": "active"
}
]

View File

@ -24,11 +24,12 @@ public class ExploreSpring5URLPatternUsingRouterFunctions {
private RouterFunction<ServerResponse> routingFunction() { private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/p?ths"), serverRequest -> ok().body(fromValue("/p?ths"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromValue(serverRequest.pathVariable("id")))) return route(GET("/t?st"), serverRequest -> ok().body(fromValue("Path /t?st is accessed"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromValue(serverRequest.pathVariable("id"))))
.andRoute(GET("/*card"), serverRequest -> ok().body(fromValue("/*card path was accessed"))) .andRoute(GET("/baeldung/*Id"), serverRequest -> ok().body(fromValue("/baeldung/*Id path was accessed")))
.andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromValue(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2")))) .andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromValue(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2"))))
.andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromValue("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung")))) .andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromValue("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung"))))
.and(RouterFunctions.resources("/files/{*filepaths}", new ClassPathResource("files/"))); .and(RouterFunctions.resources("/files/{*filepaths}", new ClassPathResource("files/")))
.and(RouterFunctions.resources("/resources/**", new ClassPathResource("resources/")));
} }
WebServer start() throws Exception { WebServer start() throws Exception {

View File

@ -27,12 +27,12 @@ public class ExploreSpring5URLPatternUsingRouterFunctionsIntegrationTest {
@Test @Test
public void givenRouter_whenGetPathWithSingleCharWildcard_thenGotPathPattern() throws Exception { public void givenRouter_whenGetPathWithSingleCharWildcard_thenGotPathPattern() throws Exception {
client.get() client.get()
.uri("/paths") .uri("/test")
.exchange() .exchange()
.expectStatus() .expectStatus()
.isOk() .isOk()
.expectBody(String.class) .expectBody(String.class)
.isEqualTo("/p?ths"); .isEqualTo("Path /t?st is accessed");
} }
@Test @Test
@ -50,12 +50,12 @@ public class ExploreSpring5URLPatternUsingRouterFunctionsIntegrationTest {
public void givenRouter_whenGetMultipleCharWildcard_thenGotPathPattern() throws Exception { public void givenRouter_whenGetMultipleCharWildcard_thenGotPathPattern() throws Exception {
client.get() client.get()
.uri("/wildcard") .uri("/baeldung/tutorialId")
.exchange() .exchange()
.expectStatus() .expectStatus()
.isOk() .isOk()
.expectBody(String.class) .expectBody(String.class)
.isEqualTo("/*card path was accessed"); .isEqualTo("/baeldung/*Id path was accessed");
} }
@Test @Test
@ -107,4 +107,14 @@ public class ExploreSpring5URLPatternUsingRouterFunctionsIntegrationTest {
.isEqualTo("hello"); .isEqualTo("hello");
} }
@Test
public void givenRouter_whenAccess_thenGot() throws Exception {
client.get()
.uri("/resources/test/test.txt")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("test");
}
} }

View File

@ -27,13 +27,21 @@ public class PathPatternsUsingHandlerMethodIntegrationTest {
@Test @Test
public void givenHandlerMethod_whenMultipleURIVariablePattern_then200() { public void givenHandlerMethod_whenMultipleURIVariablePattern_then200() {
client.get() client.get()
.uri("/spring5/ab/cd") .uri("/spring5/baeldung/tutorial")
.exchange() .exchange()
.expectStatus() .expectStatus()
.is2xxSuccessful() .is2xxSuccessful()
.expectBody() .expectBody()
.equals("/ab/cd"); .equals("/baeldung/tutorial");
client.get()
.uri("/spring5/baeldung")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("/baeldung");
} }
@Test @Test

View File

@ -0,0 +1,128 @@
<?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>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent>
<artifactId>spring-boot-3-sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-3-sample</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</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-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<!-- Treiber: nicht gegen diese API programmieren, erst zur Laufzeit benötigt -->
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- depends on javax.servlet, see https://github.com/springdoc/springdoc-openapi/issues/1284
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.9</version>
</dependency>
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<!-- This is needed when using Lombok 1.18.16 and above -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<properties>
<mapstruct.version>1.5.2.Final</mapstruct.version>
<start-class>com.baeldung.sample.TodoApplication</start-class>
</properties>
</project>

View File

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

Some files were not shown because too many files have changed in this diff Show More