diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy
new file mode 100644
index 0000000000..c0c8c98392
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy
@@ -0,0 +1,40 @@
+package com.baeldung.xml
+
+import groovy.xml.MarkupBuilder
+import groovy.xml.XmlUtil
+import spock.lang.Specification
+
+class MarkupBuilderUnitTest extends Specification {
+
+ def xmlFile = getClass().getResource("articles_short_formatted.xml")
+
+def "Should create XML properly"() {
+ given: "Node structures"
+
+ when: "Using MarkupBuilderUnitTest to create com.baeldung.xml structure"
+ def writer = new StringWriter()
+ new MarkupBuilder(writer).articles {
+ article {
+ title('First steps in Java')
+ author(id: '1') {
+ firstname('Siena')
+ lastname('Kerr')
+ }
+ 'release-date'('2018-12-01')
+ }
+ article {
+ title('Dockerize your SpringBoot application')
+ author(id: '2') {
+ firstname('Jonas')
+ lastname('Lugo')
+ }
+ 'release-date'('2018-12-01')
+ }
+ }
+
+ then: "Xml is created properly"
+ XmlUtil.serialize(writer.toString()) == XmlUtil.serialize(xmlFile.text)
+}
+
+
+}
diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy
new file mode 100644
index 0000000000..ada47406a1
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy
@@ -0,0 +1,94 @@
+package com.baeldung.xml
+
+
+import spock.lang.Shared
+import spock.lang.Specification
+
+class XmlParserUnitTest extends Specification {
+
+ def xmlFile = getClass().getResourceAsStream("articles.xml")
+
+ @Shared
+ def parser = new XmlParser()
+
+ def "Should read XML file properly"() {
+ given: "XML file"
+
+ when: "Using XmlParser to read file"
+ def articles = parser.parse(xmlFile)
+
+ then: "Xml is loaded properly"
+ articles.'*'.size() == 4
+ articles.article[0].author.firstname.text() == "Siena"
+ articles.article[2].'release-date'.text() == "2018-06-12"
+ articles.article[3].title.text() == "Java 12 insights"
+ articles.article.find { it.author.'@id'.text() == "3" }.author.firstname.text() == "Daniele"
+ }
+
+
+ def "Should add node to existing com.baeldung.xml using NodeBuilder"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Adding node to com.baeldung.xml"
+ def articleNode = new NodeBuilder().article(id: '5') {
+ title('Traversing XML in the nutshell')
+ author {
+ firstname('Martin')
+ lastname('Schmidt')
+ }
+ 'release-date'('2019-05-18')
+ }
+ articles.append(articleNode)
+
+ then: "Node is added to com.baeldung.xml properly"
+ articles.'*'.size() == 5
+ articles.article[4].title.text() == "Traversing XML in the nutshell"
+ }
+
+ def "Should replace node"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Adding node to com.baeldung.xml"
+ def articleNode = new NodeBuilder().article(id: '5') {
+ title('Traversing XML in the nutshell')
+ author {
+ firstname('Martin')
+ lastname('Schmidt')
+ }
+ 'release-date'('2019-05-18')
+ }
+ articles.article[0].replaceNode(articleNode)
+
+ then: "Node is added to com.baeldung.xml properly"
+ articles.'*'.size() == 4
+ articles.article[0].title.text() == "Traversing XML in the nutshell"
+ }
+
+ def "Should modify node"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Changing value of one of the nodes"
+ articles.article.each { it.'release-date'[0].value = "2019-05-18" }
+
+ then: "XML is updated"
+ articles.article.findAll { it.'release-date'.text() != "2019-05-18" }.isEmpty()
+ }
+
+ def "Should remove article from com.baeldung.xml"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Removing all articles but with id==3"
+ articles.article
+ .findAll { it.author.'@id'.text() != "3" }
+ .each { articles.remove(it) }
+
+ then: "There is only one article left"
+ articles.children().size() == 1
+ articles.article[0].author.'@id'.text() == "3"
+ }
+
+}
diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy
new file mode 100644
index 0000000000..ffeaa46fce
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy
@@ -0,0 +1,102 @@
+package com.baeldung.xml
+
+
+import groovy.xml.XmlUtil
+import spock.lang.Shared
+import spock.lang.Specification
+
+class XmlSlurperUnitTest extends Specification {
+
+ def xmlFile = getClass().getResourceAsStream("articles.xml")
+
+ @Shared
+ def parser = new XmlSlurper()
+
+ def "Should read XML file properly"() {
+ given: "XML file"
+
+ when: "Using XmlSlurper to read file"
+ def articles = parser.parse(xmlFile)
+
+ then: "Xml is loaded properly"
+ articles.'*'.size() == 4
+ articles.article[0].author.firstname == "Siena"
+ articles.article[2].'release-date' == "2018-06-12"
+ articles.article[3].title == "Java 12 insights"
+ articles.article.find { it.author.'@id' == "3" }.author.firstname == "Daniele"
+ }
+
+ def "Should add node to existing com.baeldung.xml"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Adding node to com.baeldung.xml"
+ articles.appendNode {
+ article(id: '5') {
+ title('Traversing XML in the nutshell')
+ author {
+ firstname('Martin')
+ lastname('Schmidt')
+ }
+ 'release-date'('2019-05-18')
+ }
+ }
+
+ articles = parser.parseText(XmlUtil.serialize(articles))
+
+ then: "Node is added to com.baeldung.xml properly"
+ articles.'*'.size() == 5
+ articles.article[4].title == "Traversing XML in the nutshell"
+ }
+
+ def "Should modify node"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Changing value of one of the nodes"
+ articles.article.each { it.'release-date' = "2019-05-18" }
+
+ then: "XML is updated"
+ articles.article.findAll { it.'release-date' != "2019-05-18" }.isEmpty()
+ }
+
+ def "Should replace node"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Replacing node"
+ articles.article[0].replaceNode {
+ article(id: '5') {
+ title('Traversing XML in the nutshell')
+ author {
+ firstname('Martin')
+ lastname('Schmidt')
+ }
+ 'release-date'('2019-05-18')
+ }
+ }
+
+ articles = parser.parseText(XmlUtil.serialize(articles))
+
+ then: "Node is replaced properly"
+ articles.'*'.size() == 4
+ articles.article[0].title == "Traversing XML in the nutshell"
+ }
+
+ def "Should remove article from com.baeldung.xml"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Removing all articles but with id==3"
+ articles.article
+ .findAll { it.author.'@id' != "3" }
+ .replaceNode {}
+
+ articles = parser.parseText(XmlUtil.serialize(articles))
+
+ then: "There is only one article left"
+ articles.children().size() == 1
+ articles.article[0].author.'@id' == "3"
+ }
+
+}
diff --git a/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml b/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml
new file mode 100644
index 0000000000..ef057405f5
--- /dev/null
+++ b/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml
@@ -0,0 +1,34 @@
+
+
+ First steps in Java
+
+ Siena
+ Kerr
+
+ 2018-12-01
+
+
+ Dockerize your SpringBoot application
+
+ Jonas
+ Lugo
+
+ 2018-12-01
+
+
+ SpringBoot tutorial
+
+ Daniele
+ Ferguson
+
+ 2018-06-12
+
+
+ Java 12 insights
+
+ Siena
+ Kerr
+
+ 2018-07-22
+
+
diff --git a/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml b/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml
new file mode 100644
index 0000000000..6492020e03
--- /dev/null
+++ b/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml
@@ -0,0 +1,18 @@
+
+
+ First steps in Java
+
+ Siena
+ Kerr
+
+ 2018-12-01
+
+
+ Dockerize your SpringBoot application
+
+ Jonas
+ Lugo
+
+ 2018-12-01
+
+
diff --git a/core-java-lang/src/main/java/com/baeldung/error/ErrorGenerator.java b/core-java-lang/src/main/java/com/baeldung/error/ErrorGenerator.java
deleted file mode 100644
index 58cbe22df5..0000000000
--- a/core-java-lang/src/main/java/com/baeldung/error/ErrorGenerator.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.baeldung.error;
-
-public class ErrorGenerator {
- public void throwException() throws Exception {
- throw new Exception("checked");
- }
-
- public void throwRuntimeException() {
- throw new RuntimeException("unchecked");
- }
-
- public void throwError() {
- throw new Error("unchecked");
- }
-}
diff --git a/core-java-lang/src/test/java/com/baeldung/error/ErrorGeneratorUnitTest.java b/core-java-lang/src/test/java/com/baeldung/error/ErrorGeneratorUnitTest.java
deleted file mode 100644
index 2a7c24f5fa..0000000000
--- a/core-java-lang/src/test/java/com/baeldung/error/ErrorGeneratorUnitTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.baeldung.error;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ErrorGeneratorUnitTest {
-
- private ErrorGenerator errorGenerator;
-
- @Before
- public void setUp() {
- errorGenerator = new ErrorGenerator();
- }
-
- @Test
- public void whenCheckedException_thenIsCaughtByCatchException() {
- try {
- errorGenerator.throwException();
- } catch (Exception e) {
- // caught! -> test pass
- }
- }
-
- @Test
- public void whenUncheckedException_thenIsCaughtByCatchException() {
- try {
- errorGenerator.throwRuntimeException();
- } catch (Exception e) {
- // caught! -> test pass
- }
- }
-
- @Test(expected = Error.class)
- public void whenError_thenIsNotCaughtByCatchException() {
- try {
- errorGenerator.throwError();
- } catch (Exception e) {
- Assert.fail(); // errors are not caught by catch exception
- }
- }
-
- @Test
- public void whenError_thenIsCaughtByCatchError() {
- try {
- errorGenerator.throwError();
- } catch (Error e) {
- // caught! -> test pass
- }
- }
-}
\ No newline at end of file
diff --git a/core-java-modules/core-java-8-2/pom.xml b/core-java-modules/core-java-8-2/pom.xml
index fbaf795b95..ff2e290086 100644
--- a/core-java-modules/core-java-8-2/pom.xml
+++ b/core-java-modules/core-java-8-2/pom.xml
@@ -22,6 +22,9 @@
1.8
1.8
64.2
+ 5.4.0.Final
+ 1.4.197
+ 2.9.8
@@ -30,7 +33,21 @@
icu4j
${icu.version}
-
+
+ org.hibernate
+ hibernate-core
+ ${hibernate.core.version}
+
+
+ com.h2database
+ h2
+ ${h2database.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.databind.version}
+
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/HandleOptionalTypeExample.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/HandleOptionalTypeExample.java
new file mode 100644
index 0000000000..c472bab077
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/HandleOptionalTypeExample.java
@@ -0,0 +1,41 @@
+package com.baeldung.optionalReturnType;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class HandleOptionalTypeExample {
+ static Map usersByName = new HashMap();
+ static {
+ User user1 = new User();
+ user1.setUserId(1l);
+ user1.setFirstName("baeldung");
+ usersByName.put("baeldung", user1);
+ }
+
+ public static void main(String[] args) {
+ changeUserName("baeldung", "baeldung-new");
+ changeUserName("user", "user-new");
+ }
+
+ public static void changeUserName(String oldFirstName, String newFirstName) {
+ Optional userOpt = findUserByName(oldFirstName);
+ if (userOpt.isPresent()) {
+ User user = userOpt.get();
+ user.setFirstName(newFirstName);
+
+ System.out.println("user with name " + oldFirstName + " is changed to " + user.getFirstName());
+ } else {
+ // user is missing
+ System.out.println("user with name " + oldFirstName + " is not found.");
+ }
+ }
+
+ public static Optional findUserByName(String name) {
+ // look up the user in the database, the user object below could be null
+ User user = usersByName.get(name);
+ Optional opt = Optional.ofNullable(user);
+
+ return opt;
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/OptionalToJsonExample.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/OptionalToJsonExample.java
new file mode 100644
index 0000000000..b44a35fae1
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/OptionalToJsonExample.java
@@ -0,0 +1,19 @@
+package com.baeldung.optionalReturnType;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class OptionalToJsonExample {
+ public static void main(String[] args) {
+ UserOptional user = new UserOptional();
+ user.setUserId(1l);
+ user.setFirstName("Bael Dung");
+
+ ObjectMapper om = new ObjectMapper();
+ try {
+ System.out.print("user in json is:" + om.writeValueAsString(user));
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample.java
new file mode 100644
index 0000000000..85c96b9bc3
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample.java
@@ -0,0 +1,26 @@
+package com.baeldung.optionalReturnType;
+
+import java.util.Optional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+public class PersistOptionalTypeExample {
+ static String persistenceUnit = "com.baeldung.optionalReturnType";
+ static EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnit);
+
+ static EntityManager entityManager = emf.createEntityManager();
+
+ // to run this app, uncomment the follow line in META-INF/persistence.xml
+ // com.baeldung.optionalReturnType.UserOptionalField
+ public static void main(String[] args) {
+ UserOptionalField user1 = new UserOptionalField();
+ user1.setUserId(1l);
+ user1.setFirstName(Optional.of("Bael Dung"));
+ entityManager.persist(user1);
+
+ UserOptional user2 = entityManager.find(UserOptional.class, 1l);
+ System.out.print("User2.firstName:" + user2.getFirstName());
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample2.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample2.java
new file mode 100644
index 0000000000..3114e7cefb
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample2.java
@@ -0,0 +1,22 @@
+package com.baeldung.optionalReturnType;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+public class PersistOptionalTypeExample2 {
+ static String persistenceUnit = "com.baeldung.optionalReturnType";
+ static EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnit);
+
+ static EntityManager em = emf.createEntityManager();
+
+ public static void main(String[] args) {
+ UserOptional user1 = new UserOptional();
+ user1.setUserId(1l);
+ user1.setFirstName("Bael Dung");
+ em.persist(user1);
+
+ UserOptional user2 = em.find(UserOptional.class, 1l);
+ System.out.print("User2.firstName:" + user2.getFirstName());
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistUserExample.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistUserExample.java
new file mode 100644
index 0000000000..f1284958e7
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistUserExample.java
@@ -0,0 +1,22 @@
+package com.baeldung.optionalReturnType;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+public class PersistUserExample {
+ static String persistenceUnit = "com.baeldung.optionalReturnType";
+ static EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnit);
+
+ static EntityManager em = emf.createEntityManager();
+
+ public static void main(String[] args) {
+ User user1 = new User();
+ user1.setUserId(1l);
+ user1.setFirstName("Bael Dung");
+ em.persist(user1);
+
+ User user2 = em.find(User.class, 1l);
+ System.out.print("User2.firstName:" + user2.getFirstName());
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/SerializeOptionalTypeExample.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/SerializeOptionalTypeExample.java
new file mode 100644
index 0000000000..d67337ad98
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/SerializeOptionalTypeExample.java
@@ -0,0 +1,41 @@
+package com.baeldung.optionalReturnType;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.Optional;
+
+public class SerializeOptionalTypeExample {
+ public static void main(String[] args) {
+ User user1 = new User();
+ user1.setUserId(1l);
+ user1.setFirstName("baeldung");
+
+ serializeObject(user1, "user1.ser");
+
+ UserOptionalField user2 = new UserOptionalField();
+ user2.setUserId(1l);
+ user2.setFirstName(Optional.of("baeldung"));
+
+ serializeObject(user2, "user2.ser");
+
+ }
+
+ public static void serializeObject(Object object, String fileName) {
+ // Serialization
+ try {
+ FileOutputStream file = new FileOutputStream(fileName);
+ ObjectOutputStream out = new ObjectOutputStream(file);
+
+ out.writeObject(object);
+
+ out.close();
+ file.close();
+
+ System.out.println("Object " + object.toString() + " has been serialized to file " + fileName);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/User.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/User.java
new file mode 100644
index 0000000000..7aa11d78cb
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/User.java
@@ -0,0 +1,31 @@
+package com.baeldung.optionalReturnType;
+
+import java.io.Serializable;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class User implements Serializable {
+ @Id
+ private long userId;
+
+ private String firstName;
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptional.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptional.java
new file mode 100644
index 0000000000..0138a84ab9
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptional.java
@@ -0,0 +1,35 @@
+package com.baeldung.optionalReturnType;
+
+import java.io.Serializable;
+import java.util.Optional;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class UserOptional implements Serializable {
+ @Id
+ private long userId;
+
+ @Column(nullable = true)
+ private String firstName;
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ public Optional getFirstName() {
+ return Optional.ofNullable(firstName);
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ Optional.ofNullable(firstName);
+ }
+
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptionalField.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptionalField.java
new file mode 100644
index 0000000000..c02430b1ba
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptionalField.java
@@ -0,0 +1,31 @@
+package com.baeldung.optionalReturnType;
+
+import java.io.Serializable;
+import java.util.Optional;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class UserOptionalField implements Serializable {
+ @Id
+ private long userId;
+
+ private Optional firstName;
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ public Optional getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(Optional firstName) {
+ this.firstName = firstName;
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/main/resources/META-INF/persistence.xml b/core-java-modules/core-java-8-2/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000000..e8cd723ec2
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ Persist Optional Return Type Demo
+ org.hibernate.jpa.HibernatePersistenceProvider
+ com.baeldung.optionalReturnType.User
+ com.baeldung.optionalReturnType.UserOptional
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exception/error/ErrorGeneratorUnitTest.java b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exception/error/ErrorGeneratorUnitTest.java
new file mode 100644
index 0000000000..de56fb7113
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exception/error/ErrorGeneratorUnitTest.java
@@ -0,0 +1,26 @@
+package com.baeldung.error;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ErrorGeneratorUnitTest {
+
+ @Test(expected = AssertionError.class)
+ public void whenError_thenIsNotCaughtByCatchException() {
+ try {
+ throw new AssertionError();
+ } catch (Exception e) {
+ Assert.fail(); // errors are not caught by catch exception
+ }
+ }
+
+ @Test
+ public void whenError_thenIsCaughtByCatchError() {
+ try {
+ throw new AssertionError();
+ } catch (Error e) {
+ // caught! -> test pass
+ }
+ }
+}
\ No newline at end of file
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000000..72d07d6392
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,61 @@
+version: '3'
+
+services:
+
+## VOLUME CONTAINER-TO-CONTAINER AND HOST-TO-CONTAINER TEST ##
+
+ volumes-example-service:
+ image: alpine:latest
+ container_name: volumes-example-service
+ volumes:
+ - /tmp:/my-volumes/host-volume
+ - /home:/my-volumes/readonly-host-volume:ro
+ - my-named-global-volume:/my-volumes/named-global-volume
+ tty: true # Needed to keep the container running
+
+ another-volumes-example-service:
+ image: alpine:latest
+ container_name: another-volumes-example-service
+ volumes:
+ - my-named-global-volume:/another-path/the-same-named-global-volume
+ tty: true # Needed to keep the container running
+
+## NETWORK CONTAINER-TO-CONTAINER TEST ##
+
+ network-example-service:
+ image: karthequian/helloworld:latest
+ container_name: network-example-service
+ networks:
+ - my-shared-network
+
+ another-service-in-the-same-network:
+ image: alpine:latest
+ container_name: another-service-in-the-same-network
+ networks:
+ - my-shared-network
+
+ tty: true # Needed to keep the container running
+
+ another-service-in-its-own-network:
+ image: alpine:latest
+ container_name: another-service-in-its-own-network
+ networks:
+ - my-private-network
+ tty: true # Needed to keep the container running
+
+## NETWORK HOST-TO-CONTAINER TEST ##
+
+ network-example-service-available-to-host-on-port-1337:
+ image: karthequian/helloworld:latest
+ container_name: network-example-service-available-to-host-on-port-1337
+ networks:
+ - my-shared-network
+ ports:
+ - "1337:80"
+
+volumes:
+ my-named-global-volume:
+
+networks:
+ my-shared-network: {}
+ my-private-network: {}
diff --git a/java-collections-conversions/src/main/java/com/baeldung/convertToMap/ConvertToMap.java b/java-collections-conversions/src/main/java/com/baeldung/convertToMap/ConvertToMap.java
index 3c14dfdba6..e33d9ee212 100644
--- a/java-collections-conversions/src/main/java/com/baeldung/convertToMap/ConvertToMap.java
+++ b/java-collections-conversions/src/main/java/com/baeldung/convertToMap/ConvertToMap.java
@@ -15,8 +15,7 @@ public class ConvertToMap {
}
public Map listToMapWithDupKey(List books) {
- return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),
- (o1, o2) -> o1));
+ return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(), (existing, replacement) -> existing));
}
public Map listToConcurrentMap(List books) {
diff --git a/java-collections-conversions/src/test/java/com/baeldung/convertToMap/ConvertToMapUnitTest.java b/java-collections-conversions/src/test/java/com/baeldung/convertToMap/ConvertToMapUnitTest.java
index d11221bbf7..d6eab461d7 100644
--- a/java-collections-conversions/src/test/java/com/baeldung/convertToMap/ConvertToMapUnitTest.java
+++ b/java-collections-conversions/src/test/java/com/baeldung/convertToMap/ConvertToMapUnitTest.java
@@ -2,6 +2,7 @@ package com.baeldung.convertToMap;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.junit.Assert.*;
@@ -34,8 +35,10 @@ public class ConvertToMapUnitTest {
}
@Test
- public void whenMapHasDuplicateKey_with_merge_function() {
- assertTrue(convertToMap.listToMapWithDupKey(bookList).size() == 2);
+ public void whenMapHasDuplicateKeyThenMergeFunctionHandlesCollision() {
+ Map booksByYear = convertToMap.listToMapWithDupKey(bookList);
+ assertEquals(2, booksByYear.size());
+ assertEquals("0395489318", booksByYear.get(1954).getIsbn());
}
@Test
diff --git a/libraries-2/pom.xml b/libraries-2/pom.xml
index d3c0f7204b..f9cb1201be 100644
--- a/libraries-2/pom.xml
+++ b/libraries-2/pom.xml
@@ -82,6 +82,12 @@
test
+
+ edu.uci.ics
+ crawler4j
+ ${crawler4j.version}
+
@@ -89,6 +95,7 @@
4.8.28
6.0.0.Final
3.9.6
+ 4.4.0
2.1.4.RELEASE
diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/CrawlerStatistics.java b/libraries-2/src/main/java/com/baeldung/crawler4j/CrawlerStatistics.java
new file mode 100644
index 0000000000..e3237a2755
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/crawler4j/CrawlerStatistics.java
@@ -0,0 +1,22 @@
+package com.baeldung.crawler4j;
+
+public class CrawlerStatistics {
+ private int processedPageCount = 0;
+ private int totalLinksCount = 0;
+
+ public void incrementProcessedPageCount() {
+ processedPageCount++;
+ }
+
+ public void incrementTotalLinksCount(int linksCount) {
+ totalLinksCount += linksCount;
+ }
+
+ public int getProcessedPageCount() {
+ return processedPageCount;
+ }
+
+ public int getTotalLinksCount() {
+ return totalLinksCount;
+ }
+}
diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawler.java b/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawler.java
new file mode 100644
index 0000000000..b77e1e075f
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawler.java
@@ -0,0 +1,48 @@
+package com.baeldung.crawler4j;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import edu.uci.ics.crawler4j.crawler.Page;
+import edu.uci.ics.crawler4j.crawler.WebCrawler;
+import edu.uci.ics.crawler4j.parser.HtmlParseData;
+import edu.uci.ics.crawler4j.url.WebURL;
+
+public class HtmlCrawler extends WebCrawler {
+
+ private final static Pattern EXCLUSIONS = Pattern.compile(".*(\\.(css|js|xml|gif|jpg|png|mp3|mp4|zip|gz|pdf))$");
+
+ private CrawlerStatistics stats;
+
+ public HtmlCrawler(CrawlerStatistics stats) {
+ this.stats = stats;
+ }
+
+ @Override
+ public boolean shouldVisit(Page referringPage, WebURL url) {
+ String urlString = url.getURL().toLowerCase();
+ return !EXCLUSIONS.matcher(urlString).matches()
+ && urlString.startsWith("https://www.baeldung.com/");
+ }
+
+ @Override
+ public void visit(Page page) {
+ String url = page.getWebURL().getURL();
+ stats.incrementProcessedPageCount();
+
+ if (page.getParseData() instanceof HtmlParseData) {
+ HtmlParseData htmlParseData = (HtmlParseData) page.getParseData();
+ String title = htmlParseData.getTitle();
+ String text = htmlParseData.getText();
+ String html = htmlParseData.getHtml();
+ Set links = htmlParseData.getOutgoingUrls();
+ stats.incrementTotalLinksCount(links.size());
+
+ System.out.printf("Page with title '%s' %n", title);
+ System.out.printf(" Text length: %d %n", text.length());
+ System.out.printf(" HTML length: %d %n", html.length());
+ System.out.printf(" %d outbound links %n", links.size());
+ }
+ }
+
+}
diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawlerController.java b/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawlerController.java
new file mode 100644
index 0000000000..82c1c6bdd7
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawlerController.java
@@ -0,0 +1,36 @@
+package com.baeldung.crawler4j;
+
+import java.io.File;
+
+import edu.uci.ics.crawler4j.crawler.CrawlConfig;
+import edu.uci.ics.crawler4j.crawler.CrawlController;
+import edu.uci.ics.crawler4j.fetcher.PageFetcher;
+import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig;
+import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer;
+
+public class HtmlCrawlerController {
+
+ public static void main(String[] args) throws Exception {
+ File crawlStorage = new File("src/test/resources/crawler4j");
+ CrawlConfig config = new CrawlConfig();
+ config.setCrawlStorageFolder(crawlStorage.getAbsolutePath());
+ config.setMaxDepthOfCrawling(2);
+
+ int numCrawlers = 12;
+
+ PageFetcher pageFetcher = new PageFetcher(config);
+ RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
+ RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);
+ CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer);
+
+ controller.addSeed("https://www.baeldung.com/");
+
+ CrawlerStatistics stats = new CrawlerStatistics();
+ CrawlController.WebCrawlerFactory factory = () -> new HtmlCrawler(stats);
+
+ controller.start(factory, numCrawlers);
+ System.out.printf("Crawled %d pages %n", stats.getProcessedPageCount());
+ System.out.printf("Total Number of outbound links = %d %n", stats.getTotalLinksCount());
+ }
+
+}
diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawler.java b/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawler.java
new file mode 100644
index 0000000000..ebb5d96f36
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawler.java
@@ -0,0 +1,49 @@
+package com.baeldung.crawler4j;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+import edu.uci.ics.crawler4j.crawler.Page;
+import edu.uci.ics.crawler4j.crawler.WebCrawler;
+import edu.uci.ics.crawler4j.parser.BinaryParseData;
+import edu.uci.ics.crawler4j.url.WebURL;
+
+public class ImageCrawler extends WebCrawler {
+ private final static Pattern EXCLUSIONS = Pattern.compile(".*(\\.(css|js|xml|gif|png|mp3|mp4|zip|gz|pdf))$");
+
+ private static final Pattern IMG_PATTERNS = Pattern.compile(".*(\\.(jpg|jpeg))$");
+
+ private File saveDir;
+
+ public ImageCrawler(File saveDir) {
+ this.saveDir = saveDir;
+ }
+
+ @Override
+ public boolean shouldVisit(Page referringPage, WebURL url) {
+ String urlString = url.getURL().toLowerCase();
+ if (EXCLUSIONS.matcher(urlString).matches()) {
+ return false;
+ }
+
+ if (IMG_PATTERNS.matcher(urlString).matches()
+ || urlString.startsWith("https://www.baeldung.com/")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void visit(Page page) {
+ String url = page.getWebURL().getURL();
+ if (IMG_PATTERNS.matcher(url).matches()
+ && page.getParseData() instanceof BinaryParseData) {
+ String extension = url.substring(url.lastIndexOf("."));
+ int contentLength = page.getContentData().length;
+
+ System.out.printf("Extension is '%s' with content length %d %n", extension, contentLength);
+ }
+ }
+
+}
diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawlerController.java b/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawlerController.java
new file mode 100644
index 0000000000..9db32f1bfb
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawlerController.java
@@ -0,0 +1,36 @@
+package com.baeldung.crawler4j;
+
+import java.io.File;
+
+import edu.uci.ics.crawler4j.crawler.CrawlConfig;
+import edu.uci.ics.crawler4j.crawler.CrawlController;
+import edu.uci.ics.crawler4j.fetcher.PageFetcher;
+import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig;
+import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer;
+
+public class ImageCrawlerController {
+
+ public static void main(String[] args) throws Exception {
+ File crawlStorage = new File("src/test/resources/crawler4j");
+ CrawlConfig config = new CrawlConfig();
+ config.setCrawlStorageFolder(crawlStorage.getAbsolutePath());
+ config.setIncludeBinaryContentInCrawling(true);
+ config.setMaxPagesToFetch(500);
+
+ File saveDir = new File("src/test/resources/crawler4j");
+
+ int numCrawlers = 12;
+
+ PageFetcher pageFetcher = new PageFetcher(config);
+ RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
+ RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);
+ CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer);
+
+ controller.addSeed("https://www.baeldung.com/");
+
+ CrawlController.WebCrawlerFactory factory = () -> new ImageCrawler(saveDir);
+
+ controller.start(factory, numCrawlers);
+ }
+
+}
diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/MultipleCrawlerController.java b/libraries-2/src/main/java/com/baeldung/crawler4j/MultipleCrawlerController.java
new file mode 100644
index 0000000000..7662607f80
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/crawler4j/MultipleCrawlerController.java
@@ -0,0 +1,54 @@
+package com.baeldung.crawler4j;
+
+import java.io.File;
+
+import edu.uci.ics.crawler4j.crawler.CrawlConfig;
+import edu.uci.ics.crawler4j.crawler.CrawlController;
+import edu.uci.ics.crawler4j.fetcher.PageFetcher;
+import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig;
+import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer;
+
+public class MultipleCrawlerController {
+ public static void main(String[] args) throws Exception {
+ File crawlStorageBase = new File("src/test/resources/crawler4j");
+ CrawlConfig htmlConfig = new CrawlConfig();
+ CrawlConfig imageConfig = new CrawlConfig();
+
+ htmlConfig.setCrawlStorageFolder(new File(crawlStorageBase, "html").getAbsolutePath());
+ imageConfig.setCrawlStorageFolder(new File(crawlStorageBase, "image").getAbsolutePath());
+ imageConfig.setIncludeBinaryContentInCrawling(true);
+
+ htmlConfig.setMaxPagesToFetch(500);
+ imageConfig.setMaxPagesToFetch(1000);
+
+ PageFetcher pageFetcherHtml = new PageFetcher(htmlConfig);
+ PageFetcher pageFetcherImage = new PageFetcher(imageConfig);
+
+ RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
+ RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcherHtml);
+
+ CrawlController htmlController = new CrawlController(htmlConfig, pageFetcherHtml, robotstxtServer);
+ CrawlController imageController = new CrawlController(imageConfig, pageFetcherImage, robotstxtServer);
+
+ htmlController.addSeed("https://www.baeldung.com/");
+ imageController.addSeed("https://www.baeldung.com/");
+
+ CrawlerStatistics stats = new CrawlerStatistics();
+ CrawlController.WebCrawlerFactory htmlFactory = () -> new HtmlCrawler(stats);
+
+ File saveDir = new File("src/test/resources/crawler4j");
+ CrawlController.WebCrawlerFactory imageFactory = () -> new ImageCrawler(saveDir);
+
+ imageController.startNonBlocking(imageFactory, 7);
+ htmlController.startNonBlocking(htmlFactory, 10);
+
+
+ htmlController.waitUntilFinish();
+ System.out.printf("Crawled %d pages %n", stats.getProcessedPageCount());
+ System.out.printf("Total Number of outbound links = %d %n", stats.getTotalLinksCount());
+
+ imageController.waitUntilFinish();
+ System.out.printf("Image Crawler is finished.");
+
+ }
+}
diff --git a/persistence-modules/hibernate-mapping/pom.xml b/persistence-modules/hibernate-mapping/pom.xml
index 6bab3c5b1f..bb8ebdab65 100644
--- a/persistence-modules/hibernate-mapping/pom.xml
+++ b/persistence-modules/hibernate-mapping/pom.xml
@@ -5,9 +5,9 @@
com.baeldung
- parent-modules
+ persistence-modules
1.0.0-SNAPSHOT
- ../../
+ ..
hibernate-mapping
@@ -31,22 +31,28 @@
h2
${h2.version}
-
+
org.hibernate
hibernate-validator
${hibernate-validator.version}
-
- javax.el
- javax.el-api
- ${javax.el-api.version}
-
org.glassfish
javax.el
${org.glassfish.javax.el.version}
+
+ javax.money
+ money-api
+ 1.0.3
+
+
+ org.javamoney
+ moneta
+ 1.3
+ pom
+
@@ -60,11 +66,10 @@
- 5.3.7.Final
+ 5.3.10.Final
3.8.0
- 5.3.3.Final
- 2.2.5
- 3.0.1-b08
+ 6.0.16.Final
+ 3.0.1-b11
-
\ No newline at end of file
+
diff --git a/persistence-modules/hibernate-mapping/src/main/java/com/baeldung/hibernate/persistmaps/mapkey/User.java b/persistence-modules/hibernate-mapping/src/main/java/com/baeldung/hibernate/persistmaps/mapkey/User.java
index f6e8f1cdd6..b2ee7e85fe 100644
--- a/persistence-modules/hibernate-mapping/src/main/java/com/baeldung/hibernate/persistmaps/mapkey/User.java
+++ b/persistence-modules/hibernate-mapping/src/main/java/com/baeldung/hibernate/persistmaps/mapkey/User.java
@@ -4,8 +4,11 @@ import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.validation.constraints.Size;
+import javax.money.MonetaryAmount;
import org.hibernate.validator.constraints.Length;
+import org.hibernate.validator.constraints.CreditCardNumber;
+import org.hibernate.validator.constraints.Currency;
@Entity
public class User {
@@ -62,5 +65,4 @@ public class User {
public void setCity(String city) {
this.city = city;
}
-
}
diff --git a/persistence-modules/hibernate-mapping/src/test/java/com/baeldung/hibernate/validation/UserAdditionalValidationUnitTest.java b/persistence-modules/hibernate-mapping/src/test/java/com/baeldung/hibernate/validation/UserAdditionalValidationUnitTest.java
new file mode 100644
index 0000000000..0f2a0403e9
--- /dev/null
+++ b/persistence-modules/hibernate-mapping/src/test/java/com/baeldung/hibernate/validation/UserAdditionalValidationUnitTest.java
@@ -0,0 +1,290 @@
+package com.baeldung.hibernate.validation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.util.Set;
+
+import javax.money.CurrencyContextBuilder;
+import javax.money.Monetary;
+import javax.money.MonetaryAmount;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import org.hibernate.validator.constraints.CodePointLength;
+import org.hibernate.validator.constraints.CreditCardNumber;
+import org.hibernate.validator.constraints.Currency;
+import org.hibernate.validator.constraints.Length;
+import org.hibernate.validator.constraints.LuhnCheck;
+import org.hibernate.validator.constraints.Range;
+import org.hibernate.validator.constraints.SafeHtml;
+import org.hibernate.validator.constraints.ScriptAssert;
+import org.hibernate.validator.constraints.URL;
+import org.hibernate.validator.constraints.time.DurationMax;
+import org.hibernate.validator.constraints.time.DurationMin;
+import org.javamoney.moneta.CurrencyUnitBuilder;
+import org.javamoney.moneta.Money;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class UserAdditionalValidationUnitTest {
+
+ private static Validator validator;
+ private Set> constraintViolations;
+
+ @BeforeClass
+ public static void before() {
+ ValidatorFactory config = Validation.buildDefaultValidatorFactory();
+ validator = config.getValidator();
+ }
+
+ @Test
+ public void whenValidationWithCCNAndNullCCN_thenNoConstraintViolation() {
+ AdditionalValidations validations = new AdditionalValidations();
+ constraintViolations = validator.validateProperty(validations, "creditCardNumber");
+ assertTrue(constraintViolations.isEmpty());
+ }
+
+ @Test
+ public void whenValidationWithCCNAndValidCCN_thenNoConstraintViolation() {
+ AdditionalValidations validations = new AdditionalValidations();
+ validations.setCreditCardNumber("79927398713");
+ constraintViolations = validator.validateProperty(validations, "creditCardNumber");
+ assertTrue(constraintViolations.isEmpty());
+ }
+
+ @Test
+ public void whenValidationWithCCNAndInvalidCCN_thenConstraintViolation() {
+ AdditionalValidations validations = new AdditionalValidations();
+ validations.setCreditCardNumber("79927398714");
+ constraintViolations = validator.validateProperty(validations, "creditCardNumber");
+ assertEquals(constraintViolations.size(), 2);
+ }
+
+ @Test
+ public void whenValidationWithCCNAndValidCCNWithDashes_thenConstraintViolation() {
+ AdditionalValidations validations = new AdditionalValidations();
+ validations.setCreditCardNumber("7992-7398-713");
+ constraintViolations = validator.validateProperty(validations, "creditCardNumber");
+ assertEquals(1, constraintViolations.size());
+ }
+
+ @Test
+ public void whenValidationWithLenientCCNAndValidCCNWithDashes_thenNoConstraintViolation() {
+ AdditionalValidations validations = new AdditionalValidations();
+ validations.setLenientCreditCardNumber("7992-7398-713");
+ constraintViolations = validator.validateProperty(validations, "lenientCreditCardNumber");
+ assertTrue(constraintViolations.isEmpty());
+ }
+
+ @Test
+ public void whenMonetaryAmountWithRightCurrency_thenNoConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setBalance(Money.of(new BigDecimal(100.0), Monetary.getCurrency("EUR")));
+ constraintViolations = validator.validateProperty(bean, "balance");
+ assertEquals(0, constraintViolations.size());
+ }
+
+ @Test
+ public void whenMonetaryAmountWithWrongCurrency_thenConstraintViolation() {
+ AdditionalValidations validations = new AdditionalValidations();
+ validations.setBalance(Money.of(new BigDecimal(100.0), Monetary.getCurrency("USD")));
+ constraintViolations = validator.validateProperty(validations, "balance");
+ assertEquals(1, constraintViolations.size());
+ }
+
+ @Test
+ public void whenDurationShorterThanMin_thenConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setDuration(Duration.ofDays(1).plusHours(1));
+ constraintViolations = validator.validateProperty(bean, "duration");
+ assertEquals(1, constraintViolations.size());
+ }
+
+ @Test
+ public void whenDurationLongerThanMax_thenConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setDuration(Duration.ofDays(2).plusHours(3));
+ constraintViolations = validator.validateProperty(bean, "duration");
+ assertEquals(1, constraintViolations.size());
+ }
+
+ @Test
+ public void whenDurationBetweenMinAndMax_thenNoConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setDuration(Duration.ofDays(2));
+ constraintViolations = validator.validateProperty(bean, "duration");
+ assertEquals(0, constraintViolations.size());
+ }
+
+ @Test
+ public void whenValueBelowRangeMin_thenConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setPercent(new BigDecimal("-1.4"));
+ constraintViolations = validator.validateProperty(bean, "percent");
+ assertEquals(1, constraintViolations.size());
+ }
+
+ @Test
+ public void whenValueAboveRangeMax_thenConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setPercent(new BigDecimal("100.03"));
+ constraintViolations = validator.validateProperty(bean, "percent");
+ assertEquals(1, constraintViolations.size());
+ }
+
+ @Test
+ public void whenValueInRange_thenNoConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setPercent(new BigDecimal("53.23"));
+ constraintViolations = validator.validateProperty(bean, "percent");
+ assertEquals(0, constraintViolations.size());
+ }
+
+ @Test
+ public void whenLengthInRange_thenNoConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setSomeString("aaa");
+ constraintViolations = validator.validateProperty(bean, "someString");
+ assertEquals(0, constraintViolations.size());
+ }
+
+ @Test
+ public void whenCodePointLengthNotInRange_thenConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+ bean.setSomeString("aa\uD835\uDD0A");
+ constraintViolations = validator.validateProperty(bean, "someString");
+ assertEquals(1, constraintViolations.size());
+ }
+
+ @Test
+ public void whenValidUrlWithWrongProtocol_thenConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+
+ bean.setUrl("https://www.google.com/");
+ constraintViolations = validator.validateProperty(bean, "url");
+ assertEquals(0, constraintViolations.size());
+
+ bean.setUrl("http://www.google.com/");
+ constraintViolations = validator.validateProperty(bean, "url");
+ assertEquals(1, constraintViolations.size());
+
+ bean.setUrl("https://foo:bar");
+ constraintViolations = validator.validateProperty(bean, "url");
+ assertEquals(1, constraintViolations.size());
+ }
+
+ @Test
+ public void whenScriptAssertFails_thenConstraintViolation() {
+ AdditionalValidations bean = new AdditionalValidations();
+
+ constraintViolations = validator.validate(bean);
+ assertEquals(0, constraintViolations.size());
+
+ bean.setValid(false);
+
+ constraintViolations = validator.validate(bean);
+ assertEquals(1, constraintViolations.size());
+
+ constraintViolations = validator.validateProperty(bean, "valid");
+ assertEquals(0, constraintViolations.size());
+ }
+
+ @ScriptAssert(lang = "nashorn", script = "_this.valid")
+ public class AdditionalValidations {
+ private boolean valid = true;
+
+ @CreditCardNumber
+ @LuhnCheck(startIndex = 0, endIndex = Integer.MAX_VALUE, checkDigitIndex = -1)
+ private String creditCardNumber;
+
+ @CreditCardNumber(ignoreNonDigitCharacters = true)
+ private String lenientCreditCardNumber;
+
+ @Currency("EUR")
+ private MonetaryAmount balance;
+
+ @DurationMin(days = 1, hours = 2)
+ @DurationMax(days = 2, hours = 1)
+ private Duration duration;
+
+ @Range(min = 0, max = 100)
+ private BigDecimal percent;
+
+ @Length(min = 1, max = 3)
+ @CodePointLength(min = 1, max = 3)
+ private String someString;
+
+ @URL(protocol = "https")
+ private String url;
+
+ public String getCreditCardNumber() {
+ return creditCardNumber;
+ }
+
+ public void setCreditCardNumber(String creditCardNumber) {
+ this.creditCardNumber = creditCardNumber;
+ }
+
+ public String getLenientCreditCardNumber() {
+ return lenientCreditCardNumber;
+ }
+
+ public void setLenientCreditCardNumber(String lenientCreditCardNumber) {
+ this.lenientCreditCardNumber = lenientCreditCardNumber;
+ }
+
+ public MonetaryAmount getBalance() {
+ return balance;
+ }
+
+ public void setBalance(MonetaryAmount balance) {
+ this.balance = balance;
+ }
+
+ public Duration getDuration() {
+ return duration;
+ }
+
+ public void setDuration(Duration duration) {
+ this.duration = duration;
+ }
+
+ public BigDecimal getPercent() {
+ return percent;
+ }
+
+ public void setPercent(BigDecimal percent) {
+ this.percent = percent;
+ }
+
+ public String getSomeString() {
+ return someString;
+ }
+
+ public void setSomeString(String someString) {
+ this.someString = someString;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public void setValid(boolean valid) {
+ this.valid = valid;
+ }
+
+ }
+}
diff --git a/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/DynamicMappingIntegrationTest.java b/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/DynamicMappingIntegrationTest.java
index f31a61f121..7a112200b5 100644
--- a/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/DynamicMappingIntegrationTest.java
+++ b/persistence-modules/hibernate5/src/test/java/com/baeldung/hibernate/DynamicMappingIntegrationTest.java
@@ -122,14 +122,10 @@ public class DynamicMappingIntegrationTest {
Employee employee = session.get(Employee.class, 1);
assertThat(employee.getGrossIncome()).isEqualTo(10_000);
- session.close();
-
- session = HibernateUtil.getSessionFactory().openSession();
- transaction = session.beginTransaction();
-
+ session.disableFilter("incomeLevelFilter");
employees = session.createQuery("from Employee").getResultList();
- assertThat(employees).hasSize(0);
+ assertThat(employees).hasSize(3);
}
diff --git a/persistence-modules/java-mongodb/README.md b/persistence-modules/java-mongodb/README.md
index 6d31467db3..045b245030 100644
--- a/persistence-modules/java-mongodb/README.md
+++ b/persistence-modules/java-mongodb/README.md
@@ -3,3 +3,4 @@
- [A Guide to MongoDB with Java](http://www.baeldung.com/java-mongodb)
- [A Simple Tagging Implementation with MongoDB](http://www.baeldung.com/mongodb-tagging)
- [MongoDB BSON Guide](https://www.baeldung.com/mongodb-bson)
+- [Geospatial Support in MongoDB](https://www.baeldung.com/mongodb-geospatial-support)
diff --git a/pom.xml b/pom.xml
index 39803fd9b0..a5c506b0c5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -123,17 +123,22 @@
-
- org.junit.jupiter
- junit-jupiter-engine
- ${junit-jupiter.version}
-
-
- org.junit.vintage
- junit-vintage-engine
- ${junit-jupiter.version}
-
-
+
+ org.junit.platform
+ junit-platform-surefire-provider
+ ${junit-platform.version}
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit-jupiter.version}
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ ${junit-jupiter.version}
+
+
org.apache.maven.plugins
@@ -921,6 +926,7 @@
spring-vault
spring-vertx
spring-zuul/spring-zuul-foos-resource
+ persistence-modules/hibernate-mapping
persistence-modules/spring-data-dynamodb
persistence-modules/spring-data-eclipselink
persistence-modules/spring-data-solr
@@ -1550,8 +1556,8 @@
1.2
2.9.8
1.3
- 1.4.2
- 5.4.2
+ 1.2.0
+ 5.2.0
0.3.1
2.5.1
0.0.1
diff --git a/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/CustomService.java b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/CustomService.java
new file mode 100644
index 0000000000..634e49fed3
--- /dev/null
+++ b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/CustomService.java
@@ -0,0 +1,10 @@
+package com.baeldung.autoconfiguration.service;
+
+public class CustomService implements SimpleService {
+
+ @Override
+ public String serve() {
+ return "Custom Service";
+ }
+
+}
diff --git a/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/DefaultService.java b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/DefaultService.java
new file mode 100644
index 0000000000..ee91bcb051
--- /dev/null
+++ b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/DefaultService.java
@@ -0,0 +1,10 @@
+package com.baeldung.autoconfiguration.service;
+
+public class DefaultService implements SimpleService {
+
+ @Override
+ public String serve() {
+ return "Default Service";
+ }
+
+}
diff --git a/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/SimpleService.java b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/SimpleService.java
new file mode 100644
index 0000000000..b6c72d7159
--- /dev/null
+++ b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/SimpleService.java
@@ -0,0 +1,7 @@
+package com.baeldung.autoconfiguration.service;
+
+public interface SimpleService {
+
+ public String serve();
+
+}
diff --git a/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnBeanIntegrationTest.java b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnBeanIntegrationTest.java
new file mode 100644
index 0000000000..32f63edde4
--- /dev/null
+++ b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnBeanIntegrationTest.java
@@ -0,0 +1,77 @@
+package com.baeldung.autoconfiguration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+public class ConditionalOnBeanIntegrationTest {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
+
+ @Test
+ public void whenDependentBeanIsPresent_thenConditionalBeanCreated() {
+ this.contextRunner.withUserConfiguration(BasicConfiguration.class, ConditionalOnBeanConfiguration.class)
+ .run((context) -> {
+ assertThat(context).hasBean("created");
+ assertThat(context).getBean("created")
+ .isEqualTo("This is always created");
+ assertThat(context).hasBean("createOnBean");
+ assertThat(context).getBean("createOnBean")
+ .isEqualTo("This is created when bean (name=created) is present");
+ });
+ }
+
+ @Test
+ public void whenDependentBeanIsPresent_thenConditionalMissingBeanIgnored() {
+ this.contextRunner.withUserConfiguration(BasicConfiguration.class, ConditionalOnMissingBeanConfiguration.class)
+ .run((context) -> {
+ assertThat(context).hasBean("created");
+ assertThat(context).getBean("created")
+ .isEqualTo("This is always created");
+ assertThat(context).doesNotHaveBean("createOnMissingBean");
+ });
+ }
+
+ @Test
+ public void whenDependentBeanIsNotPresent_thenConditionalMissingBeanCreated() {
+ this.contextRunner.withUserConfiguration(ConditionalOnMissingBeanConfiguration.class)
+ .run((context) -> {
+ assertThat(context).hasBean("createOnMissingBean");
+ assertThat(context).getBean("createOnMissingBean")
+ .isEqualTo("This is created when bean (name=created) is missing");
+ assertThat(context).doesNotHaveBean("created");
+ });
+ }
+
+ @Configuration
+ protected static class BasicConfiguration {
+ @Bean
+ public String created() {
+ return "This is always created";
+ }
+ }
+
+ @Configuration
+ @ConditionalOnBean(name = "created")
+ protected static class ConditionalOnBeanConfiguration {
+ @Bean
+ public String createOnBean() {
+ return "This is created when bean (name=created) is present";
+ }
+ }
+
+ @Configuration
+ @ConditionalOnMissingBean(name = "created")
+ protected static class ConditionalOnMissingBeanConfiguration {
+ @Bean
+ public String createOnMissingBean() {
+ return "This is created when bean (name=created) is missing";
+ }
+ }
+
+}
diff --git a/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnClassIntegrationTest.java b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnClassIntegrationTest.java
new file mode 100644
index 0000000000..f2866867f2
--- /dev/null
+++ b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnClassIntegrationTest.java
@@ -0,0 +1,76 @@
+package com.baeldung.autoconfiguration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+public class ConditionalOnClassIntegrationTest {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
+
+ @Test
+ public void whenDependentClassIsPresent_thenBeanCreated() {
+ this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
+ .run(context -> {
+ assertThat(context).hasBean("created");
+ assertThat(context.getBean("created")).isEqualTo("This is created when ConditionalOnClassIntegrationTest is present on the classpath");
+ });
+ }
+
+ @Test
+ public void whenDependentClassIsPresent_thenBeanMissing() {
+ this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
+ .run(context -> {
+ assertThat(context).doesNotHaveBean("missed");
+ });
+ }
+
+ @Test
+ public void whenDependentClassIsNotPresent_thenBeanMissing() {
+ this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
+ .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
+ .run((context) -> {
+ assertThat(context).doesNotHaveBean("created");
+ assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
+
+ });
+ }
+
+ @Test
+ public void whenDependentClassIsNotPresent_thenBeanCreated() {
+ this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
+ .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
+ .run((context) -> {
+ assertThat(context).hasBean("missed");
+ assertThat(context).getBean("missed")
+ .isEqualTo("This is missed when ConditionalOnClassIntegrationTest is present on the classpath");
+ assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
+
+ });
+ }
+
+ @Configuration
+ @ConditionalOnClass(ConditionalOnClassIntegrationTest.class)
+ protected static class ConditionalOnClassConfiguration {
+ @Bean
+ public String created() {
+ return "This is created when ConditionalOnClassIntegrationTest is present on the classpath";
+ }
+ }
+
+ @Configuration
+ @ConditionalOnMissingClass("com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest")
+ protected static class ConditionalOnMissingClassConfiguration {
+ @Bean
+ public String missed() {
+ return "This is missed when ConditionalOnClassIntegrationTest is present on the classpath";
+ }
+ }
+
+}
diff --git a/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnPropertyIntegrationTest.java b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnPropertyIntegrationTest.java
new file mode 100644
index 0000000000..c0733722dc
--- /dev/null
+++ b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnPropertyIntegrationTest.java
@@ -0,0 +1,64 @@
+package com.baeldung.autoconfiguration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.TestPropertySource;
+
+import com.baeldung.autoconfiguration.service.CustomService;
+import com.baeldung.autoconfiguration.service.DefaultService;
+import com.baeldung.autoconfiguration.service.SimpleService;
+
+public class ConditionalOnPropertyIntegrationTest {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
+
+ @Test
+ public void whenGivenCustomPropertyValue_thenCustomServiceCreated() {
+ this.contextRunner.withPropertyValues("com.baeldung.service=custom")
+ .withUserConfiguration(SimpleServiceConfiguration.class)
+ .run(context -> {
+ assertThat(context).hasBean("customService");
+ SimpleService simpleService = context.getBean(CustomService.class);
+ assertThat(simpleService.serve()).isEqualTo("Custom Service");
+ assertThat(context).doesNotHaveBean("defaultService");
+ });
+ }
+
+ @Test
+ public void whenGivenDefaultPropertyValue_thenDefaultServiceCreated() {
+ this.contextRunner.withPropertyValues("com.baeldung.service=default")
+ .withUserConfiguration(SimpleServiceConfiguration.class)
+ .run(context -> {
+ assertThat(context).hasBean("defaultService");
+ SimpleService simpleService = context.getBean(DefaultService.class);
+ assertThat(simpleService.serve()).isEqualTo("Default Service");
+ assertThat(context).doesNotHaveBean("customService");
+ });
+ }
+
+ @Configuration
+ @TestPropertySource("classpath:ConditionalOnPropertyTest.properties")
+ protected static class SimpleServiceConfiguration {
+
+ @Bean
+ @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "default")
+ @ConditionalOnMissingBean
+ public DefaultService defaultService() {
+ return new DefaultService();
+ }
+
+ @Bean
+ @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "custom")
+ @ConditionalOnMissingBean
+ public CustomService customService() {
+ return new CustomService();
+ }
+ }
+
+}
diff --git a/spring-boot-autoconfiguration/src/test/resources/ConditionalOnPropertyTest.properties b/spring-boot-autoconfiguration/src/test/resources/ConditionalOnPropertyTest.properties
new file mode 100644
index 0000000000..b6334bc1ce
--- /dev/null
+++ b/spring-boot-autoconfiguration/src/test/resources/ConditionalOnPropertyTest.properties
@@ -0,0 +1 @@
+com.baeldung.service=custom
\ No newline at end of file
diff --git a/spring-boot-flowable/src/main/resources/processes/article-workflow.bpmn20.xml b/spring-boot-flowable/src/main/resources/processes/article-workflow.bpmn20.xml
index 6bf210dcee..77c02f39a4 100644
--- a/spring-boot-flowable/src/main/resources/processes/article-workflow.bpmn20.xml
+++ b/spring-boot-flowable/src/main/resources/processes/article-workflow.bpmn20.xml
@@ -38,12 +38,12 @@
+ flowable:class="com.baeldung.service.PublishArticleService" />
+ flowable:class="com.baeldung.service.SendMailService" />
diff --git a/spring-boot-flowable/src/test/java/com/baeldung/processes/ArticleWorkflowUnitTest.java b/spring-boot-flowable/src/test/java/com/baeldung/processes/ArticleWorkflowIntegrationTest.java
similarity index 90%
rename from spring-boot-flowable/src/test/java/com/baeldung/processes/ArticleWorkflowUnitTest.java
rename to spring-boot-flowable/src/test/java/com/baeldung/processes/ArticleWorkflowIntegrationTest.java
index ef5453623a..7d4557a679 100644
--- a/spring-boot-flowable/src/test/java/com/baeldung/processes/ArticleWorkflowUnitTest.java
+++ b/spring-boot-flowable/src/test/java/com/baeldung/processes/ArticleWorkflowIntegrationTest.java
@@ -13,11 +13,11 @@ import org.flowable.task.api.Task;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.boot.test.context.SpringBootTest;
@ExtendWith(FlowableSpringExtension.class)
-@ExtendWith(SpringExtension.class)
-public class ArticleWorkflowUnitTest {
+@SpringBootTest
+public class ArticleWorkflowIntegrationTest {
@Autowired
private RuntimeService runtimeService;
@Autowired
diff --git a/spring-cloud/spring-cloud-security/auth-client/pom.xml b/spring-cloud/spring-cloud-security/auth-client/pom.xml
index 4f64f470f0..4152552640 100644
--- a/spring-cloud/spring-cloud-security/auth-client/pom.xml
+++ b/spring-cloud/spring-cloud-security/auth-client/pom.xml
@@ -24,7 +24,7 @@
org.springframework.cloud
- spring-cloud-starter-zuul
+ spring-cloud-starter-netflix-zuul
org.springframework.boot
@@ -34,14 +34,16 @@
org.webjars
jquery
+ ${jquery.version}
org.webjars
bootstrap
+ ${bootstrap.version}
org.webjars
- webjars-locator
+ webjars-locator-core
org.springframework.boot
@@ -62,8 +64,8 @@
spring-boot-starter-thymeleaf
- org.springframework.security.oauth
- spring-security-oauth2
+ org.springframework.security.oauth.boot
+ spring-security-oauth2-autoconfigure
@@ -89,8 +91,10 @@
- 2.1.0
- Dalston.SR4
+ 2.2.0
+ Greenwich.SR1
+ 3.4.1
+ 4.3.1
diff --git a/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.properties b/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.properties
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.yml
index 2a758faeae..69617555d9 100644
--- a/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.yml
+++ b/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.yml
@@ -2,7 +2,8 @@
# These are default settings, but we add them for clarity.
server:
port: 8080
- contextPath: /
+ servlet:
+ context-path: /
# Configure the Authorization Server and User Info Resource Server details
security:
@@ -21,6 +22,7 @@ person:
# Proxies the calls to http://localhost:8080/api/* to our REST service at http://localhost:8081/*
# and automatically includes our OAuth2 token in the request headers
zuul:
+ sensitiveHeaders: Cookie,Set-Cookie
routes:
resource:
path: /api/**
diff --git a/spring-cloud/spring-cloud-security/auth-resource/pom.xml b/spring-cloud/spring-cloud-security/auth-resource/pom.xml
index 22ee0528c3..a60eca740c 100644
--- a/spring-cloud/spring-cloud-security/auth-resource/pom.xml
+++ b/spring-cloud/spring-cloud-security/auth-resource/pom.xml
@@ -19,8 +19,8 @@
spring-boot-starter-web
- org.springframework.security.oauth
- spring-security-oauth2
+ org.springframework.security.oauth.boot
+ spring-security-oauth2-autoconfigure
org.springframework.boot
@@ -30,6 +30,7 @@
org.springframework.security
spring-security-jwt
+ ${spring-jwt.version}
@@ -55,7 +56,8 @@
- Edgware.RELEASE
+ Greenwich.SR1
+ 1.0.10.RELEASE
diff --git a/spring-cloud/spring-cloud-security/auth-resource/src/main/java/com/baeldung/config/ResourceConfigurer.java b/spring-cloud/spring-cloud-security/auth-resource/src/main/java/com/baeldung/config/ResourceConfigurer.java
index 977d74093a..abe942325f 100644
--- a/spring-cloud/spring-cloud-security/auth-resource/src/main/java/com/baeldung/config/ResourceConfigurer.java
+++ b/spring-cloud/spring-cloud-security/auth-resource/src/main/java/com/baeldung/config/ResourceConfigurer.java
@@ -3,7 +3,7 @@ package com.baeldung.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@@ -11,15 +11,18 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R
* REST API Resource Server.
*/
@Configuration
-@EnableWebSecurity
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true) // Allow method annotations like @PreAuthorize
public class ResourceConfigurer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
- http.httpBasic().disable();
- http.authorizeRequests().anyRequest().authenticated();
+ http.sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.NEVER)
+ .and()
+ .authorizeRequests()
+ .anyRequest().authenticated();
+
}
}
diff --git a/spring-cloud/spring-cloud-security/auth-resource/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/auth-resource/src/main/resources/application.yml
index 52e02ba41b..35063a7879 100644
--- a/spring-cloud/spring-cloud-security/auth-resource/src/main/resources/application.yml
+++ b/spring-cloud/spring-cloud-security/auth-resource/src/main/resources/application.yml
@@ -5,7 +5,6 @@ server:
# Configure the public key to use for verifying the incoming JWT tokens
security:
- sessions: NEVER
oauth2:
resource:
jwt:
diff --git a/spring-cloud/spring-cloud-security/auth-server/pom.xml b/spring-cloud/spring-cloud-security/auth-server/pom.xml
index 4b3f94b825..afd8dbef44 100644
--- a/spring-cloud/spring-cloud-security/auth-server/pom.xml
+++ b/spring-cloud/spring-cloud-security/auth-server/pom.xml
@@ -38,7 +38,7 @@
- 1.1.2.RELEASE
+ 2.1.2.RELEASE
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/AuthServerConfigurer.java b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/AuthServerConfigurer.java
index 32e445f998..7c9ee9ae18 100644
--- a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/AuthServerConfigurer.java
+++ b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/AuthServerConfigurer.java
@@ -9,6 +9,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.Resource;
import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
@@ -19,9 +20,7 @@ import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFacto
@Configuration
@EnableAuthorizationServer
@Order(6)
-public class AuthServerConfigurer
- extends
- AuthorizationServerConfigurerAdapter {
+public class AuthServerConfigurer extends AuthorizationServerConfigurerAdapter {
@Value("${jwt.certificate.store.file}")
private Resource keystore;
@@ -37,6 +36,9 @@ public class AuthServerConfigurer
@Autowired
private UserDetailsService userDetailsService;
+
+ @Autowired
+ private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(
@@ -45,8 +47,8 @@ public class AuthServerConfigurer
clients
.inMemory()
.withClient("authserver")
- .secret("passwordforauthserver")
- .redirectUris("http://localhost:8080/")
+ .secret(passwordEncoder.encode("passwordforauthserver"))
+ .redirectUris("http://localhost:8080/login")
.authorizedGrantTypes("authorization_code",
"refresh_token")
.scopes("myscope")
diff --git a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfigurer.java b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfig.java
similarity index 83%
rename from spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfigurer.java
rename to spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfig.java
index 23b56151e7..3cefd323b3 100644
--- a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfigurer.java
+++ b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfig.java
@@ -2,10 +2,10 @@ package com.baeldung.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
-public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
+public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
diff --git a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebSecurityConfigurer.java b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebSecurityConfigurer.java
index 44406b8fa0..6a48c62097 100644
--- a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebSecurityConfigurer.java
+++ b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebSecurityConfigurer.java
@@ -6,8 +6,8 @@ import org.springframework.security.config.annotation.authentication.builders.Au
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-
import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
@Configuration
@@ -34,7 +34,7 @@ public class WebSecurityConfigurer
AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
- .withUser("user").password("user")
+ .withUser("user").password(passwordEncoder().encode("user"))
.roles("USER")
.and()
.withUser("admin").password("admin")
@@ -48,5 +48,9 @@ public class WebSecurityConfigurer
return super.userDetailsServiceBean();
}
+ @Bean
+ public BCryptPasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
}
diff --git a/spring-cloud/spring-cloud-security/auth-server/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/auth-server/src/main/resources/application.yml
index 1dc63d3f0e..b6e385e5c7 100644
--- a/spring-cloud/spring-cloud-security/auth-server/src/main/resources/application.yml
+++ b/spring-cloud/spring-cloud-security/auth-server/src/main/resources/application.yml
@@ -1,7 +1,8 @@
# Make the application available at http://localhost:7070/authserver
server:
port: 7070
- contextPath: /authserver
+ servlet:
+ context-path: /authserver
# Our certificate settings for enabling JWT tokens
jwt:
@@ -11,11 +12,4 @@ jwt:
password: abirkhan04
key:
alias: myauthkey
- password: abirkhan04
-
-
-security:
- oauth2:
- resource:
- filter-order: 3
-
\ No newline at end of file
+ password: abirkhan04
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-security/pom.xml b/spring-cloud/spring-cloud-security/pom.xml
index 2eecf579a5..d65fd6520b 100644
--- a/spring-cloud/spring-cloud-security/pom.xml
+++ b/spring-cloud/spring-cloud-security/pom.xml
@@ -8,10 +8,10 @@
pom
- parent-boot-1
+ parent-boot-2
com.baeldung
0.0.1-SNAPSHOT
- ../../parent-boot-1
+ ../../parent-boot-2
diff --git a/spring-security-rest-custom/README.md b/spring-security-rest-custom/README.md
index 85f2b7532c..d7cb31e784 100644
--- a/spring-security-rest-custom/README.md
+++ b/spring-security-rest-custom/README.md
@@ -8,3 +8,4 @@ The "REST With Spring" Classes: http://github.learnspringsecurity.com
### Relevant Articles:
- [Spring Security Authentication Provider](http://www.baeldung.com/spring-security-authentication-provider)
- [Retrieve User Information in Spring Security](http://www.baeldung.com/get-user-in-spring-security)
+- [Spring Security – Run-As Authentication](https://www.baeldung.com/spring-security-run-as-auth)
diff --git a/spring-session/mongodb-session/src/test/java/com/baeldung/springsessionmongodb/SpringSessionMongoDBIntegrationTest.java b/spring-session/mongodb-session/src/test/java/com/baeldung/springsessionmongodb/SpringSessionMongoDBIntegrationTest.java
deleted file mode 100644
index c73335b49b..0000000000
--- a/spring-session/mongodb-session/src/test/java/com/baeldung/springsessionmongodb/SpringSessionMongoDBIntegrationTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.baeldung.springsessionmongodb;
-
-import org.junit.Assert;
-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.boot.test.web.client.TestRestTemplate;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
-import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
-import org.springframework.test.context.junit4.SpringRunner;
-import springsessionmongodb.SpringSessionMongoDBApplication;
-
-import java.util.Base64;
-
-
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = SpringSessionMongoDBApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
-public class SpringSessionMongoDBIntegrationTest {
-
- @Autowired
- private MongoOperationsSessionRepository repository;
-
- private TestRestTemplate restTemplate = new TestRestTemplate();
-
- @Test
- public void givenEndpointIsCalledTwiceAndResponseIsReturned_whenMongoDBIsQueriedForCount_thenCountMustBeSame() {
- HttpEntity response = restTemplate.
- exchange("http://localhost:" + 8080, HttpMethod.GET, null, String.class);
- HttpHeaders headers = response.getHeaders();
- String set_cookie = headers.getFirst(HttpHeaders.SET_COOKIE);
-
- Assert.assertEquals(response.getBody(),
- repository.findById(getSessionId(set_cookie)).getAttribute("count").toString());
- }
-
- private String getSessionId(String set_cookie) {
- return new String(Base64.getDecoder().decode(set_cookie.split(";")[0].split("=")[1]));
- }
-
-}
diff --git a/spring-session/spring-session-mongodb/pom.xml b/spring-session/spring-session-mongodb/pom.xml
index 714833cf99..16fbaccc84 100644
--- a/spring-session/spring-session-mongodb/pom.xml
+++ b/spring-session/spring-session-mongodb/pom.xml
@@ -32,6 +32,7 @@
org.springframework.boot
spring-boot-starter-data-mongodb
+ ${spring-boot-starter-data-mongodb.version}
@@ -43,6 +44,7 @@
2.1.3.RELEASE
+ 2.1.5.RELEASE
diff --git a/spring-session/spring-session-mongodb/src/main/java/com/baeldung/springsessionmongodb/controller/SpringSessionMongoDBController.java b/spring-session/spring-session-mongodb/src/main/java/com/baeldung/springsessionmongodb/controller/SpringSessionMongoDBController.java
index 1c38f419c3..b5cb4520a0 100644
--- a/spring-session/spring-session-mongodb/src/main/java/com/baeldung/springsessionmongodb/controller/SpringSessionMongoDBController.java
+++ b/spring-session/spring-session-mongodb/src/main/java/com/baeldung/springsessionmongodb/controller/SpringSessionMongoDBController.java
@@ -1,11 +1,11 @@
package com.baeldung.springsessionmongodb.controller;
-import javax.servlet.http.HttpSession;
-
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
+import javax.servlet.http.HttpSession;
+
@RestController
public class SpringSessionMongoDBController {
@@ -17,7 +17,7 @@ public class SpringSessionMongoDBController {
if (counter == null) {
counter = 1;
} else {
- counter += 1;
+ counter++;
}
session.setAttribute("count", counter);
diff --git a/spring-session/spring-session-mongodb/src/test/java/com/baeldung/springsessionmongodb/SpringSessionMongoDBIntegrationTest.java b/spring-session/spring-session-mongodb/src/test/java/com/baeldung/springsessionmongodb/SpringSessionMongoDBIntegrationTest.java
index eb9f4164a6..9dc45c5b32 100644
--- a/spring-session/spring-session-mongodb/src/test/java/com/baeldung/springsessionmongodb/SpringSessionMongoDBIntegrationTest.java
+++ b/spring-session/spring-session-mongodb/src/test/java/com/baeldung/springsessionmongodb/SpringSessionMongoDBIntegrationTest.java
@@ -1,7 +1,5 @@
package com.baeldung.springsessionmongodb;
-import java.util.Base64;
-
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -14,6 +12,8 @@ import org.springframework.http.HttpMethod;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.test.context.junit4.SpringRunner;
+import java.util.Base64;
+
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringSessionMongoDBApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@@ -26,8 +26,8 @@ public class SpringSessionMongoDBIntegrationTest {
@Test
public void givenEndpointIsCalledTwiceAndResponseIsReturned_whenMongoDBIsQueriedForCount_thenCountMustBeSame() {
- HttpEntity response = restTemplate.
- exchange("http://localhost:" + 8080, HttpMethod.GET, null, String.class);
+ HttpEntity response = restTemplate
+ .exchange("http://localhost:" + 8080, HttpMethod.GET, null, String.class);
HttpHeaders headers = response.getHeaders();
String set_cookie = headers.getFirst(HttpHeaders.SET_COOKIE);
diff --git a/spring-session/spring-session-mongodb/src/test/java/org/baeldung/SpringContextIntegrationTest.java b/spring-session/spring-session-mongodb/src/test/java/org/baeldung/SpringContextIntegrationTest.java
index 3c58b2673f..16b7404f57 100644
--- a/spring-session/spring-session-mongodb/src/test/java/org/baeldung/SpringContextIntegrationTest.java
+++ b/spring-session/spring-session-mongodb/src/test/java/org/baeldung/SpringContextIntegrationTest.java
@@ -1,12 +1,11 @@
package org.baeldung;
+import com.baeldung.springsessionmongodb.SpringSessionMongoDBApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
-import com.baeldung.springsessionmongodb.SpringSessionMongoDBApplication;
-
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringSessionMongoDBApplication.class)
public class SpringContextIntegrationTest {
diff --git a/testing-modules/spring-testing/pom.xml b/testing-modules/spring-testing/pom.xml
index 630aed0c81..10d34f169b 100644
--- a/testing-modules/spring-testing/pom.xml
+++ b/testing-modules/spring-testing/pom.xml
@@ -44,6 +44,11 @@
spring-context
LATEST
+
+ org.springframework
+ spring-webmvc
+ ${spring.version}
+
org.eclipse.persistence
javax.persistence
@@ -66,6 +71,11 @@
${awaitility.version}
test
+
+ javax.servlet
+ javax.servlet-api
+ ${servlet.api.version}
+
@@ -84,6 +94,8 @@
2.0.0.0
3.1.6
5.4.0
+ 5.1.4.RELEASE
+ 4.0.1
\ No newline at end of file
diff --git a/testing-modules/spring-testing/src/main/java/com/baeldung/config/WebConfig.java b/testing-modules/spring-testing/src/main/java/com/baeldung/config/WebConfig.java
new file mode 100644
index 0000000000..eca0aed57f
--- /dev/null
+++ b/testing-modules/spring-testing/src/main/java/com/baeldung/config/WebConfig.java
@@ -0,0 +1,17 @@
+package com.baeldung.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+import javax.servlet.ServletContext;
+
+@EnableWebMvc
+@Configuration
+@ComponentScan(basePackages = {"com.baeldung.controller.parameterized"})
+public class WebConfig {
+
+ @Autowired
+ private ServletContext ctx;
+}
diff --git a/testing-modules/spring-testing/src/main/java/com/baeldung/controller/parameterized/EmployeeRoleController.java b/testing-modules/spring-testing/src/main/java/com/baeldung/controller/parameterized/EmployeeRoleController.java
new file mode 100644
index 0000000000..0f1ed9e61d
--- /dev/null
+++ b/testing-modules/spring-testing/src/main/java/com/baeldung/controller/parameterized/EmployeeRoleController.java
@@ -0,0 +1,32 @@
+package com.baeldung.controller.parameterized;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+public class EmployeeRoleController {
+
+ private static Map userRoleCache = new HashMap<>();
+
+ static {
+ userRoleCache.put("John", Role.ADMIN);
+ userRoleCache.put("Doe", Role.EMPLOYEE);
+ }
+
+ @RequestMapping(value = "/role/{name}", method = RequestMethod.GET, produces = "application/text")
+ @ResponseBody
+ public String getEmployeeRole(@PathVariable("name") String employeeName) {
+
+ return userRoleCache.get(employeeName).toString();
+ }
+
+ private enum Role {
+ ADMIN, EMPLOYEE
+ }
+}
diff --git a/testing-modules/spring-testing/src/test/java/com/baeldung/controller/parameterized/RoleControllerIntegrationTest.java b/testing-modules/spring-testing/src/test/java/com/baeldung/controller/parameterized/RoleControllerIntegrationTest.java
new file mode 100644
index 0000000000..c362067cc0
--- /dev/null
+++ b/testing-modules/spring-testing/src/test/java/com/baeldung/controller/parameterized/RoleControllerIntegrationTest.java
@@ -0,0 +1,49 @@
+package com.baeldung.controller.parameterized;
+
+import com.baeldung.config.WebConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration
+@ContextConfiguration(classes = WebConfig.class)
+public class RoleControllerIntegrationTest {
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ private MockMvc mockMvc;
+
+ private static final String CONTENT_TYPE = "application/text;charset=ISO-8859-1";
+
+ @Before
+ public void setup() throws Exception {
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
+ }
+
+ @Test
+ public void givenEmployeeNameJohnWhenInvokeRoleThenReturnAdmin() throws Exception {
+ this.mockMvc.perform(MockMvcRequestBuilders.get("/role/John")).andDo(print()).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE))
+ .andExpect(MockMvcResultMatchers.content().string("ADMIN"));
+ }
+
+ @Test
+ public void givenEmployeeNameDoeWhenInvokeRoleThenReturnEmployee() throws Exception {
+ this.mockMvc.perform(MockMvcRequestBuilders.get("/role/Doe")).andDo(print()).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE))
+ .andExpect(MockMvcResultMatchers.content().string("EMPLOYEE"));
+ }
+
+}
\ No newline at end of file
diff --git a/testing-modules/spring-testing/src/test/java/com/baeldung/controller/parameterized/RoleControllerParameterizedClassRuleIntegrationTest.java b/testing-modules/spring-testing/src/test/java/com/baeldung/controller/parameterized/RoleControllerParameterizedClassRuleIntegrationTest.java
new file mode 100644
index 0000000000..eca294a742
--- /dev/null
+++ b/testing-modules/spring-testing/src/test/java/com/baeldung/controller/parameterized/RoleControllerParameterizedClassRuleIntegrationTest.java
@@ -0,0 +1,73 @@
+package com.baeldung.controller.parameterized;
+
+import com.baeldung.config.WebConfig;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestContextManager;
+import org.springframework.test.context.junit4.rules.SpringClassRule;
+import org.springframework.test.context.junit4.rules.SpringMethodRule;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+
+@RunWith(Parameterized.class)
+@WebAppConfiguration
+@ContextConfiguration(classes = WebConfig.class)
+public class RoleControllerParameterizedClassRuleIntegrationTest {
+
+ private static final String CONTENT_TYPE = "application/text;charset=ISO-8859-1";
+
+ @ClassRule
+ public static final SpringClassRule scr = new SpringClassRule();
+
+ @Rule
+ public final SpringMethodRule smr = new SpringMethodRule();
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ private MockMvc mockMvc;
+
+ @Parameter(value = 0)
+ public String name;
+
+ @Parameter(value = 1)
+ public String role;
+
+ @Parameters
+ public static Collection