Merge branch 'master' of https://github.com/eugenp/tutorials into task/JAVA-13629
This commit is contained in:
commit
17d2d5a999
|
@ -13,4 +13,3 @@ This module contains articles about Java 11 core features
|
||||||
- [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)
|
|
||||||
|
|
|
@ -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)
|
|
@ -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>
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
@ -10,6 +10,13 @@
|
||||||
<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>
|
||||||
<module>users-service</module>
|
<module>users-service</module>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
## Parent Boot 2
|
||||||
|
|
||||||
|
This is a parent module for all projects using Spring Boot 3.
|
|
@ -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>
|
|
@ -10,10 +10,10 @@
|
||||||
<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>
|
|
@ -6,4 +6,4 @@
|
||||||
- [Hibernate’s “Object References an Unsaved Transient Instance” Error](https://www.baeldung.com/hibernate-unsaved-transient-instance-error)
|
- [Hibernate’s “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)
|
||||||
- [Hibernate’s “Not-Null Property References a Null or Transient Value” Error](https://www.baeldung.com/hibernate-not-null-error)
|
- [Hibernate’s “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)
|
- [Hibernate’s “Detached Entity Passed to Persist” Error](https://www.baeldung.com/hibernate-detached-entity-passed-to-persist)
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
1
pom.xml
1
pom.xml
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
|
@ -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
|
|
@ -1,17 +1,26 @@
|
||||||
<?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>
|
|
||||||
<version>0.1-SNAPSHOT</version>
|
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>quarkus-vs-springboot</artifactId>
|
<artifactId>quarkus-vs-springboot</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
<artifactId>quarkus-project</artifactId>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
<properties>
|
||||||
|
<compiler-plugin.version>3.10.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.9.2.Final</quarkus.platform.version>
|
||||||
|
<surefire-plugin.version>3.0.0-M6</surefire-plugin.version>
|
||||||
|
</properties>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -23,7 +32,6 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
|
@ -39,7 +47,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-reactive-pg-client</artifactId>
|
<artifactId>quarkus-reactive-mysql-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
|
@ -60,7 +68,6 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
@ -133,18 +140,4 @@
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
</profiles>
|
</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>
|
</project>
|
|
@ -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/
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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/.
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<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>
|
||||||
|
|
||||||
|
@ -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,19 +43,121 @@
|
||||||
<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>
|
||||||
|
<builder>paketobuildpacks/builder:tiny</builder>
|
||||||
|
<env>
|
||||||
|
<BP_NATIVE_IMAGE>false</BP_NATIVE_IMAGE>
|
||||||
|
<BPL_JFR_ENABLED>true</BPL_JFR_ENABLED>
|
||||||
|
</env>
|
||||||
|
</image>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>${surefire-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<includes>
|
||||||
|
<include>**/*IT</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spring-release</id>
|
||||||
|
<name>Spring release</name>
|
||||||
|
<url>https://repo.spring.io/release</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>spring-release</id>
|
||||||
|
<name>Spring release</name>
|
||||||
|
<url>https://repo.spring.io/release</url>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>native</id>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.platform</groupId>
|
||||||
|
<artifactId>junit-platform-launcher</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<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>true</BP_NATIVE_IMAGE>
|
||||||
|
<BPL_JFR_ENABLED>true</BPL_JFR_ENABLED>
|
||||||
</env>
|
</env>
|
||||||
</image>
|
</image>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -68,7 +165,6 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.springframework.experimental</groupId>
|
<groupId>org.springframework.experimental</groupId>
|
||||||
<artifactId>spring-aot-maven-plugin</artifactId>
|
<artifactId>spring-aot-maven-plugin</artifactId>
|
||||||
<version>${spring-native.version}</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>test-generate</id>
|
<id>test-generate</id>
|
||||||
|
@ -86,93 +182,71 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
</profile>
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>spring-releases</id>
|
|
||||||
<name>Spring Releases</name>
|
|
||||||
<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>
|
|
||||||
</repositories>
|
|
||||||
<pluginRepositories>
|
|
||||||
<pluginRepository>
|
|
||||||
<id>spring-releases</id>
|
|
||||||
<name>Spring Releases</name>
|
|
||||||
<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>
|
|
||||||
</pluginRepositories>
|
|
||||||
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>native</id>
|
<id>local-native</id>
|
||||||
<properties>
|
<properties>
|
||||||
<repackage.classifier>exec</repackage.classifier>
|
<repackage.classifier>exec</repackage.classifier>
|
||||||
<native-buildtools.version>0.9.3</native-buildtools.version>
|
<native-buildtools.version>0.9.11</native-buildtools.version>
|
||||||
</properties>
|
</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.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>
|
|
@ -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" ]
|
|
|
@ -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'
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.password=root
|
||||||
spring.r2dbc.pool.enabled=true
|
spring.r2dbc.pool.enabled=true
|
||||||
spring.r2dbc.pool.maxSize=20
|
spring.r2dbc.pool.maxSize=95
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
|
@ -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 {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
test
|
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,20 @@ public class PathPatternsUsingHandlerMethodIntegrationTest {
|
||||||
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
|
||||||
|
|
|
@ -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>
|
|
@ -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
Loading…
Reference in New Issue