diff --git a/algorithms-genetic/pom.xml b/algorithms-genetic/pom.xml index 942acd69c6..4d15464138 100644 --- a/algorithms-genetic/pom.xml +++ b/algorithms-genetic/pom.xml @@ -44,18 +44,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - 3.6.1 3.7.0 diff --git a/algorithms-miscellaneous-1/pom.xml b/algorithms-miscellaneous-1/pom.xml index d9ecbd78e8..00cb4eeda9 100644 --- a/algorithms-miscellaneous-1/pom.xml +++ b/algorithms-miscellaneous-1/pom.xml @@ -49,18 +49,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - diff --git a/algorithms-miscellaneous-2/pom.xml b/algorithms-miscellaneous-2/pom.xml index 7144a7a391..89d5b3657a 100644 --- a/algorithms-miscellaneous-2/pom.xml +++ b/algorithms-miscellaneous-2/pom.xml @@ -54,18 +54,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - diff --git a/algorithms-miscellaneous-3/pom.xml b/algorithms-miscellaneous-3/pom.xml index 4b67a30abb..166a7be623 100644 --- a/algorithms-miscellaneous-3/pom.xml +++ b/algorithms-miscellaneous-3/pom.xml @@ -74,18 +74,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - 3.9.0 4.3 diff --git a/algorithms-miscellaneous-4/pom.xml b/algorithms-miscellaneous-4/pom.xml index 50fef5ff71..31a6db5354 100644 --- a/algorithms-miscellaneous-4/pom.xml +++ b/algorithms-miscellaneous-4/pom.xml @@ -34,18 +34,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - 3.9.0 27.0.1-jre diff --git a/algorithms-miscellaneous-5/pom.xml b/algorithms-miscellaneous-5/pom.xml index 615cf03467..71a5a3d410 100644 --- a/algorithms-miscellaneous-5/pom.xml +++ b/algorithms-miscellaneous-5/pom.xml @@ -49,18 +49,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - 1.0.1 3.9.0 diff --git a/algorithms-sorting-2/pom.xml b/algorithms-sorting-2/pom.xml index 529474afda..5ef90b9825 100644 --- a/algorithms-sorting-2/pom.xml +++ b/algorithms-sorting-2/pom.xml @@ -44,18 +44,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - 3.6.1 3.9.0 diff --git a/algorithms-sorting/pom.xml b/algorithms-sorting/pom.xml index 2de8eed04e..1d0b7718ac 100644 --- a/algorithms-sorting/pom.xml +++ b/algorithms-sorting/pom.xml @@ -45,18 +45,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - 3.6.1 3.9.0 diff --git a/apache-cxf/pom.xml b/apache-cxf/pom.xml index 3d64000c2e..0975996c06 100644 --- a/apache-cxf/pom.xml +++ b/apache-cxf/pom.xml @@ -30,7 +30,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} diff --git a/core-java-modules/core-java-console/pom.xml b/core-java-modules/core-java-console/pom.xml index 1d58d8c253..fc4f1875ca 100644 --- a/core-java-modules/core-java-console/pom.xml +++ b/core-java-modules/core-java-console/pom.xml @@ -45,7 +45,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} java com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed @@ -105,7 +104,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} run-benchmarks @@ -134,7 +132,6 @@ 3.0.0-M1 - 1.6.0 1.8 1.8 diff --git a/core-java-modules/core-java-io/pom.xml b/core-java-modules/core-java-io/pom.xml index 0968536e65..02b5adae7c 100644 --- a/core-java-modules/core-java-io/pom.xml +++ b/core-java-modules/core-java-io/pom.xml @@ -60,7 +60,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} java com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed @@ -108,7 +107,6 @@ org.codehaus.mojo exec-maven-plugin - run-benchmarks diff --git a/core-java-modules/core-java-jar/pom.xml b/core-java-modules/core-java-jar/pom.xml index 6e9d713d7c..45500de739 100644 --- a/core-java-modules/core-java-jar/pom.xml +++ b/core-java-modules/core-java-jar/pom.xml @@ -189,7 +189,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} java com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed @@ -249,7 +248,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} run-benchmarks @@ -397,7 +395,6 @@ 1.4.4 3.1.1 2.0.3.RELEASE - 1.6.0 1.8 1.8 diff --git a/core-java-modules/core-java-lang-math-2/pom.xml b/core-java-modules/core-java-lang-math-2/pom.xml index 42704c784a..56e18158b3 100644 --- a/core-java-modules/core-java-lang-math-2/pom.xml +++ b/core-java-modules/core-java-lang-math-2/pom.xml @@ -62,18 +62,6 @@ - - - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - - 3.6.1 3.9.0 diff --git a/core-java-modules/core-java-lang-oop-types/README.md b/core-java-modules/core-java-lang-oop-types/README.md index 6c649877b3..1ac5a5c804 100644 --- a/core-java-modules/core-java-lang-oop-types/README.md +++ b/core-java-modules/core-java-lang-oop-types/README.md @@ -13,3 +13,4 @@ This module contains articles about types in Java - [A Guide to Java Enums](https://www.baeldung.com/a-guide-to-java-enums) - [Determine if an Object is of Primitive Type](https://www.baeldung.com/java-object-primitive-type) - [Extending Enums in Java](https://www.baeldung.com/java-extending-enums) +- [Java Class File Naming Conventions](https://www.baeldung.com/java-class-file-naming) diff --git a/core-java-modules/core-java-networking-2/README.md b/core-java-modules/core-java-networking-2/README.md index 9def4c8eb6..fa49c35bf8 100644 --- a/core-java-modules/core-java-networking-2/README.md +++ b/core-java-modules/core-java-networking-2/README.md @@ -14,5 +14,4 @@ This module contains articles about networking in Java - [Handling java.net.ConnectException](https://www.baeldung.com/java-net-connectexception) - [Getting MAC addresses in Java](https://www.baeldung.com/java-mac-address) - [Sending Emails with Attachments in Java](https://www.baeldung.com/java-send-emails-attachments) -- [Finding a Free Port in Java](https://www.baeldung.com/java-free-port) - [[<-- Prev]](/core-java-modules/core-java-networking) diff --git a/core-java-modules/core-java-networking-3/README.md b/core-java-modules/core-java-networking-3/README.md index a81e85751d..09470fe88c 100644 --- a/core-java-modules/core-java-networking-3/README.md +++ b/core-java-modules/core-java-networking-3/README.md @@ -4,5 +4,5 @@ This module contains articles about networking in Java ### Relevant Articles -- TODO: add link once live +- [Finding a Free Port in Java](https://www.baeldung.com/java-free-port) - [[<-- Prev]](/core-java-modules/core-java-networking-2) diff --git a/core-java-modules/core-java-security-2/src/test/java/com/baeldung/rsa/RsaUnitTest.java b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/rsa/RsaUnitTest.java new file mode 100644 index 0000000000..ac93f71125 --- /dev/null +++ b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/rsa/RsaUnitTest.java @@ -0,0 +1,92 @@ +package com.baeldung.cipher; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.crypto.Cipher; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; + +public class RsaUnitTest { + + @Test + public void givenRsaKeyPair_whenEncryptAndDecryptString_thenCompareResults() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair pair = generator.generateKeyPair(); + PrivateKey privateKey = pair.getPrivate(); + PublicKey publicKey = pair.getPublic(); + + String secretMessage = "Baeldung secret message"; + Cipher encryptCipher = Cipher.getInstance("RSA"); + encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8); + byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes); + + Cipher decryptCipher = Cipher.getInstance("RSA"); + decryptCipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes); + String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8); + + Assertions.assertEquals(secretMessage, decryptedMessage); + } + + @Test + public void givenRsaKeyPair_whenEncryptAndDecryptFile_thenCompareResults() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair pair = generator.generateKeyPair(); + PrivateKey privateKey = pair.getPrivate(); + PublicKey publicKey = pair.getPublic(); + + String originalContent = "some secret message"; + Path tempFile = Files.createTempFile("temp", "txt"); + writeString(tempFile, originalContent); + + byte[] fileBytes = Files.readAllBytes(tempFile); + Cipher encryptCipher = Cipher.getInstance("RSA"); + encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(encryptedFileBytes); + } + + encryptedFileBytes = Files.readAllBytes(tempFile); + Cipher decryptCipher = Cipher.getInstance("RSA"); + decryptCipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(decryptedFileBytes); + } + + String fileContent = readString(tempFile); + + Assertions.assertEquals(originalContent, fileContent); + } + + private void writeString(Path path, String content) throws Exception { + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write(content); + } + } + + private String readString(Path path) throws Exception { + StringBuilder resultStringBuilder = new StringBuilder(); + try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) { + String line; + while ((line = br.readLine()) != null) { + resultStringBuilder.append(line); + } + } + return resultStringBuilder.toString(); + } +} diff --git a/core-java-modules/core-java-streams-3/README.md b/core-java-modules/core-java-streams-3/README.md index 9adde005e6..f81dca485b 100644 --- a/core-java-modules/core-java-streams-3/README.md +++ b/core-java-modules/core-java-streams-3/README.md @@ -10,4 +10,5 @@ This module contains articles about the Stream API in Java. - [Debugging Java 8 Streams with IntelliJ](https://www.baeldung.com/intellij-debugging-java-streams) - [Add BigDecimals using the Stream API](https://www.baeldung.com/java-stream-add-bigdecimals) - [Should We Close a Java Stream?](https://www.baeldung.com/java-stream-close) +- [Returning Stream vs. Collection](https://www.baeldung.com/java-return-stream-collection) - More articles: [[<-- prev>]](/../core-java-streams-2) diff --git a/core-java-modules/core-java-sun/pom.xml b/core-java-modules/core-java-sun/pom.xml index 0f53407ec1..541af2445f 100644 --- a/core-java-modules/core-java-sun/pom.xml +++ b/core-java-modules/core-java-sun/pom.xml @@ -44,7 +44,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} java com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed @@ -68,7 +67,6 @@ org.codehaus.mojo exec-maven-plugin - run-benchmarks diff --git a/core-java-modules/core-java/pom.xml b/core-java-modules/core-java/pom.xml index b8d75058eb..9b595288e6 100644 --- a/core-java-modules/core-java/pom.xml +++ b/core-java-modules/core-java/pom.xml @@ -94,7 +94,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} java com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed @@ -154,7 +153,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} run-benchmarks @@ -193,7 +191,6 @@ 1.1 3.0.0-M1 - 1.6.0 1.8 1.8 diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index 4cfe7fe34c..d44ce97f14 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -100,6 +100,7 @@ core-java-networking core-java-networking-2 + core-java-networking-3 core-java-nio core-java-nio-2 diff --git a/data-structures/pom.xml b/data-structures/pom.xml index e2d2e23090..7bf34b1c63 100644 --- a/data-structures/pom.xml +++ b/data-structures/pom.xml @@ -35,7 +35,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} diff --git a/discord4j/pom.xml b/discord4j/pom.xml index a161e502db..07fa3fa72e 100644 --- a/discord4j/pom.xml +++ b/discord4j/pom.xml @@ -2,21 +2,18 @@ 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.3.5.RELEASE - - com.baeldung discord4j 0.0.1-SNAPSHOT discord4j Demo Discord bot using Discord4J + Spring Boot - - 1.8 - + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + @@ -67,5 +64,9 @@ + + + 1.8 + diff --git a/docker/docker-spring-boot-postgres/pom.xml b/docker/docker-spring-boot-postgres/pom.xml index 0b359138f6..d08ae130db 100644 --- a/docker/docker-spring-boot-postgres/pom.xml +++ b/docker/docker-spring-boot-postgres/pom.xml @@ -2,21 +2,18 @@ 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.4.0 - - com.baeldung.docker docker-spring-boot-postgres 0.0.1-SNAPSHOT docker-spring-boot-postgres Demo project showing Spring Boot, PostgreSQL, and Docker - - - 11 - + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + @@ -44,5 +41,9 @@ + + + 11 + \ No newline at end of file diff --git a/docker/pom.xml b/docker/pom.xml index f05c303938..3fcc9ca94f 100644 --- a/docker/pom.xml +++ b/docker/pom.xml @@ -4,19 +4,19 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.3.1.RELEASE - - - com.baeldung.docker docker 0.0.1 docker Demo project showing Spring Boot and Docker pom + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + 11 diff --git a/libraries-3/pom.xml b/libraries-3/pom.xml index 2f6e9fa747..3d3b5db890 100644 --- a/libraries-3/pom.xml +++ b/libraries-3/pom.xml @@ -189,7 +189,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} start-server @@ -229,7 +228,6 @@ 4.4.13 4.5.12 2.2 - 1.6.0 0.3.0 2.8 2.1.3 diff --git a/libraries-concurrency/pom.xml b/libraries-concurrency/pom.xml index b7dc5187b1..66296d829f 100644 --- a/libraries-concurrency/pom.xml +++ b/libraries-concurrency/pom.xml @@ -47,7 +47,6 @@ org.codehaus.mojo exec-maven-plugin - 1.6.0 com.baeldung.quasar.App target/classes diff --git a/logging-modules/pom.xml b/logging-modules/pom.xml index 85d5c18219..e396d477dc 100644 --- a/logging-modules/pom.xml +++ b/logging-modules/pom.xml @@ -14,11 +14,11 @@ + flogger log4j log4j2 logback log-mdc - flogger diff --git a/maven-modules/pom.xml b/maven-modules/pom.xml index c9a2b67a6c..a476eacc64 100644 --- a/maven-modules/pom.xml +++ b/maven-modules/pom.xml @@ -20,14 +20,15 @@ maven-integration-test maven-multi-source maven-plugins - maven-unused-dependencies - maven-war-plugin maven-profiles maven-properties - versions-maven-plugin - version-collision + + maven-unused-dependencies + maven-war-plugin optional-dependencies + version-collision version-overriding-plugins + versions-maven-plugin diff --git a/micronaut/pom.xml b/micronaut/pom.xml index d6df6a0347..bd0022c9e1 100644 --- a/micronaut/pom.xml +++ b/micronaut/pom.xml @@ -102,7 +102,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec.plugin.version} java @@ -146,7 +145,6 @@ 1.2.3 3.1.6.RELEASE 3.7.0 - 1.6.0 3.1.0 diff --git a/persistence-modules/core-java-persistence-2/README.md b/persistence-modules/core-java-persistence-2/README.md index 36c33cc6e1..cebd81388d 100644 --- a/persistence-modules/core-java-persistence-2/README.md +++ b/persistence-modules/core-java-persistence-2/README.md @@ -2,3 +2,4 @@ - [Getting Database URL From JDBC Connection Object](https://www.baeldung.com/jdbc-get-url-from-connection) - [JDBC URL Format For Different Databases](https://www.baeldung.com/java-jdbc-url-format) +- [How to Check if a Database Table Exists with JDBC](https://www.baeldung.com/jdbc-check-table-exists) diff --git a/persistence-modules/hibernate-annotations/src/main/java/com/baeldung/hibernate/lazycollection/model/Branch.java b/persistence-modules/hibernate-annotations/src/main/java/com/baeldung/hibernate/lazycollection/model/Branch.java new file mode 100644 index 0000000000..de88647546 --- /dev/null +++ b/persistence-modules/hibernate-annotations/src/main/java/com/baeldung/hibernate/lazycollection/model/Branch.java @@ -0,0 +1,103 @@ +package com.baeldung.hibernate.lazycollection.model; + +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OrderColumn; +import javax.persistence.OneToMany; +import javax.persistence.Entity; +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Branch { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + public Branch() { + } + public Branch(String name) { + this.name = name; + } + + @OneToMany(mappedBy = "mainBranch") + @LazyCollection(LazyCollectionOption.TRUE) + private List mainEmployees; + + @OneToMany(mappedBy = "subBranch") + @LazyCollection(LazyCollectionOption.FALSE) + private List subEmployees; + + @OneToMany(mappedBy = "additionalBranch") + @LazyCollection(LazyCollectionOption.EXTRA) + @OrderColumn(name = "order_id") + private List additionalEmployees; + + 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 List getMainEmployees() { + return mainEmployees; + } + + public void setMainEmployees(List mainEmployees) { + this.mainEmployees = mainEmployees; + } + + public List getSubEmployees() { + return subEmployees; + } + + public void setSubEmployees(List subEmployees) { + this.subEmployees = subEmployees; + } + + public List getAdditionalEmployees() { + return additionalEmployees; + } + + public void setAdditionalEmployees(List additionalEmployees) { + this.additionalEmployees = additionalEmployees; + } + + public void addMainEmployee(Employee employee) { + if (this.mainEmployees == null) { + this.mainEmployees = new ArrayList<>(); + } + this.mainEmployees.add(employee); + } + + public void addSubEmployee(Employee employee) { + if (this.subEmployees == null) { + this.subEmployees = new ArrayList<>(); + } + this.subEmployees.add(employee); + } + + public void addAdditionalEmployee(Employee employee) { + if (this.additionalEmployees == null) { + this.additionalEmployees = new ArrayList<>(); + } + this.additionalEmployees.add(employee); + } +} \ No newline at end of file diff --git a/persistence-modules/hibernate-annotations/src/main/java/com/baeldung/hibernate/lazycollection/model/Employee.java b/persistence-modules/hibernate-annotations/src/main/java/com/baeldung/hibernate/lazycollection/model/Employee.java new file mode 100644 index 0000000000..831518a365 --- /dev/null +++ b/persistence-modules/hibernate-annotations/src/main/java/com/baeldung/hibernate/lazycollection/model/Employee.java @@ -0,0 +1,88 @@ +package com.baeldung.hibernate.lazycollection.model; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OrderColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Entity; + +@Entity +public class Employee { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + private String address; + + public Employee() { + + } + + public Employee(String name, Branch mainBranch, Branch subBranch, Branch additionalBranch) { + this.name = name; + this.mainBranch = mainBranch; + this.subBranch = subBranch; + this.additionalBranch = additionalBranch; + } + + @ManyToOne + private Branch mainBranch; + + @ManyToOne + private Branch subBranch; + + @ManyToOne + private Branch additionalBranch; + + 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 getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public Branch getMainBranch() { + return mainBranch; + } + + public void setMainBranch(Branch mainBranch) { + this.mainBranch = mainBranch; + } + + public Branch getSubBranch() { + return subBranch; + } + + public void setSubBranch(Branch subBranch) { + this.subBranch = subBranch; + } + + public Branch getAdditionalBranch() { + return additionalBranch; + } + + public void setAdditionalBranch(Branch additionalBranch) { + this.additionalBranch = additionalBranch; + } +} \ No newline at end of file diff --git a/persistence-modules/hibernate-annotations/src/test/java/com/baeldung/hibernate/lazycollection/LazyCollectionIntegrationTest.java b/persistence-modules/hibernate-annotations/src/test/java/com/baeldung/hibernate/lazycollection/LazyCollectionIntegrationTest.java new file mode 100644 index 0000000000..63ea1e1e3f --- /dev/null +++ b/persistence-modules/hibernate-annotations/src/test/java/com/baeldung/hibernate/lazycollection/LazyCollectionIntegrationTest.java @@ -0,0 +1,110 @@ +package com.baeldung.hibernate.lazycollection; + +import com.baeldung.hibernate.lazycollection.model.Branch; +import com.baeldung.hibernate.lazycollection.model.Employee; +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.service.ServiceRegistry; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.annotation.PostConstruct; + +public class LazyCollectionIntegrationTest { + + private static SessionFactory sessionFactory; + + private Session session; + + Branch branch; + + @BeforeClass + public static void beforeTests() { + Configuration configuration = new Configuration().addAnnotatedClass(Branch.class) + .addAnnotatedClass(Employee.class).setProperty("hibernate.dialect", H2Dialect.class.getName()) + .setProperty("hibernate.connection.driver_class", org.h2.Driver.class.getName()) + .setProperty("hibernate.connection.url", "jdbc:h2:mem:test") + .setProperty("hibernate.connection.username", "sa").setProperty("hibernate.connection.password", "") + .setProperty("hibernate.hbm2ddl.auto", "update"); + + ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() + .applySettings(configuration.getProperties()).build(); + + sessionFactory = configuration.buildSessionFactory(serviceRegistry); + + } + + @Before + public void setUp() { + session = sessionFactory.openSession(); + session.beginTransaction(); + + branch = new Branch("Main Branch"); + + session.save(branch); + + Employee mainEmployee1 = new Employee("main employee 1", branch, null, null); + Employee mainEmployee2 = new Employee("main employee 2", branch, null, null); + Employee mainEmployee3 = new Employee("main employee 3", branch, null, null); + + session.save(mainEmployee1); + session.save(mainEmployee2); + session.save(mainEmployee3); + + Employee subEmployee1 = new Employee("sub employee 1", null, branch, null); + Employee subEmployee2 = new Employee("sub employee 2", null, branch, null); + Employee subEmployee3 = new Employee("sub employee 3", null, branch, null); + + session.save(subEmployee1); + session.save(subEmployee2); + session.save(subEmployee3); + + Employee additionalEmployee1 = new Employee("additional employee 1", null, null, branch); + Employee additionalEmployee2 = new Employee("additional employee 2", null, null, branch); + Employee additionalEmployee3 = new Employee("additional employee 3", null, null, branch); + + session.save(additionalEmployee1); + session.save(additionalEmployee2); + session.save(additionalEmployee3); + + session.flush(); + session.refresh(branch); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testLazyFetching() { + Assert.assertFalse(Hibernate.isInitialized(branch.getMainEmployees())); + } + + @Test + public void testEagerFetching() { + Assert.assertTrue(Hibernate.isInitialized(branch.getSubEmployees())); + } + + @Test + public void testExtraFetching() { + Assert.assertFalse(Hibernate.isInitialized(branch.getAdditionalEmployees())); + } + + @After + public void tearDown() { + session.getTransaction().commit(); + session.close(); + } + + @AfterClass + public static void afterTests() { + sessionFactory.close(); + } +} diff --git a/persistence-modules/java-jpa-3/src/main/java/com/baeldung/ignorable/fields/HibernateConfig.java b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/ignorable/fields/HibernateConfig.java index 9285c23dfa..d121c81c6b 100644 --- a/persistence-modules/java-jpa-3/src/main/java/com/baeldung/ignorable/fields/HibernateConfig.java +++ b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/ignorable/fields/HibernateConfig.java @@ -9,6 +9,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.service.ServiceRegistry; public class HibernateConfig { + private static SessionFactory sessionFactory; public static SessionFactory getSessionFactory() { @@ -16,12 +17,12 @@ public class HibernateConfig { Configuration configuration = new Configuration(); Properties settings = new Properties(); - settings.put(Environment.DRIVER, "com.mysql.cj.jdbc.Driver"); - settings.put(Environment.URL, "jdbc:mysql://localhost:3306/app_db?useSSL=false"); - settings.put(Environment.USER, "root"); - settings.put(Environment.PASS, "password"); - settings.put(Environment.DIALECT, "org.hibernate.dialect.MySQL5Dialect"); - settings.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread"); + settings.put(Environment.DRIVER, "org.h2.Driver"); + settings.put(Environment.URL, "jdbc:h2:mem:test"); + settings.put(Environment.USER, "sa"); + settings.put(Environment.PASS, ""); + settings.put(Environment.DIALECT, "org.hibernate.dialect.H2Dialect"); + settings.put(Environment.HBM2DDL_AUTO, "create-drop"); configuration.setProperties(settings); configuration.addAnnotatedClass(User.class); diff --git a/persistence-modules/java-jpa-3/src/test/java/com/baeldung/ignorable/fields/TransientFieldUnitTest.java b/persistence-modules/java-jpa-3/src/test/java/com/baeldung/ignorable/fields/TransientFieldUnitTest.java index 114a4cca4c..6d4eba2c29 100644 --- a/persistence-modules/java-jpa-3/src/test/java/com/baeldung/ignorable/fields/TransientFieldUnitTest.java +++ b/persistence-modules/java-jpa-3/src/test/java/com/baeldung/ignorable/fields/TransientFieldUnitTest.java @@ -1,18 +1,24 @@ package com.baeldung.ignorable.fields; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.io.*; -import java.util.List; -import java.util.Random; - -import org.junit.Test; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class TransientFieldUnitTest { @@ -34,18 +40,20 @@ public class TransientFieldUnitTest { @Test public void givenFieldWithTransientAnnotation_whenSerializingObject_thenFieldSerialized() throws IOException, ClassNotFoundException { - FileOutputStream fout = new FileOutputStream("test.obj"); - ObjectOutputStream out = new ObjectOutputStream(fout); - out.writeObject(user); - out.flush(); - out.close(); + try (FileOutputStream fout = new FileOutputStream("test.obj")) { + ObjectOutputStream out = new ObjectOutputStream(fout); + out.writeObject(user); + out.flush(); + } - FileInputStream fin = new FileInputStream("test.obj"); - ObjectInputStream in = new ObjectInputStream(fin); - User savedUser = (User) in.readObject(); - in.close(); + try (FileInputStream fin = new FileInputStream("test.obj")) { + ObjectInputStream in = new ObjectInputStream(fin); + User savedUser = (User) in.readObject(); - assertEquals(user.getCurrentDevice(), savedUser.getCurrentDevice()); + assertEquals(user.getCurrentDevice(), savedUser.getCurrentDevice()); + } + + Files.deleteIfExists(Paths.get("test.obj")); } @Test diff --git a/persistence-modules/jnosql/jnosql-diana/pom.xml b/persistence-modules/jnosql/jnosql-diana/pom.xml index 79c455646c..d5746eef05 100644 --- a/persistence-modules/jnosql/jnosql-diana/pom.xml +++ b/persistence-modules/jnosql/jnosql-diana/pom.xml @@ -55,7 +55,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} document diff --git a/persistence-modules/pom.xml b/persistence-modules/pom.xml index 8789a8473b..32423a42dd 100644 --- a/persistence-modules/pom.xml +++ b/persistence-modules/pom.xml @@ -38,6 +38,7 @@ java-jdbi java-jpa java-jpa-2 + java-jpa-3 java-mongodb jnosql jooq diff --git a/persistence-modules/spring-data-jpa-filtering/src/main/resources/logback.xml b/persistence-modules/spring-data-jpa-filtering/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/persistence-modules/spring-data-jpa-filtering/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/persistence-modules/spring-data-jpa-filtering/src/test/java/com/baeldung/projection/JpaProjectionIntegrationTest.java b/persistence-modules/spring-data-jpa-filtering/src/test/java/com/baeldung/projection/JpaProjectionIntegrationTest.java index 96eaf4ed07..5fdcf6a787 100644 --- a/persistence-modules/spring-data-jpa-filtering/src/test/java/com/baeldung/projection/JpaProjectionIntegrationTest.java +++ b/persistence-modules/spring-data-jpa-filtering/src/test/java/com/baeldung/projection/JpaProjectionIntegrationTest.java @@ -6,18 +6,15 @@ import com.baeldung.projection.repository.PersonRepository; import com.baeldung.projection.view.AddressView; import com.baeldung.projection.view.PersonDto; import com.baeldung.projection.view.PersonView; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD; @DataJpaTest -@RunWith(SpringRunner.class) @Sql(scripts = "/projection-insert-data.sql") @Sql(scripts = "/projection-clean-up-data.sql", executionPhase = AFTER_TEST_METHOD) public class JpaProjectionIntegrationTest { diff --git a/persistence-modules/spring-jooq/README.md b/persistence-modules/spring-jooq/README.md index 515ab8be3c..d0cb946614 100644 --- a/persistence-modules/spring-jooq/README.md +++ b/persistence-modules/spring-jooq/README.md @@ -5,6 +5,7 @@ This module contains articles about Spring with jOOQ ### Relevant Articles: - [Spring Boot Support for jOOQ](https://www.baeldung.com/spring-boot-support-for-jooq) - [Introduction to jOOQ with Spring](https://www.baeldung.com/jooq-with-spring) +- [Count Query In jOOQ](https://www.baeldung.com/jooq-count-query) In order to fix the error "Plugin execution not covered by lifecycle configuration: org.jooq:jooq-codegen-maven:3.7.3:generate (execution: default, phase: generate-sources)", right-click on the error message and choose "Mark goal generated as ignore in pom.xml". Until version 1.4.x, the maven-plugin-plugin was covered by the default lifecycle mapping that ships with m2e. diff --git a/pom.xml b/pom.xml index 78a6d8d329..ab3c73ff8a 100644 --- a/pom.xml +++ b/pom.xml @@ -412,6 +412,7 @@ google-web-toolkit + graphql/graphql-java grpc @@ -614,11 +615,12 @@ spring-aop spring-apache-camel - spring-batch - spring-batch-2 + spring-batch + spring-batch-2 spring-bom spring-boot-modules spring-boot-rest + spring-boot-rest-2 spring-caching @@ -875,6 +877,7 @@ google-web-toolkit + graphql/graphql-java grpc @@ -930,6 +933,7 @@ jooby jsf json + json-2 json-path jsoup jta @@ -1068,9 +1072,11 @@ spring-apache-camel spring-batch + spring-batch-2 spring-bom spring-boot-modules spring-boot-rest + spring-boot-rest-2 spring-caching @@ -1089,6 +1095,7 @@ spring-data-rest spring-data-rest-querydsl spring-di + spring-di-2 spring-drools spring-ejb @@ -1273,7 +1280,7 @@ core-java-modules/core-java-9-streams core-java-modules/core-java-10 - core-java-modules/core-java-11 + core-java-modules/core-java-11-2 @@ -1318,6 +1325,7 @@ core-java-modules/core-java-9-streams core-java-modules/core-java-10 core-java-modules/core-java-11 + core-java-modules/core-java-11-2 @@ -1367,13 +1375,12 @@ 2.21.0 3.8.1 - 1.6.0 + 3.0.0 1.8 1.2.17 2.2.2.0 1.28 1.28 - 1.6.0 2.21.0 2.8.0 2.6 diff --git a/saas/pom.xml b/saas/pom.xml index be5d18f020..93965b37c4 100644 --- a/saas/pom.xml +++ b/saas/pom.xml @@ -40,7 +40,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} java com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed diff --git a/slack/pom.xml b/slack/pom.xml index ebe5ce2f60..51a4cf029f 100644 --- a/slack/pom.xml +++ b/slack/pom.xml @@ -45,7 +45,6 @@ org.codehaus.mojo exec-maven-plugin - 1.6.0 com.baeldung.examples.slack.MainClass diff --git a/spring-5-reactive-2/README.md b/spring-5-reactive-2/README.md index 397f6be57c..98a5f26433 100644 --- a/spring-5-reactive-2/README.md +++ b/spring-5-reactive-2/README.md @@ -9,4 +9,5 @@ This module contains articles about reactive Spring 5 - [Debugging Reactive Streams in Java](https://www.baeldung.com/spring-debugging-reactive-streams) - [Static Content in Spring WebFlux](https://www.baeldung.com/spring-webflux-static-content) - [Server-Sent Events in Spring](https://www.baeldung.com/spring-server-sent-events) +- [Backpressure Mechanism in Spring WebFlux](https://www.baeldung.com/spring-webflux-backpressure) - More articles: [[<-- prev]](/spring-5-reactive) diff --git a/spring-5-reactive-2/src/test/java/com/baeldung/backpressure/BackpressureUnitTest.java b/spring-5-reactive-2/src/test/java/com/baeldung/backpressure/BackpressureUnitTest.java new file mode 100644 index 0000000000..e7cb60dbf9 --- /dev/null +++ b/spring-5-reactive-2/src/test/java/com/baeldung/backpressure/BackpressureUnitTest.java @@ -0,0 +1,82 @@ +package com.baeldung.backpressure; + +import org.junit.jupiter.api.Test; +import reactor.core.publisher.BaseSubscriber; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +public class BackpressureUnitTest { + + @Test + public void whenLimitRateSet_thenSplitIntoChunks() throws InterruptedException { + Flux limit = Flux.range(1, 25); + + limit.limitRate(10); + limit.subscribe( + value -> System.out.println(value), + err -> err.printStackTrace(), + () -> System.out.println("Finished!!"), + subscription -> subscription.request(15) + ); + + StepVerifier.create(limit) + .expectSubscription() + .thenRequest(15) + .expectNext(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + .expectNext(11, 12, 13, 14, 15) + .thenRequest(10) + .expectNext(16, 17, 18, 19, 20, 21, 22, 23, 24, 25) + .verifyComplete(); + } + + @Test + public void whenRequestingChunks10_thenMessagesAreReceived() { + Flux request = Flux.range(1, 50); + + request.subscribe( + System.out::println, + err -> err.printStackTrace(), + () -> System.out.println("All 50 items have been successfully processed!!!"), + subscription -> { + for (int i = 0; i < 5; i++) { + System.out.println("Requesting the next 10 elements!!!"); + subscription.request(10); + } + } + ); + + StepVerifier.create(request) + .expectSubscription() + .thenRequest(10) + .expectNext(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + .thenRequest(10) + .expectNext(11, 12, 13, 14, 15, 16, 17, 18, 19, 20) + .thenRequest(10) + .expectNext(21, 22, 23, 24, 25, 26, 27 , 28, 29 ,30) + .thenRequest(10) + .expectNext(31, 32, 33, 34, 35, 36, 37 , 38, 39 ,40) + .thenRequest(10) + .expectNext(41, 42, 43, 44, 45, 46, 47 , 48, 49 ,50) + .verifyComplete(); + } + + @Test + public void whenCancel_thenSubscriptionFinished() { + Flux cancel = Flux.range(1, 10).log(); + + cancel.subscribe(new BaseSubscriber() { + @Override + protected void hookOnNext(Integer value) { + request(3); + System.out.println(value); + cancel(); + } + }); + + StepVerifier.create(cancel) + .expectNext(1, 2, 3) + .thenCancel() + .verify(); + } +} + diff --git a/spring-5/src/test/java/com/baeldung/README.md b/spring-5/src/test/java/com/baeldung/README.md deleted file mode 100644 index 0ff61914d5..0000000000 --- a/spring-5/src/test/java/com/baeldung/README.md +++ /dev/null @@ -1,3 +0,0 @@ -### Relevant Articles: - -- [Concurrent Test Execution in Spring 5](https://www.baeldung.com/spring-5-concurrent-tests) diff --git a/spring-boot-modules/spring-boot-actuator/README.md b/spring-boot-modules/spring-boot-actuator/README.md index 3e8ef3411b..9e2f30786f 100644 --- a/spring-boot-modules/spring-boot-actuator/README.md +++ b/spring-boot-modules/spring-boot-actuator/README.md @@ -10,3 +10,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Liveness and Readiness Probes in Spring Boot](https://www.baeldung.com/spring-liveness-readiness-probes) - [Custom Information in Spring Boot Info Endpoint](https://www.baeldung.com/spring-boot-info-actuator-custom) - [Health Indicators in Spring Boot](https://www.baeldung.com/spring-boot-health-indicators) +- [How to Enable All Endpoints in Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuator-enable-endpoints) diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java index 24b78642f2..894c24693e 100644 --- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java +++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java @@ -17,20 +17,20 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); auth.inMemoryAuthentication() - .withUser("user") - .password(encoder.encode("password")) - .roles("USER") - .and() - .withUser("admin") - .password(encoder.encode("admin")) - .roles("USER", "ADMIN"); + .withUser("user") + .password(encoder.encode("password")) + .roles("USER") + .and() + .withUser("admin") + .password(encoder.encode("admin")) + .roles("USER", "ADMIN"); } @Override protected void configure(HttpSecurity http) throws Exception { http.requestMatcher(EndpointRequest.toAnyEndpoint()) - .authorizeRequests((requests) -> requests.anyRequest() - .hasRole("ADMIN")); + .authorizeRequests((requests) -> requests.anyRequest() + .hasRole("ADMIN")); http.httpBasic(); } } diff --git a/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java b/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java index 8a9dd4ca72..8274619517 100644 --- a/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java +++ b/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java @@ -21,16 +21,16 @@ public class EndpointEnablingIntegrationTest { @WithMockUser(username = "user", password = "password", roles = "USER") public void givenWrongAuthentication_whenCallingActuator_thenReturns401() throws Exception { mockMvc.perform(get("/actuator")) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()); } @Test @WithMockUser(username = "admin", password = "admin", roles = "ADMIN") public void givenProperAuthentication_whenCallingActuator_thenReturnsExpectedEndpoints() throws Exception { mockMvc.perform(get("/actuator")) - .andExpect(jsonPath("$._links").exists()) - .andExpect(jsonPath("$._links.beans").exists()) - .andExpect(jsonPath("$._links.env").exists()) - .andExpect(jsonPath("$._links.shutdown").exists()); + .andExpect(jsonPath("$._links").exists()) + .andExpect(jsonPath("$._links.beans").exists()) + .andExpect(jsonPath("$._links.env").exists()) + .andExpect(jsonPath("$._links.shutdown").exists()); } } diff --git a/spring-boot-modules/spring-boot-libraries-2/README.md b/spring-boot-modules/spring-boot-libraries-2/README.md index 4218dfc1be..7bf51b5424 100644 --- a/spring-boot-modules/spring-boot-libraries-2/README.md +++ b/spring-boot-modules/spring-boot-libraries-2/README.md @@ -5,3 +5,4 @@ This module contains articles about various Spring Boot libraries ### Relevant Articles: - [Background Jobs in Spring with JobRunr](https://www.baeldung.com/java-jobrunr-spring) +- [Open API Server Implementation Using OpenAPI Generator](https://www.baeldung.com/java-openapi-generator-server) diff --git a/spring-boot-modules/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/Application.java b/spring-boot-modules/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/Application.java index 195b260399..72c55e2de5 100644 --- a/spring-boot-modules/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/Application.java +++ b/spring-boot-modules/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/Application.java @@ -3,6 +3,7 @@ package com.baeldung.lazyinitialization; import com.baeldung.lazyinitialization.services.Writer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -20,7 +21,9 @@ public class Application { } public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + System.out.println("Application context initialized!!!"); Writer writer1 = ctx.getBean("writer1", Writer.class); @@ -29,4 +32,23 @@ public class Application { Writer writer2 = ctx.getBean("writer2", Writer.class); writer2.write("Second message"); } + + /* + This method shows how to set lazy initialization and start the application using SpringApplicationBuilder + */ + private static ApplicationContext runUsingSpringApplicationBuilder(String[] args){ + return new SpringApplicationBuilder(Application.class) + .lazyInitialization(true) + .build(args) + .run(); + } + + /* + This method shows how to set lazy initialization and start the application using SpringApplication + */ + private static ApplicationContext runUsingSpringApplication(String[] args){ + SpringApplication app = new SpringApplication(Application.class); + app.setLazyInitialization(true); + return app.run(args); + } } diff --git a/spring-boot-modules/spring-boot-property-exp/property-exp-custom-config/pom.xml b/spring-boot-modules/spring-boot-property-exp/property-exp-custom-config/pom.xml index f0df50cf76..07aeca9ad2 100644 --- a/spring-boot-modules/spring-boot-property-exp/property-exp-custom-config/pom.xml +++ b/spring-boot-modules/spring-boot-property-exp/property-exp-custom-config/pom.xml @@ -69,7 +69,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} com.baeldung.propertyexpansion.SpringBootPropertyExpansionApp @@ -80,7 +79,6 @@ Custom Property Value 2.7 - 1.6.0 diff --git a/spring-cloud-bus/pom.xml b/spring-cloud-bus/pom.xml index 15eed8dcf0..82d0bccd0b 100644 --- a/spring-cloud-bus/pom.xml +++ b/spring-cloud-bus/pom.xml @@ -34,8 +34,7 @@ - Hoxton.SR4 - 2.3.3.RELEASE + 2020.0.0 diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml index c0e452afaf..efa9df40d9 100644 --- a/spring-cloud/pom.xml +++ b/spring-cloud/pom.xml @@ -44,6 +44,7 @@ spring-cloud-circuit-breaker spring-cloud-eureka-self-preservation + spring-cloud-sentinel diff --git a/spring-cloud/spring-cloud-docker/README.md b/spring-cloud/spring-cloud-docker/README.md new file mode 100644 index 0000000000..018d4ab1eb --- /dev/null +++ b/spring-cloud/spring-cloud-docker/README.md @@ -0,0 +1,3 @@ +## Relevant Articles: + +- [Dockerizing a Spring Boot Application](https://www.baeldung.com/dockerizing-spring-boot-application) diff --git a/spring-cloud/spring-cloud-sentinel/pom.xml b/spring-cloud/spring-cloud-sentinel/pom.xml new file mode 100644 index 0000000000..612cbcbfbe --- /dev/null +++ b/spring-cloud/spring-cloud-sentinel/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + spring-cloud-sentinel + spring-cloud-sentinel + http://maven.apache.org + pom + + com.baeldung.spring.cloud + spring-cloud + 1.0.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.csp + sentinel-core + 1.8.0 + + + com.alibaba.csp + sentinel-annotation-aspectj + 1.8.0 + + + com.alibaba.csp + sentinel-transport-simple-http + 1.8.0 + + + + + UTF-8 + + diff --git a/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/SentinelApplication.java b/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/SentinelApplication.java new file mode 100644 index 0000000000..4d921844df --- /dev/null +++ b/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/SentinelApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.spring.cloud.sentinel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SentinelApplication { + public static void main(String[] args) { + SpringApplication.run(SentinelApplication.class, args); + } +} diff --git a/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/config/SentinelAspectConfiguration.java b/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/config/SentinelAspectConfiguration.java new file mode 100644 index 0000000000..4dfacef999 --- /dev/null +++ b/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/config/SentinelAspectConfiguration.java @@ -0,0 +1,65 @@ +package com.baeldung.spring.cloud.sentinel.config; + +import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.csp.sentinel.slots.system.SystemRule; +import com.alibaba.csp.sentinel.slots.system.SystemRuleManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; + +@Configuration +public class SentinelAspectConfiguration { + + public static final String RESOURCE_NAME = "greeting"; + + @Bean + public SentinelResourceAspect sentinelResourceAspect() { + return new SentinelResourceAspect(); + } + + @PostConstruct + public void init() { + initFlowRules(); + initDegradeRules(); + initSystemProtectionRules(); + } + + private void initFlowRules() { + List flowRules = new ArrayList<>(); + FlowRule flowRule = new FlowRule(); + // Defined resource + flowRule.setResource(RESOURCE_NAME); + flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); + // number of requests that QPS can pass in a second + flowRule.setCount(1); + flowRules.add(flowRule); + FlowRuleManager.loadRules(flowRules); + } + + private void initDegradeRules() { + List rules = new ArrayList(); + DegradeRule rule = new DegradeRule(); + rule.setResource(RESOURCE_NAME); + rule.setCount(10); + rule.setTimeWindow(10); + rules.add(rule); + DegradeRuleManager.loadRules(rules); + } + + private void initSystemProtectionRules() { + List rules = new ArrayList<>(); + SystemRule rule = new SystemRule(); + rule.setHighestSystemLoad(10); + rules.add(rule); + SystemRuleManager.loadRules(rules); + } + +} diff --git a/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/controller/GreetingController.java b/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/controller/GreetingController.java new file mode 100644 index 0000000000..fa3852466d --- /dev/null +++ b/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/controller/GreetingController.java @@ -0,0 +1,20 @@ +package com.baeldung.spring.cloud.sentinel.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.spring.cloud.sentinel.service.GreetingService; + +@RestController +public class GreetingController { + + @Autowired + private GreetingService greetingService; + + @GetMapping("/hello") + public String getGreeting() { + return greetingService.getGreeting(); + } + +} diff --git a/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/service/GreetingService.java b/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/service/GreetingService.java new file mode 100644 index 0000000000..e60d679b92 --- /dev/null +++ b/spring-cloud/spring-cloud-sentinel/src/main/java/com/baeldung/spring/cloud/sentinel/service/GreetingService.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.cloud.sentinel.service; + +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import org.springframework.stereotype.Service; + +@Service +public class GreetingService { + + @SentinelResource(value = "greeting", fallback = "getGreetingFallback") + public String getGreeting() { + return "Hello World!"; + } + + public String getGreetingFallback(Throwable e) { + e.printStackTrace(); + return "Bye world!"; + } + +} diff --git a/spring-core-5/README.md b/spring-core-5/README.md index 4109faca67..6befae7372 100644 --- a/spring-core-5/README.md +++ b/spring-core-5/README.md @@ -5,4 +5,5 @@ This module contains articles about core Spring functionality ## Relevant Articles: - [Spring @Component Annotation](https://www.baeldung.com/spring-component-annotation) +- [Solving Spring’s “not eligible for auto-proxying” Warning](https://www.baeldung.com/spring-not-eligible-for-auto-proxying) - More articles: [[<-- prev]](/spring-core-4) diff --git a/spring-core-5/src/test/java/com/baeldung/component/autoproxying/EligibleForAutoProxyingIntegrationTest.java b/spring-core-5/src/test/java/com/baeldung/component/autoproxying/EligibleForAutoProxyingIntegrationTest.java index 4af700813a..c7943e355b 100644 --- a/spring-core-5/src/test/java/com/baeldung/component/autoproxying/EligibleForAutoProxyingIntegrationTest.java +++ b/spring-core-5/src/test/java/com/baeldung/component/autoproxying/EligibleForAutoProxyingIntegrationTest.java @@ -40,7 +40,7 @@ public class EligibleForAutoProxyingIntegrationTest { @Test public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenNotEligibleLogShouldShowAndGroupFieldPopulated() { - List notEligibleEvents = memoryAppender.search("Bean 'randomIntGenerator' of type [com.baeldung.autoproxying.RandomIntGenerator] " + + List notEligibleEvents = memoryAppender.search("Bean 'randomIntGenerator' of type [com.baeldung.component.autoproxying.RandomIntGenerator] " + "is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)"); assertEquals(0, notEligibleEvents.size()); diff --git a/spring-core-5/src/test/java/com/baeldung/component/autoproxying/NotEligibleForAutoProxyingIntegrationTest.java b/spring-core-5/src/test/java/com/baeldung/component/autoproxying/NotEligibleForAutoProxyingIntegrationTest.java index 9434f77a30..f49f1ce602 100644 --- a/spring-core-5/src/test/java/com/baeldung/component/autoproxying/NotEligibleForAutoProxyingIntegrationTest.java +++ b/spring-core-5/src/test/java/com/baeldung/component/autoproxying/NotEligibleForAutoProxyingIntegrationTest.java @@ -39,7 +39,7 @@ public class NotEligibleForAutoProxyingIntegrationTest { @Test public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenNotEligibleLogShouldShowAndGroupFieldNotPopulated() { - List notEligibleEvents = memoryAppender.search("Bean 'randomIntGenerator' of type [com.baeldung.autoproxying.RandomIntGenerator] " + + List notEligibleEvents = memoryAppender.search("Bean 'randomIntGenerator' of type [com.baeldung.component.autoproxying.RandomIntGenerator] " + "is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)"); assertEquals(1, notEligibleEvents.size()); diff --git a/spring-integration/pom.xml b/spring-integration/pom.xml index 11b2803095..f46445a39f 100644 --- a/spring-integration/pom.xml +++ b/spring-integration/pom.xml @@ -108,7 +108,6 @@ org.codehaus.mojo exec-maven-plugin - ${exec-maven-plugin.version} com.baeldung.samples.FileCopyConfig @@ -122,7 +121,6 @@ 1.4.7 1.1.1 2.10 - 1.5.0 1.4.197 diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml index 096ffb9c3f..bb2702fc9d 100644 --- a/spring-security-modules/pom.xml +++ b/spring-security-modules/pom.xml @@ -14,33 +14,34 @@ - spring-ldap spring-5-security spring-5-security-cognito spring-5-security-oauth + spring-ldap spring-security-acl spring-security-auth0 - spring-security-web-angular/server spring-security-config spring-security-core - spring-security-web-mvc - spring-security-web-boot-1 - spring-security-web-boot-2 - spring-security-web-mvc-custom - spring-security-web-digest-auth spring-security-ldap - spring-security-web-login - spring-security-web-persisted-remember-me - spring-security-web-sockets spring-security-legacy-oidc + spring-security-oauth2-sso spring-security-oidc spring-security-okta spring-security-saml + spring-security-web-angular/server + spring-security-web-boot-1 + spring-security-web-boot-2 + spring-security-web-boot-3 + spring-security-web-digest-auth + spring-security-web-login + spring-security-web-mvc-custom + spring-security-web-mvc + spring-security-web-persisted-remember-me spring-security-web-react - spring-security-web-rest spring-security-web-rest-basic-auth spring-security-web-rest-custom - spring-security-oauth2-sso + spring-security-web-rest + spring-security-web-sockets spring-security-web-thymeleaf spring-security-web-x509 spring-session diff --git a/spring-security-modules/spring-security-web-boot-3/README.md b/spring-security-modules/spring-security-web-boot-3/README.md new file mode 100644 index 0000000000..aeba397f40 --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/README.md @@ -0,0 +1,11 @@ +## Spring Boot Security MVC + +This module contains articles about Spring Security with Spring MVC in Boot applications + +### The Course +The "REST With Spring" Classes: http://github.learnspringsecurity.com + +### Relevant Articles: + +- [TLS Setup in Spring](https://www.baeldung.com/spring-tls-setup) +- More articles: [[<-- prev]](/spring-security-modules/spring-security-web-boot-2) diff --git a/spring-security-modules/spring-security-web-boot-3/pom.xml b/spring-security-modules/spring-security-web-boot-3/pom.xml new file mode 100644 index 0000000000..a6e2b48d75 --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + spring-security-web-boot-3 + 0.0.1-SNAPSHOT + spring-security-web-boot-3 + jar + Spring Security MVC Boot - 3 + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + + diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/HomeController.java b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/HomeController.java new file mode 100644 index 0000000000..c72821cc9b --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/HomeController.java @@ -0,0 +1,16 @@ +package com.baeldung.tls; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class HomeController { + + @GetMapping("/baeldung") + public ResponseEntity welcome() { + return new ResponseEntity<>("tls/baeldung", HttpStatus.OK); + } + +} diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/SecurityConfig.java b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/SecurityConfig.java new file mode 100644 index 0000000000..63b59b8cc8 --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/SecurityConfig.java @@ -0,0 +1,16 @@ +package com.baeldung.tls; + +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; + +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/**") + .permitAll(); + } +} diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/TLSEnabledApplication.java b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/TLSEnabledApplication.java new file mode 100644 index 0000000000..62ba77d769 --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/tls/TLSEnabledApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.tls; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +public class TLSEnabledApplication { + + public static void main(String... args) { + SpringApplication application = new SpringApplication(TLSEnabledApplication.class); + application.setAdditionalProfiles("tls"); + application.run(args); + } +} diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/resources/application-tls.properties b/spring-security-modules/spring-security-web-boot-3/src/main/resources/application-tls.properties new file mode 100644 index 0000000000..002d702eab --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/resources/application-tls.properties @@ -0,0 +1,23 @@ + +server.port=8443 + +# enable/disable https +server.ssl.enabled=true +# keystore format +server.ssl.key-store-type=PKCS12 +# keystore location +server.ssl.key-store=classpath:keystore/keystore.p12 +# keystore password +server.ssl.key-store-password=changeit +server.ssl.key-alias=baeldung +# SSL protocol to use +server.ssl.protocol=TLS +# Enabled SSL protocols +server.ssl.enabled-protocols=TLSv1.2 + +#server.ssl.client-auth=need + +#trust store location +#server.ssl.trust-store=classpath:keystore/truststore.p12 +#trust store password +#server.ssl.trust-store-password=changeit \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/resources/keystore/baeldung.cer b/spring-security-modules/spring-security-web-boot-3/src/main/resources/keystore/baeldung.cer new file mode 100644 index 0000000000..1a6bb4d49b --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/resources/keystore/baeldung.cer @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIExzCCAq+gAwIBAgIEbh/WszANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwls +b2NhbGhvc3QwHhcNMjEwMzIxMDgzMzU3WhcNMzEwMzE5MDgzMzU3WjAUMRIwEAYD +VQQDEwlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCD +QWvAEewDE+vFFqYPgXFJ94bMgPZT6qdb17DkWWbL2jV5QENbSYTLAPNQ1TGUgKhj +t1LCHpooLwWIo6xvhK/qZYjh3YonSIe8Eo0fBCDoZpLO+Vp0us22NBgLOYH8hvAm +zEvPXdSZo5Qkeaqjwd6kB/z083y8OL+Civ0ARXoLsn7CFslKfZp2o/aebH6i/T+3 +hWVqasIIMtfNUrxE/pnOnV8aSAt24jcm/VxbtheqIzmcOPlCXSP1RAmFez6tJsNu +2dbUhaeOf95RCaM6a43soEvLvooGa/uqBPsRojg5WEFGf7Tc7pzB+BtALwRmHAqr +hiYjVv329QGZ+g8dADBvvqvGpGysy+X0LxixvIP14KdCqG8uMYmw5cBTJHc23AHV +iP+JsfDtdu+bzZeOZmhSsv4M3DZ1QPHEX+zCnotE+SeycqEr+3SaJELyjCB1twFT +pCRCQGWFKYCRwhjQ1vGY7qhD6ZDn30a96xAlPS+T35pA01rNgORJi8j9sf3oiwEe +oPQOecgFHdua5drHJ78j7MUz/Gvj02GnwKnBKbMYDGeBKJWm0ir4MxoU7HPaDwLj +jofXgIz+CMXkp+9arVu5IsZwL2MKNZ4fiM+VWZg9R73CAVpKvs6dTlQXe++DCaOr +XGiQeCPPpIC74vqWhAHuzPncGJndHlmXYGLhi0sk0wIDAQABoyEwHzAdBgNVHQ4E +FgQUhW4a3uWREJoTxodyD5u7i7mCaacwDQYJKoZIhvcNAQELBQADggIBAD/Qfkot +qklQr4r7eoHtyFCGozfZvsPwEediZORkycU/fCZozdVh95ebt2/N/jw7RlNhv+t+ +HahMoiXbLX2RUrqM/2X5U7BbxIpy3XjhcEYTJudqFfCxDQfxD1bFrWHygQzAanOb +sPHkcEt3tu2suV2YsJpHry/1BMo83WAoTgw/3dFtJ7oEoi/LaI03v9Qlp0+hLfA5 +zwyuuRqFn24FRvdWHX5GqAweF+WUdmmlkiJiKu2RtQsPoN7ITvZQs9t4l0zZ8w2v +QV0YdhWYLkS3g53oyOP8T5YlCFGuUOyo433hRyrzaxj3aFDkMbNrX9REB9v8Iz7X +aFsmLDJsfT5Spovz68HCIMDW1Sl1WqVkNN2V3Rwt72bn7DEbKZzGW9RC5eXEW1Zw +46XeYOVdEjzl/l623moWC5ZTlwPF1qRDaoZXT/1d1eAJE8ZBHm1YjwgDD5aFvylG +0OT1qWD5gx3nOmAbBk1e3r8ESMo9k29/4hycUUUgtFuWtBwBaY/O/4YoLx59wbpL +rFR/zjKIdYFj0AM2ABTgrG7v5pEhjLTnzjc+mZV7hJCBvB+bbC5vvfDg0K7lQUpJ +ruIPvqKfJyuTwkKmoTF5jmG04jwUDtA5iGUB3U3QiQ8zcbTiVRptXLEQDYw/bzDk +0fd4xTbok1ygI7wJ/KRyMvFXdbTKSvVu/tnM +-----END CERTIFICATE----- diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/resources/keystore/keystore.p12 b/spring-security-modules/spring-security-web-boot-3/src/main/resources/keystore/keystore.p12 new file mode 100644 index 0000000000..6f0d9c508c Binary files /dev/null and b/spring-security-modules/spring-security-web-boot-3/src/main/resources/keystore/keystore.p12 differ diff --git a/spring-web-modules/pom.xml b/spring-web-modules/pom.xml index 37ee84da25..ca96dcff35 100644 --- a/spring-web-modules/pom.xml +++ b/spring-web-modules/pom.xml @@ -41,6 +41,7 @@ spring-thymeleaf spring-thymeleaf-2 spring-thymeleaf-3 + spring-boot-jsp diff --git a/spring-web-modules/spring-boot-jsp/README.md b/spring-web-modules/spring-boot-jsp/README.md new file mode 100644 index 0000000000..535f86531d --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Spring Boot With JavaServer Pages (JSP)](https://www.baeldung.com/spring-boot-jsp) diff --git a/spring-web-modules/spring-boot-jsp/pom.xml b/spring-web-modules/spring-boot-jsp/pom.xml new file mode 100644 index 0000000000..d646b6058a --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + spring-boot-jsp + 0.0.1-SNAPSHOT + spring-boot-jsp + war + + + com.baeldung + spring-web-modules + 0.0.1-SNAPSHOT + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + javax.servlet + jstl + ${jstl.version} + + + org.apache.tomcat.embed + tomcat-embed-jasper + + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + org.springframework.boot + spring-boot-starter-web + + + + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + spring-boot-jsp + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.baeldung.boot.jsp.SpringBootJspApplication + + + + + repackage + + + + + + + + + 1.2 + 5.7.1 + 2.4.4 + + + \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspApplication.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspApplication.java new file mode 100644 index 0000000000..c77554f9f6 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspApplication.java @@ -0,0 +1,19 @@ +package com.baeldung.boot.jsp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +@SpringBootApplication(scanBasePackages = "com.baeldung.boot.jsp") +public class SpringBootJspApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { + return builder.sources(SpringBootJspApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(SpringBootJspApplication.class); + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspConfiguration.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspConfiguration.java new file mode 100644 index 0000000000..7fdae2c2a6 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/SpringBootJspConfiguration.java @@ -0,0 +1,29 @@ +package com.baeldung.boot.jsp; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.baeldung.boot.jsp.repository.BookRepository; +import com.baeldung.boot.jsp.repository.impl.InMemoryBookRepository; +import com.baeldung.boot.jsp.repository.model.BookData; + +@Configuration +public class SpringBootJspConfiguration { + + @Bean + public BookRepository provideBookRepository() { + return new InMemoryBookRepository(initialBookData()); + } + + private static Map initialBookData() { + Map initData = new HashMap<>(); + initData.put("ISBN-1", new BookData("ISBN-1", "Book 1", "Book 1 Author")); + initData.put("ISBN-2", new BookData("ISBN-2", "Book 2", "Book 2 Author")); + initData.put("ISBN-3", new BookData("ISBN-3", "Book 3", "Book 3 Author")); + return initData; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/controller/BookController.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/controller/BookController.java new file mode 100644 index 0000000000..c104a9cad3 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/controller/BookController.java @@ -0,0 +1,45 @@ +package com.baeldung.boot.jsp.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.view.RedirectView; + +import com.baeldung.boot.jsp.dto.Book; +import com.baeldung.boot.jsp.service.BookService; + +@Controller +@RequestMapping("/book") +public class BookController { + + private final BookService bookService; + + public BookController(BookService bookService) { + this.bookService = bookService; + } + + @GetMapping("/viewBooks") + public String viewBooks(Model model) { + model.addAttribute("books", bookService.getBooks()); + return "view-books"; + } + + @GetMapping("/addBook") + public String addBookView(Model model) { + model.addAttribute("book", new Book()); + return "add-book"; + } + + @PostMapping("/addBook") + public RedirectView addBook(@ModelAttribute("book") Book book, RedirectAttributes redirectAttributes) { + final RedirectView redirectView = new RedirectView("/book/addBook", true); + Book savedBook = bookService.addBook(book); + redirectAttributes.addFlashAttribute("savedBook", savedBook); + redirectAttributes.addFlashAttribute("addBookSuccess", true); + return redirectView; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/dto/Book.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/dto/Book.java new file mode 100644 index 0000000000..f4cd6b0b3b --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/dto/Book.java @@ -0,0 +1,14 @@ +package com.baeldung.boot.jsp.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class Book { + private String isbn; + private String name; + private String author; +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/exception/DuplicateBookException.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/exception/DuplicateBookException.java new file mode 100644 index 0000000000..bd52a591e3 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/exception/DuplicateBookException.java @@ -0,0 +1,13 @@ +package com.baeldung.boot.jsp.exception; + +import com.baeldung.boot.jsp.dto.Book; +import lombok.Getter; + +@Getter +public class DuplicateBookException extends RuntimeException { + private final Book book; + + public DuplicateBookException(Book book) { + this.book = book; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/exception/LibraryControllerAdvice.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/exception/LibraryControllerAdvice.java new file mode 100644 index 0000000000..8217fea2e3 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/exception/LibraryControllerAdvice.java @@ -0,0 +1,19 @@ +package com.baeldung.boot.jsp.exception; + +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; + +@ControllerAdvice +public class LibraryControllerAdvice { + + @ExceptionHandler(value = DuplicateBookException.class) + public ModelAndView duplicateBookException(DuplicateBookException e) { + final ModelAndView modelAndView = new ModelAndView(); + modelAndView.addObject("ref", e.getBook().getIsbn()); + modelAndView.addObject("object", e.getBook()); + modelAndView.addObject("message", "Cannot add an already existing book"); + modelAndView.setViewName("error-book"); + return modelAndView; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/BookRepository.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/BookRepository.java new file mode 100644 index 0000000000..f10cecfa81 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/BookRepository.java @@ -0,0 +1,14 @@ +package com.baeldung.boot.jsp.repository; + +import java.util.Collection; +import java.util.Optional; + +import com.baeldung.boot.jsp.repository.model.BookData; + +public interface BookRepository { + Collection findAll(); + + Optional findById(String isbn); + + BookData add(BookData book); +} diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepository.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepository.java new file mode 100644 index 0000000000..8fae303158 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepository.java @@ -0,0 +1,36 @@ +package com.baeldung.boot.jsp.repository.impl; + +import java.util.*; + +import com.baeldung.boot.jsp.repository.BookRepository; +import com.baeldung.boot.jsp.repository.model.BookData; + +public class InMemoryBookRepository implements BookRepository { + + private final Map storedBooks; + + public InMemoryBookRepository(Map storedBooks) { + this.storedBooks = new HashMap<>(); + this.storedBooks.putAll(storedBooks); + } + + @Override + public Collection findAll() { + if (storedBooks.isEmpty()) { + return Collections.emptyList(); + } + + return storedBooks.values(); + } + + @Override + public Optional findById(String isbn) { + return Optional.ofNullable(storedBooks.get(isbn)); + } + + @Override + public BookData add(BookData book) { + storedBooks.put(book.getIsbn(), book); + return book; + } +} diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/model/BookData.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/model/BookData.java new file mode 100644 index 0000000000..7c722a92e5 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/repository/model/BookData.java @@ -0,0 +1,16 @@ +package com.baeldung.boot.jsp.repository.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class BookData { + private String isbn; + private String name; + private String author; +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/service/BookService.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/service/BookService.java new file mode 100644 index 0000000000..7c2c401ef2 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/service/BookService.java @@ -0,0 +1,11 @@ +package com.baeldung.boot.jsp.service; + +import java.util.Collection; + +import com.baeldung.boot.jsp.dto.Book; + +public interface BookService { + Collection getBooks(); + + Book addBook(Book book); +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/service/impl/BookServiceImpl.java b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/service/impl/BookServiceImpl.java new file mode 100644 index 0000000000..519ee19205 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/java/com/baeldung/boot/jsp/service/impl/BookServiceImpl.java @@ -0,0 +1,50 @@ +package com.baeldung.boot.jsp.service.impl; + +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.baeldung.boot.jsp.exception.DuplicateBookException; +import org.springframework.stereotype.Service; + +import com.baeldung.boot.jsp.dto.Book; +import com.baeldung.boot.jsp.repository.BookRepository; +import com.baeldung.boot.jsp.repository.model.BookData; +import com.baeldung.boot.jsp.service.BookService; + +@Service +public class BookServiceImpl implements BookService { + + private final BookRepository bookRepository; + + public BookServiceImpl(BookRepository bookRepository) { + this.bookRepository = bookRepository; + } + + @Override + public Collection getBooks() { + return bookRepository.findAll() + .stream() + .map(BookServiceImpl::convertBookData) + .collect(Collectors.toList()); + } + + @Override + public Book addBook(Book book) { + final Optional existingBook = bookRepository.findById(book.getIsbn()); + if (existingBook.isPresent()) { + throw new DuplicateBookException(book); + } + + final BookData savedBook = bookRepository.add(convertBook(book)); + return convertBookData(savedBook); + } + + private static Book convertBookData(BookData bookData) { + return new Book(bookData.getIsbn(), bookData.getName(), bookData.getAuthor()); + } + + private static BookData convertBook(Book book) { + return new BookData(book.getIsbn(), book.getName(), book.getAuthor()); + } +} diff --git a/spring-web-modules/spring-boot-jsp/src/main/resources/application.properties b/spring-web-modules/spring-boot-jsp/src/main/resources/application.properties new file mode 100644 index 0000000000..56638f8c1a --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/resources/application.properties @@ -0,0 +1,4 @@ +server.servlet.context-path=/spring-boot-jsp + +spring.mvc.view.prefix: /WEB-INF/jsp/ +spring.mvc.view.suffix: .jsp \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/resources/static/css/common.css b/spring-web-modules/spring-boot-jsp/src/main/resources/static/css/common.css new file mode 100644 index 0000000000..a32d81c6a2 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/resources/static/css/common.css @@ -0,0 +1,10 @@ +table { + font-family: arial, sans-serif; + border-collapse: collapse; +} + +td, th { + border: 1px solid #dddddd; + text-align: left; + padding: 8px; +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/resources/static/error/4xx.html b/spring-web-modules/spring-boot-jsp/src/main/resources/static/error/4xx.html new file mode 100644 index 0000000000..c27bd8bb7a --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/resources/static/error/4xx.html @@ -0,0 +1,10 @@ + + + + + Error + + +Opps! 4xx Error Occurred. + + \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/add-book.jsp b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/add-book.jsp new file mode 100644 index 0000000000..8195743da8 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/add-book.jsp @@ -0,0 +1,21 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + Add Book + + + +
Successfully added Book with ISBN: ${savedBook.isbn}
+
+ + + + ISBN: + Book Name: + Author Name: + + + + \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/error-book.jsp b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/error-book.jsp new file mode 100644 index 0000000000..6db90ca5c7 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/error-book.jsp @@ -0,0 +1,18 @@ +<%-- + Created by IntelliJ IDEA. + User: jason + Date: 3/13/21 + Time: 10:39 PM + To change this template use File | Settings | File Templates. +--%> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + Error + + +

Reference: ${ref}

+

Error Message: ${message}

+

Object: ${object}

+ + diff --git a/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/view-books.jsp b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/view-books.jsp new file mode 100644 index 0000000000..4a8e00a69b --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/main/webapp/WEB-INF/jsp/view-books.jsp @@ -0,0 +1,28 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> + + + View Books + " rel="stylesheet" type="text/css"> + + + + + + + + + + + + + + + + + + + +
ISBNNameAuthor
${book.isbn}${book.name}${book.author}
+ + \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerIntegrationTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerIntegrationTest.java new file mode 100644 index 0000000000..1847cbf545 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerIntegrationTest.java @@ -0,0 +1,93 @@ +package com.baeldung.boot.jsp.controller; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasProperty; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +import java.util.Collections; +import java.util.Optional; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.baeldung.boot.jsp.repository.BookRepository; +import com.baeldung.boot.jsp.repository.impl.InMemoryBookRepository; +import com.baeldung.boot.jsp.repository.model.BookData; + +@ExtendWith(SpringExtension.class) +@WebAppConfiguration +@ContextConfiguration +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BookControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private BookRepository bookRepository; + + @Test + @Order(1) + public void whenAddBook_thenBookSaved() throws Exception { + MockHttpServletRequestBuilder addBookRequest = MockMvcRequestBuilders.post("/book/addBook") + .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("isbn", "isbn1") + .param("name", "name1") + .param("author", "author1"); + mockMvc.perform(addBookRequest) + .andReturn(); + + Optional storedBookOpt = bookRepository.findById("isbn1"); + assertTrue(storedBookOpt.isPresent()); + assertEquals("name1", storedBookOpt.get() + .getName()); + assertEquals("author1", storedBookOpt.get() + .getAuthor()); + } + + @Test + @Order(2) + public void givenAlreadyExistingBook_whenAddBook_thenShowErrorPage() throws Exception { + MockHttpServletRequestBuilder addBookRequest = MockMvcRequestBuilders.post("/book/addBook") + .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("isbn", "isbn1") + .param("name", "name1") + .param("author", "author1"); + ResultActions addBookResult = mockMvc.perform(addBookRequest); + + addBookResult.andExpect(view().name("error-book")) + .andExpect(model().attribute("ref", "isbn1")) + .andExpect(model().attribute("object", hasProperty("isbn", equalTo("isbn1")))) + .andExpect(model().attribute("message", "Cannot add an already existing book")); + } + + @Configuration + @ComponentScan("com.baeldung.boot.jsp") + static class ContextConfiguration { + + @Bean + public BookRepository provideBookRepository() { + return new InMemoryBookRepository(Collections.emptyMap()); + } + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerUnitTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerUnitTest.java new file mode 100644 index 0000000000..af1d3d4956 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/controller/BookControllerUnitTest.java @@ -0,0 +1,76 @@ +package com.baeldung.boot.jsp.controller; + +import static org.hamcrest.Matchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.mockito.AdditionalAnswers; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.baeldung.boot.jsp.dto.Book; +import com.baeldung.boot.jsp.service.BookService; + +@WebMvcTest(BookController.class) +class BookControllerUnitTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private BookService bookService; + + @Test + public void whenViewBooks_thenReturnBooksView() throws Exception { + when(bookService.getBooks()).thenReturn(existingBooks()); + ResultActions viewBooksResult = mockMvc.perform(get("/book/viewBooks")); + + viewBooksResult.andExpect(view().name("view-books")) + .andExpect(model().attribute("books", hasSize(3))); + } + + @Test + public void whenAddBookView_thenReturnAddBooksView() throws Exception { + ResultActions addBookViewResult = mockMvc.perform(get("/book/addBook")); + + addBookViewResult.andExpect(view().name("add-book")) + .andExpect(model().attribute("book", isA(Book.class))); + } + + @Test + public void whenAddBookPost_thenRedirectToAddBookView() throws Exception { + when(bookService.addBook(any(Book.class))).thenAnswer(AdditionalAnswers.returnsFirstArg()); + MockHttpServletRequestBuilder addBookRequest = MockMvcRequestBuilders.post("/book/addBook") + .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("isbn", "isbn1") + .param("name", "name1") + .param("author", "author1"); + ResultActions addBookResult = mockMvc.perform(addBookRequest); + + addBookResult.andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/book/addBook")) + .andExpect(flash().attribute("savedBook", hasProperty("isbn", equalTo("isbn1")))) + .andExpect(flash().attribute("addBookSuccess", true)); + } + + private static Collection existingBooks() { + List books = new ArrayList<>(); + books.add(new Book("isbn1", "name1", "author1")); + books.add(new Book("isbn2", "name2", "author2")); + books.add(new Book("isbn3", "name3", "author3")); + return books; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepositoryUnitTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepositoryUnitTest.java new file mode 100644 index 0000000000..83f0c19e26 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/repository/impl/InMemoryBookRepositoryUnitTest.java @@ -0,0 +1,62 @@ +package com.baeldung.boot.jsp.repository.impl; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.*; + +import org.junit.jupiter.api.Test; + +import com.baeldung.boot.jsp.repository.BookRepository; +import com.baeldung.boot.jsp.repository.model.BookData; + +public class InMemoryBookRepositoryUnitTest { + + @Test + public void givenEmtpyData_whenFindAll_thenReturnEmptyCollection() { + BookRepository bookRepository = new InMemoryBookRepository(Collections.emptyMap()); + Collection storedBooks = bookRepository.findAll(); + + assertEquals(0, storedBooks.size()); + } + + @Test + public void givenInitialData_whenFindAll_thenReturnInitialData() { + BookRepository bookRepository = new InMemoryBookRepository(initialBookData()); + Collection storedBooks = bookRepository.findAll(); + + assertEquals(3, storedBooks.size()); + } + + @Test + public void givenInitialData_whenFindUnavailableIsbn_thenReturnEmpty() { + BookRepository bookRepository = new InMemoryBookRepository(initialBookData()); + Optional storedBookOpt = bookRepository.findById("isbn4"); + + assertFalse(storedBookOpt.isPresent()); + } + + @Test + public void givenInitialData_whenFindAvailableIsbn_thenReturnItem() { + BookRepository bookRepository = new InMemoryBookRepository(initialBookData()); + Optional storedBookOpt = bookRepository.findById("isbn1"); + + assertTrue(storedBookOpt.isPresent()); + } + + @Test + public void givenAddedIsbn_whenFindAvailableIsbn_thenReturnItem() { + BookRepository bookRepository = new InMemoryBookRepository(Collections.emptyMap()); + bookRepository.add(new BookData("isbn4", "name4", "author4")); + Optional storedBookOpt = bookRepository.findById("isbn4"); + + assertTrue(storedBookOpt.isPresent()); + } + + private static Map initialBookData() { + Map initData = new HashMap<>(); + initData.put("isbn1", new BookData("isbn1", "name1", "author1")); + initData.put("isbn2", new BookData("isbn2", "name2", "author2")); + initData.put("isbn3", new BookData("isbn3", "name3", "author3")); + return initData; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/BookServiceIntegrationTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/BookServiceIntegrationTest.java new file mode 100644 index 0000000000..4223f3f970 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/BookServiceIntegrationTest.java @@ -0,0 +1,84 @@ +package com.baeldung.boot.jsp.service; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.baeldung.boot.jsp.dto.Book; +import com.baeldung.boot.jsp.exception.DuplicateBookException; +import com.baeldung.boot.jsp.repository.BookRepository; +import com.baeldung.boot.jsp.repository.impl.InMemoryBookRepository; +import com.baeldung.boot.jsp.repository.model.BookData; + +@ExtendWith(SpringExtension.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@ContextConfiguration(loader = AnnotationConfigContextLoader.class) +public class BookServiceIntegrationTest { + + @Autowired + private BookService bookService; + + @Test + @Order(1) + public void givenNoAddedBooks_whenGetAllBooks_thenReturnInitialBooks() { + Collection storedBooks = bookService.getBooks(); + + assertEquals(3, storedBooks.size()); + assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("ISBN-TEST-1")))); + assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("ISBN-TEST-2")))); + assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("ISBN-TEST-3")))); + } + + @Test + @Order(2) + public void givenBookNotAlreadyExists_whenAddBook_thenReturnSuccessfully() { + Book bookToBeAdded = new Book("ISBN-ADD-TEST-4", "Added Book 4", "Added Book 4 Author"); + Book storedBook = bookService.addBook(bookToBeAdded); + + assertEquals(bookToBeAdded.getIsbn(), storedBook.getIsbn()); + } + + @Test + @Order(3) + public void givenBookAlreadyExists_whenAddBook_thenDuplicateBookException() { + Book bookToBeAdded = new Book("ISBN-ADD-TEST-4", "Updated Book 4", "Updated Book 4 Author"); + + assertThrows(DuplicateBookException.class, () -> bookService.addBook(bookToBeAdded)); + } + + @Configuration + @ComponentScan("com.baeldung.boot.jsp") + static class ContextConfiguration { + + @Bean + public BookRepository provideBookRepository() { + return new InMemoryBookRepository(initialBookData()); + } + + private static Map initialBookData() { + Map initData = new HashMap<>(); + initData.put("ISBN-TEST-1", new BookData("ISBN-TEST-1", "Book 1", "Book 1 Author")); + initData.put("ISBN-TEST-2", new BookData("ISBN-TEST-2", "Book 2", "Book 2 Author")); + initData.put("ISBN-TEST-3", new BookData("ISBN-TEST-3", "Book 3", "Book 3 Author")); + return initData; + } + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/impl/BookServiceImplUnitTest.java b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/impl/BookServiceImplUnitTest.java new file mode 100644 index 0000000000..defbf71fd9 --- /dev/null +++ b/spring-web-modules/spring-boot-jsp/src/test/java/com/baeldung/boot/jsp/service/impl/BookServiceImplUnitTest.java @@ -0,0 +1,75 @@ +package com.baeldung.boot.jsp.service.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.AdditionalAnswers; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.baeldung.boot.jsp.dto.Book; +import com.baeldung.boot.jsp.exception.DuplicateBookException; +import com.baeldung.boot.jsp.repository.BookRepository; +import com.baeldung.boot.jsp.repository.model.BookData; +import com.baeldung.boot.jsp.service.BookService; + +@ExtendWith(MockitoExtension.class) +public class BookServiceImplUnitTest { + + @Mock + private BookRepository bookRepository; + + @Test + public void whenGetBooks_thenAllBooksReturned() { + when(bookRepository.findAll()).thenReturn(existingBooks()); + BookService bookService = new BookServiceImpl(bookRepository); + + Collection storedBooks = bookService.getBooks(); + assertEquals(3, storedBooks.size()); + assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("isbn1")))); + assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("isbn2")))); + assertThat(storedBooks, hasItem(hasProperty("isbn", equalTo("isbn3")))); + } + + @Test + public void whenAddBook_thenAddSuccessful() { + when(bookRepository.findById(anyString())).thenReturn(Optional.empty()); + when(bookRepository.add(any(BookData.class))).thenAnswer(AdditionalAnswers.returnsFirstArg()); + BookService bookService = new BookServiceImpl(bookRepository); + Book book = bookService.addBook(new Book("isbn1", "name1", "author1")); + + assertEquals("isbn1", book.getIsbn()); + assertEquals("name1", book.getName()); + assertEquals("author1", book.getAuthor()); + } + + @Test + public void givenBookAlreadyExist_whenAddBook_thenDuplicateBookException() { + BookData existingBook = new BookData("isbn1", "name1", "author1"); + when(bookRepository.findById("isbn1")).thenReturn(Optional.of(existingBook)); + BookService bookService = new BookServiceImpl(bookRepository); + Book bookToBeAdded = new Book("isbn1", "name1", "author1"); + + assertThrows(DuplicateBookException.class, () -> bookService.addBook(bookToBeAdded)); + } + + private static Collection existingBooks() { + List books = new ArrayList<>(); + books.add(new BookData("isbn1", "name1", "author1")); + books.add(new BookData("isbn2", "name2", "author2")); + books.add(new BookData("isbn3", "name3", "author3")); + return books; + } +} \ No newline at end of file diff --git a/testing-modules/junit-5/pom.xml b/testing-modules/junit-5/pom.xml index ded3e9e26d..90898ebb3f 100644 --- a/testing-modules/junit-5/pom.xml +++ b/testing-modules/junit-5/pom.xml @@ -136,7 +136,6 @@ 2.8.2 2.0.0 2.22.0 - 1.6.0 5.0.1.RELEASE 3.0.0-M3 diff --git a/testing-modules/junit5-migration/pom.xml b/testing-modules/junit5-migration/pom.xml index e3ef21f506..bab7bc0406 100644 --- a/testing-modules/junit5-migration/pom.xml +++ b/testing-modules/junit5-migration/pom.xml @@ -47,23 +47,6 @@ true - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - java - - - - - com.baeldung.TestLauncher - - - @@ -71,7 +54,6 @@ 1.2.0 5.2.0 2.21.0 - 1.6.0 diff --git a/testing-modules/mockito-3/README.md b/testing-modules/mockito-3/README.md new file mode 100644 index 0000000000..c9766031a3 --- /dev/null +++ b/testing-modules/mockito-3/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Mocking Static Methods With Mockito](https://www.baeldung.com/mockito-mock-static-methods) diff --git a/testing-modules/pom.xml b/testing-modules/pom.xml index c0c28e085d..228e47838c 100644 --- a/testing-modules/pom.xml +++ b/testing-modules/pom.xml @@ -16,35 +16,35 @@ assertion-libraries easy-random + easymock gatling groovy-spock + hamcrest + junit-4 + junit-5-advanced + junit-5-basics junit-5 junit5-annotations junit5-migration load-testing-comparison + mockito-2 + mockito-3 mockito - mockito-2 - mockito-3 - hamcrest mocks mockserver parallel-tests-junit + powermock rest-assured rest-testing selenium-junit-testng - spring-testing spring-testing-2 + spring-testing test-containers testing-assertions - testng - junit-5-basics - easymock - junit-5-advanced - xmlunit-2 - junit-4 - testing-libraries testing-libraries-2 - powermock + testing-libraries + testng + xmlunit-2 zerocode diff --git a/testing-modules/test-containers/pom.xml b/testing-modules/test-containers/pom.xml index 1946b7306f..2280a89b4a 100644 --- a/testing-modules/test-containers/pom.xml +++ b/testing-modules/test-containers/pom.xml @@ -79,23 +79,6 @@ true - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin.version} - - - - java - - - - - com.baeldung.TestLauncher - - -