diff --git a/cloud-foundry-uaa/cf-uaa-config/uaa.yml b/cloud-foundry-uaa/cf-uaa-config/uaa.yml
index b782c2b681..ebaa99fb6c 100644
--- a/cloud-foundry-uaa/cf-uaa-config/uaa.yml
+++ b/cloud-foundry-uaa/cf-uaa-config/uaa.yml
@@ -1,12 +1,7 @@
issuer:
uri: http://localhost:8080/uaa
-spring_profiles: postgresql,default
-
-database.driverClassName: org.postgresql.Driver
-database.url: jdbc:postgresql:uaadb2
-database.username: postgres
-database.password: postgres
+spring_profiles: default,hsqldb
encryption:
active_key_label: CHANGE-THIS-KEY
diff --git a/cloud-foundry-uaa/cf-uaa-oauth2-client/src/main/resources/application.properties b/cloud-foundry-uaa/cf-uaa-oauth2-client/src/main/resources/application.properties
index de8e1a7b9f..8e8797ce54 100644
--- a/cloud-foundry-uaa/cf-uaa-oauth2-client/src/main/resources/application.properties
+++ b/cloud-foundry-uaa/cf-uaa-oauth2-client/src/main/resources/application.properties
@@ -1,23 +1,11 @@
-# SECURITY OAUTH2 CLIENT (OAuth2ClientProperties)
-#spring.security.oauth2.client.provider.*= # OAuth provider details.
-#spring.security.oauth2.client.registration.*= # OAuth client registrations.
-
server.port=8081
-#server.servlet.context-path=/uaa-client-webapp
-uaa.url=http://localhost:8080/uaa
resource.server.url=http://localhost:8082
-spring.security.oauth2.client.registration.uaa.client-name=UAA OAuth2 Client
-spring.security.oauth2.client.registration.uaa.client-id=client1
-spring.security.oauth2.client.registration.uaa.client-secret=client1
-spring.security.oauth2.client.registration.uaa.authorization-grant-type=authorization_code
+spring.security.oauth2.client.registration.uaa.client-name=Web App Client
+spring.security.oauth2.client.registration.uaa.client-id=webappclient
+spring.security.oauth2.client.registration.uaa.client-secret=webappclientsecret
spring.security.oauth2.client.registration.uaa.scope=resource.read,resource.write,openid,profile
-spring.security.oauth2.client.registration.uaa.redirect-uri=http://localhost:8081/login/oauth2/code/uaa
-#spring.security.oauth2.client.registration.uaa.redirect-uri=http://localhost:8081/**
-spring.security.oauth2.client.provider.uaa.token-uri=${uaa.url}/oauth/token
-spring.security.oauth2.client.provider.uaa.authorization-uri=${uaa.url}/oauth/authorize
-spring.security.oauth2.client.provider.uaa.jwk-set-uri=${uaa.url}/token_keys
-spring.security.oauth2.client.provider.uaa.user-info-uri=${uaa.url}/userinfo
-spring.security.oauth2.client.provider.uaa.user-name-attribute=user_name
+spring.security.oauth2.client.provider.uaa.issuer-uri=http://localhost:8080/uaa/oauth/token
+
diff --git a/cloud-foundry-uaa/cf-uaa-oauth2-resource-server/src/main/resources/application.properties b/cloud-foundry-uaa/cf-uaa-oauth2-resource-server/src/main/resources/application.properties
index ba9b95e0d4..a6e846a00f 100644
--- a/cloud-foundry-uaa/cf-uaa-oauth2-resource-server/src/main/resources/application.properties
+++ b/cloud-foundry-uaa/cf-uaa-oauth2-resource-server/src/main/resources/application.properties
@@ -1,16 +1,3 @@
server.port=8082
-uaa.url=http://localhost:8080/uaa
-
-#approch1
-spring.security.oauth2.resourceserver.jwt.issuer-uri=${uaa.url}/oauth/token
-
-#approch2
-#spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${uaa.url}/token_key
-
-# SECURITY OAUTH2 CLIENT (OAuth2ClientProperties)
-#security.oauth2.client.client-id=client1
-#security.oauth2.client.client-secret=client1
-
-#security.oauth2.resource.jwt.key-uri=${uaa.url}/token_key
-#security.oauth2.resource.token-info-uri=${uaa.url}/oauth/check_token
+spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/uaa/oauth/token
diff --git a/core-groovy/README.md b/core-groovy/README.md
index bac91bf028..606a317747 100644
--- a/core-groovy/README.md
+++ b/core-groovy/README.md
@@ -9,3 +9,5 @@
- [A Quick Guide to Iterating a Map in Groovy](https://www.baeldung.com/groovy-map-iterating)
- [An Introduction to Traits in Groovy](https://www.baeldung.com/groovy-traits)
- [Lists in Groovy](https://www.baeldung.com/groovy-lists)
+- [Converting a String to a Date in Groovy](https://www.baeldung.com/groovy-string-to-date)
+- [Guide to I/O in Groovy](https://www.baeldung.com/groovy-io)
diff --git a/core-groovy/src/main/groovy/com/baeldung/Person.groovy b/core-groovy/src/main/groovy/com/baeldung/Person.groovy
new file mode 100644
index 0000000000..6a009aeee0
--- /dev/null
+++ b/core-groovy/src/main/groovy/com/baeldung/Person.groovy
@@ -0,0 +1,37 @@
+package com.baeldung
+
+class Person {
+ private String firstname
+ private String lastname
+ private Integer age
+
+ Person(String firstname, String lastname, Integer age) {
+ this.firstname = firstname
+ this.lastname = lastname
+ this.age = age
+ }
+
+ String getFirstname() {
+ return firstname
+ }
+
+ void setFirstname(String firstname) {
+ this.firstname = firstname
+ }
+
+ String getLastname() {
+ return lastname
+ }
+
+ void setLastname(String lastname) {
+ this.lastname = lastname
+ }
+
+ Integer getAge() {
+ return age
+ }
+
+ void setAge(Integer age) {
+ this.age = age
+ }
+}
diff --git a/core-groovy/src/main/groovy/com/baeldung/closures/Closures.groovy b/core-groovy/src/main/groovy/com/baeldung/closures/Closures.groovy
new file mode 100644
index 0000000000..607329ce88
--- /dev/null
+++ b/core-groovy/src/main/groovy/com/baeldung/closures/Closures.groovy
@@ -0,0 +1,87 @@
+package com.baeldung.closures
+
+class Closures {
+
+ def printWelcome = {
+ println "Welcome to Closures!"
+ }
+
+ def print = { name ->
+ println name
+ }
+
+ def formatToLowerCase(name) {
+ return name.toLowerCase()
+ }
+ def formatToLowerCaseClosure = { name ->
+ return name.toLowerCase()
+ }
+
+ def count=0
+
+ def increaseCount = {
+ count++
+ }
+
+ def greet = {
+ return "Hello! ${it}"
+ }
+
+ def multiply = { x, y ->
+ return x*y
+ }
+
+ def calculate = {int x, int y, String operation ->
+
+ //log closure
+ def log = {
+ println "Performing $it"
+ }
+
+ def result = 0
+ switch(operation) {
+ case "ADD":
+ log("Addition")
+ result = x+y
+ break
+ case "SUB":
+ log("Subtraction")
+ result = x-y
+ break
+ case "MUL":
+ log("Multiplication")
+ result = x*y
+ break
+ case "DIV":
+ log("Division")
+ result = x/y
+ break
+ }
+ return result
+ }
+
+ def addAll = { int... args ->
+ return args.sum()
+ }
+
+ def volume(Closure areaCalculator, int... dimensions) {
+ if(dimensions.size() == 3) {
+
+ //consider dimension[0] = length, dimension[1] = breadth, dimension[2] = height
+ //for cube and cuboid
+ return areaCalculator(dimensions[0], dimensions[1]) * dimensions[2]
+ } else if(dimensions.size() == 2) {
+
+ //consider dimension[0] = radius, dimension[1] = height
+ //for cylinder and cone
+ return areaCalculator(dimensions[0]) * dimensions[1]
+ } else if(dimensions.size() == 1) {
+
+ //consider dimension[0] = radius
+ //for sphere
+ return areaCalculator(dimensions[0]) * dimensions[0]
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/core-groovy/src/main/groovy/com/baeldung/closures/Employee.groovy b/core-groovy/src/main/groovy/com/baeldung/closures/Employee.groovy
new file mode 100644
index 0000000000..78eb5aadeb
--- /dev/null
+++ b/core-groovy/src/main/groovy/com/baeldung/closures/Employee.groovy
@@ -0,0 +1,6 @@
+package com.baeldung.closures
+
+class Employee {
+
+ String fullName
+}
\ No newline at end of file
diff --git a/core-groovy/src/test/groovy/com/baeldung/closures/ClosuresUnitTest.groovy b/core-groovy/src/test/groovy/com/baeldung/closures/ClosuresUnitTest.groovy
new file mode 100644
index 0000000000..32c67e99bc
--- /dev/null
+++ b/core-groovy/src/test/groovy/com/baeldung/closures/ClosuresUnitTest.groovy
@@ -0,0 +1,80 @@
+package com.baeldung.closures
+
+import spock.lang.Specification
+
+class ClosuresUnitTest extends GroovyTestCase {
+
+ Closures closures = new Closures()
+
+ void testDeclaration() {
+
+ closures.print("Hello! Closure")
+ closures.formatToLowerCaseClosure("Hello! Closure")
+
+ closures.print.call("Hello! Closure")
+ closures.formatToLowerCaseClosure.call("Hello! Closure")
+
+ }
+
+ void testClosureVsMethods() {
+ assert closures.formatToLowerCase("TONY STARK") == closures.formatToLowerCaseClosure("Tony STark")
+ }
+
+ void testParameters() {
+ //implicit parameter
+ assert closures.greet("Alex") == "Hello! Alex"
+
+ //multiple parameters
+ assert closures.multiply(2, 4) == 8
+
+ assert closures.calculate(12, 4, "ADD") == 16
+ assert closures.calculate(12, 4, "SUB") == 8
+ assert closures.calculate(43, 8, "DIV") == 5.375
+
+ //varags
+ assert closures.addAll(12, 10, 14) == 36
+
+ }
+
+ void testClosureAsAnArgument() {
+ assert closures.volume({ l, b -> return l*b }, 12, 6, 10) == 720
+
+ assert closures.volume({ radius -> return Math.PI*radius*radius/3 }, 5, 10) == Math.PI * 250/3
+ }
+
+ void testGStringsLazyEvaluation() {
+ def name = "Samwell"
+ def welcomeMsg = "Welcome! $name"
+
+ assert welcomeMsg == "Welcome! Samwell"
+
+ // changing the name does not affect original interpolated value
+ name = "Tarly"
+ assert welcomeMsg != "Welcome! Tarly"
+
+ def fullName = "Tarly Samson"
+ def greetStr = "Hello! ${-> fullName}"
+
+ assert greetStr == "Hello! Tarly Samson"
+
+ // this time changing the variable affects the interpolated String's value
+ fullName = "Jon Smith"
+ assert greetStr == "Hello! Jon Smith"
+ }
+
+ void testClosureInLists() {
+ def list = [10, 11, 12, 13, 14, true, false, "BUNTHER"]
+ list.each {
+ println it
+ }
+
+ assert [13, 14] == list.findAll{ it instanceof Integer && it >= 13}
+ }
+
+ void testClosureInMaps() {
+ def map = [1:10, 2:30, 4:5]
+
+ assert [10, 60, 20] == map.collect{it.key * it.value}
+ }
+
+}
\ No newline at end of file
diff --git a/core-groovy/src/test/groovy/com/baeldung/lists/ListTest.groovy b/core-groovy/src/test/groovy/com/baeldung/lists/ListTest.groovy
index f682503ed4..7771028132 100644
--- a/core-groovy/src/test/groovy/com/baeldung/lists/ListTest.groovy
+++ b/core-groovy/src/test/groovy/com/baeldung/lists/ListTest.groovy
@@ -129,13 +129,13 @@ class ListTest{
assertTrue(filterList.findAll{it > 3} == [4, 5, 6, 76])
assertTrue(filterList.findAll{ it instanceof Number} == [2, 1, 3, 4, 5, 6, 76])
-
+
assertTrue(filterList.grep( Number )== [2, 1, 3, 4, 5, 6, 76])
-
+
assertTrue(filterList.grep{ it> 6 }== [76])
def conditionList = [2, 1, 3, 4, 5, 6, 76]
-
+
assertFalse(conditionList.every{ it < 6})
assertTrue(conditionList.any{ it%2 == 0})
@@ -165,7 +165,7 @@ class ListTest{
def strList = ["na", "ppp", "as"]
assertTrue(strList.max() == "ppp")
-
+
Comparator minc = {a,b -> a == b? 0: a < b? -1 : 1}
def numberList = [3, 2, 0, 7]
assertTrue(numberList.min(minc) == 0)
diff --git a/core-groovy/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy b/core-groovy/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy
new file mode 100644
index 0000000000..9617c099ce
--- /dev/null
+++ b/core-groovy/src/test/groovy/com/baeldung/lists/ListUnitTest.groovy
@@ -0,0 +1,58 @@
+package com.baeldung.lists
+
+import com.baeldung.Person
+import org.junit.Test
+
+import static org.junit.Assert.*
+
+class ListUnitTest {
+
+ private final personList = [
+ new Person("Regina", "Fitzpatrick", 25),
+ new Person("Abagail", "Ballard", 26),
+ new Person("Lucian", "Walter", 30),
+ ]
+
+ @Test
+ void whenListContainsElement_thenCheckReturnsTrue() {
+ def list = ['a', 'b', 'c']
+
+ assertTrue(list.indexOf('a') > -1)
+ assertTrue(list.contains('a'))
+ }
+
+ @Test
+ void whenListContainsElement_thenCheckWithMembershipOperatorReturnsTrue() {
+ def list = ['a', 'b', 'c']
+
+ assertTrue('a' in list)
+ }
+
+ @Test
+ void givenListOfPerson_whenUsingStreamMatching_thenShouldEvaluateList() {
+ assertTrue(personList.stream().anyMatch {it.age > 20})
+ assertFalse(personList.stream().allMatch {it.age < 30})
+ }
+
+ @Test
+ void givenListOfPerson_whenUsingCollectionMatching_thenShouldEvaluateList() {
+ assertTrue(personList.any {it.age > 20})
+ assertFalse(personList.every {it.age < 30})
+ }
+
+ @Test
+ void givenListOfPerson_whenUsingStreamFind_thenShouldReturnMatchingElements() {
+ assertTrue(personList.stream().filter {it.age > 20}.findAny().isPresent())
+ assertFalse(personList.stream().filter {it.age > 30}.findAny().isPresent())
+ assertTrue(personList.stream().filter {it.age > 20}.findAll().size() == 3)
+ assertTrue(personList.stream().filter {it.age > 30}.findAll().isEmpty())
+ }
+
+ @Test
+ void givenListOfPerson_whenUsingCollectionFind_thenShouldReturnMatchingElements() {
+ assertNotNull(personList.find {it.age > 20})
+ assertNull(personList.find {it.age > 30})
+ assertTrue(personList.findAll {it.age > 20}.size() == 3)
+ assertTrue(personList.findAll {it.age > 30}.isEmpty())
+ }
+}
diff --git a/core-groovy/src/test/groovy/com/baeldung/map/MapUnitTest.groovy b/core-groovy/src/test/groovy/com/baeldung/map/MapUnitTest.groovy
index 97ffc50c76..0d6bbed04b 100644
--- a/core-groovy/src/test/groovy/com/baeldung/map/MapUnitTest.groovy
+++ b/core-groovy/src/test/groovy/com/baeldung/map/MapUnitTest.groovy
@@ -1,10 +1,18 @@
package com.baeldung.map
-import static org.junit.Assert.*
+import com.baeldung.Person
import org.junit.Test
+import static org.junit.Assert.*
+
class MapUnitTest {
+ private final personMap = [
+ Regina : new Person("Regina", "Fitzpatrick", 25),
+ Abagail: new Person("Abagail", "Ballard", 26),
+ Lucian : new Person("Lucian", "Walter", 30)
+ ]
+
@Test
void whenUsingEach_thenMapIsIterated() {
def map = [
@@ -63,7 +71,7 @@ class MapUnitTest {
'FF6347' : 'Tomato',
'FF4500' : 'Orange Red'
]
-
+
map.eachWithIndex { key, val, index ->
def indent = ((index == 0 || index % 2 == 0) ? " " : "")
println "$indent Hex Code: $key = Color Name: $val"
@@ -82,4 +90,65 @@ class MapUnitTest {
println "Hex Code: $entry.key = Color Name: $entry.value"
}
}
+
+ @Test
+ void whenMapContainsKeyElement_thenCheckReturnsTrue() {
+ def map = [a: 'd', b: 'e', c: 'f']
+
+ assertTrue(map.containsKey('a'))
+ assertFalse(map.containsKey('e'))
+ assertTrue(map.containsValue('e'))
+ }
+
+ @Test
+ void whenMapContainsKeyElement_thenCheckByMembershipReturnsTrue() {
+ def map = [a: 'd', b: 'e', c: 'f']
+
+ assertTrue('a' in map)
+ assertFalse('f' in map)
+ }
+
+ @Test
+ void whenMapContainsFalseBooleanValues_thenCheckReturnsFalse() {
+ def map = [a: true, b: false, c: null]
+
+ assertTrue(map.containsKey('b'))
+ assertTrue('a' in map)
+ assertFalse('b' in map)
+ assertFalse('c' in map)
+ }
+
+ @Test
+ void givenMapOfPerson_whenUsingStreamMatching_thenShouldEvaluateMap() {
+ assertTrue(personMap.keySet().stream().anyMatch {it == "Regina"})
+ assertFalse(personMap.keySet().stream().allMatch {it == "Albert"})
+ assertFalse(personMap.values().stream().allMatch {it.age < 30})
+ assertTrue(personMap.entrySet().stream().anyMatch {it.key == "Abagail" && it.value.lastname == "Ballard"})
+ }
+
+ @Test
+ void givenMapOfPerson_whenUsingCollectionMatching_thenShouldEvaluateMap() {
+ assertTrue(personMap.keySet().any {it == "Regina"})
+ assertFalse(personMap.keySet().every {it == "Albert"})
+ assertFalse(personMap.values().every {it.age < 30})
+ assertTrue(personMap.any {firstname, person -> firstname == "Abagail" && person.lastname == "Ballard"})
+ }
+
+ @Test
+ void givenMapOfPerson_whenUsingCollectionFind_thenShouldReturnElements() {
+ assertNotNull(personMap.find {it.key == "Abagail" && it.value.lastname == "Ballard"})
+ assertTrue(personMap.findAll {it.value.age > 20}.size() == 3)
+ }
+
+ @Test
+ void givenMapOfPerson_whenUsingStreamFind_thenShouldReturnElements() {
+ assertTrue(
+ personMap.entrySet().stream()
+ .filter {it.key == "Abagail" && it.value.lastname == "Ballard"}
+ .findAny().isPresent())
+ assertTrue(
+ personMap.entrySet().stream()
+ .filter {it.value.age > 20}
+ .findAll().size() == 3)
+ }
}
diff --git a/core-groovy/src/test/groovy/com/baeldung/set/SetUnitTest.groovy b/core-groovy/src/test/groovy/com/baeldung/set/SetUnitTest.groovy
new file mode 100644
index 0000000000..1248c9ac91
--- /dev/null
+++ b/core-groovy/src/test/groovy/com/baeldung/set/SetUnitTest.groovy
@@ -0,0 +1,16 @@
+package com.baeldung.set
+
+import org.junit.Test
+
+import static org.junit.Assert.assertTrue
+
+class SetUnitTest {
+
+ @Test
+ void whenSetContainsElement_thenCheckReturnsTrue() {
+ def set = ['a', 'b', 'c'] as Set
+
+ assertTrue(set.contains('a'))
+ assertTrue('a' in set)
+ }
+}
\ No newline at end of file
diff --git a/core-java-11/README.md b/core-java-11/README.md
index 5e2c07178b..b09649f4f1 100644
--- a/core-java-11/README.md
+++ b/core-java-11/README.md
@@ -5,3 +5,4 @@
- [Java 11 String API Additions](https://www.baeldung.com/java-11-string-api)
- [Java 11 Nest Based Access Control](https://www.baeldung.com/java-nest-based-access-control)
- [Exploring the New HTTP Client in Java 9 and 11](https://www.baeldung.com/java-9-http-client)
+- [An Introduction to Epsilon GC: A No-Op Experimental Garbage Collector](https://www.baeldung.com/jvm-epsilon-gc-garbage-collector)
diff --git a/core-java-11/src/modules/jlinkModule/com/baeldung/jlink/HelloWorld.java b/core-java-11/src/modules/jlinkModule/com/baeldung/jlink/HelloWorld.java
new file mode 100644
index 0000000000..47fe62ba40
--- /dev/null
+++ b/core-java-11/src/modules/jlinkModule/com/baeldung/jlink/HelloWorld.java
@@ -0,0 +1,12 @@
+package com.baeldung.jlink;
+
+import java.util.logging.Logger;
+
+public class HelloWorld {
+
+ private static final Logger LOG = Logger.getLogger(HelloWorld.class.getName());
+
+ public static void main(String[] args) {
+ LOG.info("Hello World!");
+ }
+}
diff --git a/core-java-11/src/modules/jlinkModule/module-info.java b/core-java-11/src/modules/jlinkModule/module-info.java
new file mode 100644
index 0000000000..0587c65b53
--- /dev/null
+++ b/core-java-11/src/modules/jlinkModule/module-info.java
@@ -0,0 +1,3 @@
+module jlinkModule {
+ requires java.logging;
+}
\ No newline at end of file
diff --git a/core-java-12/pom.xml b/core-java-12/pom.xml
new file mode 100644
index 0000000000..defef5e9d3
--- /dev/null
+++ b/core-java-12/pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+ com.baeldung
+ core-java-12
+ 0.1.0-SNAPSHOT
+ core-java-12
+ jar
+ http://maven.apache.org
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source.version}
+ ${maven.compiler.target.version}
+
+
+
+
+
+
+ 12
+ 12
+ 3.6.1
+
+
+
\ No newline at end of file
diff --git a/core-java-12/src/test/java/com/baeldung/collectors/CollectorsUnitTest.java b/core-java-12/src/test/java/com/baeldung/collectors/CollectorsUnitTest.java
new file mode 100644
index 0000000000..7c4cb9e8f0
--- /dev/null
+++ b/core-java-12/src/test/java/com/baeldung/collectors/CollectorsUnitTest.java
@@ -0,0 +1,71 @@
+package com.baeldung.collectors;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+import org.junit.Test;
+
+import static java.util.stream.Collectors.maxBy;
+import static java.util.stream.Collectors.minBy;
+import static java.util.stream.Collectors.teeing;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit tests for collectors additions in Java 12.
+ */
+public class CollectorsUnitTest {
+
+ @Test
+ public void whenTeeing_ItShouldCombineTheResultsAsExpected() {
+ List numbers = Arrays.asList(42, 4, 2, 24);
+ Range range = numbers.stream()
+ .collect(teeing(minBy(Integer::compareTo), maxBy(Integer::compareTo), (min, max) -> new Range(min.orElse(null), max.orElse(null))));
+
+ assertThat(range).isEqualTo(new Range(2, 42));
+ }
+
+ /**
+ * Represents a closed range of numbers between {@link #min} and
+ * {@link #max}, both inclusive.
+ */
+ private static class Range {
+
+ private final Integer min;
+
+ private final Integer max;
+
+ Range(Integer min, Integer max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ Integer getMin() {
+ return min;
+ }
+
+ Integer getMax() {
+ return max;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Range range = (Range) o;
+ return Objects.equals(getMin(), range.getMin()) && Objects.equals(getMax(), range.getMax());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getMin(), getMax());
+ }
+
+ @Override
+ public String toString() {
+ return "Range{" + "min=" + min + ", max=" + max + '}';
+ }
+ }
+}
diff --git a/core-java-8/src/test/java/com/baeldung/java8/Java8SortUnitTest.java b/core-java-8/src/test/java/com/baeldung/java8/Java8SortUnitTest.java
index 71ec5b147f..57d9d8347b 100644
--- a/core-java-8/src/test/java/com/baeldung/java8/Java8SortUnitTest.java
+++ b/core-java-8/src/test/java/com/baeldung/java8/Java8SortUnitTest.java
@@ -124,11 +124,44 @@ public class Java8SortUnitTest {
@Test
public final void givenStreamCustomOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
-
final List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
final Comparator nameComparator = (h1, h2) -> h1.getName().compareTo(h2.getName());
final List sortedHumans = humans.stream().sorted(nameComparator).collect(Collectors.toList());
Assert.assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}
+
+ @Test
+ public final void givenStreamComparatorOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
+ final List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
+
+ final List sortedHumans = humans.stream().sorted(Comparator.comparing(Human::getName)).collect(Collectors.toList());
+ Assert.assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
+ }
+
+ @Test
+ public final void givenStreamNaturalOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
+ final List letters = Lists.newArrayList("B", "A", "C");
+
+ final List reverseSortedLetters = letters.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
+ Assert.assertThat(reverseSortedLetters.get(0), equalTo("C"));
+ }
+
+ @Test
+ public final void givenStreamCustomOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
+ final List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
+ final Comparator reverseNameComparator = (h1, h2) -> h2.getName().compareTo(h1.getName());
+
+ final List reverseSortedHumans = humans.stream().sorted(reverseNameComparator).collect(Collectors.toList());
+ Assert.assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
+ }
+
+ @Test
+ public final void givenStreamComparatorOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
+ final List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
+
+ final List reverseSortedHumans = humans.stream().sorted(Comparator.comparing(Human::getName, Comparator.reverseOrder())).collect(Collectors.toList());
+ Assert.assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
+ }
+
}
diff --git a/core-java-9/README.md b/core-java-9/README.md
index fb7f69dc42..a423f0cb14 100644
--- a/core-java-9/README.md
+++ b/core-java-9/README.md
@@ -27,3 +27,6 @@
- [Immutable Set in Java](https://www.baeldung.com/java-immutable-set)
- [Multi-Release Jar Files](https://www.baeldung.com/java-multi-release-jar)
- [Ahead of Time Compilation (AoT)](https://www.baeldung.com/ahead-of-time-compilation)
+- [Java 9 Process API Improvements](https://www.baeldung.com/java-9-process-api)
+- [Guide to java.lang.Process API](https://www.baeldung.com/java-process-api)
+
diff --git a/core-java-collections/README.md b/core-java-collections/README.md
index 80d4385c45..1e504ded65 100644
--- a/core-java-collections/README.md
+++ b/core-java-collections/README.md
@@ -33,3 +33,4 @@
- [Differences Between HashMap and Hashtable](https://www.baeldung.com/hashmap-hashtable-differences)
- [Java ArrayList vs Vector](https://www.baeldung.com/java-arraylist-vs-vector)
- [Defining a Char Stack in Java](https://www.baeldung.com/java-char-stack)
+- [Time Comparison of Arrays.sort(Object[]) and Arrays.sort(int[])](https://www.baeldung.com/arrays-sortobject-vs-sortint)
diff --git a/core-java-concurrency-basic/README.md b/core-java-concurrency-basic/README.md
index ad3de4a758..7d106095e7 100644
--- a/core-java-concurrency-basic/README.md
+++ b/core-java-concurrency-basic/README.md
@@ -16,3 +16,4 @@
- [Life Cycle of a Thread in Java](http://www.baeldung.com/java-thread-lifecycle)
- [Runnable vs. Callable in Java](http://www.baeldung.com/java-runnable-callable)
- [What is Thread-Safety and How to Achieve it](https://www.baeldung.com/java-thread-safety)
+- [How to Start a Thread in Java](https://www.baeldung.com/java-start-thread)
diff --git a/core-java-jvm/README.md b/core-java-jvm/README.md
new file mode 100644
index 0000000000..529453f3c4
--- /dev/null
+++ b/core-java-jvm/README.md
@@ -0,0 +1,6 @@
+=========
+
+## Core Java JVM Cookbooks and Examples
+
+### Relevant Articles:
+- [Method Inlining in the JVM](http://www.baeldung.com/method-inlining-in-the-jvm/)
diff --git a/core-java-jvm/pom.xml b/core-java-jvm/pom.xml
new file mode 100644
index 0000000000..752b26f03f
--- /dev/null
+++ b/core-java-jvm/pom.xml
@@ -0,0 +1,40 @@
+
+ 4.0.0
+ com.baeldung
+ core-java-jvm
+ 0.1.0-SNAPSHOT
+ jar
+ core-java-jvm
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+
+ 3.5
+ 3.6.1
+
+
diff --git a/core-java/src/main/java/com/baeldung/inlining/ConsecutiveNumbersSum.java b/core-java-jvm/src/main/java/com/baeldung/inlining/ConsecutiveNumbersSum.java
similarity index 100%
rename from core-java/src/main/java/com/baeldung/inlining/ConsecutiveNumbersSum.java
rename to core-java-jvm/src/main/java/com/baeldung/inlining/ConsecutiveNumbersSum.java
diff --git a/core-java/src/main/java/com/baeldung/inlining/InliningExample.java b/core-java-jvm/src/main/java/com/baeldung/inlining/InliningExample.java
similarity index 100%
rename from core-java/src/main/java/com/baeldung/inlining/InliningExample.java
rename to core-java-jvm/src/main/java/com/baeldung/inlining/InliningExample.java
diff --git a/core-java/src/test/java/com/baeldung/inlining/ConsecutiveNumbersSumUnitTest.java b/core-java-jvm/src/test/java/com/baeldung/inlining/ConsecutiveNumbersSumUnitTest.java
similarity index 100%
rename from core-java/src/test/java/com/baeldung/inlining/ConsecutiveNumbersSumUnitTest.java
rename to core-java-jvm/src/test/java/com/baeldung/inlining/ConsecutiveNumbersSumUnitTest.java
diff --git a/core-java-networking/src/main/java/com/baeldung/socket/read/Client.java b/core-java-networking/src/main/java/com/baeldung/socket/read/Client.java
new file mode 100644
index 0000000000..5e2a84a767
--- /dev/null
+++ b/core-java-networking/src/main/java/com/baeldung/socket/read/Client.java
@@ -0,0 +1,29 @@
+package com.baeldung.socket.read;
+
+import java.net.*;
+import java.nio.charset.StandardCharsets;
+import java.io.*;
+
+public class Client {
+
+ public void runClient(String ip, int port) {
+ try {
+ Socket socket = new Socket(ip, port);
+ System.out.println("Connected to server ...");
+ DataInputStream in = new DataInputStream(System.in);
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+
+ char type = 's'; // s for string
+ int length = 29;
+ String data = "This is a string of length 29";
+ byte[] dataInBytes = data.getBytes(StandardCharsets.UTF_8);
+ //Sending data in TLV format
+ out.writeChar(type);
+ out.writeInt(length);
+ out.write(dataInBytes);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core-java-networking/src/main/java/com/baeldung/socket/read/Server.java b/core-java-networking/src/main/java/com/baeldung/socket/read/Server.java
new file mode 100644
index 0000000000..2ab91c6cdc
--- /dev/null
+++ b/core-java-networking/src/main/java/com/baeldung/socket/read/Server.java
@@ -0,0 +1,49 @@
+package com.baeldung.socket.read;
+
+import java.net.*;
+import java.nio.charset.StandardCharsets;
+import java.io.*;
+
+public class Server {
+
+ public void runServer(int port) {
+ //Start the server and wait for connection
+ try {
+ ServerSocket server = new ServerSocket(port);
+ System.out.println("Server Started. Waiting for connection ...");
+ Socket socket = server.accept();
+ System.out.println("Got connection from client.");
+ //Get input stream from socket variable and convert the same to DataInputStream
+ DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
+ //Read type and length of data
+ char dataType = in.readChar();
+ int length = in.readInt();
+ System.out.println("Type : "+dataType);
+ System.out.println("Lenght :"+length);
+ if(dataType == 's') {
+ //Read String data in bytes
+ byte[] messageByte = new byte[length];
+ boolean end = false;
+ StringBuilder dataString = new StringBuilder(length);
+ int totalBytesRead = 0;
+ //We need to run while loop, to read all data in that stream
+ while(!end) {
+ int currentBytesRead = in.read(messageByte);
+ totalBytesRead = currentBytesRead + totalBytesRead;
+ if(totalBytesRead <= length) {
+ dataString.append(new String(messageByte,0,currentBytesRead,StandardCharsets.UTF_8));
+ } else {
+ dataString.append(new String(messageByte,0,length - totalBytesRead + currentBytesRead,StandardCharsets.UTF_8));
+ }
+ if(dataString.length()>=length) {
+ end = true;
+ }
+ }
+ System.out.println("Read "+length+" bytes of message from client. Message = "+dataString);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core-java-networking/src/test/java/com/baeldung/socket/read/SocketReadAllDataLiveTest.java b/core-java-networking/src/test/java/com/baeldung/socket/read/SocketReadAllDataLiveTest.java
new file mode 100644
index 0000000000..da7f5b8d3f
--- /dev/null
+++ b/core-java-networking/src/test/java/com/baeldung/socket/read/SocketReadAllDataLiveTest.java
@@ -0,0 +1,37 @@
+package com.baeldung.socket.read;
+
+import java.util.concurrent.TimeUnit;
+import org.junit.jupiter.api.Test;
+
+public class SocketReadAllDataLiveTest {
+
+ @Test
+ public void givenServerAndClient_whenClientSendsAndServerReceivesData_thenCorrect() {
+ //Run server in new thread
+ Runnable runnable1 = () -> { runServer(); };
+ Thread thread1 = new Thread(runnable1);
+ thread1.start();
+ //Wait for 10 seconds
+ try {
+ TimeUnit.SECONDS.sleep(10);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ //Run client in a new thread
+ Runnable runnable2 = () -> { runClient(); };
+ Thread thread2 = new Thread(runnable2);
+ thread2.start();
+ }
+
+ public static void runServer() {
+ //Run Server
+ Server server = new Server();
+ server.runServer(5555);
+ }
+
+ public static void runClient() {
+ //Run Client
+ Client client = new Client();
+ client.runClient("127.0.0.1", 5555);
+ }
+}
\ No newline at end of file
diff --git a/core-java-os/README.md b/core-java-os/README.md
index 90e1d29a58..5f5d373d9b 100644
--- a/core-java-os/README.md
+++ b/core-java-os/README.md
@@ -6,3 +6,5 @@ This module uses Java 9, so make sure to have the JDK 9 installed to run it.
### Relevant Articles:
- [Java 9 Process API Improvements](http://www.baeldung.com/java-9-process-api)
- [Guide to java.lang.Process API](https://www.baeldung.com/java-process-api)
+- [Guide to java.lang.ProcessBuilder API](https://www.baeldung.com/java-lang-processbuilder-api)
+
diff --git a/core-java/README.md b/core-java/README.md
index e1325ab29f..cbc9251b0b 100644
--- a/core-java/README.md
+++ b/core-java/README.md
@@ -50,3 +50,4 @@
- [Finding Leap Years in Java](https://www.baeldung.com/java-leap-year)
- [Java Bitwise Operators](https://www.baeldung.com/java-bitwise-operators)
- [Guide to Creating and Running a Jar File in Java](https://www.baeldung.com/java-create-jar)
+- [Making a JSON POST Request With HttpURLConnection](https://www.baeldung.com/httpurlconnection-post)
diff --git a/core-kotlin-2/README.md b/core-kotlin-2/README.md
index 8d22c4f1a8..4ac1c2c7bb 100644
--- a/core-kotlin-2/README.md
+++ b/core-kotlin-2/README.md
@@ -2,3 +2,5 @@
- [Void Type in Kotlin](https://www.baeldung.com/kotlin-void-type)
- [How to use Kotlin Range Expressions](https://www.baeldung.com/kotlin-ranges)
+- [Split a List into Parts in Kotlin](https://www.baeldung.com/kotlin-split-list-into-parts)
+- [String Comparison in Kotlin](https://www.baeldung.com/kotlin-string-comparison)
diff --git a/core-kotlin-2/pom.xml b/core-kotlin-2/pom.xml
index 81df3cee81..e329611593 100644
--- a/core-kotlin-2/pom.xml
+++ b/core-kotlin-2/pom.xml
@@ -13,4 +13,66 @@
../parent-kotlin
-
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ ${kotlin.version}
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ org.jetbrains.kotlin
+ kotlin-test
+ ${kotlin.version}
+ test
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+
+ 1.8
+
+
+
+
+
+
+ 1.2.71
+ 1.1.1
+ 5.2.0
+ 3.10.0
+
+
+
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Annotations.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Annotations.kt
new file mode 100644
index 0000000000..a8f83446dc
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Annotations.kt
@@ -0,0 +1,7 @@
+package com.baeldung.annotations
+
+@Target(AnnotationTarget.FIELD)
+annotation class Positive
+
+@Target(AnnotationTarget.FIELD)
+annotation class AllowedNames(val names: Array)
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Item.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Item.kt
new file mode 100644
index 0000000000..6864fe416e
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Item.kt
@@ -0,0 +1,3 @@
+package com.baeldung.annotations
+
+class Item(@Positive val amount: Float, @AllowedNames(["Alice", "Bob"]) val name: String)
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Main.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Main.kt
new file mode 100644
index 0000000000..2b7f2c5590
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Main.kt
@@ -0,0 +1,7 @@
+package com.baeldung.annotations
+
+fun main(args: Array) {
+ val item = Item(amount = 1.0f, name = "Bob")
+ val validator = Validator()
+ println("Is instance valid? ${validator.isValid(item)}")
+}
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Validator.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Validator.kt
new file mode 100644
index 0000000000..40139048ab
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/annotations/Validator.kt
@@ -0,0 +1,38 @@
+package com.baeldung.annotations
+
+/**
+ * Naive annotation-based validator.
+ * @author A.Shcherbakov
+ */
+class Validator() {
+
+ /**
+ * Return true if every item's property annotated with @Positive is positive and if
+ * every item's property annotated with @AllowedNames has a value specified in that annotation.
+ */
+ fun isValid(item: Item): Boolean {
+ val fields = item::class.java.declaredFields
+ for (field in fields) {
+ field.isAccessible = true
+ for (annotation in field.annotations) {
+ val value = field.get(item)
+ if (field.isAnnotationPresent(Positive::class.java)) {
+ val amount = value as Float
+ if (amount < 0) {
+ return false
+ }
+ }
+ if (field.isAnnotationPresent(AllowedNames::class.java)) {
+ val allowedNames = field.getAnnotation(AllowedNames::class.java)?.names
+ val name = value as String
+ allowedNames?.let {
+ if (!it.contains(name)) {
+ return false
+ }
+ }
+ }
+ }
+ }
+ return true
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/CharRange.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/CharRange.kt
new file mode 100644
index 0000000000..3151674d61
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/CharRange.kt
@@ -0,0 +1,13 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ for (ch in 'a'..'f') {
+ print(ch)
+ }
+ println()
+
+ for (ch in 'f' downTo 'a') {
+ print(ch)
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/Color.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Color.kt
new file mode 100644
index 0000000000..ef7adf06b5
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Color.kt
@@ -0,0 +1,21 @@
+package com.baeldung.range
+
+enum class Color(val rgb: Int) {
+ BLUE(0x0000FF),
+ GREEN(0x008000),
+ RED(0xFF0000),
+ MAGENTA(0xFF00FF),
+ YELLOW(0xFFFF00);
+}
+
+fun main(args: Array) {
+
+ println(Color.values().toList());
+ val red = Color.RED
+ val yellow = Color.YELLOW
+ val range = red..yellow
+
+ println(range.contains(Color.MAGENTA))
+ println(range.contains(Color.BLUE))
+ println(range.contains(Color.GREEN))
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/Filter.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Filter.kt
new file mode 100644
index 0000000000..0e611b14cf
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Filter.kt
@@ -0,0 +1,18 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+ val r = 1..10
+
+ //Apply filter
+ val f = r.filter { it -> it % 2 == 0 }
+ println(f)
+
+ //Map
+ val m = r.map { it -> it * it }
+ println(m)
+
+ //Reduce
+ val rdc = r.reduce { a, b -> a + b }
+ println(rdc)
+
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/FirstLast.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/FirstLast.kt
new file mode 100644
index 0000000000..b82f5a8b9b
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/FirstLast.kt
@@ -0,0 +1,8 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ println((1..9).first)
+ println((1..9 step 2).step)
+ println((3..9).reversed().last)
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/OtherRangeFunctions.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/OtherRangeFunctions.kt
new file mode 100644
index 0000000000..19dcab89b2
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/OtherRangeFunctions.kt
@@ -0,0 +1,14 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ val r = 1..20
+ println(r.min())
+ println(r.max())
+ println(r.sum())
+ println(r.average())
+ println(r.count())
+
+ val repeated = listOf(1, 1, 2, 4, 4, 6, 10)
+ println(repeated.distinct())
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/Range.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Range.kt
new file mode 100644
index 0000000000..c313181599
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Range.kt
@@ -0,0 +1,28 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ for (i in 1..9) {
+ print(i)
+ }
+ println()
+
+ for (i in 9 downTo 1) {
+ print(i)
+ }
+ println()
+
+ for (i in 1.rangeTo(9)) {
+ print(i)
+ }
+ println()
+
+ for (i in 9.downTo(1)) {
+ print(i)
+ }
+ println()
+
+ for (i in 1 until 9) {
+ print(i)
+ }
+}
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/ReverseRange.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/ReverseRange.kt
new file mode 100644
index 0000000000..875cf62200
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/ReverseRange.kt
@@ -0,0 +1,14 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ (1..9).reversed().forEach {
+ print(it)
+ }
+
+ println()
+
+ (1..9).reversed().step(3).forEach {
+ print(it)
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/Step.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Step.kt
new file mode 100644
index 0000000000..b9c5d48588
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Step.kt
@@ -0,0 +1,15 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ for(i in 1..9 step 2){
+ print(i)
+ }
+
+ println()
+
+ for (i in 9 downTo 1 step 2){
+ print(i)
+ }
+
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/UntilRange.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/UntilRange.kt
new file mode 100644
index 0000000000..2c116a286f
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/UntilRange.kt
@@ -0,0 +1,8 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ for (i in 1 until 9) {
+ print(i)
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/scope/ScopeFunctions.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/scope/ScopeFunctions.kt
new file mode 100644
index 0000000000..37ad8c65e2
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/scope/ScopeFunctions.kt
@@ -0,0 +1,25 @@
+package com.baeldung.scope
+
+data class Student(var studentId: String = "", var name: String = "", var surname: String = "") {
+}
+
+data class Teacher(var teacherId: Int = 0, var name: String = "", var surname: String = "") {
+ fun setId(anId: Int): Teacher = apply { teacherId = anId }
+ fun setName(aName: String): Teacher = apply { name = aName }
+ fun setSurname(aSurname: String): Teacher = apply { surname = aSurname }
+}
+
+data class Headers(val headerInfo: String)
+
+data class Response(val headers: Headers)
+
+data class RestClient(val url: String) {
+ fun getResponse() = Response(Headers("some header info"))
+}
+
+data class BankAccount(val id: Int) {
+ fun checkAuthorization(username: String) = Unit
+ fun addPayee(payee: String) = Unit
+ fun makePayment(paymentDetails: String) = Unit
+
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/annotations/ValidationTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/annotations/ValidationTest.kt
new file mode 100644
index 0000000000..5c2b6ef47f
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/annotations/ValidationTest.kt
@@ -0,0 +1,41 @@
+package com.baeldung.annotations
+
+import org.junit.Test
+import kotlin.test.assertTrue
+import kotlin.test.assertFalse
+
+class ValidationTest {
+
+ @Test
+ fun whenAmountIsOneAndNameIsAlice_thenTrue() {
+ assertTrue(Validator().isValid(Item(1f, "Alice")))
+ }
+
+ @Test
+ fun whenAmountIsOneAndNameIsBob_thenTrue() {
+ assertTrue(Validator().isValid(Item(1f, "Bob")))
+ }
+
+
+ @Test
+ fun whenAmountIsMinusOneAndNameIsAlice_thenFalse() {
+ assertFalse(Validator().isValid(Item(-1f, "Alice")))
+ }
+
+ @Test
+ fun whenAmountIsMinusOneAndNameIsBob_thenFalse() {
+ assertFalse(Validator().isValid(Item(-1f, "Bob")))
+ }
+
+ @Test
+ fun whenAmountIsOneAndNameIsTom_thenFalse() {
+ assertFalse(Validator().isValid(Item(1f, "Tom")))
+ }
+
+ @Test
+ fun whenAmountIsMinusOneAndNameIsTom_thenFalse() {
+ assertFalse(Validator().isValid(Item(-1f, "Tom")))
+ }
+
+
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/CharRangeTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/CharRangeTest.kt
new file mode 100644
index 0000000000..0e23f508b6
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/CharRangeTest.kt
@@ -0,0 +1,17 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class CharRangeTest {
+
+ @Test
+ fun testCharRange() {
+ assertEquals(listOf('a', 'b', 'c'), ('a'..'c').toList())
+ }
+
+ @Test
+ fun testCharDownRange() {
+ assertEquals(listOf('c', 'b', 'a'), ('c'.downTo('a')).toList())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/ColorTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/ColorTest.kt
new file mode 100644
index 0000000000..4ac3270fcc
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/ColorTest.kt
@@ -0,0 +1,20 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class ColorTest {
+
+ @Test
+ fun testEnumRange() {
+
+ println(Color.values().toList());
+ val red = Color.RED
+ val yellow = Color.YELLOW
+ val range = red..yellow
+
+ assertTrue { range.contains(Color.MAGENTA) }
+ assertFalse { range.contains(Color.BLUE) }
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/FilterTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/FilterTest.kt
new file mode 100644
index 0000000000..d0e2df8860
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/FilterTest.kt
@@ -0,0 +1,24 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class FilterTest {
+
+ val r = 1..10
+
+ @Test
+ fun filterTest() {
+ assertEquals(listOf(2, 4, 6, 8, 10), r.filter { it -> it % 2 == 0 }.toList())
+ }
+
+ @Test
+ fun mapTest() {
+ assertEquals(listOf(1, 4, 9, 16, 25, 36, 49, 64, 81, 100), r.map { it -> it * it }.toList())
+ }
+
+ @Test
+ fun reduceTest() {
+ assertEquals(55, r.reduce { a, b -> a + b })
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/FirstLastTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/FirstLastTest.kt
new file mode 100644
index 0000000000..ca797e9c9b
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/FirstLastTest.kt
@@ -0,0 +1,22 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class FirstLastTest {
+
+ @Test
+ fun testFirst() {
+ assertEquals(1, (1..9).first)
+ }
+
+ @Test
+ fun testLast() {
+ assertEquals(9, (1..9).last)
+ }
+
+ @Test
+ fun testStep() {
+ assertEquals(2, (1..9 step 2).step)
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/OtherRangeFunctionsTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/OtherRangeFunctionsTest.kt
new file mode 100644
index 0000000000..d2d36bbfae
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/OtherRangeFunctionsTest.kt
@@ -0,0 +1,40 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class OtherRangeFunctionsTest {
+
+ val r = 1..20
+ val repeated = listOf(1, 1, 2, 4, 4, 6, 10)
+
+ @Test
+ fun testMin() {
+ assertEquals(1, r.min())
+ }
+
+ @Test
+ fun testMax() {
+ assertEquals(20, r.max())
+ }
+
+ @Test
+ fun testSum() {
+ assertEquals(210, r.sum())
+ }
+
+ @Test
+ fun testAverage() {
+ assertEquals(10.5, r.average())
+ }
+
+ @Test
+ fun testCount() {
+ assertEquals(20, r.count())
+ }
+
+ @Test
+ fun testDistinct() {
+ assertEquals(listOf(1, 2, 4, 6, 10), repeated.distinct())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/RangeTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/RangeTest.kt
new file mode 100644
index 0000000000..48fa483924
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/RangeTest.kt
@@ -0,0 +1,22 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class RangeTest {
+
+ @Test
+ fun testRange() {
+ assertEquals(listOf(1,2,3), (1.rangeTo(3).toList()))
+ }
+
+ @Test
+ fun testDownTo(){
+ assertEquals(listOf(3,2,1), (3.downTo(1).toList()))
+ }
+
+ @Test
+ fun testUntil(){
+ assertEquals(listOf(1,2), (1.until(3).toList()))
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/ReverseRangeTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/ReverseRangeTest.kt
new file mode 100644
index 0000000000..7e1c7badb7
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/ReverseRangeTest.kt
@@ -0,0 +1,12 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class ReverseRangeTest {
+
+ @Test
+ fun reversedTest() {
+ assertEquals(listOf(9, 6, 3), (1..9).reversed().step(3).toList())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/StepTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/StepTest.kt
new file mode 100644
index 0000000000..4570ceeb0a
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/StepTest.kt
@@ -0,0 +1,17 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class StepTest {
+
+ @Test
+ fun testStep() {
+ assertEquals(listOf(1, 3, 5, 7, 9), (1..9 step 2).toList())
+ }
+
+ @Test
+ fun testStepDown() {
+ assertEquals(listOf(9, 7, 5, 3, 1), (9 downTo 1 step 2).toList())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/UntilRangeTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/UntilRangeTest.kt
new file mode 100644
index 0000000000..f941c7f1e6
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/UntilRangeTest.kt
@@ -0,0 +1,12 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class UntilRangeTest {
+
+ @Test
+ fun testUntil() {
+ assertEquals(listOf(1, 2, 3, 4), (1 until 5).toList())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/scope/ScopeFunctionsUnitTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/scope/ScopeFunctionsUnitTest.kt
new file mode 100644
index 0000000000..cb3ed98006
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/scope/ScopeFunctionsUnitTest.kt
@@ -0,0 +1,143 @@
+package com.baeldung.scope
+
+import org.junit.Test
+import kotlin.test.assertTrue
+
+
+class ScopeFunctionsUnitTest {
+
+ class Logger {
+
+ var called : Boolean = false
+
+ fun info(message: String) {
+ called = true
+ }
+
+ fun wasCalled() = called
+ }
+
+ @Test
+ fun shouldTransformWhenLetFunctionUsed() {
+ val stringBuider = StringBuilder()
+ val numberOfCharacters = stringBuider.let {
+ it.append("This is a transformation function.")
+ it.append("It takes a StringBuilder instance and returns the number of characters in the generated String")
+ it.length
+ }
+
+ assertTrue {
+ numberOfCharacters == 128
+ }
+ }
+
+ @Test
+ fun shouldHandleNullabilityWhenLetFunctionUsed() {
+
+ val message: String? = "hello there!"
+ val charactersInMessage = message?.let {
+ "At this point is safe to reference the variable. Let's print the message: $it"
+ } ?: "default value"
+
+ assertTrue {
+ charactersInMessage.equals("At this point is safe to reference the variable. Let's print the message: hello there!")
+ }
+
+ val aNullMessage = null
+ val thisIsNull = aNullMessage?.let {
+ "At this point it would be safe to reference the variable. But it will not really happen because it is null. Let's reference: $it"
+ } ?: "default value"
+
+ assertTrue {
+ thisIsNull.equals("default value")
+ }
+ }
+
+ @Test
+ fun shouldInitializeObjectWhenUsingApply() {
+ val aStudent = Student().apply {
+ studentId = "1234567"
+ name = "Mary"
+ surname = "Smith"
+ }
+
+ assertTrue {
+ aStudent.name.equals("Mary")
+ }
+ }
+
+ @Test
+ fun shouldAllowBuilderStyleObjectDesignWhenApplyUsedInClassMethods() {
+ val teacher = Teacher()
+ .setId(1000)
+ .setName("Martha")
+ .setSurname("Spector")
+
+ assertTrue {
+ teacher.surname.equals("Spector")
+ }
+ }
+
+ @Test
+ fun shouldAllowSideEffectWhenUsingAlso() {
+ val restClient = RestClient("http://www.someurl.com")
+
+ val logger = Logger()
+
+ val headers = restClient
+ .getResponse()
+ .also { logger.info(it.toString()) }
+ .headers
+
+ assertTrue {
+ logger.wasCalled() && headers.headerInfo.equals("some header info")
+ }
+
+ }
+
+ @Test
+ fun shouldInitializeFieldWhenAlsoUsed() {
+ val aStudent = Student().also { it.name = "John"}
+
+ assertTrue {
+ aStudent.name.equals("John")
+ }
+ }
+
+ @Test
+ fun shouldLogicallyGroupObjectCallsWhenUsingWith() {
+ val bankAccount = BankAccount(1000)
+ with (bankAccount) {
+ checkAuthorization("someone")
+ addPayee("some payee")
+ makePayment("payment information")
+ }
+ }
+
+ @Test
+ fun shouldConvertObjectWhenRunUsed() {
+ val stringBuider = StringBuilder()
+ val numberOfCharacters = stringBuider.run {
+ append("This is a transformation function.")
+ append("It takes a StringBuilder instance and returns the number of characters in the generated String")
+ length
+ }
+
+ assertTrue {
+ numberOfCharacters == 128
+ }
+ }
+
+ @Test
+ fun shouldHandleNullabilityWhenRunIsUsed() {
+ val message: String? = "hello there!"
+ val charactersInMessage = message?.run {
+ "At this point is safe to reference the variable. Let's print the message: $this"
+ } ?: "default value"
+
+ assertTrue {
+ charactersInMessage.equals("At this point is safe to reference the variable. Let's print the message: hello there!")
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core-kotlin/README.md b/core-kotlin/README.md
index 95c57336b9..73a78eccff 100644
--- a/core-kotlin/README.md
+++ b/core-kotlin/README.md
@@ -55,3 +55,4 @@
- [Building DSLs in Kotlin](https://www.baeldung.com/kotlin-dsl)
- [Static Methods Behavior in Kotlin](https://www.baeldung.com/kotlin-static-methods)
- [Inline Functions in Kotlin](https://www.baeldung.com/kotlin-inline-functions)
+- [Delegation Pattern in Kotlin](https://www.baeldung.com/kotlin-delegation-pattern)
diff --git a/gson/README.md b/gson/README.md
index 02b06eac20..fec0506488 100644
--- a/gson/README.md
+++ b/gson/README.md
@@ -11,3 +11,6 @@
- [Convert JSON to a Map Using Gson](https://www.baeldung.com/gson-json-to-map)
- [Working with Primitive Values in Gson](https://www.baeldung.com/java-gson-primitives)
- [Convert String to JsonObject with Gson](https://www.baeldung.com/gson-string-to-jsonobject)
+- [Mapping Multiple JSON Fields to One Java Field](https://www.baeldung.com/json-multiple-fields-single-java-field)
+- [Serializing and Deserializing a List with Gson](https://www.baeldung.com/gson-list)
+
diff --git a/jackson-2/.gitignore b/jackson-2/.gitignore
new file mode 100644
index 0000000000..83c05e60c8
--- /dev/null
+++ b/jackson-2/.gitignore
@@ -0,0 +1,13 @@
+*.class
+
+#folders#
+/target
+/neoDb*
+/data
+/src/main/webapp/WEB-INF/classes
+*/META-INF/*
+
+# Packaged files #
+*.jar
+*.war
+*.ear
\ No newline at end of file
diff --git a/jackson-2/README.md b/jackson-2/README.md
new file mode 100644
index 0000000000..ec147f5fd9
--- /dev/null
+++ b/jackson-2/README.md
@@ -0,0 +1,9 @@
+=========
+
+## Jackson Cookbooks and Examples
+
+###The Course
+The "REST With Spring" Classes: http://bit.ly/restwithspring
+
+### Relevant Articles:
+- [Mapping Multiple JSON Fields to a Single Java Field](https://www.baeldung.com/json-multiple-fields-single-java-field)
diff --git a/jackson-2/pom.xml b/jackson-2/pom.xml
new file mode 100644
index 0000000000..ddbcb81dcc
--- /dev/null
+++ b/jackson-2/pom.xml
@@ -0,0 +1,52 @@
+
+ 4.0.0
+ jackson-2
+ 0.1-SNAPSHOT
+ jackson-2
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../parent-java
+
+
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+
+ jackson-2
+
+
+ src/main/resources
+ true
+
+
+
+
+
+
+
+ 3.11.0
+
+
+
diff --git a/jackson/src/main/java/com/baeldung/jackson/entities/Weather.java b/jackson-2/src/main/java/com/baeldung/jackson/entities/Weather.java
similarity index 100%
rename from jackson/src/main/java/com/baeldung/jackson/entities/Weather.java
rename to jackson-2/src/main/java/com/baeldung/jackson/entities/Weather.java
diff --git a/jackson-2/src/main/resources/logback.xml b/jackson-2/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/jackson-2/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jackson/src/test/java/com/baeldung/jackson/deserialization/jsonalias/JsonAliasUnitTest.java b/jackson-2/src/test/java/com/baeldung/jackson/deserialization/jsonalias/JsonAliasUnitTest.java
similarity index 100%
rename from jackson/src/test/java/com/baeldung/jackson/deserialization/jsonalias/JsonAliasUnitTest.java
rename to jackson-2/src/test/java/com/baeldung/jackson/deserialization/jsonalias/JsonAliasUnitTest.java
diff --git a/java-collections-maps-2/pom.xml b/java-collections-maps-2/pom.xml
new file mode 100644
index 0000000000..b025c5e32d
--- /dev/null
+++ b/java-collections-maps-2/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+ java-collections-maps-2
+ 0.1.0-SNAPSHOT
+ java-collections-maps-2
+ jar
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../parent-java
+
+
+
+
+ org.eclipse.collections
+ eclipse-collections
+ ${eclipse-collections.version}
+
+
+ net.sf.trove4j
+ trove4j
+ 3.0.2
+
+
+ it.unimi.dsi
+ fastutil
+ 8.1.0
+
+
+ colt
+ colt
+ 1.2.0
+
+
+
+
+ 8.2.0
+
+
+
\ No newline at end of file
diff --git a/java-collections-maps-2/src/main/java/com/baeldung/map/PrimitiveMaps.java b/java-collections-maps-2/src/main/java/com/baeldung/map/PrimitiveMaps.java
new file mode 100644
index 0000000000..d835950c68
--- /dev/null
+++ b/java-collections-maps-2/src/main/java/com/baeldung/map/PrimitiveMaps.java
@@ -0,0 +1,69 @@
+package com.baeldung.map;
+
+import cern.colt.map.AbstractIntDoubleMap;
+import cern.colt.map.OpenIntDoubleHashMap;
+import gnu.trove.map.TDoubleIntMap;
+import gnu.trove.map.hash.TDoubleIntHashMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanSortedMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanSortedMaps;
+import org.eclipse.collections.api.map.primitive.ImmutableIntIntMap;
+import org.eclipse.collections.api.map.primitive.MutableIntIntMap;
+import org.eclipse.collections.api.map.primitive.MutableObjectDoubleMap;
+import org.eclipse.collections.impl.factory.primitive.IntIntMaps;
+import org.eclipse.collections.impl.factory.primitive.ObjectDoubleMaps;
+
+public class PrimitiveMaps {
+
+ public static void main(String[] args) {
+
+ eclipseCollectionsMap();
+ troveMap();
+ coltMap();
+ fastutilMap();
+ }
+
+ private static void fastutilMap() {
+ Int2BooleanMap int2BooleanMap = new Int2BooleanOpenHashMap();
+ int2BooleanMap.put(1, true);
+ int2BooleanMap.put(7, false);
+ int2BooleanMap.put(4, true);
+
+ boolean value = int2BooleanMap.get(1);
+
+ Int2BooleanSortedMap int2BooleanSorted = Int2BooleanSortedMaps.EMPTY_MAP;
+ }
+
+ private static void coltMap() {
+ AbstractIntDoubleMap map = new OpenIntDoubleHashMap();
+ map.put(1, 4.5);
+ double value = map.get(1);
+ }
+
+ private static void eclipseCollectionsMap() {
+ MutableIntIntMap mutableIntIntMap = IntIntMaps.mutable.empty();
+ mutableIntIntMap.addToValue(1, 1);
+
+ ImmutableIntIntMap immutableIntIntMap = IntIntMaps.immutable.empty();
+
+ MutableObjectDoubleMap dObject = ObjectDoubleMaps.mutable.empty();
+ dObject.addToValue("price", 150.5);
+ dObject.addToValue("quality", 4.4);
+ dObject.addToValue("stability", 0.8);
+ }
+
+ private static void troveMap() {
+ double[] doubles = new double[] {1.2, 4.5, 0.3};
+ int[] ints = new int[] {1, 4, 0};
+
+ TDoubleIntMap doubleIntMap = new TDoubleIntHashMap(doubles, ints);
+
+ doubleIntMap.put(1.2, 22);
+ doubleIntMap.put(4.5, 16);
+
+ doubleIntMap.adjustValue(1.2, 1);
+ doubleIntMap.adjustValue(4.5, 4);
+ doubleIntMap.adjustValue(0.3, 7);
+ }
+}
diff --git a/java-dates-2/.gitignore b/java-dates-2/.gitignore
new file mode 100644
index 0000000000..6471aabbcf
--- /dev/null
+++ b/java-dates-2/.gitignore
@@ -0,0 +1,29 @@
+*.class
+
+0.*
+
+#folders#
+/target
+/neoDb*
+/data
+/src/main/webapp/WEB-INF/classes
+*/META-INF/*
+.resourceCache
+
+# Packaged files #
+*.jar
+*.war
+*.ear
+
+# Files generated by integration tests
+*.txt
+backup-pom.xml
+/bin/
+/temp
+
+#IntelliJ specific
+.idea/
+*.iml
+
+#jenv
+.java-version
\ No newline at end of file
diff --git a/java-dates-2/pom.xml b/java-dates-2/pom.xml
new file mode 100644
index 0000000000..c2464ed47f
--- /dev/null
+++ b/java-dates-2/pom.xml
@@ -0,0 +1,55 @@
+
+ 4.0.0
+ com.baeldung
+ java-dates-2
+ 0.1.0-SNAPSHOT
+ jar
+ java-dates-2
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../parent-java
+
+
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+
+ java-dates-2
+
+
+ src/main/resources
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+
+
+
+
+ 3.6.1
+ 1.9
+ 1.9
+
+
diff --git a/java-dates-2/src/test/java/com/baeldung/xmlgregoriancalendar/XmlGregorianCalendarConverterUnitTest.java b/java-dates-2/src/test/java/com/baeldung/xmlgregoriancalendar/XmlGregorianCalendarConverterUnitTest.java
new file mode 100644
index 0000000000..b221c04199
--- /dev/null
+++ b/java-dates-2/src/test/java/com/baeldung/xmlgregoriancalendar/XmlGregorianCalendarConverterUnitTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.xmlgregoriancalendar;
+
+import org.junit.Test;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeConstants;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+import java.time.LocalDate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XmlGregorianCalendarConverterUnitTest {
+
+ @Test
+ public void fromLocalDateToXMLGregorianCalendar() throws DatatypeConfigurationException {
+ LocalDate localDate = LocalDate.of(2017, 4, 25);
+ XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(localDate.toString());
+
+ assertThat(xmlGregorianCalendar.getYear()).isEqualTo(localDate.getYear());
+ assertThat(xmlGregorianCalendar.getMonth()).isEqualTo(localDate.getMonthValue());
+ assertThat(xmlGregorianCalendar.getDay()).isEqualTo(localDate.getDayOfMonth());
+ assertThat(xmlGregorianCalendar.getTimezone()).isEqualTo(DatatypeConstants.FIELD_UNDEFINED);
+ }
+
+ @Test
+ public void fromXMLGregorianCalendarToLocalDate() throws DatatypeConfigurationException {
+ XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar("2017-04-25");
+ LocalDate localDate = LocalDate.of(xmlGregorianCalendar.getYear(), xmlGregorianCalendar.getMonth(), xmlGregorianCalendar.getDay());
+
+ assertThat(localDate.getYear()).isEqualTo(xmlGregorianCalendar.getYear());
+ assertThat(localDate.getMonthValue()).isEqualTo(xmlGregorianCalendar.getMonth());
+ assertThat(localDate.getDayOfMonth()).isEqualTo(xmlGregorianCalendar.getDay());
+ }
+
+}
diff --git a/java-streams-2/README.md b/java-streams-2/README.md
new file mode 100644
index 0000000000..83ef97686f
--- /dev/null
+++ b/java-streams-2/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+- [Guide to Stream.reduce()](https://www.baeldung.com/java-stream-reduce)
+
diff --git a/java-streams/README.md b/java-streams/README.md
index b931c0d7d9..e294e5aee1 100644
--- a/java-streams/README.md
+++ b/java-streams/README.md
@@ -17,3 +17,4 @@
- [Java Stream Filter with Lambda Expression](https://www.baeldung.com/java-stream-filter-lambda)
- [Counting Matches on a Stream Filter](https://www.baeldung.com/java-stream-filter-count)
- [Java 8 Streams peek() API](https://www.baeldung.com/java-streams-peek-api)
+- [Working With Maps Using Streams](https://www.baeldung.com/java-maps-streams)
diff --git a/javax-servlets/src/main/java/com/baeldung/filters/EmptyParamFilter.java b/javax-servlets/src/main/java/com/baeldung/filters/EmptyParamFilter.java
new file mode 100644
index 0000000000..b0b5392237
--- /dev/null
+++ b/javax-servlets/src/main/java/com/baeldung/filters/EmptyParamFilter.java
@@ -0,0 +1,35 @@
+package com.baeldung.filters;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+import java.io.IOException;
+
+@WebFilter(urlPatterns = "/uppercase")
+public class EmptyParamFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+ FilterChain filterChain) throws IOException, ServletException {
+ String inputString = servletRequest.getParameter("input");
+
+ if (inputString != null && inputString.matches("[A-Za-z0-9]+")) {
+ filterChain.doFilter(servletRequest, servletResponse);
+ } else {
+ servletResponse.getWriter().println("Missing input parameter");
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+}
diff --git a/javax-servlets/src/main/java/com/baeldung/listeners/AppListener.java b/javax-servlets/src/main/java/com/baeldung/listeners/AppListener.java
new file mode 100644
index 0000000000..ed16dd1654
--- /dev/null
+++ b/javax-servlets/src/main/java/com/baeldung/listeners/AppListener.java
@@ -0,0 +1,21 @@
+package com.baeldung.listeners;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+
+@WebListener
+public class AppListener implements ServletContextListener {
+
+ @Override
+ public void contextInitialized(ServletContextEvent event) {
+ ServletContext context = event.getServletContext();
+ context.setAttribute("counter", 0);
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent event) {
+
+ }
+}
diff --git a/javax-servlets/src/main/java/com/baeldung/listeners/RequestListener.java b/javax-servlets/src/main/java/com/baeldung/listeners/RequestListener.java
new file mode 100644
index 0000000000..7f0c37b666
--- /dev/null
+++ b/javax-servlets/src/main/java/com/baeldung/listeners/RequestListener.java
@@ -0,0 +1,24 @@
+package com.baeldung.listeners;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.annotation.WebListener;
+import javax.servlet.http.HttpServletRequest;
+
+@WebListener
+public class RequestListener implements ServletRequestListener {
+
+ @Override
+ public void requestInitialized(ServletRequestEvent event) {
+ }
+
+ @Override
+ public void requestDestroyed(ServletRequestEvent event) {
+ HttpServletRequest request = (HttpServletRequest)event.getServletRequest();
+ if (!request.getServletPath().equals("/counter")) {
+ ServletContext context = event.getServletContext();
+ context.setAttribute("counter", (int)context.getAttribute("counter") + 1);
+ }
+ }
+}
diff --git a/javax-servlets/src/main/java/com/baeldung/servlets/CounterServlet.java b/javax-servlets/src/main/java/com/baeldung/servlets/CounterServlet.java
new file mode 100644
index 0000000000..a11f084db2
--- /dev/null
+++ b/javax-servlets/src/main/java/com/baeldung/servlets/CounterServlet.java
@@ -0,0 +1,21 @@
+package com.baeldung.servlets;
+
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+@WebServlet(urlPatterns = "/counter", name = "counterServlet")
+public class CounterServlet extends HttpServlet {
+
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ PrintWriter out = response.getWriter();
+
+ int count = (int)request.getServletContext().getAttribute("counter");
+
+ out.println("Request counter: " + count);
+ }
+
+}
diff --git a/javax-servlets/src/main/java/com/baeldung/servlets/UppercaseServlet.java b/javax-servlets/src/main/java/com/baeldung/servlets/UppercaseServlet.java
new file mode 100644
index 0000000000..766ec2e6ff
--- /dev/null
+++ b/javax-servlets/src/main/java/com/baeldung/servlets/UppercaseServlet.java
@@ -0,0 +1,20 @@
+package com.baeldung.servlets;
+
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+@WebServlet(urlPatterns = "/uppercase", name = "uppercaseServlet")
+public class UppercaseServlet extends HttpServlet {
+
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ String inputString = request.getParameter("input").toUpperCase();
+
+ PrintWriter out = response.getWriter();
+
+ out.println(inputString);
+ }
+}
diff --git a/kotlin-libraries/pom.xml b/kotlin-libraries/pom.xml
index 507e5820d4..e3f69b4ea9 100644
--- a/kotlin-libraries/pom.xml
+++ b/kotlin-libraries/pom.xml
@@ -19,6 +19,14 @@
exposed
https://dl.bintray.com/kotlin/exposed
+
+
+ false
+
+ kotlinx
+ bintray
+ https://dl.bintray.com/kotlin/kotlinx
+
@@ -112,9 +120,30 @@
3.3.0
pom
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+
+ com.google.guava
+ guava
+ 27.1-jre
+
+
+
+ org.jetbrains.kotlinx
+ kotlinx-collections-immutable
+ 0.1
+
+ 4.12
1.5.0
4.1.0
3.0.4
diff --git a/kotlin-libraries/src/test/kotlin/com/baeldung/kotlin/immutable/KotlinxImmutablesUnitTest.kt b/kotlin-libraries/src/test/kotlin/com/baeldung/kotlin/immutable/KotlinxImmutablesUnitTest.kt
new file mode 100644
index 0000000000..971f2de4c2
--- /dev/null
+++ b/kotlin-libraries/src/test/kotlin/com/baeldung/kotlin/immutable/KotlinxImmutablesUnitTest.kt
@@ -0,0 +1,27 @@
+package com.baeldung.kotlin.immutable
+
+import junit.framework.Assert.assertEquals
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.immutableListOf
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.ExpectedException
+
+class KotlinxImmutablesUnitTest{
+
+
+ @Rule
+ @JvmField
+ var ee : ExpectedException = ExpectedException.none()
+
+ @Test
+ fun givenKICLList_whenAddTried_checkExceptionThrown(){
+
+ val list: ImmutableList = immutableListOf("I", "am", "immutable")
+
+ list.add("My new item")
+
+ assertEquals(listOf("I", "am", "immutable"), list)
+
+ }
+}
\ No newline at end of file
diff --git a/kotlin-libraries/src/test/kotlin/com/baeldung/kotlin/immutable/ReadOnlyUnitTest.kt b/kotlin-libraries/src/test/kotlin/com/baeldung/kotlin/immutable/ReadOnlyUnitTest.kt
new file mode 100644
index 0000000000..62c4a4eb88
--- /dev/null
+++ b/kotlin-libraries/src/test/kotlin/com/baeldung/kotlin/immutable/ReadOnlyUnitTest.kt
@@ -0,0 +1,73 @@
+package com.baeldung.kotlin.immutable
+
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.ImmutableSet
+import junit.framework.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.ExpectedException
+
+class ReadOnlyUnitTest{
+
+ @Test
+ fun givenReadOnlyList_whenCastToMutableList_checkNewElementsAdded(){
+
+ val list: List = listOf("This", "Is", "Totally", "Immutable")
+
+ (list as MutableList)[2] = "Not"
+
+ assertEquals(listOf("This", "Is", "Not", "Immutable"), list)
+
+ }
+
+ @Rule
+ @JvmField
+ var ee : ExpectedException = ExpectedException.none()
+
+ @Test
+ fun givenImmutableList_whenAddTried_checkExceptionThrown(){
+
+ val list: List = ImmutableList.of("I", "am", "actually", "immutable")
+
+ ee.expect(UnsupportedOperationException::class.java)
+
+ (list as MutableList).add("Oops")
+
+ }
+
+ @Test
+ fun givenMutableList_whenCopiedAndAddTried_checkExceptionThrown(){
+
+ val mutableList : List = listOf("I", "Am", "Definitely", "Immutable")
+
+ (mutableList as MutableList)[2] = "100% Not"
+
+ assertEquals(listOf("I", "Am", "100% Not", "Immutable"), mutableList)
+
+ val list: List = ImmutableList.copyOf(mutableList)
+
+ ee.expect(UnsupportedOperationException::class.java)
+
+ (list as MutableList)[2] = "Really?"
+
+ }
+
+ @Test
+ fun givenImmutableSetBuilder_whenAddTried_checkExceptionThrown(){
+
+ val mutableList : List = listOf("Hello", "Baeldung")
+ val set: ImmutableSet = ImmutableSet.builder()
+ .add("I","am","immutable")
+ .addAll(mutableList)
+ .build()
+
+ assertEquals(setOf("Hello", "Baeldung", "I", "am", "immutable"), set)
+
+ ee.expect(UnsupportedOperationException::class.java)
+
+ (set as MutableSet).add("Oops")
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/lombok/README.md b/lombok/README.md
index e3d08d4e26..4ff7ca7921 100644
--- a/lombok/README.md
+++ b/lombok/README.md
@@ -6,4 +6,5 @@
- [Lombok Builder with Default Value](https://www.baeldung.com/lombok-builder-default-value)
- [Lombok Builder with Custom Setter](https://www.baeldung.com/lombok-builder-custom-setter)
- [Setting up Lombok with Eclipse and Intellij](https://www.baeldung.com/lombok-ide)
+- [Using the @Singular Annotation with Lombok Builders](https://www.baeldung.com/lombok-builder-singular)
diff --git a/lombok/src/main/java/com/baeldung/lombok/builder/singular/Person.java b/lombok/src/main/java/com/baeldung/lombok/builder/singular/Person.java
new file mode 100644
index 0000000000..d7d95feb66
--- /dev/null
+++ b/lombok/src/main/java/com/baeldung/lombok/builder/singular/Person.java
@@ -0,0 +1,25 @@
+package com.baeldung.lombok.builder.singular;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Singular;
+
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Getter
+@Builder
+public class Person {
+
+ private final String givenName;
+ private final String additionalName;
+ private final String familyName;
+
+ private final List tags;
+ @Singular private final List interests;
+ @Singular private final Set skills;
+ @Singular private final Map awards;
+}
diff --git a/lombok/src/main/java/com/baeldung/lombok/builder/singular/Sea.java b/lombok/src/main/java/com/baeldung/lombok/builder/singular/Sea.java
new file mode 100644
index 0000000000..8cf38e5f1a
--- /dev/null
+++ b/lombok/src/main/java/com/baeldung/lombok/builder/singular/Sea.java
@@ -0,0 +1,14 @@
+package com.baeldung.lombok.builder.singular;
+
+import java.util.List;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Singular;
+
+@Getter
+@Builder
+public class Sea {
+
+ @Singular private final List grasses;
+ @Singular("oneFish") private final List fish;
+}
diff --git a/lombok/src/test/java/com/baeldung/lombok/builder/singular/BuilderWithSingularSupportForCollectionsUnitTest.java b/lombok/src/test/java/com/baeldung/lombok/builder/singular/BuilderWithSingularSupportForCollectionsUnitTest.java
new file mode 100644
index 0000000000..41d5b19df7
--- /dev/null
+++ b/lombok/src/test/java/com/baeldung/lombok/builder/singular/BuilderWithSingularSupportForCollectionsUnitTest.java
@@ -0,0 +1,192 @@
+package com.baeldung.lombok.builder.singular;
+
+import org.junit.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+public class BuilderWithSingularSupportForCollectionsUnitTest {
+
+ @Test
+ public void canAddMultipleElementsAsNewCollection() throws Exception {
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .tags(Arrays.asList("fictional", "incidental"))
+ .build();
+
+ assertThat(person.getTags(), containsInAnyOrder("fictional", "incidental"));
+ }
+
+ @Test
+ public void canUpdateCollectionAfterBuildIfMutableCollectionPassedToBuilder() throws Exception {
+
+ List tags = new ArrayList();
+ tags.add("fictional");
+ tags.add("incidental");
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .tags(tags)
+ .build();
+ person.getTags()
+ .clear();
+ person.getTags()
+ .add("non-fictional");
+ person.getTags()
+ .add("important");
+
+ assertThat(person.getTags(), containsInAnyOrder("non-fictional", "important"));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void cannotUpdateCollectionAfterBuildIfImmutableCollectionPassedToBuilder() throws Exception {
+ List tags = Arrays.asList("fictional", "incidental");
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .tags(tags)
+ .build();
+ person.getTags()
+ .clear();
+ }
+
+ @Test
+ public void canAssignToSingularAnnotatedCollectionOneByOne() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .interest("history")
+ .interest("sport")
+ .build();
+
+ assertThat(person.getInterests(), containsInAnyOrder("sport", "history"));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void singularAnnotatedBuilderCreatesImmutableCollection() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .interest("history")
+ .interest("sport")
+ .build();
+ person.getInterests()
+ .clear();
+ }
+
+ @Test
+ public void unpopulatedListsCreatedAsNullIfNotSingularButEmptyArrayIfSingular() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .build();
+ assertThat(person.getInterests(), hasSize(0));
+ assertThat(person.getSkills(), hasSize(0));
+ assertThat(person.getAwards()
+ .keySet(), hasSize(0));
+ assertThat(person.getTags(), is(nullValue()));
+ }
+
+ @Test
+ public void singularSupportsSetsToo() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .skill("singing")
+ .skill("dancing")
+ .build();
+ assertThat(person.getSkills(), contains("singing", "dancing"));
+ }
+
+ @Test
+ public void singularSetsAreLenientWithDuplicates() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .interest("singing")
+ .interest("singing")
+ .skill("singing")
+ .skill("singing")
+ .build();
+ assertThat(person.getInterests(), contains("singing", "singing"));
+ assertThat(person.getSkills(), contains("singing"));
+ }
+
+ @Test
+ public void singularSupportsMapsToo() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .award("Singer of the Year", LocalDate.now()
+ .minusYears(5))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(2))
+ .build();
+ assertThat(person.getAwards()
+ .keySet(), contains("Singer of the Year", "Best Dancer"));
+ assertThat(person.getAwards()
+ .get("Best Dancer"),
+ is(LocalDate.now()
+ .minusYears(2)));
+ }
+
+ @Test
+ public void singularIsLenientWithMapKeys() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(5))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(4))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(3))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(2))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(1))
+ .build();
+ assertThat(person.getAwards()
+ .keySet(), hasSize(1));
+ assertThat(person.getAwards()
+ .get("Best Dancer"),
+ is(LocalDate.now()
+ .minusYears(1)));
+ }
+
+ @Test
+ public void wordsWithNonStandardPlurals() throws Exception {
+ Sea sea = Sea.builder()
+ .grass("Dulse")
+ .grass("Kelp")
+ .oneFish("Cod")
+ .oneFish("Mackerel")
+ .build();
+ assertThat(sea.getGrasses(), contains("Dulse", "Kelp"));
+ assertThat(sea.getFish(), contains("Cod", "Mackerel"));
+ }
+
+}
diff --git a/maven/README.md b/maven/README.md
index 1352a2a10f..ca648ec203 100644
--- a/maven/README.md
+++ b/maven/README.md
@@ -15,3 +15,4 @@
- [Use the Latest Version of a Dependency in Maven](https://www.baeldung.com/maven-dependency-latest-version)
- [Multi-Module Project with Maven](https://www.baeldung.com/maven-multi-module)
- [Maven Enforcer Plugin](https://www.baeldung.com/maven-enforcer-plugin)
+- [Eclipse Error: web.xml is missing and failOnMissingWebXml is set to true](https://www.baeldung.com/eclipse-error-web-xml-missing)
diff --git a/patterns/README.md b/patterns/README.md
index 9a15cdff02..1c9a59ea21 100644
--- a/patterns/README.md
+++ b/patterns/README.md
@@ -1,3 +1,4 @@
### Relevant Articles:
- [A Guide to the Front Controller Pattern in Java](http://www.baeldung.com/java-front-controller-pattern)
- [Introduction to Intercepting Filter Pattern in Java](http://www.baeldung.com/intercepting-filter-pattern-in-java)
+- [Introduction to the Null Object Pattern](https://www.baeldung.com/java-null-object-pattern)
diff --git a/persistence-modules/core-java-persistence/README.md b/persistence-modules/core-java-persistence/README.md
index ca0ce81eef..26bd4bf00f 100644
--- a/persistence-modules/core-java-persistence/README.md
+++ b/persistence-modules/core-java-persistence/README.md
@@ -7,3 +7,4 @@
- [Batch Processing in JDBC](http://www.baeldung.com/jdbc-batch-processing)
- [Introduction to the JDBC RowSet Interface in Java](http://www.baeldung.com/java-jdbc-rowset)
- [A Simple Guide to Connection Pooling in Java](https://www.baeldung.com/java-connection-pooling)
+- [Guide to the JDBC ResultSet Interface](https://www.baeldung.com/jdbc-resultset)
diff --git a/persistence-modules/hibernate5/pom.xml b/persistence-modules/hibernate5/pom.xml
index a09669c8b5..c7f08e50d5 100644
--- a/persistence-modules/hibernate5/pom.xml
+++ b/persistence-modules/hibernate5/pom.xml
@@ -83,13 +83,18 @@
jmh-generator-annprocess
${openjdk-jmh.version}
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.0
+
hibernate5
- src/main/resources
+ src/test/resources
true
diff --git a/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java b/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java
index ea0af97d5a..48c9b9d5c2 100644
--- a/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java
+++ b/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java
@@ -113,6 +113,7 @@ public class HibernateUtil {
metadataSources.addAnnotatedClass(OptimisticLockingCourse.class);
metadataSources.addAnnotatedClass(OptimisticLockingStudent.class);
metadataSources.addAnnotatedClass(OfficeEmployee.class);
+ metadataSources.addAnnotatedClass(Post.class);
Metadata metadata = metadataSources.getMetadataBuilder()
.applyBasicType(LocalDateStringType.INSTANCE)
diff --git a/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Post.java b/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Post.java
new file mode 100644
index 0000000000..25e51e35d0
--- /dev/null
+++ b/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Post.java
@@ -0,0 +1,59 @@
+package com.baeldung.hibernate.pojo;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "posts")
+public class Post {
+
+ @Id
+ @GeneratedValue
+ private int id;
+
+ private String title;
+
+ private String body;
+
+ public Post() { }
+
+ public Post(String title, String body) {
+ this.title = title;
+ this.body = body;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+
+ @Override
+ public String toString() {
+ return "Post{" +
+ "id=" + id +
+ ", title='" + title + '\'' +
+ ", body='" + body + '\'' +
+ '}';
+ }
+}
diff --git a/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/transaction/PostService.java b/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/transaction/PostService.java
new file mode 100644
index 0000000000..5a4eb20079
--- /dev/null
+++ b/persistence-modules/hibernate5/src/main/java/com/baeldung/hibernate/transaction/PostService.java
@@ -0,0 +1,29 @@
+package com.baeldung.hibernate.transaction;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.query.Query;
+
+public class PostService {
+
+
+ private Session session;
+
+ public PostService(Session session) {
+ this.session = session;
+ }
+
+
+ public void updatePost(String title, String body, int id) {
+ Transaction txn = session.beginTransaction();
+ Query updateQuery = session.createQuery("UPDATE Post p SET p.title = ?1, p.body = ?2 WHERE p.id = ?3");
+ updateQuery.setParameter(1, title);
+ updateQuery.setParameter(2, body);
+ updateQuery.setParameter(3, id);
+ updateQuery.executeUpdate();
+ txn.commit();
+ }
+
+
+
+}
diff --git a/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/CustomClassIntegrationTest.java b/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/CustomClassIntegrationTest.java
index 29ae55b773..e64e836924 100644
--- a/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/CustomClassIntegrationTest.java
+++ b/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/CustomClassIntegrationTest.java
@@ -74,4 +74,6 @@ public class CustomClassIntegrationTest {
assertEquals("John Smith", result.getEmployeeName());
assertEquals("Sales", result.getDepartmentName());
}
+
+
}
diff --git a/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/transaction/TransactionIntegrationTest.java b/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/transaction/TransactionIntegrationTest.java
new file mode 100644
index 0000000000..246a7d59f9
--- /dev/null
+++ b/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/transaction/TransactionIntegrationTest.java
@@ -0,0 +1,57 @@
+package com.baeldung.hibernate.transaction;
+
+import com.baeldung.hibernate.HibernateUtil;
+import com.baeldung.hibernate.pojo.Post;
+import com.baeldung.hibernate.transaction.PostService;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+
+public class TransactionIntegrationTest {
+
+ private static PostService postService;
+ private static Session session;
+ private static Logger logger = LoggerFactory.getLogger(TransactionIntegrationTest.class);
+
+ @BeforeClass
+ public static void init() throws IOException {
+ Properties properties = new Properties();
+ properties.setProperty("hibernate.connection.driver_class", "org.h2.Driver");
+ properties.setProperty("hibernate.connection.url", "jdbc:h2:mem:mydb1;DB_CLOSE_DELAY=-1");
+ properties.setProperty("hibernate.connection.username", "sa");
+ properties.setProperty("hibernate.show_sql", "true");
+ properties.setProperty("jdbc.password", "");
+ properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
+ properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
+ SessionFactory sessionFactory = HibernateUtil.getSessionFactoryByProperties(properties);
+ session = sessionFactory.openSession();
+ postService = new PostService(session);
+ }
+
+ @Test
+ public void givenTitleAndBody_whenRepositoryUpdatePost_thenUpdatePost() {
+
+ Post post = new Post("This is a title", "This is a sample post");
+ session.persist(post);
+
+ String title = "[UPDATE] Java HowTos";
+ String body = "This is an updated posts on Java how-tos";
+ postService.updatePost(title, body, post.getId());
+
+ session.refresh(post);
+
+ assertEquals(post.getTitle(), title);
+ assertEquals(post.getBody(), body);
+ }
+
+
+}
diff --git a/persistence-modules/java-jpa/README.md b/persistence-modules/java-jpa/README.md
index 2c26581bab..2424999fb3 100644
--- a/persistence-modules/java-jpa/README.md
+++ b/persistence-modules/java-jpa/README.md
@@ -6,3 +6,4 @@
- [JPA Entity Graph](https://www.baeldung.com/jpa-entity-graph)
- [JPA 2.2 Support for Java 8 Date/Time Types](https://www.baeldung.com/jpa-java-time)
- [Converting Between LocalDate and SQL Date](https://www.baeldung.com/java-convert-localdate-sql-date)
+- [Combining JPA And/Or Criteria Predicates](https://www.baeldung.com/jpa-and-or-criteria-predicates)
diff --git a/persistence-modules/spring-boot-persistence-mongodb/README.md b/persistence-modules/spring-boot-persistence-mongodb/README.md
index f093d4baf0..40f9f40749 100644
--- a/persistence-modules/spring-boot-persistence-mongodb/README.md
+++ b/persistence-modules/spring-boot-persistence-mongodb/README.md
@@ -1,3 +1,4 @@
# Relevant Articles
- [Auto-Generated Field for MongoDB using Spring Boot](https://www.baeldung.com/spring-boot-mongodb-auto-generated-field)
+- [Spring Boot Integration Testing with Embedded MongoDB](http://www.baeldung.com/spring-boot-embedded-mongodb)
diff --git a/persistence-modules/spring-boot-persistence-mongodb/pom.xml b/persistence-modules/spring-boot-persistence-mongodb/pom.xml
index 86b93c7826..585e54bf57 100644
--- a/persistence-modules/spring-boot-persistence-mongodb/pom.xml
+++ b/persistence-modules/spring-boot-persistence-mongodb/pom.xml
@@ -25,6 +25,11 @@
org.springframework.boot
spring-boot-starter-data-mongodb
+
+ de.flapdoodle.embed
+ de.flapdoodle.embed.mongo
+ test
+
org.junit.jupiter
@@ -102,4 +107,4 @@
-
\ No newline at end of file
+
diff --git a/spring-boot/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
similarity index 100%
rename from spring-boot/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
rename to persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
diff --git a/spring-boot/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
similarity index 90%
rename from spring-boot/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
rename to persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
index 39127f62e9..954bae3684 100644
--- a/spring-boot/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
+++ b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
@@ -2,7 +2,6 @@ package com.baeldung.mongodb;
import static org.assertj.core.api.Assertions.assertThat;
-import org.baeldung.boot.Application;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -12,10 +11,11 @@ import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
+import com.baeldung.SpringBootPersistenceApplication;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
-@ContextConfiguration(classes = Application.class)
+@ContextConfiguration(classes = SpringBootPersistenceApplication.class)
@DataMongoTest
@ExtendWith(SpringExtension.class)
public class MongoDbSpringIntegrationTest {
diff --git a/persistence-modules/spring-boot-persistence/README.MD b/persistence-modules/spring-boot-persistence/README.MD
index f62ca57a19..ee7c2e298e 100644
--- a/persistence-modules/spring-boot-persistence/README.MD
+++ b/persistence-modules/spring-boot-persistence/README.MD
@@ -7,3 +7,4 @@
- [Hibernate Field Naming with Spring Boot](https://www.baeldung.com/hibernate-field-naming-spring-boot)
- [Integrating Spring Boot with HSQLDB](https://www.baeldung.com/spring-boot-hsqldb)
- [Configuring a DataSource Programmatically in Spring Boot](https://www.baeldung.com/spring-boot-configure-data-source-programmatic)
+- [Resolving “Failed to Configure a DataSource” Error](https://www.baeldung.com/spring-boot-failed-to-configure-data-source)
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/Car.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/Car.java
new file mode 100644
index 0000000000..bf09caf6ff
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/Car.java
@@ -0,0 +1,48 @@
+package com.baeldung.exists;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+/**
+ * @author paullatzelsperger
+ * @since 2019-03-20
+ */
+@Entity
+public class Car {
+
+ @Id
+ @GeneratedValue
+ private int id;
+ private Integer power;
+ private String model;
+
+ Car() {
+
+ }
+
+ public Car(int power, String model) {
+ this.power = power;
+ this.model = model;
+ }
+
+ public Integer getPower() {
+ return power;
+ }
+
+ public void setPower(Integer power) {
+ this.power = power;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public int getId() {
+ return id;
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/CarRepository.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/CarRepository.java
new file mode 100644
index 0000000000..a54f19f4cd
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/CarRepository.java
@@ -0,0 +1,24 @@
+package com.baeldung.exists;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author paullatzelsperger
+ * @since 2019-03-20
+ */
+@Repository
+public interface CarRepository extends JpaRepository {
+
+ boolean existsCarByPower(int power);
+
+ boolean existsCarByModel(String model);
+
+ @Query("select case when count(c)> 0 then true else false end from Car c where c.model = :model")
+ boolean existsCarExactCustomQuery(@Param("model") String model);
+
+ @Query("select case when count(c)> 0 then true else false end from Car c where lower(c.model) like lower(:model)")
+ boolean existsCarLikeCustomQuery(@Param("model") String model);
+}
diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/exists/CarRepositoryIntegrationTest.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/exists/CarRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..1633df35d6
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/exists/CarRepositoryIntegrationTest.java
@@ -0,0 +1,86 @@
+package com.baeldung.exists;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.ignoreCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class CarRepositoryIntegrationTest {
+
+ @Autowired
+ private CarRepository repository;
+ private int searchId;
+
+ @Before
+ public void setup() {
+ List cars = repository.saveAll(Arrays.asList(new Car(200, "BMW"), new Car(300, "Audi")));
+ searchId = cars.get(0).getId();
+ }
+
+ @After
+ public void teardown() {
+ repository.deleteAll();
+ }
+
+ @Test
+ public void whenIdIsCorrect_thenExistsShouldReturnTrue() {
+ assertThat(repository.existsById(searchId)).isTrue();
+ }
+
+ @Test
+ public void givenExample_whenExists_thenIsTrue() {
+ ExampleMatcher modelMatcher = ExampleMatcher.matching()
+ .withIgnorePaths("id") // must explicitly ignore -> PK
+ .withMatcher("model", ignoreCase());
+ Car probe = new Car();
+ probe.setModel("bmw");
+
+ Example example = Example.of(probe, modelMatcher);
+
+ assertThat(repository.exists(example)).isTrue();
+ }
+
+ @Test
+ public void givenPower_whenExists_thenIsFalse() {
+ assertThat(repository.existsCarByPower(200)).isTrue();
+ assertThat(repository.existsCarByPower(800)).isFalse();
+ }
+
+ @Test
+ public void existsByDerivedQuery_byModel() {
+ assertThat(repository.existsCarByModel("Audi")).isTrue();
+ assertThat(repository.existsCarByModel("audi")).isFalse();
+ assertThat(repository.existsCarByModel("AUDI")).isFalse();
+ assertThat(repository.existsCarByModel("")).isFalse();
+ }
+
+ @Test
+ public void givenModelName_whenExistsExact_thenIsTrue() {
+ assertThat(repository.existsCarExactCustomQuery("BMW")).isTrue();
+ assertThat(repository.existsCarExactCustomQuery("Bmw")).isFalse();
+ assertThat(repository.existsCarExactCustomQuery("bmw")).isFalse();
+ assertThat(repository.existsCarExactCustomQuery("")).isFalse();
+ }
+
+ @Test
+ public void givenModelName_whenExistsLike_thenIsTrue() {
+ assertThat(repository.existsCarLikeCustomQuery("BMW")).isTrue();
+ assertThat(repository.existsCarLikeCustomQuery("Bmw")).isTrue();
+ assertThat(repository.existsCarLikeCustomQuery("bmw")).isTrue();
+ assertThat(repository.existsCarLikeCustomQuery("")).isFalse();
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-2/README.md b/persistence-modules/spring-data-jpa-2/README.md
new file mode 100644
index 0000000000..295a434d17
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/README.md
@@ -0,0 +1,5 @@
+=========
+
+## Spring Data JPA Example Project
+
+### Relevant Articles:
diff --git a/persistence-modules/spring-data-jpa-2/pom.xml b/persistence-modules/spring-data-jpa-2/pom.xml
new file mode 100644
index 0000000000..8e46112659
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/pom.xml
@@ -0,0 +1,30 @@
+
+
+ 4.0.0
+ com.baeldung
+ spring-data-jpa-2
+ spring-data-jpa
+
+
+ parent-boot-2
+ com.baeldung
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ com.h2database
+ h2
+
+
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/Application.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/Application.java
new file mode 100644
index 0000000000..3ea3d113da
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/Application.java
@@ -0,0 +1,13 @@
+package com.baeldung;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/entity/Fruit.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/entity/Fruit.java
new file mode 100644
index 0000000000..f82022e67e
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/entity/Fruit.java
@@ -0,0 +1,38 @@
+package com.baeldung.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Fruit {
+
+ @Id
+ private long id;
+ private String name;
+ private String color;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/repository/FruitRepository.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/repository/FruitRepository.java
new file mode 100644
index 0000000000..9f92909b66
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/repository/FruitRepository.java
@@ -0,0 +1,27 @@
+package com.baeldung.repository;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.entity.Fruit;
+
+@Repository
+public interface FruitRepository extends JpaRepository {
+
+ Long deleteByName(String name);
+
+ List deleteByColor(String color);
+
+ Long removeByName(String name);
+
+ List removeByColor(String color);
+
+ @Modifying
+ @Query("delete from Fruit f where f.name=:name or f.color=:color")
+ List deleteFruits(@Param("name") String name, @Param("color") String color);
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-2/src/main/resources/application.properties
new file mode 100644
index 0000000000..72fc330767
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/resources/application.properties
@@ -0,0 +1 @@
+spring.jpa.show-sql=true
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/repository/FruitRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/repository/FruitRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..866cf1157b
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/repository/FruitRepositoryIntegrationTest.java
@@ -0,0 +1,77 @@
+package com.baeldung.repository;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baeldung.entity.Fruit;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+class FruitRepositoryIntegrationTest {
+
+ @Autowired
+ private FruitRepository fruitRepository;
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenDeletedByColor_ThenDeletedFruitsShouldReturn() {
+
+ List fruits = fruitRepository.deleteByColor("green");
+
+ assertEquals("number of fruits are not matching", 2, fruits.size());
+ fruits.forEach(fruit -> assertEquals("Its not a green fruit", "green", fruit.getColor()));
+ }
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenDeletedByName_ThenDeletedFruitCountShouldReturn() {
+
+ Long deletedFruitCount = fruitRepository.deleteByName("apple");
+
+ assertEquals("deleted fruit count is not matching", 1, deletedFruitCount.intValue());
+ }
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenRemovedByColor_ThenDeletedFruitsShouldReturn() {
+
+ List fruits = fruitRepository.removeByColor("green");
+
+ assertEquals("number of fruits are not matching", 2, fruits.size());
+ fruits.forEach(fruit -> assertEquals("Its not a green fruit", "green", fruit.getColor()));
+ }
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenRemovedByName_ThenDeletedFruitCountShouldReturn() {
+
+ Long deletedFruitCount = fruitRepository.removeByName("apple");
+
+ assertEquals("deleted fruit count is not matching", 1, deletedFruitCount.intValue());
+ }
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenDeletedByColorOrName_ThenDeletedFruitsShouldReturn() {
+
+ List fruits = fruitRepository.deleteFruits("apple", "green");
+
+ assertEquals("number of fruits are not matching", 3, fruits.size());
+ fruits.forEach(fruit -> assertTrue("Its not a green fruit or apple", ("green".equals(fruit.getColor())) || "apple".equals(fruit.getColor())));
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-2/src/test/resources/test-fruit-data.sql b/persistence-modules/spring-data-jpa-2/src/test/resources/test-fruit-data.sql
new file mode 100644
index 0000000000..ce2189121f
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/test/resources/test-fruit-data.sql
@@ -0,0 +1,4 @@
+insert into fruit(id,name,color) values (1,'apple','red');
+insert into fruit(id,name,color) values (2,'custard apple','green');
+insert into fruit(id,name,color) values (3,'mango','yellow');
+insert into fruit(id,name,color) values (4,'guava','green');
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa/README.md b/persistence-modules/spring-data-jpa/README.md
index 9512ad336d..48c3180262 100644
--- a/persistence-modules/spring-data-jpa/README.md
+++ b/persistence-modules/spring-data-jpa/README.md
@@ -22,6 +22,7 @@
- [Spring Data JPA Query by Example](https://www.baeldung.com/spring-data-query-by-example)
- [DB Integration Tests with Spring Boot and Testcontainers](https://www.baeldung.com/spring-boot-testcontainers-integration-test)
- [Spring Data JPA @Modifying Annotation](https://www.baeldung.com/spring-data-jpa-modifying-annotation)
+- [Spring Data JPA Batch Inserts](https://www.baeldung.com/spring-data-jpa-batch-inserts)
### Eclipse Config
After importing the project into Eclipse, you may see the following error:
diff --git a/persistence-modules/spring-data-jpa/pom.xml b/persistence-modules/spring-data-jpa/pom.xml
index 401f4877ac..c512994931 100644
--- a/persistence-modules/spring-data-jpa/pom.xml
+++ b/persistence-modules/spring-data-jpa/pom.xml
@@ -53,6 +53,15 @@
spring-security-test
test
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
com.google.guava
diff --git a/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/CustomerController.java b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/CustomerController.java
new file mode 100644
index 0000000000..7623d4d166
--- /dev/null
+++ b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/CustomerController.java
@@ -0,0 +1,43 @@
+package com.baeldung.batchinserts;
+
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.batchinserts.model.Customer;
+import com.baeldung.batchinserts.repository.CustomerRepository;
+
+/**
+ * A simple controller to test the JPA CrudRepository operations
+ * controllers
+ *
+ * @author ysharma2512
+ *
+ */
+@RestController
+public class CustomerController {
+
+ @Autowired
+ CustomerRepository customerRepository;
+
+ public CustomerController(CustomerRepository customerRepository2) {
+ this.customerRepository = customerRepository2;
+ }
+
+ @PostMapping("/customers")
+ public ResponseEntity> insertCustomers() throws URISyntaxException {
+ Customer c1 = new Customer("James", "Gosling");
+ Customer c2 = new Customer("Doug", "Lea");
+ Customer c3 = new Customer("Martin", "Fowler");
+ Customer c4 = new Customer("Brian", "Goetz");
+ List customers = Arrays.asList(c1, c2, c3, c4);
+ customerRepository.saveAll(customers);
+ return ResponseEntity.ok(customers);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/model/Customer.java b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/model/Customer.java
new file mode 100644
index 0000000000..4d82cf12a2
--- /dev/null
+++ b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/model/Customer.java
@@ -0,0 +1,56 @@
+package com.baeldung.batchinserts.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+/**
+ * Customer Entity class
+ * @author ysharma2512
+ *
+ */
+@Entity
+public class Customer {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+ private String firstName;
+ private String lastName;
+
+ public Customer(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/repository/CustomerRepository.java b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/repository/CustomerRepository.java
new file mode 100644
index 0000000000..ab0214bade
--- /dev/null
+++ b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/repository/CustomerRepository.java
@@ -0,0 +1,15 @@
+package com.baeldung.batchinserts.repository;
+
+import org.springframework.data.repository.CrudRepository;
+
+import com.baeldung.batchinserts.model.Customer;
+
+/**
+ * JPA CrudRepository interface
+ *
+ * @author ysharma2512
+ *
+ */
+public interface CustomerRepository extends CrudRepository{
+
+}
diff --git a/persistence-modules/spring-data-jpa/src/main/resources/application.properties b/persistence-modules/spring-data-jpa/src/main/resources/application.properties
index 37fb9ca9c4..239f81db7b 100644
--- a/persistence-modules/spring-data-jpa/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa/src/main/resources/application.properties
@@ -14,4 +14,9 @@ hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFa
spring.datasource.data=import_entities.sql
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
+spring.main.allow-bean-definition-overriding=true
+
+spring.jpa.properties.hibernate.jdbc.batch_size=4
+spring.jpa.properties.hibernate.order_inserts=true
+spring.jpa.properties.hibernate.order_updates=true
+spring.jpa.properties.hibernate.generate_statistics=true
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java b/persistence-modules/spring-data-jpa/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java
new file mode 100644
index 0000000000..f60e0d21bf
--- /dev/null
+++ b/persistence-modules/spring-data-jpa/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java
@@ -0,0 +1,44 @@
+package com.baeldung.batchinserts;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import com.baeldung.batchinserts.CustomerController;
+import com.baeldung.batchinserts.repository.CustomerRepository;
+import com.baeldung.config.PersistenceConfiguration;
+import com.baeldung.config.PersistenceProductConfiguration;
+import com.baeldung.config.PersistenceUserConfiguration;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@AutoConfigureMockMvc
+@ContextConfiguration(classes = { PersistenceConfiguration.class, PersistenceProductConfiguration.class, PersistenceUserConfiguration.class })
+public class BatchInsertIntegrationTest {
+
+ @Autowired
+ private CustomerRepository customerRepository;
+ private MockMvc mockMvc;
+ @Before
+ public void setUp() throws Exception {
+ mockMvc = MockMvcBuilders.standaloneSetup( new CustomerController(customerRepository))
+ .build();
+ }
+
+ @Test
+ public void whenInsertingCustomers_thenCustomersAreCreated() throws Exception {
+ this.mockMvc.perform(post("/customers"))
+ .andExpect(status().isOk());
+ }
+
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 826b8eea63..615fc7b46e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -377,6 +377,7 @@
core-groovy
+
core-java-8
@@ -393,7 +394,8 @@
core-java-networking
core-java-perf
core-java-sun
- core-java
+ core-java
+ core-java-jvm
core-scala
couchbase
custom-pmd
@@ -435,9 +437,11 @@
immutables
jackson
+ jackson-2
java-collections-conversions
java-collections-maps
+
java-lite
java-numbers
@@ -573,6 +577,7 @@
spring-4
spring-5
+ spring-5-webflux
spring-5-mvc
spring-5-reactive
spring-5-reactive-client
@@ -1077,6 +1082,7 @@
immutables
jackson
+ jackson-2
java-collections-conversions
java-collections-maps
diff --git a/reactor-core/README.md b/reactor-core/README.md
index 6f90e59894..5f9d2d1bc2 100644
--- a/reactor-core/README.md
+++ b/reactor-core/README.md
@@ -2,3 +2,4 @@
- [Intro To Reactor Core](http://www.baeldung.com/reactor-core)
- [Combining Publishers in Project Reactor](http://www.baeldung.com/reactor-combine-streams)
+- [Programmatically Creating Sequences with Project Reactor](https://www.baeldung.com/flux-sequences-reactor)
diff --git a/rxjava-2/src/test/java/com/baeldung/rxjava/RxJavaHooksManualTest.java b/rxjava-2/src/test/java/com/baeldung/rxjava/RxJavaHooksManualTest.java
new file mode 100644
index 0000000000..b79fa9af22
--- /dev/null
+++ b/rxjava-2/src/test/java/com/baeldung/rxjava/RxJavaHooksManualTest.java
@@ -0,0 +1,84 @@
+package com.baeldung.rxjava;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Test;
+
+import io.reactivex.Observable;
+import io.reactivex.plugins.RxJavaPlugins;
+import io.reactivex.schedulers.Schedulers;
+
+public class RxJavaHooksManualTest {
+
+ private boolean initHookCalled = false;
+ private boolean hookCalled = false;
+
+ @Test
+ public void givenIOScheduler_whenCalled_shouldExecuteTheHooks() {
+
+ RxJavaPlugins.setInitIoSchedulerHandler((scheduler) -> {
+ initHookCalled = true;
+ return scheduler.call();
+ });
+
+ RxJavaPlugins.setIoSchedulerHandler((scheduler) -> {
+ hookCalled = true;
+ return scheduler;
+ });
+
+ Observable.range(1, 10)
+ .map(v -> v * 2)
+ .subscribeOn(Schedulers.io())
+ .test();
+ assertTrue(hookCalled && initHookCalled);
+ }
+
+ @Test
+ public void givenNewThreadScheduler_whenCalled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setInitNewThreadSchedulerHandler((scheduler) -> {
+ initHookCalled = true;
+ return scheduler.call();
+ });
+
+ RxJavaPlugins.setNewThreadSchedulerHandler((scheduler) -> {
+ hookCalled = true;
+ return scheduler;
+ });
+
+ Observable.range(1, 15)
+ .map(v -> v * 2)
+ .subscribeOn(Schedulers.newThread())
+ .test();
+ assertTrue(hookCalled && initHookCalled);
+ }
+
+ @Test
+ public void givenSingleScheduler_whenCalled_shouldExecuteTheHooks() {
+
+ RxJavaPlugins.setInitSingleSchedulerHandler((scheduler) -> {
+ initHookCalled = true;
+ return scheduler.call();
+ });
+
+ RxJavaPlugins.setSingleSchedulerHandler((scheduler) -> {
+ hookCalled = true;
+ return scheduler;
+ });
+
+ Observable.range(1, 10)
+ .map(v -> v * 2)
+ .subscribeOn(Schedulers.single())
+ .test();
+ assertTrue(hookCalled && initHookCalled);
+
+ }
+
+ @After
+ public void reset() {
+ hookCalled = false;
+ initHookCalled = false;
+ RxJavaPlugins.reset();
+ }
+}
diff --git a/rxjava-2/src/test/java/com/baeldung/rxjava/RxJavaHooksUnitTest.java b/rxjava-2/src/test/java/com/baeldung/rxjava/RxJavaHooksUnitTest.java
new file mode 100644
index 0000000000..dd4287a4a9
--- /dev/null
+++ b/rxjava-2/src/test/java/com/baeldung/rxjava/RxJavaHooksUnitTest.java
@@ -0,0 +1,244 @@
+package com.baeldung.rxjava;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Test;
+
+import io.reactivex.Completable;
+import io.reactivex.Flowable;
+import io.reactivex.Maybe;
+import io.reactivex.Observable;
+import io.reactivex.Single;
+import io.reactivex.flowables.ConnectableFlowable;
+import io.reactivex.observables.ConnectableObservable;
+import io.reactivex.plugins.RxJavaPlugins;
+import io.reactivex.schedulers.Schedulers;
+
+public class RxJavaHooksUnitTest {
+
+ private boolean initHookCalled = false;
+ private boolean hookCalled = false;
+
+ @Test
+ public void givenObservable_whenError_shouldExecuteTheHook() {
+ RxJavaPlugins.setErrorHandler(throwable -> {
+ hookCalled = true;
+ });
+
+ Observable.error(new IllegalStateException())
+ .subscribe();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenCompletable_whenAssembled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnCompletableAssembly(completable -> {
+ hookCalled = true;
+ return completable;
+ });
+ Completable.fromSingle(Single.just(1));
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenCompletable_whenSubscribed_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnCompletableSubscribe((completable, observer) -> {
+ hookCalled = true;
+ return observer;
+ });
+
+ Completable.fromSingle(Single.just(1))
+ .test();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenObservable_whenAssembled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnObservableAssembly(observable -> {
+ hookCalled = true;
+ return observable;
+ });
+
+ Observable.range(1, 10);
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenObservable_whenSubscribed_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnObservableSubscribe((observable, observer) -> {
+ hookCalled = true;
+ return observer;
+ });
+
+ Observable.range(1, 10)
+ .test();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenConnectableObservable_whenAssembled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnConnectableObservableAssembly(connectableObservable -> {
+ hookCalled = true;
+ return connectableObservable;
+ });
+
+ ConnectableObservable.range(1, 10)
+ .publish()
+ .connect();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenFlowable_whenAssembled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnFlowableAssembly(flowable -> {
+ hookCalled = true;
+ return flowable;
+ });
+
+ Flowable.range(1, 10);
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenFlowable_whenSubscribed_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnFlowableSubscribe((flowable, observer) -> {
+ hookCalled = true;
+ return observer;
+ });
+
+ Flowable.range(1, 10)
+ .test();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenConnectableFlowable_whenAssembled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnConnectableFlowableAssembly(connectableFlowable -> {
+ hookCalled = true;
+ return connectableFlowable;
+ });
+
+ ConnectableFlowable.range(1, 10)
+ .publish()
+ .connect();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenParallel_whenAssembled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnParallelAssembly(parallelFlowable -> {
+ hookCalled = true;
+ return parallelFlowable;
+ });
+
+ Flowable.range(1, 10)
+ .parallel();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenMaybe_whenAssembled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnMaybeAssembly(maybe -> {
+ hookCalled = true;
+ return maybe;
+ });
+
+ Maybe.just(1);
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenMaybe_whenSubscribed_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnMaybeSubscribe((maybe, observer) -> {
+ hookCalled = true;
+ return observer;
+ });
+
+ Maybe.just(1)
+ .test();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenSingle_whenAssembled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnSingleAssembly(single -> {
+ hookCalled = true;
+ return single;
+ });
+
+ Single.just(1);
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenSingle_whenSubscribed_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setOnSingleSubscribe((single, observer) -> {
+ hookCalled = true;
+ return observer;
+ });
+
+ Single.just(1)
+ .test();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenAnyScheduler_whenCalled_shouldExecuteTheHook() {
+
+ RxJavaPlugins.setScheduleHandler((runnable) -> {
+ hookCalled = true;
+ return runnable;
+ });
+
+ Observable.range(1, 10)
+ .map(v -> v * 2)
+ .subscribeOn(Schedulers.single())
+ .test();
+ hookCalled = false;
+ Observable.range(1, 10)
+ .map(v -> v * 2)
+ .subscribeOn(Schedulers.computation())
+ .test();
+ assertTrue(hookCalled);
+ }
+
+ @Test
+ public void givenComputationScheduler_whenCalled_shouldExecuteTheHooks() {
+
+ RxJavaPlugins.setInitComputationSchedulerHandler((scheduler) -> {
+ initHookCalled = true;
+ return scheduler.call();
+ });
+ RxJavaPlugins.setComputationSchedulerHandler((scheduler) -> {
+ hookCalled = true;
+ return scheduler;
+ });
+
+ Observable.range(1, 10)
+ .map(v -> v * 2)
+ .subscribeOn(Schedulers.computation())
+ .test();
+ assertTrue(hookCalled && initHookCalled);
+ }
+
+ @After
+ public void reset() {
+ initHookCalled = false;
+ hookCalled = false;
+ RxJavaPlugins.reset();
+ }
+}
diff --git a/spring-5-reactive/README.md b/spring-5-reactive/README.md
index ef2f7d07eb..538a15c879 100644
--- a/spring-5-reactive/README.md
+++ b/spring-5-reactive/README.md
@@ -19,3 +19,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Logging a Reactive Sequence](https://www.baeldung.com/spring-reactive-sequence-logging)
- [Testing Reactive Streams Using StepVerifier and TestPublisher](https://www.baeldung.com/reactive-streams-step-verifier-test-publisher)
- [Debugging Reactive Streams in Spring 5](https://www.baeldung.com/spring-debugging-reactive-streams)
+- [Static Content in Spring WebFlux](https://www.baeldung.com/spring-webflux-static-content)
diff --git a/spring-5-webflux/.gitignore b/spring-5-webflux/.gitignore
new file mode 100644
index 0000000000..aa4871eeea
--- /dev/null
+++ b/spring-5-webflux/.gitignore
@@ -0,0 +1,2 @@
+# Files #
+*.log
\ No newline at end of file
diff --git a/spring-5-webflux/pom.xml b/spring-5-webflux/pom.xml
index d7fb7b7930..3306fd1729 100644
--- a/spring-5-webflux/pom.xml
+++ b/spring-5-webflux/pom.xml
@@ -1,23 +1,20 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
-
com.baeldung
spring-5-webflux
1.0-SNAPSHOT
-
spring-5-webflux
-
http://www.baeldung.com
-
- UTF-8
- 1.8
- 1.8
- 2.0.2.RELEASE
-
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
@@ -58,12 +55,8 @@
- maven-compiler-plugin
- 3.8.0
-
-
- maven-surefire-plugin
- 2.22.1
+ org.springframework.boot
+ spring-boot-maven-plugin
diff --git a/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/CustomNettyWebServerFactory.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/CustomNettyWebServerFactory.java
new file mode 100644
index 0000000000..f9de3b4006
--- /dev/null
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/CustomNettyWebServerFactory.java
@@ -0,0 +1,36 @@
+package com.baeldung.spring.serverconfig;
+
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
+import org.springframework.boot.web.embedded.netty.NettyServerCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import reactor.netty.http.server.HttpServer;
+
+@Configuration
+@Profile("skipAutoConfig")
+public class CustomNettyWebServerFactory {
+
+ @Bean
+ public NettyReactiveWebServerFactory nettyReactiveWebServerFactory() {
+ NettyReactiveWebServerFactory webServerFactory = new NettyReactiveWebServerFactory();
+ webServerFactory.addServerCustomizers(new EventLoopNettyCustomizer());
+ return webServerFactory;
+ }
+
+ private static class EventLoopNettyCustomizer implements NettyServerCustomizer {
+
+ @Override
+ public HttpServer apply(HttpServer httpServer) {
+ EventLoopGroup parentGroup = new NioEventLoopGroup();
+ EventLoopGroup childGroup = new NioEventLoopGroup();
+ return httpServer
+ .tcpConfiguration(tcpServer -> tcpServer.bootstrap(
+ serverBootstrap -> serverBootstrap.group(parentGroup, childGroup).channel(NioServerSocketChannel.class)
+ ));
+ }
+ }
+}
diff --git a/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingController.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingController.java
new file mode 100644
index 0000000000..990ea5cf83
--- /dev/null
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingController.java
@@ -0,0 +1,23 @@
+package com.baeldung.spring.serverconfig;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+@RestController
+@RequestMapping("/greet")
+public class GreetingController {
+
+ private final GreetingService greetingService;
+
+ public GreetingController(GreetingService greetingService) {
+ this.greetingService = greetingService;
+ }
+
+ @GetMapping("/{name}")
+ private Mono greet(@PathVariable String name) {
+ return greetingService.greet(name);
+ }
+}
diff --git a/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingService.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingService.java
new file mode 100644
index 0000000000..a6243b2bd0
--- /dev/null
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingService.java
@@ -0,0 +1,12 @@
+package com.baeldung.spring.serverconfig;
+
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+@Service
+public class GreetingService {
+
+ public Mono greet(String name) {
+ return Mono.just("Greeting " + name);
+ }
+}
diff --git a/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactoryPortCustomizer.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactoryPortCustomizer.java
new file mode 100644
index 0000000000..fdde130286
--- /dev/null
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactoryPortCustomizer.java
@@ -0,0 +1,30 @@
+package com.baeldung.spring.serverconfig;
+
+import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
+import org.springframework.boot.web.embedded.netty.NettyServerCustomizer;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.stereotype.Component;
+import reactor.netty.http.server.HttpServer;
+
+@Component
+public class NettyWebServerFactoryPortCustomizer implements WebServerFactoryCustomizer {
+
+ @Override
+ public void customize(NettyReactiveWebServerFactory serverFactory) {
+ serverFactory.addServerCustomizers(new PortCustomizer(8443));
+ }
+
+ private static class PortCustomizer implements NettyServerCustomizer {
+
+ private final int port;
+
+ private PortCustomizer(int port) {
+ this.port = port;
+ }
+
+ @Override
+ public HttpServer apply(HttpServer httpServer) {
+ return httpServer.port(port);
+ }
+ }
+}
diff --git a/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactorySslCustomizer.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactorySslCustomizer.java
new file mode 100644
index 0000000000..cf4e5ac8ea
--- /dev/null
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactorySslCustomizer.java
@@ -0,0 +1,26 @@
+package com.baeldung.spring.serverconfig;
+
+import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
+import org.springframework.boot.web.embedded.netty.SslServerCustomizer;
+import org.springframework.boot.web.server.Http2;
+import org.springframework.boot.web.server.Ssl;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NettyWebServerFactorySslCustomizer implements WebServerFactoryCustomizer {
+
+ @Override
+ public void customize(NettyReactiveWebServerFactory serverFactory) {
+ Ssl ssl = new Ssl();
+ ssl.setEnabled(true);
+ ssl.setKeyStore("classpath:sample.jks");
+ ssl.setKeyAlias("alias");
+ ssl.setKeyPassword("password");
+ ssl.setKeyStorePassword("secret");
+ Http2 http2 = new Http2();
+ http2.setEnabled(false);
+ serverFactory.addServerCustomizers(new SslServerCustomizer(ssl, http2, null));
+ serverFactory.setPort(8443);
+ }
+}
diff --git a/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/ServerConfigApplication.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/ServerConfigApplication.java
new file mode 100644
index 0000000000..b4f6006be3
--- /dev/null
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/ServerConfigApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.spring.serverconfig;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ServerConfigApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ServerConfigApplication.class, args);
+ }
+}
diff --git a/spring-5-webflux/src/main/resources/logback.xml b/spring-5-webflux/src/main/resources/logback.xml
new file mode 100644
index 0000000000..48b68c6bf1
--- /dev/null
+++ b/spring-5-webflux/src/main/resources/logback.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+ %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
+
+
+
+
+
+ netty-access.log
+
+ %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-5-webflux/src/main/resources/sample.jks b/spring-5-webflux/src/main/resources/sample.jks
new file mode 100644
index 0000000000..6aa9a28053
Binary files /dev/null and b/spring-5-webflux/src/main/resources/sample.jks differ
diff --git a/spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerTests.java b/spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerLiveTest.java
similarity index 97%
rename from spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerTests.java
rename to spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerLiveTest.java
index 5112c8ceb2..4c6708e423 100644
--- a/spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerTests.java
+++ b/spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerLiveTest.java
@@ -9,7 +9,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
-public class ResponseStatusControllerTests {
+public class ResponseStatusControllerLiveTest {
@Autowired
private WebTestClient testClient;
diff --git a/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingControllerIntegrationTest.java b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingControllerIntegrationTest.java
new file mode 100644
index 0000000000..ce156beb3f
--- /dev/null
+++ b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingControllerIntegrationTest.java
@@ -0,0 +1,41 @@
+package com.baeldung.spring.serverconfig;
+
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import reactor.core.publisher.Mono;
+
+@RunWith(SpringRunner.class)
+@WebFluxTest
+public class GreetingControllerIntegrationTest {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @MockBean
+ private GreetingService greetingService;
+
+ private final String name = "Baeldung";
+
+ @Before
+ public void setUp() {
+ when(greetingService.greet(name)).thenReturn(Mono.just("Greeting Baeldung"));
+ }
+
+ @Test
+ public void shouldGreet() {
+ webClient.get().uri("/greet/{name}", name)
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody(String.class)
+ .isEqualTo("Greeting Baeldung");
+ }
+}
diff --git a/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingLiveTest.java b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingLiveTest.java
new file mode 100644
index 0000000000..2400272c6e
--- /dev/null
+++ b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingLiveTest.java
@@ -0,0 +1,57 @@
+package com.baeldung.spring.serverconfig;
+
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import javax.net.ssl.SSLException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
+import reactor.netty.http.client.HttpClient;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
+@DirtiesContext
+public class GreetingLiveTest {
+
+ private static final String BASE_URL = "https://localhost:8443";
+
+ private WebTestClient webTestClient;
+
+ @Before
+ public void setup() throws SSLException {
+ webTestClient = WebTestClient.bindToServer(getConnector())
+ .baseUrl(BASE_URL)
+ .build();
+ }
+
+ @Test
+ public void shouldGreet() {
+ final String name = "Baeldung";
+
+ ResponseSpec response = webTestClient.get()
+ .uri("/greet/{name}", name)
+ .exchange();
+
+ response.expectStatus()
+ .isOk()
+ .expectBody(String.class)
+ .isEqualTo("Greeting Baeldung");
+ }
+
+ private ReactorClientHttpConnector getConnector() throws SSLException {
+ SslContext sslContext = SslContextBuilder
+ .forClient()
+ .trustManager(InsecureTrustManagerFactory.INSTANCE)
+ .build();
+ HttpClient httpClient = HttpClient.create().secure(t -> t.sslContext(sslContext));
+ return new ReactorClientHttpConnector(httpClient);
+ }
+}
diff --git a/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingSkipAutoConfigLiveTest.java b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingSkipAutoConfigLiveTest.java
new file mode 100644
index 0000000000..45918dfd70
--- /dev/null
+++ b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingSkipAutoConfigLiveTest.java
@@ -0,0 +1,8 @@
+package com.baeldung.spring.serverconfig;
+
+import org.springframework.test.context.ActiveProfiles;
+
+@ActiveProfiles("skipAutoConfig")
+public class GreetingSkipAutoConfigLiveTest extends GreetingLiveTest {
+
+}
diff --git a/spring-5-webflux/src/test/resources/logback-test.xml b/spring-5-webflux/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..12cedf5952
--- /dev/null
+++ b/spring-5-webflux/src/test/resources/logback-test.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
+
+
+
+
+
+
+
diff --git a/spring-all/src/main/java/org/baeldung/sampleabstract/BallService.java b/spring-all/src/main/java/org/baeldung/sampleabstract/BallService.java
new file mode 100644
index 0000000000..9a75de7fa1
--- /dev/null
+++ b/spring-all/src/main/java/org/baeldung/sampleabstract/BallService.java
@@ -0,0 +1,28 @@
+package org.baeldung.sampleabstract;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.annotation.PostConstruct;
+
+public abstract class BallService {
+
+ private RuleRepository ruleRepository;
+
+ private LogRepository logRepository;
+
+ public BallService(RuleRepository ruleRepository) {
+ this.ruleRepository = ruleRepository;
+ }
+
+ @Autowired
+ public final void setLogRepository(LogRepository logRepository) {
+ this.logRepository = logRepository;
+ }
+
+ @PostConstruct
+ public void afterInitialize() {
+
+ System.out.println(ruleRepository.toString());
+ System.out.println(logRepository.toString());
+ }
+}
diff --git a/spring-all/src/main/java/org/baeldung/sampleabstract/BasketballService.java b/spring-all/src/main/java/org/baeldung/sampleabstract/BasketballService.java
new file mode 100644
index 0000000000..c117231d3c
--- /dev/null
+++ b/spring-all/src/main/java/org/baeldung/sampleabstract/BasketballService.java
@@ -0,0 +1,13 @@
+package org.baeldung.sampleabstract;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class BasketballService extends BallService {
+
+ @Autowired
+ public BasketballService(RuleRepository ruleRepository) {
+ super(ruleRepository);
+ }
+}
diff --git a/spring-all/src/main/java/org/baeldung/sampleabstract/DemoApp.java b/spring-all/src/main/java/org/baeldung/sampleabstract/DemoApp.java
new file mode 100644
index 0000000000..615d354ecf
--- /dev/null
+++ b/spring-all/src/main/java/org/baeldung/sampleabstract/DemoApp.java
@@ -0,0 +1,18 @@
+package org.baeldung.sampleabstract;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ComponentScan(basePackages = "org.baeldung.sampleabstract")
+public class DemoApp {
+
+
+ public static void main(String[] args) {
+
+ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(DemoApp.class);
+ }
+
+}
diff --git a/spring-all/src/main/java/org/baeldung/sampleabstract/LogRepository.java b/spring-all/src/main/java/org/baeldung/sampleabstract/LogRepository.java
new file mode 100644
index 0000000000..3a65671493
--- /dev/null
+++ b/spring-all/src/main/java/org/baeldung/sampleabstract/LogRepository.java
@@ -0,0 +1,12 @@
+package org.baeldung.sampleabstract;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class LogRepository {
+
+ @Override
+ public String toString() {
+ return "logRepository";
+ }
+}
diff --git a/spring-all/src/main/java/org/baeldung/sampleabstract/RuleRepository.java b/spring-all/src/main/java/org/baeldung/sampleabstract/RuleRepository.java
new file mode 100644
index 0000000000..fd42178ab6
--- /dev/null
+++ b/spring-all/src/main/java/org/baeldung/sampleabstract/RuleRepository.java
@@ -0,0 +1,12 @@
+package org.baeldung.sampleabstract;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class RuleRepository {
+
+ @Override
+ public String toString() {
+ return "ruleRepository";
+ }
+}
diff --git a/spring-boot-angular-ecommerce/src/main/frontend/package.json b/spring-boot-angular-ecommerce/src/main/frontend/package.json
index 958e9f1023..17363ef961 100644
--- a/spring-boot-angular-ecommerce/src/main/frontend/package.json
+++ b/spring-boot-angular-ecommerce/src/main/frontend/package.json
@@ -7,7 +7,7 @@
"build": "ng build",
"postbuild": "npm run deploy",
"predeploy": "rimraf ../resources/static/ && mkdirp ../resources/static",
- "deploy": "copyfiles -f dist/** ../resources/static",
+ "deploy": "copyfiles -f dist/frontend/** ../resources/static",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
diff --git a/spring-boot-angular/README.md b/spring-boot-angular/README.md
new file mode 100644
index 0000000000..cfc1ea69f4
--- /dev/null
+++ b/spring-boot-angular/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+- [Building a Web Application with Spring Boot and Angular](https://www.baeldung.com/spring-boot-angular-web)
+
diff --git a/spring-boot-data/pom.xml b/spring-boot-data/pom.xml
index 9ef4cc69c8..8735a54e7b 100644
--- a/spring-boot-data/pom.xml
+++ b/spring-boot-data/pom.xml
@@ -119,4 +119,4 @@
2.2.4
-
\ No newline at end of file
+
diff --git a/spring-boot-ops/README.md b/spring-boot-ops/README.md
index 553b02ebf0..d5063f14e5 100644
--- a/spring-boot-ops/README.md
+++ b/spring-boot-ops/README.md
@@ -10,3 +10,5 @@
- [Spring Boot Console Application](http://www.baeldung.com/spring-boot-console-app)
- [Comparing Embedded Servlet Containers in Spring Boot](http://www.baeldung.com/spring-boot-servlet-containers)
- [Programmatically Restarting a Spring Boot Application](https://www.baeldung.com/java-restart-spring-boot-app)
+ - [Spring Properties File Outside jar](https://www.baeldung.com/spring-properties-file-outside-jar)
+
diff --git a/spring-boot-rest/README.md b/spring-boot-rest/README.md
index 8fbc9527b8..af372077f0 100644
--- a/spring-boot-rest/README.md
+++ b/spring-boot-rest/README.md
@@ -10,3 +10,4 @@ Module for the articles that are part of the Spring REST E-book:
8. [Http Message Converters with the Spring Framework](http://www.baeldung.com/spring-httpmessageconverter-rest)
9. [ETags for REST with Spring](http://www.baeldung.com/etags-for-rest-with-spring)
10. [Testing REST with multiple MIME types](http://www.baeldung.com/testing-rest-api-with-multiple-media-types)
+11. [Testing Web APIs with Postman Collections](https://www.baeldung.com/postman-testing-collections)
diff --git a/spring-boot-rest/src/main/java/com/baeldung/persistence/IOperations.java b/spring-boot-rest/src/main/java/com/baeldung/persistence/IOperations.java
index 1cc732ab08..fbbba23013 100644
--- a/spring-boot-rest/src/main/java/com/baeldung/persistence/IOperations.java
+++ b/spring-boot-rest/src/main/java/com/baeldung/persistence/IOperations.java
@@ -9,7 +9,7 @@ public interface IOperations {
// read - one
- T findOne(final long id);
+ T findById(final long id);
// read - all
diff --git a/spring-boot-rest/src/main/java/com/baeldung/persistence/service/common/AbstractService.java b/spring-boot-rest/src/main/java/com/baeldung/persistence/service/common/AbstractService.java
index 5900c443b8..f589eaecf5 100644
--- a/spring-boot-rest/src/main/java/com/baeldung/persistence/service/common/AbstractService.java
+++ b/spring-boot-rest/src/main/java/com/baeldung/persistence/service/common/AbstractService.java
@@ -18,9 +18,8 @@ public abstract class AbstractService implements IOperat
@Override
@Transactional(readOnly = true)
- public T findOne(final long id) {
- return getDao().findById(id)
- .get();
+ public T findById(final long id) {
+ return getDao().findById(id).orElse(null);
}
// read - all
diff --git a/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java b/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java
index 4b876a8338..ab16b61e1d 100644
--- a/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java
+++ b/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java
@@ -55,7 +55,7 @@ public class WebConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean shallowEtagHeaderFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>( new ShallowEtagHeaderFilter());
- filterRegistrationBean.addUrlPatterns("/auth/foos/*");
+ filterRegistrationBean.addUrlPatterns("/foos/*");
filterRegistrationBean.setName("etagFilter");
return filterRegistrationBean;
}
diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java b/spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java
index 255fcaabb7..8174480078 100644
--- a/spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java
+++ b/spring-boot-rest/src/main/java/com/baeldung/web/controller/FooController.java
@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.util.UriComponentsBuilder;
import com.baeldung.persistence.model.Foo;
@@ -32,7 +33,7 @@ import com.baeldung.web.util.RestPreconditions;
import com.google.common.base.Preconditions;
@RestController
-@RequestMapping(value = "/auth/foos")
+@RequestMapping(value = "/foos")
public class FooController {
@Autowired
@@ -51,22 +52,29 @@ public class FooController {
@GetMapping(value = "/{id}/custom-etag")
public ResponseEntity findByIdWithCustomEtag(@PathVariable("id") final Long id,
final HttpServletResponse response) {
- final Foo resourceById = RestPreconditions.checkFound(service.findOne(id));
+ final Foo foo = RestPreconditions.checkFound(service.findById(id));
eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
return ResponseEntity.ok()
- .eTag(Long.toString(resourceById.getVersion()))
- .body(resourceById);
+ .eTag(Long.toString(foo.getVersion()))
+ .body(foo);
}
// read - one
@GetMapping(value = "/{id}")
public Foo findById(@PathVariable("id") final Long id, final HttpServletResponse response) {
- final Foo resourceById = RestPreconditions.checkFound(service.findOne(id));
+ try {
+ final Foo resourceById = RestPreconditions.checkFound(service.findById(id));
+
+ eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
+ return resourceById;
+ }
+ catch (MyResourceNotFoundException exc) {
+ throw new ResponseStatusException(
+ HttpStatus.NOT_FOUND, "Foo Not Found", exc);
+ }
- eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
- return resourceById;
}
// read - all
@@ -120,7 +128,7 @@ public class FooController {
@ResponseStatus(HttpStatus.OK)
public void update(@PathVariable("id") final Long id, @RequestBody final Foo resource) {
Preconditions.checkNotNull(resource);
- RestPreconditions.checkFound(service.findOne(resource.getId()));
+ RestPreconditions.checkFound(service.findById(resource.getId()));
service.update(resource);
}
diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/controller/RootController.java b/spring-boot-rest/src/main/java/com/baeldung/web/controller/RootController.java
index 436e41e8eb..d618e9f0bf 100644
--- a/spring-boot-rest/src/main/java/com/baeldung/web/controller/RootController.java
+++ b/spring-boot-rest/src/main/java/com/baeldung/web/controller/RootController.java
@@ -7,34 +7,28 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.util.UriTemplate;
import com.baeldung.web.util.LinkUtil;
@Controller
-@RequestMapping(value = "/auth/")
public class RootController {
- public RootController() {
- super();
- }
-
// API
// discover
- @RequestMapping(value = "admin", method = RequestMethod.GET)
+ @GetMapping("/")
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void adminRoot(final HttpServletRequest request, final HttpServletResponse response) {
final String rootUri = request.getRequestURL()
.toString();
- final URI fooUri = new UriTemplate("{rootUri}/{resource}").expand(rootUri, "foo");
- final String linkToFoo = LinkUtil.createLinkHeader(fooUri.toASCIIString(), "collection");
- response.addHeader("Link", linkToFoo);
+ final URI fooUri = new UriTemplate("{rootUri}{resource}").expand(rootUri, "foos");
+ final String linkToFoos = LinkUtil.createLinkHeader(fooUri.toASCIIString(), "collection");
+ response.addHeader("Link", linkToFoos);
}
}
diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/controller/students/Student.java b/spring-boot-rest/src/main/java/com/baeldung/web/controller/students/Student.java
new file mode 100644
index 0000000000..3b6a5c0298
--- /dev/null
+++ b/spring-boot-rest/src/main/java/com/baeldung/web/controller/students/Student.java
@@ -0,0 +1,53 @@
+package com.baeldung.web.controller.students;
+
+public class Student {
+
+ private long id;
+ private String firstName;
+ private String lastName;
+
+ public Student() {}
+
+ public Student(String firstName, String lastName) {
+ super();
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public Student(long id, String firstName, String lastName) {
+ super();
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return "Student [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + "]";
+ }
+
+}
diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/controller/students/StudentController.java b/spring-boot-rest/src/main/java/com/baeldung/web/controller/students/StudentController.java
new file mode 100644
index 0000000000..f937e0c757
--- /dev/null
+++ b/spring-boot-rest/src/main/java/com/baeldung/web/controller/students/StudentController.java
@@ -0,0 +1,73 @@
+package com.baeldung.web.controller.students;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+import com.baeldung.web.controller.students.StudentService;
+
+@RestController
+@RequestMapping("/students")
+public class StudentController {
+
+ @Autowired
+ private StudentService service;
+
+ @GetMapping("/")
+ public List read() {
+ return service.readAll();
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity read(@PathVariable("id") Long id) {
+ Student foundStudent = service.read(id);
+ if (foundStudent == null) {
+ return ResponseEntity.notFound().build();
+ } else {
+ return ResponseEntity.ok(foundStudent);
+ }
+ }
+
+ @PostMapping("/")
+ public ResponseEntity create(@RequestBody Student student) throws URISyntaxException {
+ Student createdStudent = service.create(student);
+
+ URI uri = ServletUriComponentsBuilder.fromCurrentRequest()
+ .path("/{id}")
+ .buildAndExpand(createdStudent.getId())
+ .toUri();
+
+ return ResponseEntity.created(uri)
+ .body(createdStudent);
+
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity update(@RequestBody Student student, @PathVariable Long id) {
+ Student updatedStudent = service.update(id, student);
+ if (updatedStudent == null) {
+ return ResponseEntity.notFound().build();
+ } else {
+ return ResponseEntity.ok(updatedStudent);
+ }
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity