diff --git a/java-collections-maps-3/README.md b/java-collections-maps-3/README.md
deleted file mode 100644
index ed68eb00a0..0000000000
--- a/java-collections-maps-3/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-## Java Collections Cookbooks and Examples
-
-This module contains articles about Map data structures in Java.
-
-### Relevant Articles:
-
-- More articles: [[<-- prev>]](/../java-collections-maps)
-- More articles: [[<-- prev>]](/../java-collections-maps-2)
diff --git a/jee-7/pom.xml b/jee-7/pom.xml
index a2593e46a5..7352c6550a 100644
--- a/jee-7/pom.xml
+++ b/jee-7/pom.xml
@@ -242,6 +242,29 @@
+
+
+ org.codehaus.mojo
+ jaxws-maven-plugin
+ 2.6
+
+
+ wsimport-from-jdk
+
+ wsimport
+
+
+
+
+ src/main/resources
+
+ country.wsdl
+
+ true
+ com.baeldung.soap.ws.client.generated
+ src/main/java
+
+
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/Country.java b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/Country.java
new file mode 100644
index 0000000000..6a810b9afa
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/Country.java
@@ -0,0 +1,129 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Java class for country complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType name="country">
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="capital" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ * <element name="currency" type="{http://server.ws.soap.baeldung.com/}currency" minOccurs="0"/>
+ * <element name="name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ * <element name="population" type="{http://www.w3.org/2001/XMLSchema}int"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "country", propOrder = { "capital", "currency", "name", "population" })
+public class Country {
+
+ protected String capital;
+ @XmlSchemaType(name = "string")
+ protected Currency currency;
+ protected String name;
+ protected int population;
+
+ /**
+ * Gets the value of the capital property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getCapital() {
+ return capital;
+ }
+
+ /**
+ * Sets the value of the capital property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setCapital(String value) {
+ this.capital = value;
+ }
+
+ /**
+ * Gets the value of the currency property.
+ *
+ * @return
+ * possible object is
+ * {@link Currency }
+ *
+ */
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ /**
+ * Sets the value of the currency property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Currency }
+ *
+ */
+ public void setCurrency(Currency value) {
+ this.currency = value;
+ }
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+ /**
+ * Gets the value of the population property.
+ *
+ */
+ public int getPopulation() {
+ return population;
+ }
+
+ /**
+ * Sets the value of the population property.
+ *
+ */
+ public void setPopulation(int value) {
+ this.population = value;
+ }
+
+}
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java
new file mode 100644
index 0000000000..bda4a305a5
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java
@@ -0,0 +1,34 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import javax.jws.WebMethod;
+import javax.jws.WebParam;
+import javax.jws.WebResult;
+import javax.jws.WebService;
+import javax.jws.soap.SOAPBinding;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.ws.Action;
+
+/**
+ * This class was generated by the JAX-WS RI.
+ * JAX-WS RI 2.3.2
+ * Generated source version: 2.2
+ *
+ */
+@WebService(name = "CountryService", targetNamespace = "http://server.ws.soap.baeldung.com/")
+@SOAPBinding(style = SOAPBinding.Style.RPC)
+@XmlSeeAlso({ ObjectFactory.class })
+public interface CountryService {
+
+ /**
+ *
+ * @param arg0
+ * @return
+ * returns com.baeldung.soap.ws.client.generated.Country
+ */
+ @WebMethod
+ @WebResult(partName = "return")
+ @Action(input = "http://server.ws.soap.baeldung.com/CountryService/findByNameRequest", output = "http://server.ws.soap.baeldung.com/CountryService/findByNameResponse")
+ public Country findByName(@WebParam(name = "arg0", partName = "arg0") String arg0);
+
+}
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java
new file mode 100644
index 0000000000..09f4c29202
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java
@@ -0,0 +1,91 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.xml.namespace.QName;
+import javax.xml.ws.Service;
+import javax.xml.ws.WebEndpoint;
+import javax.xml.ws.WebServiceClient;
+import javax.xml.ws.WebServiceException;
+import javax.xml.ws.WebServiceFeature;
+
+/**
+ * This class was generated by the JAX-WS RI.
+ * JAX-WS RI 2.3.2
+ * Generated source version: 2.2
+ *
+ */
+@WebServiceClient(name = "CountryServiceImplService", targetNamespace = "http://server.ws.soap.baeldung.com/", wsdlLocation = "file:src/main/resources/country.wsdl")
+public class CountryServiceImplService extends Service {
+
+ private final static URL COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION;
+ private final static WebServiceException COUNTRYSERVICEIMPLSERVICE_EXCEPTION;
+ private final static QName COUNTRYSERVICEIMPLSERVICE_QNAME = new QName("http://server.ws.soap.baeldung.com/", "CountryServiceImplService");
+
+ static {
+ URL url = null;
+ WebServiceException e = null;
+ try {
+ url = new URL("file:src/main/resources/country.wsdl");
+ } catch (MalformedURLException ex) {
+ e = new WebServiceException(ex);
+ }
+ COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION = url;
+ COUNTRYSERVICEIMPLSERVICE_EXCEPTION = e;
+ }
+
+ public CountryServiceImplService() {
+ super(__getWsdlLocation(), COUNTRYSERVICEIMPLSERVICE_QNAME);
+ }
+
+ public CountryServiceImplService(WebServiceFeature... features) {
+ super(__getWsdlLocation(), COUNTRYSERVICEIMPLSERVICE_QNAME, features);
+ }
+
+ public CountryServiceImplService(URL wsdlLocation) {
+ super(wsdlLocation, COUNTRYSERVICEIMPLSERVICE_QNAME);
+ }
+
+ public CountryServiceImplService(URL wsdlLocation, WebServiceFeature... features) {
+ super(wsdlLocation, COUNTRYSERVICEIMPLSERVICE_QNAME, features);
+ }
+
+ public CountryServiceImplService(URL wsdlLocation, QName serviceName) {
+ super(wsdlLocation, serviceName);
+ }
+
+ public CountryServiceImplService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
+ super(wsdlLocation, serviceName, features);
+ }
+
+ /**
+ *
+ * @return
+ * returns CountryService
+ */
+ @WebEndpoint(name = "CountryServiceImplPort")
+ public CountryService getCountryServiceImplPort() {
+ return super.getPort(new QName("http://server.ws.soap.baeldung.com/", "CountryServiceImplPort"), CountryService.class);
+ }
+
+ /**
+ *
+ * @param features
+ * A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the features
parameter will have their default values.
+ * @return
+ * returns CountryService
+ */
+ @WebEndpoint(name = "CountryServiceImplPort")
+ public CountryService getCountryServiceImplPort(WebServiceFeature... features) {
+ return super.getPort(new QName("http://server.ws.soap.baeldung.com/", "CountryServiceImplPort"), CountryService.class, features);
+ }
+
+ private static URL __getWsdlLocation() {
+ if (COUNTRYSERVICEIMPLSERVICE_EXCEPTION != null) {
+ throw COUNTRYSERVICEIMPLSERVICE_EXCEPTION;
+ }
+ return COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION;
+ }
+
+}
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java
new file mode 100644
index 0000000000..8b9355edc5
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java
@@ -0,0 +1,37 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Java class for currency.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <simpleType name="currency">
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ * <enumeration value="EUR"/>
+ * <enumeration value="INR"/>
+ * <enumeration value="USD"/>
+ * </restriction>
+ * </simpleType>
+ *
+ *
+ */
+@XmlType(name = "currency")
+@XmlEnum
+public enum Currency {
+
+ EUR, INR, USD;
+
+ public String value() {
+ return name();
+ }
+
+ public static Currency fromValue(String v) {
+ return valueOf(v);
+ }
+
+}
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java
new file mode 100644
index 0000000000..241debe758
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java
@@ -0,0 +1,38 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import javax.xml.bind.annotation.XmlRegistry;
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the com.baeldung.soap.ws.client.generated package.
+ * An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.baeldung.soap.ws.client.generated
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link Country }
+ *
+ */
+ public Country createCountry() {
+ return new Country();
+ }
+
+}
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/package-info.java b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/package-info.java
new file mode 100644
index 0000000000..6df70b70f1
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/client/generated/package-info.java
@@ -0,0 +1,2 @@
+@javax.xml.bind.annotation.XmlSchema(namespace = "http://server.ws.soap.baeldung.com/", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
+package com.baeldung.soap.ws.client.generated;
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/server/Country.java b/jee-7/src/main/java/com/baeldung/soap/ws/server/Country.java
new file mode 100644
index 0000000000..62ea4a22ed
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/server/Country.java
@@ -0,0 +1,41 @@
+package com.baeldung.soap.ws.server;
+
+public class Country {
+ protected String name;
+ protected int population;
+ protected String capital;
+ protected Currency currency;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getPopulation() {
+ return population;
+ }
+
+ public void setPopulation(int population) {
+ this.population = population;
+ }
+
+ public String getCapital() {
+ return capital;
+ }
+
+ public void setCapital(String capital) {
+ this.capital = capital;
+ }
+
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(Currency currency) {
+ this.currency = currency;
+ }
+
+}
\ No newline at end of file
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryRepository.java b/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryRepository.java
new file mode 100644
index 0000000000..558f7c1293
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryRepository.java
@@ -0,0 +1,43 @@
+package com.baeldung.soap.ws.server;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CountryRepository {
+
+ private static final Map countries = new HashMap<>();
+
+ {
+ initData();
+ }
+
+ private final static void initData() {
+ Country usa = new Country();
+ usa.setName("USA");
+ usa.setCapital("Washington D.C.");
+ usa.setCurrency(Currency.USD);
+ usa.setPopulation(323947000);
+
+ countries.put(usa.getName(), usa);
+
+ Country india = new Country();
+ india.setName("India");
+ india.setCapital("New Delhi");
+ india.setCurrency(Currency.INR);
+ india.setPopulation(1295210000);
+
+ countries.put(india.getName(), india);
+
+ Country france = new Country();
+ france.setName("France");
+ france.setCapital("Paris");
+ france.setCurrency(Currency.EUR);
+ france.setPopulation(66710000);
+
+ countries.put(france.getName(), france);
+ }
+
+ public Country findCountry(String name) {
+ return countries.get(name);
+ }
+}
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryService.java b/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryService.java
new file mode 100644
index 0000000000..e3f68a4e59
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryService.java
@@ -0,0 +1,15 @@
+package com.baeldung.soap.ws.server;
+
+import javax.jws.WebMethod;
+import javax.jws.WebService;
+import javax.jws.soap.SOAPBinding;
+import javax.jws.soap.SOAPBinding.Style;
+
+@WebService
+@SOAPBinding(style=Style.RPC)
+public interface CountryService {
+
+ @WebMethod
+ Country findByName(String name);
+
+}
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java b/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java
new file mode 100644
index 0000000000..a8c6250354
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java
@@ -0,0 +1,15 @@
+package com.baeldung.soap.ws.server;
+
+import javax.jws.WebService;
+
+@WebService(endpointInterface = "com.baeldung.soap.ws.server.CountryService")
+public class CountryServiceImpl implements CountryService {
+
+ private CountryRepository countryRepository = new CountryRepository();
+
+ @Override
+ public Country findByName(String name) {
+ return countryRepository.findCountry(name);
+ }
+
+}
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java b/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java
new file mode 100644
index 0000000000..e7c1c480f4
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java
@@ -0,0 +1,19 @@
+package com.baeldung.soap.ws.server;
+
+import javax.xml.ws.Endpoint;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class CountryServicePublisher {
+
+ private static final Logger logger = LoggerFactory.getLogger(CountryServicePublisher.class);
+
+ public static void main(String[] args) {
+ Endpoint endpoint = Endpoint.create(new CountryServiceImpl());
+ endpoint.publish("http://localhost:8888/ws/country");
+
+ logger.info("Country web service ready to consume requests!");
+ }
+}
\ No newline at end of file
diff --git a/jee-7/src/main/java/com/baeldung/soap/ws/server/Currency.java b/jee-7/src/main/java/com/baeldung/soap/ws/server/Currency.java
new file mode 100644
index 0000000000..d1b25a26c6
--- /dev/null
+++ b/jee-7/src/main/java/com/baeldung/soap/ws/server/Currency.java
@@ -0,0 +1,15 @@
+package com.baeldung.soap.ws.server;
+
+public enum Currency {
+
+ EUR, INR, USD;
+
+ public String value() {
+ return name();
+ }
+
+ public static Currency fromValue(String v) {
+ return valueOf(v);
+ }
+
+}
diff --git a/jee-7/src/main/resources/country.wsdl b/jee-7/src/main/resources/country.wsdl
new file mode 100644
index 0000000000..4d41fce322
--- /dev/null
+++ b/jee-7/src/main/resources/country.wsdl
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jee-7/src/main/resources/country.xsd b/jee-7/src/main/resources/country.xsd
new file mode 100644
index 0000000000..c94b6047f9
--- /dev/null
+++ b/jee-7/src/main/resources/country.xsd
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jee-7/src/test/java/com/baeldung/soap/ws/client/CountryClientLiveTest.java b/jee-7/src/test/java/com/baeldung/soap/ws/client/CountryClientLiveTest.java
new file mode 100644
index 0000000000..ae423f9bdd
--- /dev/null
+++ b/jee-7/src/test/java/com/baeldung/soap/ws/client/CountryClientLiveTest.java
@@ -0,0 +1,39 @@
+package com.baeldung.soap.ws.client;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baeldung.soap.ws.client.generated.CountryService;
+import com.baeldung.soap.ws.client.generated.CountryServiceImplService;
+import com.baeldung.soap.ws.client.generated.Currency;
+
+//Ensure that com.baeldung.soap.ws.server.CountryServicePublisher is running before executing this test
+public class CountryClientLiveTest {
+
+ private static CountryService countryService;
+
+ @BeforeClass
+ public static void setup() {
+ CountryServiceImplService service = new CountryServiceImplService();
+ countryService = service.getCountryServiceImplPort();
+ }
+
+ @Test
+ public void givenCountryService_whenCountryIndia_thenCapitalIsNewDelhi() {
+ assertEquals("New Delhi", countryService.findByName("India").getCapital());
+ }
+
+ @Test
+ public void givenCountryService_whenCountryFrance_thenPopulationCorrect() {
+ assertEquals(66710000, countryService.findByName("France").getPopulation());
+ }
+
+ @Test
+ public void givenCountryService_whenCountryUSA_thenCurrencyUSD() {
+ assertEquals(Currency.USD, countryService.findByName("USA").getCurrency());
+ }
+
+
+}
diff --git a/jhipster-5/pom.xml b/jhipster-5/pom.xml
index cebbe25d8b..2a5132e50e 100644
--- a/jhipster-5/pom.xml
+++ b/jhipster-5/pom.xml
@@ -9,10 +9,10 @@
pom
- parent-boot-1
+ parent-boot-2
com.baeldung
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
diff --git a/jjwt/pom.xml b/jjwt/pom.xml
index 073f12a922..aa238fafb5 100644
--- a/jjwt/pom.xml
+++ b/jjwt/pom.xml
@@ -10,9 +10,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
@@ -30,6 +30,11 @@
org.springframework.boot
spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
io.jsonwebtoken
diff --git a/jmeter/pom.xml b/jmeter/pom.xml
index 64642695ff..945210edd7 100644
--- a/jmeter/pom.xml
+++ b/jmeter/pom.xml
@@ -9,9 +9,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
diff --git a/kaniko/dockerfile b/kaniko/dockerfile
new file mode 100644
index 0000000000..0290bf16ed
--- /dev/null
+++ b/kaniko/dockerfile
@@ -0,0 +1,2 @@
+FROM ubuntu
+ENTRYPOINT ["/bin/bash", "-c", "echo hello"]
diff --git a/kaniko/pod.yaml b/kaniko/pod.yaml
new file mode 100644
index 0000000000..17f9a81b6d
--- /dev/null
+++ b/kaniko/pod.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: kaniko
+spec:
+ containers:
+ - name: kaniko
+ image: gcr.io/kaniko-project/executor:latest
+ args: ["--dockerfile=/workspace/dockerfile",
+ "--context=dir://workspace",
+ "--no-push"]
+ volumeMounts:
+ - name: dockerfile-storage
+ mountPath: /workspace
+ restartPolicy: Never
+ volumes:
+ - name: dockerfile-storage
+ persistentVolumeClaim:
+ claimName: dockerfile-claim
diff --git a/kaniko/volume-claim.yaml b/kaniko/volume-claim.yaml
new file mode 100644
index 0000000000..7a1abbf05c
--- /dev/null
+++ b/kaniko/volume-claim.yaml
@@ -0,0 +1,11 @@
+kind: PersistentVolumeClaim
+apiVersion: v1
+metadata:
+ name: dockerfile-claim
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 8Gi
+ storageClassName: local-storage
diff --git a/kaniko/volume.yaml b/kaniko/volume.yaml
new file mode 100644
index 0000000000..e44663ec5a
--- /dev/null
+++ b/kaniko/volume.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: dockerfile
+ labels:
+ type: local
+spec:
+ capacity:
+ storage: 10Gi
+ accessModes:
+ - ReadWriteOnce
+ storageClassName: local-storage
+ hostPath:
+ path: /home/docker/kaniko # Path to the local mount directory that was setup
diff --git a/libraries-data-2/README.md b/libraries-data-2/README.md
index 24618b5e37..f992186bd9 100644
--- a/libraries-data-2/README.md
+++ b/libraries-data-2/README.md
@@ -11,4 +11,7 @@ This module contains articles about libraries for data processing in Java.
- [Guide to JMapper](https://www.baeldung.com/jmapper)
- [An Introduction to SuanShu](https://www.baeldung.com/suanshu)
- [Intro to Derive4J](https://www.baeldung.com/derive4j)
-More articles: [[<-- prev]](/../libraries-data)
\ No newline at end of file
+More articles: [[<-- prev]](/../libraries-data)
+
+##### Building the project
+You can build the project from the command line using: *mvn clean install*, or in an IDE. If you have issues with the derive4j imports in your IDE, you have to add the folder: *target/generated-sources/annotations* to the project build path in your IDE.
diff --git a/libraries-security/pom.xml b/libraries-security/pom.xml
index e287de4527..e02f766141 100644
--- a/libraries-security/pom.xml
+++ b/libraries-security/pom.xml
@@ -8,9 +8,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
@@ -23,7 +23,9 @@
org.springframework.security.oauth
spring-security-oauth2
+ ${spring-boot.version}
+
org.springframework
spring-web
diff --git a/libraries-security/src/main/resources/Baeldung.p12 b/libraries-security/src/main/resources/Baeldung.p12
new file mode 100644
index 0000000000..65ec8bc8fe
Binary files /dev/null and b/libraries-security/src/main/resources/Baeldung.p12 differ
diff --git a/maven-all/maven/README.md b/maven-all/maven/README.md
index ced4a52703..c5f46ca184 100644
--- a/maven-all/maven/README.md
+++ b/maven-all/maven/README.md
@@ -7,7 +7,6 @@ have their own dedicated modules.
- [Guide to the Core Maven Plugins](https://www.baeldung.com/core-maven-plugins)
- [Maven Resources Plugin](https://www.baeldung.com/maven-resources-plugin)
-- [Maven Compiler Plugin](https://www.baeldung.com/maven-compiler-plugin)
- [Quick Guide to the Maven Surefire Plugin](https://www.baeldung.com/maven-surefire-plugin)
- [The Maven Failsafe Plugin](https://www.baeldung.com/maven-failsafe-plugin)
- [The Maven Verifier Plugin](https://www.baeldung.com/maven-verifier-plugin)
diff --git a/maven-all/maven/custom-rule/README.md b/maven-all/maven/custom-rule/README.md
deleted file mode 100644
index 44d43050e7..0000000000
--- a/maven-all/maven/custom-rule/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-### Relevant Articles:
-
-- [Maven Enforcer Plugin](https://www.baeldung.com/maven-enforcer-plugin)
diff --git a/maven-all/versions-maven-plugin/original/README.md b/maven-all/versions-maven-plugin/original/README.md
deleted file mode 100644
index 5652a9b7e4..0000000000
--- a/maven-all/versions-maven-plugin/original/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-### Relevant Articles:
-
-- [Use the Latest Version of a Dependency in Maven](https://www.baeldung.com/maven-dependency-latest-version)
diff --git a/mesos-marathon/README.md b/mesos-marathon/README.md
index 65b2361698..8e5b8e4974 100644
--- a/mesos-marathon/README.md
+++ b/mesos-marathon/README.md
@@ -6,4 +6,4 @@ This module contains articles about Marathon and Mesos.
- [Simple Jenkins Pipeline with Marathon and Mesos](https://www.baeldung.com/jenkins-pipeline-with-marathon-mesos)
- To run the pipeline, please modify the dockerise.sh file with your own useranema and password for docker login.
+ To run the pipeline, please modify the dockerise.sh file with your own username and password for docker login.
diff --git a/mesos-marathon/pom.xml b/mesos-marathon/pom.xml
index 4fb819c434..42798bb209 100644
--- a/mesos-marathon/pom.xml
+++ b/mesos-marathon/pom.xml
@@ -7,9 +7,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
diff --git a/mesos-marathon/src/test/java/com/baeldung/DemoApplicationIntegrationTest.java b/mesos-marathon/src/test/java/com/baeldung/DemoApplicationIntegrationTest.java
index 85331516f9..dfe944a316 100644
--- a/mesos-marathon/src/test/java/com/baeldung/DemoApplicationIntegrationTest.java
+++ b/mesos-marathon/src/test/java/com/baeldung/DemoApplicationIntegrationTest.java
@@ -3,8 +3,8 @@ package com.baeldung;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;
diff --git a/netty/README.md b/netty/README.md
deleted file mode 100644
index b006c1c686..0000000000
--- a/netty/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-## Netty
-
-This module contains articles about Netty.
-
-### Relevant Articles:
-
diff --git a/persistence-modules/flyway/pom.xml b/persistence-modules/flyway/pom.xml
index 01b3dab6ee..f2e393abbf 100644
--- a/persistence-modules/flyway/pom.xml
+++ b/persistence-modules/flyway/pom.xml
@@ -9,9 +9,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../../parent-boot-1
+ ../../parent-boot-2
@@ -63,7 +63,7 @@
- 5.0.2
+ 5.2.3
5.0.2
diff --git a/persistence-modules/flyway/src/main/resources/application.properties b/persistence-modules/flyway/src/main/resources/application.properties
index 4d339eca5c..cee75fa4d9 100644
--- a/persistence-modules/flyway/src/main/resources/application.properties
+++ b/persistence-modules/flyway/src/main/resources/application.properties
@@ -1 +1 @@
-#flyway.enabled=false
\ No newline at end of file
+#spring.flyway.enabled=false
\ No newline at end of file
diff --git a/persistence-modules/spring-jpa/README.md b/persistence-modules/spring-jpa/README.md
index 27a2fe8368..599a667a13 100644
--- a/persistence-modules/spring-jpa/README.md
+++ b/persistence-modules/spring-jpa/README.md
@@ -10,7 +10,7 @@
- [Self-Contained Testing Using an In-Memory Database](https://www.baeldung.com/spring-jpa-test-in-memory-database)
- [A Guide to Spring AbstractRoutingDatasource](https://www.baeldung.com/spring-abstract-routing-data-source)
- [Obtaining Auto-generated Keys in Spring JDBC](https://www.baeldung.com/spring-jdbc-autogenerated-keys)
-- [Transactions with Spring 4 and JPA](https://www.baeldung.com/transaction-configuration-with-jpa-and-spring)
+- [Transactions with Spring and JPA](https://www.baeldung.com/transaction-configuration-with-jpa-and-spring)
- [Use Criteria Queries in a Spring Data Application](https://www.baeldung.com/spring-data-criteria-queries)
- [Many-To-Many Relationship in JPA](https://www.baeldung.com/jpa-many-to-many)
- [Spring Persistence (Hibernate and JPA) with a JNDI datasource](https://www.baeldung.com/spring-persistence-hibernate-and-jpa-with-a-jndi-datasource/)
diff --git a/persistence-modules/spring-persistence-simple-2/src/main/java/com/baeldung/jdbc/Employee.java b/persistence-modules/spring-persistence-simple-2/src/main/java/com/baeldung/jdbc/Employee.java
index a43eb265c7..bd6fe0fb15 100644
--- a/persistence-modules/spring-persistence-simple-2/src/main/java/com/baeldung/jdbc/Employee.java
+++ b/persistence-modules/spring-persistence-simple-2/src/main/java/com/baeldung/jdbc/Employee.java
@@ -7,7 +7,12 @@ public class Employee {
private String lastName;
- private String address;
+
+ public Employee(int id, String firstName, String lastName) {
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
public int getId() {
return id;
@@ -33,12 +38,5 @@ public class Employee {
this.lastName = lastName;
}
- public String getAddress() {
- return address;
- }
-
- public void setAddress(final String address) {
- this.address = address;
- }
}
diff --git a/persistence-modules/spring-persistence-simple-2/src/main/java/com/baeldung/jdbc/EmployeeDAO.java b/persistence-modules/spring-persistence-simple-2/src/main/java/com/baeldung/jdbc/EmployeeDAO.java
index b5bf9452ed..2ea42381eb 100644
--- a/persistence-modules/spring-persistence-simple-2/src/main/java/com/baeldung/jdbc/EmployeeDAO.java
+++ b/persistence-modules/spring-persistence-simple-2/src/main/java/com/baeldung/jdbc/EmployeeDAO.java
@@ -1,21 +1,67 @@
package com.baeldung.jdbc;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;
@Repository
public class EmployeeDAO {
private JdbcTemplate jdbcTemplate;
+ private NamedParameterJdbcTemplate namedJdbcTemplate;
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
+ namedJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int getCountOfEmployees() {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EMPLOYEE", Integer.class);
}
-
+ public List getEmployeesFromIdListNamed(List ids) {
+ SqlParameterSource parameters = new MapSqlParameterSource("ids", ids);
+ List employees = namedJdbcTemplate.query(
+ "SELECT * FROM EMPLOYEE WHERE id IN (:ids)",
+ parameters,
+ (rs, rowNum) -> new Employee(rs.getInt("id"), rs.getString("first_name"), rs.getString("last_name")));
+
+ return employees;
+ }
+
+ public List getEmployeesFromIdList(List ids) {
+ String inSql = String.join(",", Collections.nCopies(ids.size(), "?"));
+ List employees = jdbcTemplate.query(
+ String.format("SELECT * FROM EMPLOYEE WHERE id IN (%s)", inSql),
+ ids.toArray(),
+ (rs, rowNum) -> new Employee(rs.getInt("id"), rs.getString("first_name"), rs.getString("last_name")));
+
+ return employees;
+ }
+
+ public List getEmployeesFromLargeIdList(List ids) {
+ jdbcTemplate.execute("CREATE TEMPORARY TABLE IF NOT EXISTS employee_tmp (id INT NOT NULL)");
+
+ List employeeIds = new ArrayList<>();
+ for (Integer id : ids) {
+ employeeIds.add(new Object[] { id });
+ }
+ jdbcTemplate.batchUpdate("INSERT INTO employee_tmp VALUES(?)", employeeIds);
+
+ List employees = jdbcTemplate.query(
+ "SELECT * FROM EMPLOYEE WHERE id IN (SELECT id FROM employee_tmp)",
+ (rs, rowNum) -> new Employee(rs.getInt("id"), rs.getString("first_name"), rs.getString("last_name")));
+
+ jdbcTemplate.update("DELETE FROM employee_tmp");
+
+ return employees;
+ }
+
}
diff --git a/persistence-modules/spring-persistence-simple-2/src/main/resources/jdbc/schema.sql b/persistence-modules/spring-persistence-simple-2/src/main/resources/jdbc/schema.sql
index 3401c5d963..be102431ca 100644
--- a/persistence-modules/spring-persistence-simple-2/src/main/resources/jdbc/schema.sql
+++ b/persistence-modules/spring-persistence-simple-2/src/main/resources/jdbc/schema.sql
@@ -2,6 +2,5 @@ CREATE TABLE EMPLOYEE
(
ID int NOT NULL PRIMARY KEY,
FIRST_NAME varchar(255),
- LAST_NAME varchar(255),
- ADDRESS varchar(255)
+ LAST_NAME varchar(255)
);
\ No newline at end of file
diff --git a/persistence-modules/spring-persistence-simple-2/src/main/resources/jdbc/test-data.sql b/persistence-modules/spring-persistence-simple-2/src/main/resources/jdbc/test-data.sql
index c1669c156d..5421c09849 100644
--- a/persistence-modules/spring-persistence-simple-2/src/main/resources/jdbc/test-data.sql
+++ b/persistence-modules/spring-persistence-simple-2/src/main/resources/jdbc/test-data.sql
@@ -1,4 +1,4 @@
-INSERT INTO EMPLOYEE VALUES (1, 'James', 'Gosling', 'Canada');
-INSERT INTO EMPLOYEE VALUES (2, 'Donald', 'Knuth', 'USA');
-INSERT INTO EMPLOYEE VALUES (3, 'Linus', 'Torvalds', 'Finland');
-INSERT INTO EMPLOYEE VALUES (4, 'Dennis', 'Ritchie', 'USA');
\ No newline at end of file
+INSERT INTO EMPLOYEE VALUES (1, 'James', 'Gosling');
+INSERT INTO EMPLOYEE VALUES (2, 'Donald', 'Knuth');
+INSERT INTO EMPLOYEE VALUES (3, 'Linus', 'Torvalds');
+INSERT INTO EMPLOYEE VALUES (4, 'Dennis', 'Ritchie');
\ No newline at end of file
diff --git a/persistence-modules/spring-persistence-simple-2/src/test/java/com/baeldung/jdbc/EmployeeDAOUnitTest.java b/persistence-modules/spring-persistence-simple-2/src/test/java/com/baeldung/jdbc/EmployeeDAOUnitTest.java
index 71e8fb4263..369725bafd 100644
--- a/persistence-modules/spring-persistence-simple-2/src/test/java/com/baeldung/jdbc/EmployeeDAOUnitTest.java
+++ b/persistence-modules/spring-persistence-simple-2/src/test/java/com/baeldung/jdbc/EmployeeDAOUnitTest.java
@@ -2,8 +2,12 @@ package com.baeldung.jdbc;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.sql.DataSource;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -14,13 +18,24 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.test.util.ReflectionTestUtils;
-
@RunWith(MockitoJUnitRunner.class)
public class EmployeeDAOUnitTest {
@Mock
JdbcTemplate jdbcTemplate;
+ DataSource dataSource;
+
+ @Before
+ public void setup() {
+ dataSource = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
+ .generateUniqueName(true)
+ .addScript("classpath:jdbc/schema.sql")
+ .addScript("classpath:jdbc/test-data.sql")
+ .build();
+
+ }
+
@Test
public void whenMockJdbcTemplate_thenReturnCorrectEmployeeCount() {
EmployeeDAO employeeDAO = new EmployeeDAO();
@@ -38,14 +53,55 @@ public class EmployeeDAOUnitTest {
@Test
public void whenInjectInMemoryDataSource_thenReturnCorrectEmployeeCount() {
- DataSource dataSource = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
- .addScript("classpath:jdbc/schema.sql")
- .addScript("classpath:jdbc/test-data.sql")
- .build();
-
EmployeeDAO employeeDAO = new EmployeeDAO();
employeeDAO.setDataSource(dataSource);
assertEquals(4, employeeDAO.getCountOfEmployees());
}
+
+ @Test
+ public void givenSmallIdList_whenGetEmployeesFromIdList_thenReturnCorrectEmployees() {
+ List ids = new ArrayList<>();
+ ids.add(1);
+ ids.add(3);
+ ids.add(4);
+ EmployeeDAO employeeDAO = new EmployeeDAO();
+ employeeDAO.setDataSource(dataSource);
+
+ List employees = employeeDAO.getEmployeesFromIdList(ids);
+
+ assertEquals(3, employees.size());
+ assertEquals(1, employees.get(0).getId());
+ assertEquals(3, employees.get(1).getId());
+ assertEquals(4, employees.get(2).getId());
+
+ employees = employeeDAO.getEmployeesFromIdListNamed(ids);
+
+ assertEquals(3, employees.size());
+ assertEquals(1, employees.get(0).getId());
+ assertEquals(3, employees.get(1).getId());
+ assertEquals(4, employees.get(2).getId());
+ }
+
+ @Test
+ public void givenLargeIdList_whenGetEmployeesFromIdList_thenReturnCorrectEmployees() {
+ List ids = new ArrayList<>();
+ ids.add(1);
+ ids.add(3);
+ ids.add(4);
+ EmployeeDAO employeeDAO = new EmployeeDAO();
+ employeeDAO.setDataSource(dataSource);
+
+ List employees = employeeDAO.getEmployeesFromLargeIdList(ids);
+
+ assertEquals(3, employees.size());
+ assertEquals(1, employees.get(0).getId());
+ assertEquals(3, employees.get(1).getId());
+ assertEquals(4, employees.get(2).getId());
+
+ ids.clear();
+ ids.add(2);
+ employees = employeeDAO.getEmployeesFromLargeIdList(ids);
+ assertEquals(1, employees.size());
+ }
}
diff --git a/spring-boot-groovy/pom.xml b/spring-boot-groovy/pom.xml
new file mode 100644
index 0000000000..f61398c5d6
--- /dev/null
+++ b/spring-boot-groovy/pom.xml
@@ -0,0 +1,83 @@
+
+
+
+ 4.0.0
+ com.baeldung.app
+ spring-boot-groovy
+ spring-boot-groovy
+ war
+ Spring Boot Todo Application with Groovy
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.codehaus.groovy
+ groovy
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ net.bytebuddy
+ byte-buddy-dep
+ 1.10.9
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+ 1.9.0
+
+
+
+ addSources
+ addTestSources
+ generateStubs
+ compile
+ generateTestStubs
+ compileTests
+ removeStubs
+ removeTestStubs
+
+
+
+
+
+
+
+
+ com.baeldung.app.SpringBootGroovyApplication
+
+
+
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/SpringBootGroovyApplication.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/SpringBootGroovyApplication.groovy
new file mode 100644
index 0000000000..226a2ff53d
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/SpringBootGroovyApplication.groovy
@@ -0,0 +1,13 @@
+package com.baeldung.app
+
+import org.springframework.boot.SpringApplication
+import org.springframework.boot.autoconfigure.SpringBootApplication
+
+import com.baeldung.app.SpringBootGroovyApplication
+
+@SpringBootApplication
+class SpringBootGroovyApplication {
+ static void main(String[] args) {
+ SpringApplication.run SpringBootGroovyApplication, args
+ }
+}
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/controller/TodoController.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/controller/TodoController.groovy
new file mode 100644
index 0000000000..02f6d0223b
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/controller/TodoController.groovy
@@ -0,0 +1,48 @@
+package com.baeldung.app.controller
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.web.bind.annotation.DeleteMapping
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.PathVariable
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.PutMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RequestMethod
+import org.springframework.web.bind.annotation.RestController
+
+import com.baeldung.app.entity.Todo
+import com.baeldung.app.service.TodoService
+
+@RestController
+@RequestMapping('todo')
+public class TodoController {
+
+ @Autowired
+ TodoService todoService
+
+ @GetMapping
+ List getAllTodoList(){
+ todoService.findAll()
+ }
+
+ @PostMapping
+ Todo saveTodo(@RequestBody Todo todo){
+ todoService.saveTodo todo
+ }
+
+ @PutMapping
+ Todo updateTodo(@RequestBody Todo todo){
+ todoService.updateTodo todo
+ }
+
+ @DeleteMapping('/{todoId}')
+ deleteTodo(@PathVariable Integer todoId){
+ todoService.deleteTodo todoId
+ }
+
+ @GetMapping('/{todoId}')
+ Todo getTodoById(@PathVariable Integer todoId){
+ todoService.findById todoId
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/entity/Todo.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/entity/Todo.groovy
new file mode 100644
index 0000000000..9f1253c5b3
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/entity/Todo.groovy
@@ -0,0 +1,23 @@
+package com.baeldung.app.entity
+
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.GeneratedValue
+import javax.persistence.GenerationType
+import javax.persistence.Id
+import javax.persistence.Table
+
+@Entity
+@Table(name = 'todo')
+class Todo {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Integer id
+
+ @Column
+ String task
+
+ @Column
+ Boolean isCompleted
+
+}
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/repository/TodoRepository.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/repository/TodoRepository.groovy
new file mode 100644
index 0000000000..c0b35cc37d
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/repository/TodoRepository.groovy
@@ -0,0 +1,9 @@
+package com.baeldung.app.repository
+
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+import com.baeldung.app.entity.Todo
+
+@Repository
+interface TodoRepository extends JpaRepository {}
\ No newline at end of file
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/TodoService.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/TodoService.groovy
new file mode 100644
index 0000000000..0a59d93330
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/TodoService.groovy
@@ -0,0 +1,16 @@
+package com.baeldung.app.service
+
+import com.baeldung.app.entity.Todo
+
+interface TodoService {
+
+ List findAll()
+
+ Todo findById(Integer todoId)
+
+ Todo saveTodo(Todo todo)
+
+ Todo updateTodo(Todo todo)
+
+ Todo deleteTodo(Integer todoId)
+}
diff --git a/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/impl/TodoServiceImpl.groovy b/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/impl/TodoServiceImpl.groovy
new file mode 100644
index 0000000000..6d0ee03a9f
--- /dev/null
+++ b/spring-boot-groovy/src/main/groovy/com/baeldung/app/service/impl/TodoServiceImpl.groovy
@@ -0,0 +1,40 @@
+package com.baeldung.app.service.impl
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+
+import com.baeldung.app.entity.Todo
+import com.baeldung.app.repository.TodoRepository
+import com.baeldung.app.service.TodoService
+
+@Service
+class TodoServiceImpl implements TodoService {
+
+ @Autowired
+ TodoRepository todoRepository
+
+ @Override
+ List findAll() {
+ todoRepository.findAll()
+ }
+
+ @Override
+ Todo findById(Integer todoId) {
+ todoRepository.findById todoId get()
+ }
+
+ @Override
+ Todo saveTodo(Todo todo){
+ todoRepository.save todo
+ }
+
+ @Override
+ Todo updateTodo(Todo todo){
+ todoRepository.save todo
+ }
+
+ @Override
+ Todo deleteTodo(Integer todoId){
+ todoRepository.deleteById todoId
+ }
+}
diff --git a/spring-boot-groovy/src/main/resources/application.properties b/spring-boot-groovy/src/main/resources/application.properties
new file mode 100644
index 0000000000..8d53a190bb
--- /dev/null
+++ b/spring-boot-groovy/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+spring.datasource.url=jdbc:h2:mem:todo
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=sa
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
\ No newline at end of file
diff --git a/spring-boot-groovy/src/test/groovy/com/baeldung/app/TodoAppUnitTest.groovy b/spring-boot-groovy/src/test/groovy/com/baeldung/app/TodoAppUnitTest.groovy
new file mode 100644
index 0000000000..faf2d64ba7
--- /dev/null
+++ b/spring-boot-groovy/src/test/groovy/com/baeldung/app/TodoAppUnitTest.groovy
@@ -0,0 +1,97 @@
+package com.baeldung.app
+
+import static org.junit.jupiter.api.Assertions.assertEquals
+import static org.junit.jupiter.api.Assertions.assertTrue
+
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+import org.springframework.test.context.event.annotation.BeforeTestClass
+import org.springframework.test.context.junit4.SpringRunner
+
+import com.baeldung.app.entity.Todo
+
+import io.restassured.RestAssured
+import io.restassured.response.Response
+
+class TodoAppUnitTest {
+ static API_ROOT = 'http://localhost:8081/todo'
+ static readingTodoId
+ static writingTodoId
+
+ @BeforeClass
+ static void populateDummyData() {
+ Todo readingTodo = new Todo(task: 'Reading', isCompleted: false)
+ Todo writingTodo = new Todo(task: 'Writing', isCompleted: false)
+
+ final Response readingResponse =
+ RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(readingTodo).post(API_ROOT)
+
+ Todo cookingTodoResponse = readingResponse.as Todo.class
+ readingTodoId = cookingTodoResponse.getId()
+
+ final Response writingResponse =
+ RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(writingTodo).post(API_ROOT)
+
+ Todo writingTodoResponse = writingResponse.as Todo.class
+ writingTodoId = writingTodoResponse.getId()
+ }
+
+ @Test
+ void whenGetAllTodoList_thenOk(){
+ final Response response = RestAssured.get(API_ROOT)
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ assertTrue response.as(List.class).size() > 0
+ }
+
+ @Test
+ void whenGetTodoById_thenOk(){
+ final Response response =
+ RestAssured.get("$API_ROOT/$readingTodoId")
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ Todo todoResponse = response.as Todo.class
+ assertEquals readingTodoId,todoResponse.getId()
+ }
+
+ @Test
+ void whenUpdateTodoById_thenOk(){
+ Todo todo = new Todo(id:readingTodoId, isCompleted: true)
+ final Response response =
+ RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(todo).put(API_ROOT)
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ Todo todoResponse = response.as Todo.class
+ assertTrue todoResponse.getIsCompleted()
+ }
+
+ @Test
+ void whenDeleteTodoById_thenOk(){
+ final Response response =
+ RestAssured.given()
+ .delete("$API_ROOT/$writingTodoId")
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ }
+
+ @Test
+ void whenSaveTodo_thenOk(){
+ Todo todo = new Todo(task: 'Blogging', isCompleted: false)
+ final Response response =
+ RestAssured.given()
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .body(todo).post(API_ROOT)
+
+ assertEquals HttpStatus.OK.value(),response.getStatusCode()
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/group/LogGroupApplication.java b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/group/LogGroupApplication.java
new file mode 100644
index 0000000000..021f27bd98
--- /dev/null
+++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/group/LogGroupApplication.java
@@ -0,0 +1,26 @@
+package com.baeldung.group;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@SpringBootApplication
+@ActiveProfiles("log-group")
+public class LogGroupApplication {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(LogGroupApplication.class);
+
+ @RequestMapping("/log-group")
+ public void justLog() {
+ LOGGER.debug("Received a request");
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(LogGroupApplication.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/application-log-group.properties b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/application-log-group.properties
new file mode 100644
index 0000000000..6dfe11724e
--- /dev/null
+++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/application-log-group.properties
@@ -0,0 +1 @@
+logging.level.web=debug
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-testing/README.md b/spring-boot-modules/spring-boot-testing/README.md
index 9bbaf337d7..192f5cee99 100644
--- a/spring-boot-modules/spring-boot-testing/README.md
+++ b/spring-boot-modules/spring-boot-testing/README.md
@@ -13,4 +13,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Setting the Log Level in Spring Boot when Testing](https://www.baeldung.com/spring-boot-testing-log-level)
- [Embedded Redis Server with Spring Boot Test](https://www.baeldung.com/spring-embedded-redis)
- [Testing Spring Boot @ConfigurationProperties](https://www.baeldung.com/spring-boot-testing-configurationproperties)
-- [Prevent ApplicationRunner or CommandLineRunner Beans From Executing During Junit Testing]()
+- [Prevent ApplicationRunner or CommandLineRunner Beans From Executing During Junit Testing](https://www.baeldung.com/spring-junit-prevent-runner-beans-testing-execution)
diff --git a/spring-core-3/src/main/java/com/baeldung/factorybean/README.md b/spring-core-3/src/main/java/com/baeldung/factorybean/README.md
deleted file mode 100644
index 13f9f379e0..0000000000
--- a/spring-core-3/src/main/java/com/baeldung/factorybean/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-### Relevant Articles:
-- [How to use the Spring FactoryBean?](http://www.baeldung.com/spring-factorybean)
diff --git a/spring-mockito/pom.xml b/spring-mockito/pom.xml
index f3e0b04808..5d2cd7c445 100644
--- a/spring-mockito/pom.xml
+++ b/spring-mockito/pom.xml
@@ -28,7 +28,7 @@
- 2.21.0
+ 2.24.0
diff --git a/spring-mvc-basics-2/README.md b/spring-mvc-basics-2/README.md
index 9d1402a210..e52459bd6e 100644
--- a/spring-mvc-basics-2/README.md
+++ b/spring-mvc-basics-2/README.md
@@ -9,6 +9,7 @@ This module contains articles about Spring MVC
- [Servlet Redirect vs Forward](https://www.baeldung.com/servlet-redirect-forward)
- [Apache Tiles Integration with Spring MVC](https://www.baeldung.com/spring-mvc-apache-tiles)
- [Guide to Spring Email](https://www.baeldung.com/spring-email)
+- [Using ThymeLeaf and FreeMarker Emails Templates with Spring](https://www.baeldung.com/thymeleaf-freemarker-email)
- [Request Method Not Supported (405) in Spring](https://www.baeldung.com/spring-request-method-not-supported-405)
- [Spring @RequestParam Annotation](https://www.baeldung.com/spring-request-param)
- More articles: [[more -->]](/spring-mvc-basics-3)
diff --git a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java
index 941a984684..e70e801577 100644
--- a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java
+++ b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java
@@ -2,7 +2,6 @@ package com.baeldung.spring.configuration;
import java.util.ArrayList;
import java.util.List;
-import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@@ -10,9 +9,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
-import org.springframework.mail.SimpleMailMessage;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
@@ -65,29 +61,5 @@ public class ApplicationConfiguration implements WebMvcConfigurer {
converters.add(new RssChannelHttpMessageConverter());
converters.add(new JsonChannelHttpMessageConverter());
}
-
- @Bean
- public SimpleMailMessage templateSimpleMessage() {
- SimpleMailMessage message = new SimpleMailMessage();
- message.setText("This is the test email template for your email:\n%s\n");
- return message;
- }
-
- @Bean
- public JavaMailSender getJavaMailSender() {
- JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
- mailSender.setHost("smtp.gmail.com");
- mailSender.setPort(587);
-
- mailSender.setUsername("my.gmail@gmail.com");
- mailSender.setPassword("password");
-
- Properties props = mailSender.getJavaMailProperties();
- props.put("mail.transport.protocol", "smtp");
- props.put("mail.smtp.auth", "true");
- props.put("mail.smtp.starttls.enable", "true");
- props.put("mail.debug", "true");
-
- return mailSender;
- }
+
}
diff --git a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/EmailConfiguration.java b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/EmailConfiguration.java
new file mode 100644
index 0000000000..1bbbc51304
--- /dev/null
+++ b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/EmailConfiguration.java
@@ -0,0 +1,89 @@
+package com.baeldung.spring.configuration;
+
+import java.util.Properties;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.ResourceBundleMessageSource;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
+import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
+import org.thymeleaf.spring4.SpringTemplateEngine;
+import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
+
+@Configuration
+@ComponentScan(basePackages = { "com.baeldung.spring.mail" })
+public class EmailConfiguration {
+
+ @Bean
+ public JavaMailSender getJavaMailSender() {
+ JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
+
+ mailSender.setHost("smtp.gmail.com");
+ mailSender.setPort(587);
+
+ mailSender.setUsername("my.gmail@gmail.com");
+ mailSender.setPassword("password");
+
+ Properties props = mailSender.getJavaMailProperties();
+ props.put("mail.transport.protocol", "smtp");
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "false");
+ props.put("mail.debug", "true");
+
+ return mailSender;
+ }
+
+ @Bean
+ public SimpleMailMessage templateSimpleMessage() {
+ SimpleMailMessage message = new SimpleMailMessage();
+ message.setText("This is the test email template for your email:\n%s\n");
+ return message;
+ }
+
+ @Bean
+ public SpringTemplateEngine thymeleafTemplateEngine() {
+ SpringTemplateEngine templateEngine = new SpringTemplateEngine();
+ templateEngine.setTemplateResolver(thymeleafTemplateResolver());
+ templateEngine.setTemplateEngineMessageSource(emailMessageSource());
+ return templateEngine;
+ }
+
+ @Bean
+ public SpringResourceTemplateResolver thymeleafTemplateResolver() {
+ SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
+ templateResolver.setPrefix("/WEB-INF/views/mail/");
+ templateResolver.setSuffix(".html");
+ templateResolver.setTemplateMode("HTML");
+ templateResolver.setCharacterEncoding("UTF-8");
+ return templateResolver;
+ }
+
+ @Bean
+ public FreeMarkerConfigurer freemarkerConfig() {
+ FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
+ freeMarkerConfigurer.setTemplateLoaderPath("/WEB-INF/views/mail");
+ return freeMarkerConfigurer;
+ }
+
+ @Bean
+ public FreeMarkerViewResolver freemarkerViewResolver() {
+ FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
+ resolver.setCache(true);
+ resolver.setPrefix("");
+ resolver.setSuffix(".ftl");
+ return resolver;
+ }
+
+
+ @Bean
+ public ResourceBundleMessageSource emailMessageSource() {
+ final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
+ messageSource.setBasename("/mailMessages");
+ return messageSource;
+ }
+
+}
diff --git a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/WebInitializer.java b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/WebInitializer.java
index 74094a11c7..4d43549440 100644
--- a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/WebInitializer.java
+++ b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/WebInitializer.java
@@ -20,6 +20,7 @@ public class WebInitializer implements WebApplicationInitializer {
// ctx.register(GroovyConfiguration.class);
// ctx.register(JadeTemplateConfiguration.class);
// ctx.register(PushConfiguration.class);
+ ctx.register(EmailConfiguration.class);
// ctx.setServletContext(container);
//ctx.register(TilesApplicationConfiguration.class);
diff --git a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/MailController.java b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/MailController.java
index 16d1202eef..b6e19a4c39 100644
--- a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/MailController.java
+++ b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/MailController.java
@@ -1,10 +1,17 @@
package com.baeldung.spring.controller;
-import com.baeldung.spring.mail.EmailServiceImpl;
-import com.baeldung.spring.domain.MailObject;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.mail.MessagingException;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
@@ -12,26 +19,21 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
-import javax.servlet.http.HttpServletRequest;
-import javax.validation.Valid;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
+import com.baeldung.spring.domain.MailObject;
+import com.baeldung.spring.mail.EmailService;
+
+import freemarker.template.TemplateException;
@Controller
@RequestMapping("/mail")
public class MailController {
@Autowired
- public EmailServiceImpl emailService;
+ public EmailService emailService;
@Value("${attachment.invoice}")
private String attachmentPath;
- @Autowired
- public SimpleMailMessage template;
-
private static final Map> labels;
static {
@@ -46,7 +48,7 @@ public class MailController {
//Email with template
props = new HashMap<>();
- props.put("headerText", "Send Email Using Template");
+ props.put("headerText", "Send Email Using Text Template");
props.put("messageLabel", "Template Parameter");
props.put("additionalInfo",
"The parameter value will be added to the following message template: " +
@@ -60,6 +62,7 @@ public class MailController {
props.put("messageLabel", "Message");
props.put("additionalInfo", "To make sure that you send an attachment with this email, change the value for the 'attachment.invoice' in the application.properties file to the path to the attachment.");
labels.put("sendAttachment", props);
+
}
@RequestMapping(method = RequestMethod.GET)
@@ -85,6 +88,7 @@ public class MailController {
return "mail/send";
}
+
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String createMail(Model model,
@ModelAttribute("mailObject") @Valid MailObject mailObject,
@@ -95,7 +99,7 @@ public class MailController {
emailService.sendSimpleMessage(mailObject.getTo(),
mailObject.getSubject(), mailObject.getText());
- return "redirect:/home";
+ return "emails";
}
@RequestMapping(value = "/sendTemplate", method = RequestMethod.POST)
@@ -107,10 +111,9 @@ public class MailController {
}
emailService.sendSimpleMessageUsingTemplate(mailObject.getTo(),
mailObject.getSubject(),
- template,
mailObject.getText());
- return "redirect:/home";
+ return "redirect:/mail";
}
@RequestMapping(value = "/sendAttachment", method = RequestMethod.POST)
@@ -127,6 +130,47 @@ public class MailController {
attachmentPath
);
- return "redirect:/home";
+ return "redirect:/mail";
+ }
+
+
+ @RequestMapping(value = {"/sendHtml"}, method = RequestMethod.GET)
+ public String getHtmlMailView(Model model,
+ HttpServletRequest request) {
+
+ Map templateEngines = new HashMap<>();
+ templateEngines.put("Thymeleaf", "Thymeleaf");
+ templateEngines.put("Freemarker", "Freemarker");
+ model.addAttribute("mailObject", new MailObject());
+ model.addAttribute("templateEngines", templateEngines);
+ return "mail/sendHtml";
+ }
+
+ @RequestMapping(value = "/sendHtml", method = RequestMethod.POST)
+ public String createHtmlMail(Model model,
+ @ModelAttribute("mailObject") @Valid MailObject mailObject,
+ Errors errors) throws IOException, MessagingException, TemplateException {
+ if (errors.hasErrors()) {
+ return "mail/send";
+ }
+
+ Map templateModel = new HashMap<>();
+ templateModel.put("recipientName", mailObject.getRecipientName());
+ templateModel.put("text", mailObject.getText());
+ templateModel.put("senderName", mailObject.getSenderName());
+
+ if (mailObject.getTemplateEngine().equalsIgnoreCase("thymeleaf")) {
+ emailService.sendMessageUsingThymeleafTemplate(
+ mailObject.getTo(),
+ mailObject.getSubject(),
+ templateModel);
+ } else {
+ emailService.sendMessageUsingFreemarkerTemplate(
+ mailObject.getTo(),
+ mailObject.getSubject(),
+ templateModel);
+ }
+
+ return "redirect:/mail";
}
}
diff --git a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/domain/MailObject.java b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/domain/MailObject.java
index aceaf685fa..d425ca9a26 100644
--- a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/domain/MailObject.java
+++ b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/domain/MailObject.java
@@ -12,8 +12,11 @@ public class MailObject {
@NotNull
@Size(min = 1, message = "Please, set an email address to send the message to it")
private String to;
+ private String recipientName;
private String subject;
private String text;
+ private String senderName;
+ private String templateEngine;
public String getTo() {
return to;
@@ -38,4 +41,30 @@ public class MailObject {
public void setText(String text) {
this.text = text;
}
+
+ public String getRecipientName() {
+ return recipientName;
+ }
+
+ public void setRecipientName(String recipientName) {
+ this.recipientName = recipientName;
+ }
+
+ public String getSenderName() {
+ return senderName;
+ }
+
+ public void setSenderName(String senderName) {
+ this.senderName = senderName;
+ }
+
+ public String getTemplateEngine() {
+ return templateEngine;
+ }
+
+ public void setTemplateEngine(String templateEngine) {
+ this.templateEngine = templateEngine;
+ }
+
+
}
diff --git a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailService.java b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailService.java
index 43d7378227..b7d5be09c8 100644
--- a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailService.java
+++ b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailService.java
@@ -1,6 +1,11 @@
package com.baeldung.spring.mail;
-import org.springframework.mail.SimpleMailMessage;
+import java.io.IOException;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+
+import freemarker.template.TemplateException;
/**
* Created by Olga on 8/22/2016.
@@ -11,10 +16,19 @@ public interface EmailService {
String text);
void sendSimpleMessageUsingTemplate(String to,
String subject,
- SimpleMailMessage template,
- String ...templateArgs);
+ String ...templateModel);
void sendMessageWithAttachment(String to,
String subject,
String text,
String pathToAttachment);
+
+ void sendMessageUsingThymeleafTemplate(String to,
+ String subject,
+ Map templateModel)
+ throws IOException, MessagingException;
+
+ void sendMessageUsingFreemarkerTemplate(String to,
+ String subject,
+ Map templateModel)
+ throws IOException, TemplateException, MessagingException;
}
diff --git a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailServiceImpl.java b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailServiceImpl.java
index 039b970d8e..0592415ab5 100644
--- a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailServiceImpl.java
+++ b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailServiceImpl.java
@@ -1,26 +1,49 @@
package com.baeldung.spring.mail;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.FileSystemResource;
-import org.springframework.mail.MailException;
-import org.springframework.mail.SimpleMailMessage;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.MimeMessageHelper;
-import org.springframework.stereotype.Component;
-
import java.io.File;
+import java.io.IOException;
+import java.util.Map;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.mail.MailException;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Service;
+import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
+import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
+import org.thymeleaf.context.Context;
+import org.thymeleaf.spring4.SpringTemplateEngine;
+
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
/**
* Created by Olga on 7/15/2016.
*/
-@Component
+@Service("EmailService")
public class EmailServiceImpl implements EmailService {
@Autowired
public JavaMailSender emailSender;
+
+ @Autowired
+ public SimpleMailMessage template;
+
+ @Autowired
+ private SpringTemplateEngine thymeleafTemplateEngine;
+
+ @Autowired
+ private FreeMarkerConfigurer freemarkerConfigurer;
+
+ @Value("classpath:/mail-logo.png")
+ Resource resourceFile;
public void sendSimpleMessage(String to, String subject, String text) {
try {
@@ -38,9 +61,8 @@ public class EmailServiceImpl implements EmailService {
@Override
public void sendSimpleMessageUsingTemplate(String to,
String subject,
- SimpleMailMessage template,
- String ...templateArgs) {
- String text = String.format(template.getText(), templateArgs);
+ String ...templateModel) {
+ String text = String.format(template.getText(), templateModel);
sendSimpleMessage(to, subject, text);
}
@@ -66,4 +88,42 @@ public class EmailServiceImpl implements EmailService {
e.printStackTrace();
}
}
+
+
+ @Override
+ public void sendMessageUsingThymeleafTemplate(
+ String to, String subject, Map templateModel)
+ throws MessagingException {
+
+ Context thymeleafContext = new Context();
+ thymeleafContext.setVariables(templateModel);
+
+ String htmlBody = thymeleafTemplateEngine.process("template-thymeleaf.html", thymeleafContext);
+
+ sendHtmlMessage(to, subject, htmlBody);
+ }
+
+ @Override
+ public void sendMessageUsingFreemarkerTemplate(
+ String to, String subject, Map templateModel)
+ throws IOException, TemplateException, MessagingException {
+
+ Template freemarkerTemplate = freemarkerConfigurer.createConfiguration().getTemplate("template-freemarker.ftl");
+ String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);
+
+ sendHtmlMessage(to, subject, htmlBody);
+ }
+
+ private void sendHtmlMessage(String to, String subject, String htmlBody) throws MessagingException {
+
+ MimeMessage message = emailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
+ helper.setTo(to);
+ helper.setSubject(subject);
+ helper.setText(htmlBody, true);
+ helper.addInline("attachment.png", resourceFile);
+ emailSender.send(message);
+
+ }
+
}
diff --git a/spring-mvc-basics-2/src/main/resources/mail-logo.png b/spring-mvc-basics-2/src/main/resources/mail-logo.png
new file mode 100644
index 0000000000..edb83efbe3
Binary files /dev/null and b/spring-mvc-basics-2/src/main/resources/mail-logo.png differ
diff --git a/spring-mvc-basics-2/src/main/resources/mailMessages.properties b/spring-mvc-basics-2/src/main/resources/mailMessages.properties
new file mode 100644
index 0000000000..46f7a80ed3
--- /dev/null
+++ b/spring-mvc-basics-2/src/main/resources/mailMessages.properties
@@ -0,0 +1,5 @@
+greetings=Hi {0},
+subscriptions=Please find below your current subscriptions to our forum:
+regards=Regards,
+unsubscribe=Unsubscribe from these emails here
+signature={0} at Baeldung
\ No newline at end of file
diff --git a/spring-mvc-basics-2/src/main/resources/mailMessages_fr_FR.properties b/spring-mvc-basics-2/src/main/resources/mailMessages_fr_FR.properties
new file mode 100644
index 0000000000..6f300a9ab7
--- /dev/null
+++ b/spring-mvc-basics-2/src/main/resources/mailMessages_fr_FR.properties
@@ -0,0 +1,5 @@
+greetings=Bonjour {0},
+subscriptions=Voici vos différentes souscriptions sur notre forum :
+regards=Cordialement,
+unsubscribe=Se désinscrire de ces emails ici
+signature={0} à Baeldung
\ No newline at end of file
diff --git a/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/emails.jsp b/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/emails.jsp
index 63351bbf3a..dafa71cbb0 100644
--- a/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/emails.jsp
+++ b/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/emails.jsp
@@ -26,7 +26,7 @@
-
+
@@ -34,6 +34,11 @@
+
+
+
+
+
diff --git a/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/sendHtml.jsp b/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/sendHtml.jsp
new file mode 100644
index 0000000000..f91a8826d1
--- /dev/null
+++ b/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/sendHtml.jsp
@@ -0,0 +1,73 @@
+<%--
+ User: Benjamin CAURE
+ Date: 4/14/2020
+--%>
+<%@ page pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+
+
+ Send HTML Email
+
+
+
+
Send Email Using Text Template
+
+
+
+
+
+
+
+
diff --git a/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/template-freemarker.ftl b/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/template-freemarker.ftl
new file mode 100644
index 0000000000..066fc0302c
--- /dev/null
+++ b/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/template-freemarker.ftl
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Hi ${recipientName}
+ ${text}
+ Regards,
+
+ ${senderName} at Baeldung
+
+
+
+
diff --git a/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/template-thymeleaf.html b/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/template-thymeleaf.html
new file mode 100644
index 0000000000..b255b5dee5
--- /dev/null
+++ b/spring-mvc-basics-2/src/main/webapp/WEB-INF/views/mail/template-thymeleaf.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-mvc-basics-2/src/test/java/com/baeldung/SpringContextTest.java b/spring-mvc-basics-2/src/test/java/com/baeldung/SpringContextTest.java
index ab0b23c4a5..5c276254d3 100644
--- a/spring-mvc-basics-2/src/test/java/com/baeldung/SpringContextTest.java
+++ b/spring-mvc-basics-2/src/test/java/com/baeldung/SpringContextTest.java
@@ -7,10 +7,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.baeldung.spring.configuration.ApplicationConfiguration;
+import com.baeldung.spring.configuration.EmailConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(classes={ApplicationConfiguration.class})
+@ContextConfiguration(classes={ApplicationConfiguration.class, EmailConfiguration.class})
@WebAppConfiguration
public class SpringContextTest {
diff --git a/spring-mvc-basics-2/src/test/java/com/baeldung/controller/rss/ArticleRssIntegrationTest.java b/spring-mvc-basics-2/src/test/java/com/baeldung/controller/rss/ArticleRssIntegrationTest.java
index fe7aeeb570..2ed70489c0 100644
--- a/spring-mvc-basics-2/src/test/java/com/baeldung/controller/rss/ArticleRssIntegrationTest.java
+++ b/spring-mvc-basics-2/src/test/java/com/baeldung/controller/rss/ArticleRssIntegrationTest.java
@@ -1,6 +1,8 @@
package com.baeldung.controller.rss;
import com.baeldung.spring.configuration.ApplicationConfiguration;
+import com.baeldung.spring.configuration.EmailConfiguration;
+
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,7 +15,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-@SpringJUnitWebConfig(ApplicationConfiguration.class)
+@SpringJUnitWebConfig(classes={ApplicationConfiguration.class, EmailConfiguration.class})
public class ArticleRssIntegrationTest {
public static final String APPLICATION_RSS_XML = "application/rss+xml";
public static final String APPLICATION_RSS_JSON = "application/rss+json";
diff --git a/spring-security-modules/spring-security-x509/README.md b/spring-security-modules/spring-security-x509/README.md
index b1eb0debf5..da431d862c 100644
--- a/spring-security-modules/spring-security-x509/README.md
+++ b/spring-security-modules/spring-security-x509/README.md
@@ -4,3 +4,10 @@ This module contains articles about X.509 authentication with Spring Security
### Relevant Articles:
- [X.509 Authentication in Spring Security](https://www.baeldung.com/x-509-authentication-in-spring-security)
+
+###### Note for the [X.509 Authentication in Spring Security](https://www.baeldung.com/x-509-authentication-in-spring-security):
+All the ready to use certificates are located in the [store](store) directory. The application is already configured to use these files.
+This means the app works out of the box.
+
+However, it's highly recommended that you follow the article step by step and generate all the needed files by yourself.
+This will let you understand the topic more deeply.
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-x509/keystore/Makefile b/spring-security-modules/spring-security-x509/keystore/Makefile
deleted file mode 100644
index 2cd7c55c5b..0000000000
--- a/spring-security-modules/spring-security-x509/keystore/Makefile
+++ /dev/null
@@ -1,92 +0,0 @@
-PASSWORD=changeit
-KEYSTORE=keystore.jks
-HOSTNAME=localhost
-CLIENTNAME=cid
-CLIENT_PRIVATE_KEY="${CLIENTNAME}_pk"
-
-# CN = Common Name
-# OU = Organization Unit
-# O = Organization Name
-# L = Locality Name
-# ST = State Name
-# C = Country (2-letter Country Code)
-# E = Email
-DNAME_CA='CN=Baeldung CA,OU=baeldung.com,O=Baeldung,L=SomeCity,ST=SomeState,C=CC'
-# For server certificates, the Common Name (CN) must be the hostname
-DNAME_HOST='CN=$(HOSTNAME),OU=baeldung.com,O=Baeldung,L=SomeCity,ST=SomeState,C=CC'
-DNAME_CLIENT='CN=$(CLIENTNAME),OU=baeldung.com,O=Baeldung,L=SomeCity,ST=SomeState,C=CC'
-TRUSTSTORE=truststore.jks
-
-all: clean create-keystore add-host create-truststore add-client
-
-create-keystore:
- # Generate a certificate authority (CA)
- keytool -genkey -alias ca -ext san=dns:localhost,ip:127.0.0.1 -ext BC=ca:true \
- -keyalg RSA -keysize 4096 -sigalg SHA512withRSA -keypass $(PASSWORD) \
- -validity 3650 -dname $(DNAME_CA) \
- -keystore $(KEYSTORE) -storepass $(PASSWORD)
-
-add-host:
- # Generate a host certificate
- keytool -genkey -alias $(HOSTNAME) -ext san=dns:localhost,ip:127.0.0.1 \
- -keyalg RSA -keysize 4096 -sigalg SHA512withRSA -keypass $(PASSWORD) \
- -validity 3650 -dname $(DNAME_HOST) \
- -keystore $(KEYSTORE) -storepass $(PASSWORD)
- # Generate a host certificate signing request
- keytool -certreq -alias $(HOSTNAME) -ext san=dns:localhost,ip:127.0.0.1 -ext BC=ca:true \
- -keyalg RSA -keysize 4096 -sigalg SHA512withRSA \
- -validity 3650 -file "$(HOSTNAME).csr" \
- -keystore $(KEYSTORE) -storepass $(PASSWORD)
- # Generate signed certificate with the certificate authority
- keytool -gencert -alias ca -ext san=dns:localhost,ip:127.0.0.1 \
- -validity 3650 -sigalg SHA512withRSA \
- -infile "$(HOSTNAME).csr" -outfile "$(HOSTNAME).crt" -rfc \
- -keystore $(KEYSTORE) -storepass $(PASSWORD)
- # Import signed certificate into the keystore
- keytool -import -trustcacerts -alias $(HOSTNAME) -ext san=dns:localhost,ip:127.0.0.1 \
- -file "$(HOSTNAME).crt" \
- -keystore $(KEYSTORE) -storepass $(PASSWORD)
-
-export-authority:
- # Export certificate authority
- keytool -export -alias ca -ext san=dns:localhost,ip:127.0.0.1 -file ca.crt -rfc \
- -keystore $(KEYSTORE) -storepass $(PASSWORD)
-
-
-create-truststore: export-authority
- # Import certificate authority into a new truststore
- keytool -import -trustcacerts -noprompt -alias ca -ext san=dns:localhost,ip:127.0.0.1 -file ca.crt \
- -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
-
-add-client:
- # Generate client certificate
- keytool -genkey -alias $(CLIENT_PRIVATE_KEY) -ext san=dns:localhost,ip:127.0.0.1 \
- -keyalg RSA -keysize 4096 -sigalg SHA512withRSA -keypass $(PASSWORD) \
- -validity 3650 -dname $(DNAME_CLIENT) \
- -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
- # Generate a host certificate signing request
- keytool -certreq -alias $(CLIENT_PRIVATE_KEY) -ext san=dns:localhost,ip:127.0.0.1 -ext BC=ca:true \
- -keyalg RSA -keysize 4096 -sigalg SHA512withRSA \
- -validity 3650 -file "$(CLIENTNAME).csr" \
- -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
- # Generate signed certificate with the certificate authority
- keytool -gencert -alias ca -ext san=dns:localhost,ip:127.0.0.1 \
- -validity 3650 -sigalg SHA512withRSA \
- -infile "$(CLIENTNAME).csr" -outfile "$(CLIENTNAME).crt" -rfc \
- -keystore $(KEYSTORE) -storepass $(PASSWORD)
- # Import signed certificate into the truststore
- keytool -import -trustcacerts -alias $(CLIENTNAME) -ext san=dns:localhost,ip:127.0.0.1 \
- -file "$(CLIENTNAME).crt" \
- -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
- # Export private certificate for importing into a browser
- keytool -importkeystore -srcalias $(CLIENT_PRIVATE_KEY) -ext san=dns:localhost,ip:127.0.0.1 \
- -srckeystore $(TRUSTSTORE) -srcstorepass $(PASSWORD) \
- -destkeystore "$(CLIENTNAME).p12" -deststorepass $(PASSWORD) \
- -deststoretype PKCS12
- # Delete client private key as truststore should not contain any private keys
- keytool -delete -alias $(CLIENT_PRIVATE_KEY) \
- -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
-
-clean:
- # Remove generated artifacts
- find . \( -name "$(CLIENTNAME)*" -o -name "$(HOSTNAME)*" -o -name "$(KEYSTORE)" -o -name "$(TRUSTSTORE)" -o -name ca.crt \) -type f -exec rm -f {} \;
diff --git a/spring-security-modules/spring-security-x509/keystore/keystore.jks b/spring-security-modules/spring-security-x509/keystore/keystore.jks
deleted file mode 100644
index 044a820c39..0000000000
Binary files a/spring-security-modules/spring-security-x509/keystore/keystore.jks and /dev/null differ
diff --git a/spring-security-modules/spring-security-x509/spring-security-x509-basic-auth/src/main/resources/application.properties b/spring-security-modules/spring-security-x509/spring-security-x509-basic-auth/src/main/resources/application.properties
index 53dfe9976a..0ba5fa1b8c 100644
--- a/spring-security-modules/spring-security-x509/spring-security-x509-basic-auth/src/main/resources/application.properties
+++ b/spring-security-modules/spring-security-x509/spring-security-x509-basic-auth/src/main/resources/application.properties
@@ -1,4 +1,4 @@
-server.ssl.key-store=keystore/keystore.jks
+server.ssl.key-store=../store/keystore.jks
server.ssl.key-store-password=changeit
server.ssl.key-alias=localhost
server.ssl.key-password=changeit
diff --git a/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java b/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java
index 050423238e..18bbfba835 100644
--- a/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java
+++ b/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java
@@ -23,7 +23,11 @@ public class X509AuthenticationServer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
- http.authorizeRequests().anyRequest().authenticated().and().x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)").userDetailsService(userDetailsService());
+ http.authorizeRequests().anyRequest().authenticated()
+ .and()
+ .x509()
+ .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
+ .userDetailsService(userDetailsService());
}
@Bean
@@ -31,7 +35,7 @@ public class X509AuthenticationServer extends WebSecurityConfigurerAdapter {
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
- if (username.equals("cid")) {
+ if (username.equals("Bob")) {
return new User(username, "", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
throw new UsernameNotFoundException("User not found!");
diff --git a/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/resources/application.properties b/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/resources/application.properties
index 743c9c4582..fc2fc89396 100644
--- a/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/resources/application.properties
+++ b/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/resources/application.properties
@@ -1,4 +1,4 @@
-server.ssl.key-store=../keystore/keystore.jks
+server.ssl.key-store=../store/keystore.jks
server.ssl.key-store-password=changeit
server.ssl.key-alias=localhost
server.ssl.key-password=changeit
@@ -6,6 +6,6 @@ server.ssl.enabled=true
server.port=8443
spring.security.user.name=Admin
spring.security.user.password=admin
-server.ssl.trust-store=../keystore/truststore.jks
+server.ssl.trust-store=../store/truststore.jks
server.ssl.trust-store-password=changeit
server.ssl.client-auth=need
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/resources/keystore.jks b/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/resources/keystore.jks
deleted file mode 100644
index 044a820c39..0000000000
Binary files a/spring-security-modules/spring-security-x509/spring-security-x509-client-auth/src/main/resources/keystore.jks and /dev/null differ
diff --git a/spring-security-modules/spring-security-x509/store/clientBob.p12 b/spring-security-modules/spring-security-x509/store/clientBob.p12
new file mode 100644
index 0000000000..b5de8ab129
Binary files /dev/null and b/spring-security-modules/spring-security-x509/store/clientBob.p12 differ
diff --git a/spring-security-modules/spring-security-x509/store/keystore.jks b/spring-security-modules/spring-security-x509/store/keystore.jks
new file mode 100644
index 0000000000..c317c1d5ba
Binary files /dev/null and b/spring-security-modules/spring-security-x509/store/keystore.jks differ
diff --git a/spring-security-modules/spring-security-x509/store/localhost.ext b/spring-security-modules/spring-security-x509/store/localhost.ext
new file mode 100644
index 0000000000..45324cc75a
--- /dev/null
+++ b/spring-security-modules/spring-security-x509/store/localhost.ext
@@ -0,0 +1,5 @@
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+subjectAltName = @alt_names
+[alt_names]
+DNS.1 = localhost
diff --git a/spring-security-modules/spring-security-x509/store/rootCA.crt b/spring-security-modules/spring-security-x509/store/rootCA.crt
new file mode 100644
index 0000000000..b2ab681744
--- /dev/null
+++ b/spring-security-modules/spring-security-x509/store/rootCA.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFDzCCAvegAwIBAgIUDmhG1yLlF83ydOWPit8/MYNbaC8wDQYJKoZIhvcNAQEL
+BQAwFzEVMBMGA1UEAwwMQmFlbGR1bmcuY29tMB4XDTIwMDQyMjE1MzEyMFoXDTMw
+MDQyMDE1MzEyMFowFzEVMBMGA1UEAwwMQmFlbGR1bmcuY29tMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEAmkOSeQRQBQdoJio7Odm19kKVm4Y1ju1zscGM
+LBWQ4GU8d3Y5AiOVzHtYUbKyJvmmUSYOH/mYdQ8F5nKKaXhTz92LIMnSXnusAqdD
+YSvKa+mBoMLsd4Gl9lljipTvRwkkPlPXGVBDEVzXPf32l+5YxpGZzyVyj0WYT1cP
+sZyThbOwue4h6gwer4SZ0HNPSts8TG7oiA4UTZSN5hhhbJmRBc87Xz+hJOMayZp6
+HA3tGTlrTkP/Vc3nii/G9tBeydmTTKj+BGFQW4qzDG6nJVvYyB4iri4ActREREGD
+ycPS7SVXqEcA4rvSMR7DYoHVLkOg0uiQDWtj4zYcN9qgWVjcnIlUSPxrCgdJbakl
+lRzrVpbkdlOC3hFSytoOBmCPS56gJ5npeLFh6IoPPdoXJwIXIJ1twCWtKzvlvlzb
+DkMDytRVk2LYjh7IvtAWPTz5QofRdd2fW6iAWPdWbwcnq1xjO5BT8uGj+zOiA19/
+T2YebwD+pLAUM2w0ykLm+bH+DiSRa76wpKxuo6sSUefkeEt8Avojwh8nNbEOvXhf
+El4zyimwTlfUYnaKEllUjRWVPhQPGCeCBHe1ES8UFf8hqGS2LRjKAZK70OcFdLyd
+18sas/EXkNbd+Mpgata/zO9Oy/3h+xY426T5bPAt+wU38yMUaE+z5BS84m2GOtuQ
+nC3a/HcCAwEAAaNTMFEwHQYDVR0OBBYEFFyRjr/LWlOZHv7JU7kQ1g76nq4SMB8G
+A1UdIwQYMBaAFFyRjr/LWlOZHv7JU7kQ1g76nq4SMA8GA1UdEwEB/wQFMAMBAf8w
+DQYJKoZIhvcNAQELBQADggIBAJWjUR/HBw5f7ogfyCVK/5fJutOFIIHqzNhExvDe
+5wu9msPRAj+5ruGHtMWwOKm5qT82gZKJnAVct8XZXTIHU8mKS5Lbk02hu3e2tR/0
+RCzH4TCCD3fDJaW/jAZyU3oPtvcCaSmPwibG7SvDXtUvSSwCW8Omg7WqhnSWGUOn
+WH3105lw8UKRTg57CaNu0GunqO4r0GcrgVShNKjCvNv7nGcP3+KWouZekBdn/iY5
+3Q8llL8WUMOHRwH6Raa6CV+vckCthUSpJdBAgGN44QtkA0iL/afVuE7VuTqsnCBA
+nbOz/ssZOP0TUVYwoRiDN50gJdB8IKMHZu9Px2m7fJuGgZ7QDe56+tePypgM9KDq
+yx4MKMP3Qc5xLE4pGM9SI1sGo+waW0+gPShNECHY4z8fOHw5bn+J1mrEWQbHfMGg
+Z/352qps5Hd6PljLAHmWAJL2nXS2zlXbccdYuv4ZtNIeDUT8eX/9NuJdXrITq9QQ
+oSBmyzH1bUJi2/mULcFy0Ibcu+OY/r8t67NrGKwLPLyozScwnFQE9SZR9d2cu6sC
+yxQtcd68vdvAIEDTk4DcSldeT44HIJ7RYQuBNZ5NU3SngMLDleec/3AZSUXTExFW
+TrbMTb+djM+XcTkRyO0wO0MYpjKGqN3sAGfppx0G9kgmJ9HB38nZfvqj3G1L9YFQ
+6kSA
+-----END CERTIFICATE-----
diff --git a/spring-security-modules/spring-security-x509/store/truststore.jks b/spring-security-modules/spring-security-x509/store/truststore.jks
new file mode 100644
index 0000000000..1d0d200580
Binary files /dev/null and b/spring-security-modules/spring-security-x509/store/truststore.jks differ
diff --git a/spring-soap/pom.xml b/spring-soap/pom.xml
index 9403b70636..d0987329c0 100644
--- a/spring-soap/pom.xml
+++ b/spring-soap/pom.xml
@@ -58,6 +58,28 @@
+
+
+ org.jvnet.jaxb2.maven2
+ maven-jaxb2-plugin
+ 0.14.0
+
+
+
+ generate
+
+
+
+
+ WSDL
+ ${project.basedir}/src/main/java
+ com.baeldung.springsoap.client.gen
+ ${project.basedir}/src/main/resources
+
+ countries.wsdl
+
+
+
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClient.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClient.java
new file mode 100644
index 0000000000..e0b9172ece
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClient.java
@@ -0,0 +1,26 @@
+package com.baeldung.springsoap.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
+
+import com.baeldung.springsoap.client.gen.GetCountryRequest;
+import com.baeldung.springsoap.client.gen.GetCountryResponse;
+
+public class CountryClient extends WebServiceGatewaySupport {
+
+ private static final Logger logger = LoggerFactory.getLogger(CountryClient.class);
+
+ public GetCountryResponse getCountry(String country) {
+
+ GetCountryRequest request = new GetCountryRequest();
+ request.setName(country);
+
+ logger.info("Requesting information for " + country);
+
+ GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate().marshalSendAndReceive(request);
+
+ return response;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClientConfig.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClientConfig.java
new file mode 100644
index 0000000000..8dabdbc00d
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClientConfig.java
@@ -0,0 +1,25 @@
+package com.baeldung.springsoap.client;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.oxm.jaxb.Jaxb2Marshaller;
+
+@Configuration
+public class CountryClientConfig {
+
+ @Bean
+ public Jaxb2Marshaller marshaller() {
+ Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
+ marshaller.setContextPath("com.baeldung.springsoap.client.gen");
+ return marshaller;
+ }
+
+ @Bean
+ public CountryClient countryClient(Jaxb2Marshaller marshaller) {
+ CountryClient client = new CountryClient();
+ client.setDefaultUri("http://localhost:8080/ws");
+ client.setMarshaller(marshaller);
+ client.setUnmarshaller(marshaller);
+ return client;
+ }
+}
\ No newline at end of file
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Country.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Country.java
new file mode 100644
index 0000000000..e17dce55f9
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Country.java
@@ -0,0 +1,146 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for country complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType name="country">
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="name" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="population" type="{http://www.w3.org/2001/XMLSchema}int"/>
+ * <element name="capital" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="currency" type="{http://www.baeldung.com/springsoap/gen}currency"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "country", propOrder = {
+ "name",
+ "population",
+ "capital",
+ "currency"
+})
+public class Country {
+
+ @XmlElement(required = true)
+ protected String name;
+ protected int population;
+ @XmlElement(required = true)
+ protected String capital;
+ @XmlElement(required = true)
+ @XmlSchemaType(name = "string")
+ protected Currency currency;
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+ /**
+ * Gets the value of the population property.
+ *
+ */
+ public int getPopulation() {
+ return population;
+ }
+
+ /**
+ * Sets the value of the population property.
+ *
+ */
+ public void setPopulation(int value) {
+ this.population = value;
+ }
+
+ /**
+ * Gets the value of the capital property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getCapital() {
+ return capital;
+ }
+
+ /**
+ * Sets the value of the capital property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setCapital(String value) {
+ this.capital = value;
+ }
+
+ /**
+ * Gets the value of the currency property.
+ *
+ * @return
+ * possible object is
+ * {@link Currency }
+ *
+ */
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ /**
+ * Sets the value of the currency property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Currency }
+ *
+ */
+ public void setCurrency(Currency value) {
+ this.currency = value;
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Currency.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Currency.java
new file mode 100644
index 0000000000..12fdef58c2
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Currency.java
@@ -0,0 +1,47 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for currency.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <simpleType name="currency">
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ * <enumeration value="GBP"/>
+ * <enumeration value="EUR"/>
+ * <enumeration value="PLN"/>
+ * </restriction>
+ * </simpleType>
+ *
+ *
+ */
+@XmlType(name = "currency")
+@XmlEnum
+public enum Currency {
+
+ GBP,
+ EUR,
+ PLN;
+
+ public String value() {
+ return name();
+ }
+
+ public static Currency fromValue(String v) {
+ return valueOf(v);
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryRequest.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryRequest.java
new file mode 100644
index 0000000000..5739ee3b96
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryRequest.java
@@ -0,0 +1,71 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for anonymous complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="name" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "name"
+})
+@XmlRootElement(name = "getCountryRequest")
+public class GetCountryRequest {
+
+ @XmlElement(required = true)
+ protected String name;
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryResponse.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryResponse.java
new file mode 100644
index 0000000000..ba1ab56cf8
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryResponse.java
@@ -0,0 +1,71 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for anonymous complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="country" type="{http://www.baeldung.com/springsoap/gen}country"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "country"
+})
+@XmlRootElement(name = "getCountryResponse")
+public class GetCountryResponse {
+
+ @XmlElement(required = true)
+ protected Country country;
+
+ /**
+ * Gets the value of the country property.
+ *
+ * @return
+ * possible object is
+ * {@link Country }
+ *
+ */
+ public Country getCountry() {
+ return country;
+ }
+
+ /**
+ * Sets the value of the country property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Country }
+ *
+ */
+ public void setCountry(Country value) {
+ this.country = value;
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/ObjectFactory.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/ObjectFactory.java
new file mode 100644
index 0000000000..88b27245be
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/ObjectFactory.java
@@ -0,0 +1,63 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlRegistry;
+
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the com.baeldung.springsoap.client.gen package.
+ * An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.baeldung.springsoap.client.gen
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link GetCountryRequest }
+ *
+ */
+ public GetCountryRequest createGetCountryRequest() {
+ return new GetCountryRequest();
+ }
+
+ /**
+ * Create an instance of {@link GetCountryResponse }
+ *
+ */
+ public GetCountryResponse createGetCountryResponse() {
+ return new GetCountryResponse();
+ }
+
+ /**
+ * Create an instance of {@link Country }
+ *
+ */
+ public Country createCountry() {
+ return new Country();
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/package-info.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/package-info.java
new file mode 100644
index 0000000000..eefed169a8
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/package-info.java
@@ -0,0 +1,9 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.baeldung.com/springsoap/gen", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
+package com.baeldung.springsoap.client.gen;
diff --git a/spring-soap/src/main/resources/countries.wsdl b/spring-soap/src/main/resources/countries.wsdl
new file mode 100644
index 0000000000..2a2eaf05bb
--- /dev/null
+++ b/spring-soap/src/main/resources/countries.wsdl
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-soap/src/test/java/com/baeldung/springsoap/client/CountryClientLiveTest.java b/spring-soap/src/test/java/com/baeldung/springsoap/client/CountryClientLiveTest.java
new file mode 100644
index 0000000000..63dbe2a5cf
--- /dev/null
+++ b/spring-soap/src/test/java/com/baeldung/springsoap/client/CountryClientLiveTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.springsoap.client;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+
+import com.baeldung.springsoap.client.gen.Currency;
+import com.baeldung.springsoap.client.gen.GetCountryResponse;
+
+//Ensure that the server - com.baeldung.springsoap.Application - is running before executing this test
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = CountryClientConfig.class, loader = AnnotationConfigContextLoader.class)
+public class CountryClientLiveTest {
+
+ @Autowired
+ CountryClient client;
+
+ @Test
+ public void givenCountryService_whenCountryPoland_thenCapitalIsWarsaw() {
+ GetCountryResponse response = client.getCountry("Poland");
+ assertEquals("Warsaw", response.getCountry()
+ .getCapital());
+ }
+
+ @Test
+ public void givenCountryService_whenCountrySpain_thenCurrencyEUR() {
+ GetCountryResponse response = client.getCountry("Spain");
+ assertEquals(Currency.EUR, response.getCountry()
+ .getCurrency());
+ }
+}
diff --git a/testing-modules/mockito-2/README.md b/testing-modules/mockito-2/README.md
index 064366dfd5..6c9ddee01d 100644
--- a/testing-modules/mockito-2/README.md
+++ b/testing-modules/mockito-2/README.md
@@ -4,3 +4,4 @@
- [Lazy Verification with Mockito 2](https://www.baeldung.com/mockito-2-lazy-verification)
- [Mockito Strict Stubbing and The UnnecessaryStubbingException](https://www.baeldung.com/mockito-unnecessary-stubbing-exception)
- [Mockito and Fluent APIs](https://www.baeldung.com/mockito-fluent-apis)
+- [Mocking the ObjectMapper readValue() Method](https://www.baeldung.com/mockito-mock-jackson-read-value)
diff --git a/testing-modules/mockito-2/pom.xml b/testing-modules/mockito-2/pom.xml
index 76608a3039..340af89c82 100644
--- a/testing-modules/mockito-2/pom.xml
+++ b/testing-modules/mockito-2/pom.xml
@@ -14,8 +14,23 @@
../../
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ org.mockito
+ mockito-junit-jupiter
+ ${mockito.version}
+ test
+
+
+
2.21.0
+ 2.10.3
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/Flower.java b/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/Flower.java
new file mode 100644
index 0000000000..bc366a39f9
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/Flower.java
@@ -0,0 +1,32 @@
+package com.baeldung.mockito.objectmapper;
+
+public class Flower {
+
+ private String name;
+ private Integer petals;
+
+ public Flower(String name, Integer petals) {
+ this.name = name;
+ this.petals = petals;
+ }
+
+ public Flower() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getPetals() {
+ return petals;
+ }
+
+ public void setPetals(Integer petals) {
+ this.petals = petals;
+ }
+
+}
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidator.java b/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidator.java
new file mode 100644
index 0000000000..91bad66e6d
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidator.java
@@ -0,0 +1,17 @@
+package com.baeldung.mockito.objectmapper;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class FlowerJsonStringValidator {
+ private ObjectMapper objectMapper;
+
+ public FlowerJsonStringValidator(ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ }
+
+ public boolean flowerHasPetals(String jsonFlowerAsString) throws JsonProcessingException {
+ Flower flower = objectMapper.readValue(jsonFlowerAsString, Flower.class);
+ return flower.getPetals() > 0;
+ }
+}
diff --git a/testing-modules/mockito-2/src/test/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidatorUnitTest.java b/testing-modules/mockito-2/src/test/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidatorUnitTest.java
new file mode 100644
index 0000000000..31c3f0d01d
--- /dev/null
+++ b/testing-modules/mockito-2/src/test/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidatorUnitTest.java
@@ -0,0 +1,53 @@
+package com.baeldung.mockito.objectmapper;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class FlowerJsonStringValidatorUnitTest {
+
+ @Mock
+ private ObjectMapper objectMapper;
+
+ private FlowerJsonStringValidator flowerJsonStringValidator;
+
+ @BeforeEach
+ public void setUp() {
+ flowerJsonStringValidator = new FlowerJsonStringValidator(objectMapper);
+ }
+
+ @Test
+ public void whenCallingHasPetalsWithPetals_thenReturnsTrue() throws JsonProcessingException {
+ Flower rose = new Flower("testFlower", 100);
+
+ when(objectMapper.readValue(anyString(), eq(Flower.class))).thenReturn(rose);
+
+ assertTrue(flowerJsonStringValidator.flowerHasPetals("this can be a very long json flower"));
+
+ verify(objectMapper, times(1)).readValue(anyString(), eq(Flower.class));
+ }
+
+ @Test
+ public void whenCallingHasPetalsWithZeroPetal_thenReturnsFalse() throws JsonProcessingException {
+ Flower rose = new Flower("testFlowerWithoutPetal", 0);
+
+ when(objectMapper.readValue(anyString(), eq(Flower.class))).thenReturn(rose);
+
+ assertFalse(flowerJsonStringValidator.flowerHasPetals("this can be a very long json flower"));
+
+ verify(objectMapper, times(1)).readValue(anyString(), eq(Flower.class));
+ }
+}