diff --git a/.gitignore b/.gitignore index 88c5e49808..48b52bec06 100644 --- a/.gitignore +++ b/.gitignore @@ -89,5 +89,14 @@ testing-modules/report-*.json libraries-2/*.db +apache-spark/data/output +logs/ +libraries-data-io/*.docx +persistence-modules/spring-hibernate-5/com.* +spring-boot-modules/spring-boot-react/frontend/build +spring-boot-modules/spring-boot-react/frontend/node +spring-boot-modules/spring-boot-react/frontend/yarn.lock +spring-boot-modules/spring-boot-properties-3/*.log + # SDKMan -.sdkmanrc +.sdkmanrc \ No newline at end of file diff --git a/algorithms-miscellaneous-3/pom.xml b/algorithms-miscellaneous-3/pom.xml index d5416e543d..19eca8eca7 100644 --- a/algorithms-miscellaneous-3/pom.xml +++ b/algorithms-miscellaneous-3/pom.xml @@ -20,19 +20,16 @@ ${org.assertj.core.version} test - org.apache.commons commons-collections4 ${commons-collections4.version} - com.google.guava guava ${guava.version} - com.squareup.retrofit2 retrofit @@ -43,13 +40,11 @@ converter-jackson ${retrofit.version} - org.apache.commons commons-lang3 ${commons.lang3.version} - pl.pragmatists JUnitParams diff --git a/algorithms-miscellaneous-5/pom.xml b/algorithms-miscellaneous-5/pom.xml index 8b7cfc7b25..1ba535dfbc 100644 --- a/algorithms-miscellaneous-5/pom.xml +++ b/algorithms-miscellaneous-5/pom.xml @@ -39,7 +39,6 @@ junit-platform-commons ${junit.platform.version} - org.assertj assertj-core diff --git a/algorithms-searching/src/main/java/com/baeldung/algorithms/binarysearch/BinarySearch.java b/algorithms-searching/src/main/java/com/baeldung/algorithms/binarysearch/BinarySearch.java index 82aefe282b..078914595e 100644 --- a/algorithms-searching/src/main/java/com/baeldung/algorithms/binarysearch/BinarySearch.java +++ b/algorithms-searching/src/main/java/com/baeldung/algorithms/binarysearch/BinarySearch.java @@ -12,7 +12,7 @@ public class BinarySearch { while (low <= high) { - int mid = (low + high) / 2; + int mid = low + ((high - low) / 2); if (sortedArray[mid] < key) { low = mid + 1; @@ -28,7 +28,7 @@ public class BinarySearch { public int runBinarySearchRecursively(int[] sortedArray, int key, int low, int high) { - int middle = (low + high) / 2; + int middle = low + ((high - low) / 2); if (high < low) { return -1; } diff --git a/apache-cxf/cxf-aegis/pom.xml b/apache-cxf/cxf-aegis/pom.xml index 43a6750475..996c6fc0cd 100644 --- a/apache-cxf/cxf-aegis/pom.xml +++ b/apache-cxf/cxf-aegis/pom.xml @@ -20,4 +20,4 @@ - + \ No newline at end of file diff --git a/apache-cxf/cxf-introduction/pom.xml b/apache-cxf/cxf-introduction/pom.xml index d56c7ecd81..12529e55c1 100644 --- a/apache-cxf/cxf-introduction/pom.xml +++ b/apache-cxf/cxf-introduction/pom.xml @@ -37,4 +37,4 @@ - + \ No newline at end of file diff --git a/apache-cxf/cxf-jaxrs-implementation/pom.xml b/apache-cxf/cxf-jaxrs-implementation/pom.xml index 04c94455e4..515f527a5b 100644 --- a/apache-cxf/cxf-jaxrs-implementation/pom.xml +++ b/apache-cxf/cxf-jaxrs-implementation/pom.xml @@ -52,4 +52,4 @@ 4.5.2 - + \ No newline at end of file diff --git a/apache-cxf/cxf-spring/pom.xml b/apache-cxf/cxf-spring/pom.xml index 340b3d486a..772ece81da 100644 --- a/apache-cxf/cxf-spring/pom.xml +++ b/apache-cxf/cxf-spring/pom.xml @@ -107,4 +107,4 @@ 1.6.1 - + \ No newline at end of file diff --git a/apache-cxf/pom.xml b/apache-cxf/pom.xml index 3f08c11db0..6b7e396b9e 100644 --- a/apache-cxf/pom.xml +++ b/apache-cxf/pom.xml @@ -38,4 +38,4 @@ 3.1.8 - + \ No newline at end of file diff --git a/apache-cxf/sse-jaxrs/pom.xml b/apache-cxf/sse-jaxrs/pom.xml index 737fc43dc2..1ac2948439 100644 --- a/apache-cxf/sse-jaxrs/pom.xml +++ b/apache-cxf/sse-jaxrs/pom.xml @@ -18,4 +18,4 @@ sse-jaxrs-client - + \ No newline at end of file diff --git a/apache-cxf/sse-jaxrs/sse-jaxrs-server/pom.xml b/apache-cxf/sse-jaxrs/sse-jaxrs-server/pom.xml index 187f03a431..0467fd9e5d 100644 --- a/apache-cxf/sse-jaxrs/sse-jaxrs-server/pom.xml +++ b/apache-cxf/sse-jaxrs/sse-jaxrs-server/pom.xml @@ -14,7 +14,6 @@ - javax.ws.rs javax.ws.rs-api @@ -33,7 +32,6 @@ ${bind-api.version} provided - @@ -85,4 +83,4 @@ 1.0 - + \ No newline at end of file diff --git a/apache-libraries/pom.xml b/apache-libraries/pom.xml index 856b01daa0..ded10b939f 100644 --- a/apache-libraries/pom.xml +++ b/apache-libraries/pom.xml @@ -183,7 +183,6 @@ - org.apache.meecrowave @@ -216,4 +215,4 @@ 6.4.0 - + \ No newline at end of file diff --git a/apache-olingo/olingo2/src/main/resources/application.yml b/apache-olingo/olingo2/src/main/resources/application.yml index 71df0c4166..3e09ea8dd5 100644 --- a/apache-olingo/olingo2/src/main/resources/application.yml +++ b/apache-olingo/olingo2/src/main/resources/application.yml @@ -6,7 +6,12 @@ spring: application-path: /odata jpa: + defer-datasource-initialization: true show-sql: true open-in-view: false hibernate: - ddl-auto: update \ No newline at end of file + ddl-auto: update + + sql: + init: + mode: always \ No newline at end of file diff --git a/apache-rocketmq/pom.xml b/apache-rocketmq/pom.xml index a5f552d6f1..48399b6d51 100644 --- a/apache-rocketmq/pom.xml +++ b/apache-rocketmq/pom.xml @@ -25,4 +25,5 @@ 1.6.0 2.0.4 + \ No newline at end of file diff --git a/apache-spark/pom.xml b/apache-spark/pom.xml index c942795532..05c5088662 100644 --- a/apache-spark/pom.xml +++ b/apache-spark/pom.xml @@ -94,20 +94,20 @@ SparkPackagesRepo - http://dl.bintray.com/spark-packages/maven + https://repos.spark-packages.org - 2.3.0 - 2.3.0 - 2.3.0 - 2.3.0 - 2.3.0 - 0.7.0-spark2.4-s_2.11 - 2.3.0 - 2.3.0 - 1.5.2 + 2.4.8 + 2.4.8 + 2.4.8 + 2.4.8 + 2.4.8 + 0.8.1-spark3.0-s_2.12 + 2.4.8 + 2.5.2 + 1.6.0-M1 \ No newline at end of file diff --git a/apache-tapestry/pom.xml b/apache-tapestry/pom.xml index c7cdc0bf3d..13ae49b0ba 100644 --- a/apache-tapestry/pom.xml +++ b/apache-tapestry/pom.xml @@ -77,7 +77,6 @@ true - org.apache.maven.plugins maven-surefire-plugin @@ -88,7 +87,6 @@ - org.mortbay.jetty @@ -119,7 +117,6 @@ jboss http://repository.jboss.org/nexus/content/groups/public/ - diff --git a/asciidoctor/pom.xml b/asciidoctor/pom.xml index ec998a67b8..e24917f2f4 100644 --- a/asciidoctor/pom.xml +++ b/asciidoctor/pom.xml @@ -68,4 +68,4 @@ 1.5.0-alpha.15 - + \ No newline at end of file diff --git a/atomix/pom.xml b/atomix/pom.xml index 2b9fc9be37..77e554852c 100644 --- a/atomix/pom.xml +++ b/atomix/pom.xml @@ -31,4 +31,4 @@ 1.0.0-rc9 - + \ No newline at end of file diff --git a/aws-lambda/pom.xml b/aws-lambda/pom.xml index 8014a87126..3264356977 100644 --- a/aws-lambda/pom.xml +++ b/aws-lambda/pom.xml @@ -20,4 +20,4 @@ todo-reminder/ToDoFunction - + \ No newline at end of file diff --git a/aws-lambda/todo-reminder/ToDoFunction/pom.xml b/aws-lambda/todo-reminder/ToDoFunction/pom.xml index f80cbdf22f..832cee841d 100644 --- a/aws-lambda/todo-reminder/ToDoFunction/pom.xml +++ b/aws-lambda/todo-reminder/ToDoFunction/pom.xml @@ -1,15 +1,12 @@ - 4.0.0 helloworld - HelloWorld + ToDoFunction 1.0 jar - To Do Application Example. - - 1.8 - 1.8 - + ToDoFunction @@ -18,9 +15,9 @@ 1.2.1 - com.amazonaws - aws-lambda-java-events - 3.6.0 + com.amazonaws + aws-lambda-java-events + 3.6.0 uk.org.webcompere @@ -58,10 +55,10 @@ 5.0.1 - junit - junit - 4.13.1 - test + junit + junit + 4.13.1 + test uk.org.webcompere @@ -84,22 +81,28 @@ - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - - - package - - shade - - - - - + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + + + package + + shade + + + + + - + + + 1.8 + 1.8 + + + \ No newline at end of file diff --git a/aws-reactive/pom.xml b/aws-reactive/pom.xml index 9f0144b15c..ea1e0c44a4 100644 --- a/aws-reactive/pom.xml +++ b/aws-reactive/pom.xml @@ -93,4 +93,5 @@ 2.2.1.RELEASE 2.10.27 + \ No newline at end of file diff --git a/blade/pom.xml b/blade/pom.xml index b763f69c85..6ab3a594f2 100644 --- a/blade/pom.xml +++ b/blade/pom.xml @@ -83,39 +83,6 @@ - - - com.bazaarvoice.maven.plugins - process-exec-maven-plugin - ${process-exec-maven-plugin.version} - - - - blade-process - pre-integration-test - - start - - - Blade - false - - java - -jar - blade.jar - - - - - - stop-all - post-integration-test - - stop-all - - - - maven-assembly-plugin ${assembly.plugin.version} diff --git a/bootique/pom.xml b/bootique/pom.xml index 5710259632..34fc75a5c0 100644 --- a/bootique/pom.xml +++ b/bootique/pom.xml @@ -48,13 +48,11 @@ - org.apache.maven.plugins maven-shade-plugin ${maven-shade-plugin.version} - diff --git a/cloud-foundry-uaa/pom.xml b/cloud-foundry-uaa/pom.xml index a8a46b921d..03a5b978d4 100644 --- a/cloud-foundry-uaa/pom.xml +++ b/cloud-foundry-uaa/pom.xml @@ -1,6 +1,5 @@ - 4.0.0 @@ -20,4 +19,4 @@ cf-uaa-oauth2-resource-server - + \ No newline at end of file diff --git a/core-groovy-2/pom.xml b/core-groovy-2/pom.xml index 37f8629ae8..f8ef654293 100644 --- a/core-groovy-2/pom.xml +++ b/core-groovy-2/pom.xml @@ -161,21 +161,6 @@ - - - bintray - Groovy Bintray - https://dl.bintray.com/groovy/maven - - - never - - - false - - - - 1.0.0 2.4.0 diff --git a/core-java-modules/core-java-11-2/pom.xml b/core-java-modules/core-java-11-2/pom.xml index 5f76c0bf3b..e26e31da44 100644 --- a/core-java-modules/core-java-11-2/pom.xml +++ b/core-java-modules/core-java-11-2/pom.xml @@ -55,6 +55,23 @@ commons-lang3 ${commons-lang3.version} + + jakarta.xml.ws + jakarta.xml.ws-api + ${jakarta.ws-api.version} + + + com.sun.xml.ws + jaxws-rt + ${jaxws-rt.version} + runtime + + + com.sun.xml.ws + jaxws-ri + ${jaxws-ri.version} + pom + @@ -68,6 +85,20 @@ ${maven.compiler.target.version} + + + com.sun.xml.ws + jaxws-maven-plugin + ${jaxws-maven-plugin.version} + + + http://localhost:8888/ws/country?wsdl + + true + com.baeldung.soap.ws.client.generated + src/main/java + + @@ -79,6 +110,10 @@ 3.17.2 5.11.1 3.12.0 + 3.0.0 + 3.0.0 + 2.3.1 + 2.3.2 \ No newline at end of file diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Country.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Country.java new file mode 100644 index 0000000000..950d588661 --- /dev/null +++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Country.java @@ -0,0 +1,135 @@ + +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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java new file mode 100644 index 0000000000..807d152cf1 --- /dev/null +++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java @@ -0,0 +1,40 @@ + +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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java new file mode 100644 index 0000000000..97d6c82145 --- /dev/null +++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java @@ -0,0 +1,94 @@ + +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 = "http://localhost:8888/ws/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("http://localhost:8888/ws/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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java new file mode 100644 index 0000000000..c010f5533c --- /dev/null +++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java @@ -0,0 +1,40 @@ + +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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java new file mode 100644 index 0000000000..9ed85fe2b9 --- /dev/null +++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java @@ -0,0 +1,40 @@ + +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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/package-info.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/package-info.java new file mode 100644 index 0000000000..dfc556859f --- /dev/null +++ b/core-java-modules/core-java-11-2/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/") +package com.baeldung.soap.ws.client.generated; diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Country.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Country.java new file mode 100644 index 0000000000..62ea4a22ed --- /dev/null +++ b/core-java-modules/core-java-11-2/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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryRepository.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryRepository.java new file mode 100644 index 0000000000..558f7c1293 --- /dev/null +++ b/core-java-modules/core-java-11-2/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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryService.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryService.java new file mode 100644 index 0000000000..b11765b326 --- /dev/null +++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryService.java @@ -0,0 +1,15 @@ +package com.baeldung.soap.ws.server; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebService; +import jakarta.jws.soap.SOAPBinding; +import jakarta.jws.soap.SOAPBinding.Style; + +@WebService +@SOAPBinding(style=Style.RPC) +public interface CountryService { + + @WebMethod + Country findByName(String name); + +} diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java new file mode 100644 index 0000000000..da159a1475 --- /dev/null +++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java @@ -0,0 +1,15 @@ +package com.baeldung.soap.ws.server; + +import jakarta.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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java new file mode 100644 index 0000000000..314a91dc48 --- /dev/null +++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java @@ -0,0 +1,19 @@ +package com.baeldung.soap.ws.server; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.xml.ws.Endpoint; + + +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/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Currency.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Currency.java new file mode 100644 index 0000000000..d1b25a26c6 --- /dev/null +++ b/core-java-modules/core-java-11-2/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/core-java-modules/core-java-11-2/src/test/java/com/baeldung/version/VersionUnitTest.java b/core-java-modules/core-java-11-2/src/test/java/com/baeldung/version/VersionUnitTest.java index fb7d5647a5..128c7f60f4 100644 --- a/core-java-modules/core-java-11-2/src/test/java/com/baeldung/version/VersionUnitTest.java +++ b/core-java-modules/core-java-11-2/src/test/java/com/baeldung/version/VersionUnitTest.java @@ -9,7 +9,7 @@ public class VersionUnitTest { @Test public void givenJava_whenUsingRuntime_thenGetVersion() { - String expectedVersion = "11"; + String expectedVersion = "15"; Runtime.Version runtimeVersion = Runtime.version(); String version = String.valueOf(runtimeVersion.version().get(0)); Assertions.assertThat(version).isEqualTo(expectedVersion); diff --git a/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java b/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java index a3a5592cd9..3b9db284c8 100644 --- a/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java +++ b/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java @@ -17,8 +17,8 @@ import java.nio.file.Paths; import java.security.NoSuchAlgorithmException; import java.time.Duration; +import org.junit.Ignore; import org.junit.Test; -import org.junit.jupiter.api.Disabled; public class HttpRequestUnitTest { @@ -51,10 +51,10 @@ public class HttpRequestUnitTest { /* * This test will fail as soon as the given URL returns a HTTP 2 response. - * Therefore, let's leave it commented out. + * Therefore, let's leave it ignored. * */ - @Test - @Disabled + @Test + @Ignore public void shouldFallbackToHttp1_1WhenWebsiteDoesNotUseHttp2() throws IOException, InterruptedException, URISyntaxException, NoSuchAlgorithmException { HttpRequest request = HttpRequest.newBuilder() .uri(new URI("https://postman-echo.com/get")) diff --git a/core-java-modules/core-java-12/pom.xml b/core-java-modules/core-java-12/pom.xml index 8f6abdda5b..ce7ec72aeb 100644 --- a/core-java-modules/core-java-12/pom.xml +++ b/core-java-modules/core-java-12/pom.xml @@ -1,49 +1,60 @@ - 4.0.0 - core-java-12 - 0.1.0-SNAPSHOT - core-java-12 - jar - http://maven.apache.org + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + core-java-12 + 0.1.0-SNAPSHOT + core-java-12 + jar + http://maven.apache.org - - com.baeldung - parent-modules - 1.0.0-SNAPSHOT - ../../ - + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + ../../ + - - - org.assertj - assertj-core - ${assertj.version} - test - - + + + org.assertj + assertj-core + ${assertj.version} + test + + + commons-io + commons-io + 2.11.0 + + - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${maven.compiler.source.version} - ${maven.compiler.target.version} - --enable-preview - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source.version} + ${maven.compiler.target.version} + --enable-preview + + + + maven-surefire-plugin + + --enable-preview + + + + - - 12 - 12 - 3.6.1 - + + 12 + 12 + 3.6.1 + \ No newline at end of file diff --git a/core-java-modules/core-java-12/src/main/java/com/baeldung/file/content/comparison/CompareFileContents.java b/core-java-modules/core-java-12/src/main/java/com/baeldung/file/content/comparison/CompareFileContents.java new file mode 100644 index 0000000000..637ddb4a91 --- /dev/null +++ b/core-java-modules/core-java-12/src/main/java/com/baeldung/file/content/comparison/CompareFileContents.java @@ -0,0 +1,88 @@ +package com.baeldung.file.content.comparison; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; + +public class CompareFileContents { + + public static long filesCompareByByte(Path path1, Path path2) throws IOException { + + if (path1.getFileSystem() + .provider() + .isSameFile(path1, path2)) { + return -1; + } + + try (BufferedInputStream fis1 = new BufferedInputStream(new FileInputStream(path1.toFile())); + BufferedInputStream fis2 = new BufferedInputStream(new FileInputStream(path2.toFile()))) { + int ch = 0; + long pos = 1; + while ((ch = fis1.read()) != -1) { + if (ch != fis2.read()) { + return pos; + } + pos++; + } + if (fis2.read() == -1) { + return -1; + } else { + return pos; + } + } + } + + public static long filesCompareByLine(Path path1, Path path2) throws IOException { + + if (path1.getFileSystem() + .provider() + .isSameFile(path1, path2)) { + return -1; + } + + try (BufferedReader bf1 = Files.newBufferedReader(path1); + BufferedReader bf2 = Files.newBufferedReader(path2)) { + + long lineNumber = 1; + String line1 = "", line2 = ""; + while ((line1 = bf1.readLine()) != null) { + line2 = bf2.readLine(); + if (line2 == null || !line1.equals(line2)) { + return lineNumber; + } + lineNumber++; + } + if (bf2.readLine() == null) { + return -1; + } else { + return lineNumber; + } + } + } + + public static boolean compareByMemoryMappedFiles(Path path1, Path path2) throws IOException { + + try (RandomAccessFile randomAccessFile1 = new RandomAccessFile(path1.toFile(), "r"); + RandomAccessFile randomAccessFile2 = new RandomAccessFile(path2.toFile(), "r")) { + + FileChannel ch1 = randomAccessFile1.getChannel(); + FileChannel ch2 = randomAccessFile2.getChannel(); + if (ch1.size() != ch2.size()) { + + return false; + } + long size = ch1.size(); + MappedByteBuffer m1 = ch1.map(FileChannel.MapMode.READ_ONLY, 0L, size); + MappedByteBuffer m2 = ch2.map(FileChannel.MapMode.READ_ONLY, 0L, size); + + return m1.equals(m2); + } + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CampareFileContentsApacheIOUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CampareFileContentsApacheIOUnitTest.java new file mode 100644 index 0000000000..0d86abae11 --- /dev/null +++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CampareFileContentsApacheIOUnitTest.java @@ -0,0 +1,88 @@ +package com.baeldung.file.content.comparison; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CampareFileContentsApacheIOUnitTest { + + public static Path path1 = null; + public static Path path2 = null; + + @BeforeAll + public static void setup() throws IOException { + + path1 = Files.createTempFile("file1Test", ".txt"); + path2 = Files.createTempFile("file2Test", ".txt"); + } + + @Test + public void whenFilesIdentical_thenReturnTrue() throws IOException { + + InputStream inputStream1 = new FileInputStream(path1.toFile()); + InputStream inputStream2 = new FileInputStream(path2.toFile()); + + Files.writeString(path1, "testing line 1" + System.lineSeparator() + "line 2"); + Files.writeString(path2, "testing line 1" + System.lineSeparator() + "line 2"); + + assertTrue(IOUtils.contentEquals(inputStream1, inputStream2)); + } + + @Test + public void whenFilesDifferent_thenReturnFalse() throws IOException { + + InputStream inputStream1 = new FileInputStream(path1.toFile()); + InputStream inputStream2 = new FileInputStream(path2.toFile()); + + Files.writeString(path1, "testing line " + System.lineSeparator() + "line 2"); + Files.writeString(path2, "testing line 1" + System.lineSeparator() + "line 2"); + + assertFalse(IOUtils.contentEquals(inputStream1, inputStream2)); + } + + @Test + public void whenFilesIdenticalIgnoreEOF_thenReturnTrue() throws IOException { + + Files.writeString(path1, "testing line 1 \n line 2"); + Files.writeString(path2, "testing line 1 \r\n line 2"); + + Reader reader1 = new BufferedReader(new FileReader(path1.toFile())); + Reader reader2 = new BufferedReader(new FileReader(path2.toFile())); + + assertTrue(IOUtils.contentEqualsIgnoreEOL(reader1, reader2)); + } + + @Test + public void whenFilesNotIdenticalIgnoreEOF_thenReturnFalse() throws IOException { + + Files.writeString(path1, "testing line \n line 2"); + Files.writeString(path2, "testing line 1 \r\n line 2"); + + Reader reader1 = new BufferedReader(new FileReader(path1.toFile())); + Reader reader2 = new BufferedReader(new FileReader(path2.toFile())); + + assertFalse(IOUtils.contentEqualsIgnoreEOL(reader1, reader2)); + } + + @AfterAll + public static void shutDown() { + + path1.toFile() + .deleteOnExit(); + path2.toFile() + .deleteOnExit(); + } +} diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareByMemoryMappedFilesUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareByMemoryMappedFilesUnitTest.java new file mode 100644 index 0000000000..0405ac3b52 --- /dev/null +++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareByMemoryMappedFilesUnitTest.java @@ -0,0 +1,42 @@ +package com.baeldung.file.content.comparison; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CompareByMemoryMappedFilesUnitTest { + + public static Path path1 = null; + public static Path path2 = null; + + @BeforeAll + public static void setup() throws IOException { + + path1 = Files.createTempFile("file1Test", ".txt"); + path2 = Files.createTempFile("file2Test", ".txt"); + } + + @Test + public void whenFilesIdentical_thenReturnTrue() throws IOException { + + Files.writeString(path1, "testing line 1" + System.lineSeparator() + "line 2"); + Files.writeString(path2, "testing line 1" + System.lineSeparator() + "line 2"); + + assertTrue(CompareFileContents.compareByMemoryMappedFiles(path1, path2)); + } + + @Test + public void whenFilesDifferent_thenReturnFalse() throws IOException { + + Files.writeString(path1, "testing line " + System.lineSeparator() + "line 2"); + Files.writeString(path2, "testing line 1" + System.lineSeparator() + "line 2"); + + assertFalse(CompareFileContents.compareByMemoryMappedFiles(path1, path2)); + } +} diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByBytesUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByBytesUnitTest.java new file mode 100644 index 0000000000..15efc952c2 --- /dev/null +++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByBytesUnitTest.java @@ -0,0 +1,96 @@ +package com.baeldung.file.content.comparison; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CompareFileContentsByBytesUnitTest { + + public static Path path1 = null; + public static Path path2 = null; + + @BeforeAll + public static void setup() throws IOException { + + path1 = Files.createTempFile("file1Test", ".txt"); + path2 = Files.createTempFile("file2Test", ".txt"); + } + + @Test + public void whenFirstFileShorter_thenPositionInSecondFile() throws IOException { + + Files.writeString(path1, "testing"); + Files.writeString(path2, "testing1"); + + assertEquals(8, CompareFileContents.filesCompareByByte(path1, path2)); + } + + @Test + public void whenSecondFileShorter_thenPositionInFirstFile() throws IOException { + + Files.writeString(path1, "testing1"); + Files.writeString(path2, "testing"); + + assertEquals(8, CompareFileContents.filesCompareByByte(path1, path2)); + + } + + @Test + public void whenFilesIdentical_thenSuccess() throws IOException { + + Files.writeString(path1, "testing"); + Files.writeString(path2, "testing"); + + assertEquals(-1, CompareFileContents.filesCompareByByte(path1, path2)); + + } + + @Test + public void whenFilesDifferent_thenPosition() throws IOException { + + Files.writeString(path1, "tesXing"); + Files.writeString(path2, "testing"); + + assertEquals(4, CompareFileContents.filesCompareByByte(path1, path2)); + } + + @Test + public void whenBothFilesEmpty_thenEqual() throws IOException { + + Files.writeString(path1, ""); + Files.writeString(path2, ""); + + assertEquals(-1, CompareFileContents.filesCompareByByte(path1, path2)); + } + + @Test + public void whenFirstEmpty_thenPositionFirst() throws IOException { + + Files.writeString(path1, ""); + Files.writeString(path2, "test"); + + assertEquals(1, CompareFileContents.filesCompareByByte(path1, path2)); + } + + @Test + public void whenSecondEmpty_thenPositionFirst() throws IOException { + + Files.writeString(path1, "test"); + Files.writeString(path2, ""); + + assertEquals(1, CompareFileContents.filesCompareByByte(path1, path2)); + } + + @AfterAll + public static void shutDown() { + + path1.toFile().deleteOnExit(); + path2.toFile().deleteOnExit(); + } +} diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByLinesUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByLinesUnitTest.java new file mode 100644 index 0000000000..63170221d3 --- /dev/null +++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByLinesUnitTest.java @@ -0,0 +1,94 @@ +package com.baeldung.file.content.comparison; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CompareFileContentsByLinesUnitTest { + + public static Path path1 = null; + public static Path path2 = null; + + @BeforeAll + public static void setup() throws IOException { + + path1 = Files.createTempFile("file1Test", ".txt"); + path2 = Files.createTempFile("file2Test", ".txt"); + } + + @Test + public void whenFirstFileShorter_thenLineNumbersFirstFile() throws IOException { + + Files.writeString(path1, "testing line 1"); + Files.writeString(path2, "testing1 line 1" + System.lineSeparator() + "line 2"); + + assertEquals(1, CompareFileContents.filesCompareByLine(path1, path2)); + } + + @Test + public void whenSecondFileShorter_thenLineNumbersSecondFile() throws IOException { + + Files.writeString(path1, "testing1 line 1" + System.lineSeparator() + "line 2"); + Files.writeString(path2, "testing line 1"); + + assertEquals(1, CompareFileContents.filesCompareByLine(path1, path2)); + } + + @Test + public void whenFileIdentical_thenLineSuccess() throws IOException { + + Files.writeString(path1, "testing1 line 1" + System.lineSeparator() + "line 2"); + Files.writeString(path2, "testing1 line 1" + System.lineSeparator() + "line 2"); + + assertEquals(-1, CompareFileContents.filesCompareByLine(path1, path2)); + } + + @Test + public void whenFilesDifferent_thenLineNumber() throws IOException { + + Files.writeString(path1, "testing1 line 1" + System.lineSeparator() + "line 2"); + Files.writeString(path2, "testing1 line 1" + System.lineSeparator() + "linX 2"); + + assertEquals(2, CompareFileContents.filesCompareByLine(path1, path2)); + } + + @Test + public void whenBothFilesEmpty_thenEqual() throws IOException { + + Files.writeString(path1, ""); + Files.writeString(path2, ""); + + assertEquals(-1, CompareFileContents.filesCompareByByte(path1, path2)); + } + + @Test + public void whenFirstEmpty_thenPositionFirst() throws IOException { + + Files.writeString(path1, ""); + Files.writeString(path2, "testing1 line 1" + System.lineSeparator() + "line 2"); + + assertEquals(1, CompareFileContents.filesCompareByByte(path1, path2)); + } + + @Test + public void whenSecondEmpty_thenPositionFirst() throws IOException { + + Files.writeString(path1, "testing1 line 1" + System.lineSeparator() + "line 2"); + Files.writeString(path2, ""); + + assertEquals(1, CompareFileContents.filesCompareByByte(path1, path2)); + } + + @AfterAll + public static void shutDown() { + + path1.toFile().deleteOnExit(); + path2.toFile().deleteOnExit(); + } +} diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/CompactNumbersUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/CompactNumbersUnitTest.java index 08a6d58d72..a557ce5545 100644 --- a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/CompactNumbersUnitTest.java +++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/CompactNumbersUnitTest.java @@ -1,6 +1,6 @@ -package java.com.baeldung.newfeatures; +package com.baeldung.newfeatures; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.text.NumberFormat; import java.util.Locale; @@ -16,6 +16,6 @@ public class CompactNumbersUnitTest { assertEquals("2.59K", likesShort.format(2592)); NumberFormat likesLong = NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.LONG); likesLong.setMaximumFractionDigits(2); - assertEquals("2.59 thousand", likesShort.format(2592)); + assertEquals("2.59 thousand", likesLong.format(2592)); } } diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/FileMismatchUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/FileMismatchUnitTest.java index 7f081fe399..93fcfbda02 100644 --- a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/FileMismatchUnitTest.java +++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/FileMismatchUnitTest.java @@ -1,6 +1,6 @@ -package java.com.baeldung.newfeatures; +package com.baeldung.newfeatures; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/StringUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/StringUnitTest.java index 5ae51bd960..1651fe0ee6 100644 --- a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/StringUnitTest.java +++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/StringUnitTest.java @@ -1,6 +1,6 @@ -package java.com.baeldung.newfeatures; +package com.baeldung.newfeatures; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.junit.Assert.assertEquals; diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/TeeingCollectorUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/TeeingCollectorUnitTest.java index 30a5cb40a7..a925e693ff 100644 --- a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/TeeingCollectorUnitTest.java +++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/TeeingCollectorUnitTest.java @@ -1,6 +1,6 @@ -package java.com.baeldung.newfeatures; +package com.baeldung.newfeatures; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -13,6 +13,6 @@ public class TeeingCollectorUnitTest { public void givenSetOfNumbers_thenCalculateAverage() { double mean = Stream.of(1, 2, 3, 4, 5) .collect(Collectors.teeing(Collectors.summingDouble(i -> i), Collectors.counting(), (sum, count) -> sum / count)); - assertEquals(3.0, mean); + assertEquals(3.0, mean, 0); } } diff --git a/core-java-modules/core-java-16/README.md b/core-java-modules/core-java-16/README.md new file mode 100644 index 0000000000..760513189f --- /dev/null +++ b/core-java-modules/core-java-16/README.md @@ -0,0 +1,3 @@ +### Relevant articles: + +- [Collect a Java Stream to an Immutable Collection](https://www.baeldung.com/java-stream-immutable-collection) diff --git a/core-java-modules/core-java-16/pom.xml b/core-java-modules/core-java-16/pom.xml new file mode 100644 index 0000000000..a8a84511db --- /dev/null +++ b/core-java-modules/core-java-16/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + core-java-16 + 0.1.0-SNAPSHOT + core-java-16 + jar + http://maven.apache.org + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + ../../ + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source.version} + ${maven.compiler.target.version} + + + + + + + 16 + 16 + 3.6.1 + + + \ No newline at end of file diff --git a/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPost.java b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPost.java new file mode 100644 index 0000000000..adaa1044ff --- /dev/null +++ b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPost.java @@ -0,0 +1,38 @@ +package com.baeldung.java_16_features.groupingby; + +public class BlogPost { + + private String title; + private String author; + private BlogPostType type; + private int likes; + record AuthPostTypesLikes(String author, BlogPostType type, int likes) {}; + + public BlogPost(String title, String author, BlogPostType type, int likes) { + this.title = title; + this.author = author; + this.type = type; + this.likes = likes; + } + + public String getTitle() { + return title; + } + + public String getAuthor() { + return author; + } + + public BlogPostType getType() { + return type; + } + + public int getLikes() { + return likes; + } + + @Override + public String toString() { + return "BlogPost{" + "title='" + title + '\'' + ", type=" + type + ", likes=" + likes + '}'; + } +} diff --git a/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPostType.java b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPostType.java new file mode 100644 index 0000000000..df38b7e1c4 --- /dev/null +++ b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPostType.java @@ -0,0 +1,5 @@ +package com.baeldung.java_16_features.groupingby; + +public enum BlogPostType { + NEWS, REVIEW, GUIDE +} diff --git a/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/Tuple.java b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/Tuple.java new file mode 100644 index 0000000000..ad41207aa4 --- /dev/null +++ b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/Tuple.java @@ -0,0 +1,41 @@ +package com.baeldung.java_16_features.groupingby; + +import java.util.Objects; + +public class Tuple { + private final BlogPostType type; + private final String author; + + public Tuple(BlogPostType type, String author) { + this.type = type; + this.author = author; + } + + public BlogPostType getType() { + return type; + } + + public String getAuthor() { + return author; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Tuple tuple = (Tuple) o; + return type == tuple.type && author.equals(tuple.author); + } + + @Override + public int hashCode() { + return Objects.hash(type, author); + } + + @Override + public String toString() { + return "Tuple{" + "type=" + type + ", author='" + author + '\'' + '}'; + } +} diff --git a/core-java-modules/core-java-16/src/test/java/com/baeldung/java_16_features/groupingby/JavaGroupingByCollectorUnitTest.java b/core-java-modules/core-java-16/src/test/java/com/baeldung/java_16_features/groupingby/JavaGroupingByCollectorUnitTest.java new file mode 100644 index 0000000000..0e926246ff --- /dev/null +++ b/core-java-modules/core-java-16/src/test/java/com/baeldung/java_16_features/groupingby/JavaGroupingByCollectorUnitTest.java @@ -0,0 +1,254 @@ +package com.baeldung.java_16_features.groupingby; + +import static java.util.Comparator.comparingInt; +import static java.util.stream.Collectors.averagingInt; +import static java.util.stream.Collectors.counting; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.groupingByConcurrent; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.mapping; +import static java.util.stream.Collectors.maxBy; +import static java.util.stream.Collectors.summarizingInt; +import static java.util.stream.Collectors.summingInt; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.EnumMap; +import java.util.IntSummaryStatistics; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.Test; + +public class JavaGroupingByCollectorUnitTest { + + private static final List posts = Arrays.asList(new BlogPost("News item 1", "Author 1", BlogPostType.NEWS, 15), new BlogPost("Tech review 1", "Author 2", BlogPostType.REVIEW, 5), + new BlogPost("Programming guide", "Author 1", BlogPostType.GUIDE, 20), new BlogPost("News item 2", "Author 2", BlogPostType.NEWS, 35), new BlogPost("Tech review 2", "Author 1", BlogPostType.REVIEW, 15)); + + @Test + public void givenAListOfPosts_whenGroupedByType_thenGetAMapBetweenTypeAndPosts() { + Map> postsPerType = posts.stream() + .collect(groupingBy(BlogPost::getType)); + + assertEquals(2, postsPerType.get(BlogPostType.NEWS) + .size()); + assertEquals(1, postsPerType.get(BlogPostType.GUIDE) + .size()); + assertEquals(2, postsPerType.get(BlogPostType.REVIEW) + .size()); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeAndTheirTitlesAreJoinedInAString_thenGetAMapBetweenTypeAndCsvTitles() { + Map postsPerType = posts.stream() + .collect(groupingBy(BlogPost::getType, mapping(BlogPost::getTitle, joining(", ", "Post titles: [", "]")))); + + assertEquals("Post titles: [News item 1, News item 2]", postsPerType.get(BlogPostType.NEWS)); + assertEquals("Post titles: [Programming guide]", postsPerType.get(BlogPostType.GUIDE)); + assertEquals("Post titles: [Tech review 1, Tech review 2]", postsPerType.get(BlogPostType.REVIEW)); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeAndSumTheLikes_thenGetAMapBetweenTypeAndPostLikes() { + Map likesPerType = posts.stream() + .collect(groupingBy(BlogPost::getType, summingInt(BlogPost::getLikes))); + + assertEquals(50, likesPerType.get(BlogPostType.NEWS) + .intValue()); + assertEquals(20, likesPerType.get(BlogPostType.REVIEW) + .intValue()); + assertEquals(20, likesPerType.get(BlogPostType.GUIDE) + .intValue()); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeInAnEnumMap_thenGetAnEnumMapBetweenTypeAndPosts() { + EnumMap> postsPerType = posts.stream() + .collect(groupingBy(BlogPost::getType, () -> new EnumMap<>(BlogPostType.class), toList())); + + assertEquals(2, postsPerType.get(BlogPostType.NEWS) + .size()); + assertEquals(1, postsPerType.get(BlogPostType.GUIDE) + .size()); + assertEquals(2, postsPerType.get(BlogPostType.REVIEW) + .size()); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeInSets_thenGetAMapBetweenTypesAndSetsOfPosts() { + Map> postsPerType = posts.stream() + .collect(groupingBy(BlogPost::getType, toSet())); + + assertEquals(2, postsPerType.get(BlogPostType.NEWS) + .size()); + assertEquals(1, postsPerType.get(BlogPostType.GUIDE) + .size()); + assertEquals(2, postsPerType.get(BlogPostType.REVIEW) + .size()); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeConcurrently_thenGetAMapBetweenTypeAndPosts() { + ConcurrentMap> postsPerType = posts.parallelStream() + .collect(groupingByConcurrent(BlogPost::getType)); + + assertEquals(2, postsPerType.get(BlogPostType.NEWS) + .size()); + assertEquals(1, postsPerType.get(BlogPostType.GUIDE) + .size()); + assertEquals(2, postsPerType.get(BlogPostType.REVIEW) + .size()); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeAndAveragingLikes_thenGetAMapBetweenTypeAndAverageNumberOfLikes() { + Map averageLikesPerType = posts.stream() + .collect(groupingBy(BlogPost::getType, averagingInt(BlogPost::getLikes))); + + assertEquals(25, averageLikesPerType.get(BlogPostType.NEWS) + .intValue()); + assertEquals(20, averageLikesPerType.get(BlogPostType.GUIDE) + .intValue()); + assertEquals(10, averageLikesPerType.get(BlogPostType.REVIEW) + .intValue()); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeAndCounted_thenGetAMapBetweenTypeAndNumberOfPosts() { + Map numberOfPostsPerType = posts.stream() + .collect(groupingBy(BlogPost::getType, counting())); + + assertEquals(2, numberOfPostsPerType.get(BlogPostType.NEWS) + .intValue()); + assertEquals(1, numberOfPostsPerType.get(BlogPostType.GUIDE) + .intValue()); + assertEquals(2, numberOfPostsPerType.get(BlogPostType.REVIEW) + .intValue()); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeAndMaxingLikes_thenGetAMapBetweenTypeAndMaximumNumberOfLikes() { + Map> maxLikesPerPostType = posts.stream() + .collect(groupingBy(BlogPost::getType, maxBy(comparingInt(BlogPost::getLikes)))); + + assertTrue(maxLikesPerPostType.get(BlogPostType.NEWS) + .isPresent()); + assertEquals(35, maxLikesPerPostType.get(BlogPostType.NEWS) + .get() + .getLikes()); + + assertTrue(maxLikesPerPostType.get(BlogPostType.GUIDE) + .isPresent()); + assertEquals(20, maxLikesPerPostType.get(BlogPostType.GUIDE) + .get() + .getLikes()); + + assertTrue(maxLikesPerPostType.get(BlogPostType.REVIEW) + .isPresent()); + assertEquals(15, maxLikesPerPostType.get(BlogPostType.REVIEW) + .get() + .getLikes()); + } + + @Test + public void givenAListOfPosts_whenGroupedByAuthorAndThenByType_thenGetAMapBetweenAuthorAndMapsBetweenTypeAndBlogPosts() { + Map>> map = posts.stream() + .collect(groupingBy(BlogPost::getAuthor, groupingBy(BlogPost::getType))); + + assertEquals(1, map.get("Author 1") + .get(BlogPostType.NEWS) + .size()); + assertEquals(1, map.get("Author 1") + .get(BlogPostType.GUIDE) + .size()); + assertEquals(1, map.get("Author 1") + .get(BlogPostType.REVIEW) + .size()); + + assertEquals(1, map.get("Author 2") + .get(BlogPostType.NEWS) + .size()); + assertEquals(1, map.get("Author 2") + .get(BlogPostType.REVIEW) + .size()); + assertNull(map.get("Author 2") + .get(BlogPostType.GUIDE)); + } + + @Test + public void givenAListOfPosts_whenGroupedByTypeAndSummarizingLikes_thenGetAMapBetweenTypeAndSummary() { + Map likeStatisticsPerType = posts.stream() + .collect(groupingBy(BlogPost::getType, summarizingInt(BlogPost::getLikes))); + + IntSummaryStatistics newsLikeStatistics = likeStatisticsPerType.get(BlogPostType.NEWS); + + assertEquals(2, newsLikeStatistics.getCount()); + assertEquals(50, newsLikeStatistics.getSum()); + assertEquals(25.0, newsLikeStatistics.getAverage(), 0.001); + assertEquals(35, newsLikeStatistics.getMax()); + assertEquals(15, newsLikeStatistics.getMin()); + } + + @Test + public void givenAListOfPosts_whenGroupedByComplexMapPairKeyType_thenGetAMapBetweenPairAndList() { + + Map, List> postsPerTypeAndAuthor = posts.stream() + .collect(groupingBy(post -> new ImmutablePair<>(post.getType(), post.getAuthor()))); + + List result = postsPerTypeAndAuthor.get(new ImmutablePair<>(BlogPostType.GUIDE, "Author 1")); + + assertThat(result.size()).isEqualTo(1); + + BlogPost blogPost = result.get(0); + + assertThat(blogPost.getTitle()).isEqualTo("Programming guide"); + assertThat(blogPost.getType()).isEqualTo(BlogPostType.GUIDE); + assertThat(blogPost.getAuthor()).isEqualTo("Author 1"); + } + + @Test + public void givenAListOfPosts_whenGroupedByComplexMapKeyType_thenGetAMapBetweenTupleAndList() { + + Map> postsPerTypeAndAuthor = posts.stream() + .collect(groupingBy(post -> new Tuple(post.getType(), post.getAuthor()))); + + List result = postsPerTypeAndAuthor.get(new Tuple(BlogPostType.GUIDE, "Author 1")); + + assertThat(result.size()).isEqualTo(1); + + BlogPost blogPost = result.get(0); + + assertThat(blogPost.getTitle()).isEqualTo("Programming guide"); + assertThat(blogPost.getType()).isEqualTo(BlogPostType.GUIDE); + assertThat(blogPost.getAuthor()).isEqualTo("Author 1"); + } + + @Test + public void givenAListOfPosts_whenGroupedByRecord_thenGetAMapBetweenRecordAndList() { + + Map> postsPerTypeAndAuthor = posts.stream() + .collect(groupingBy(post -> new BlogPost.AuthPostTypesLikes(post.getAuthor(), post.getType(), post.getLikes()))); + + List result = postsPerTypeAndAuthor.get(new BlogPost.AuthPostTypesLikes("Author 1", BlogPostType.GUIDE, 20)); + + assertThat(result.size()).isEqualTo(1); + + BlogPost blogPost = result.get(0); + + assertThat(blogPost.getTitle()).isEqualTo("Programming guide"); + assertThat(blogPost.getType()).isEqualTo(BlogPostType.GUIDE); + assertThat(blogPost.getAuthor()).isEqualTo("Author 1"); + assertThat(blogPost.getLikes()).isEqualTo(20); + } + +} diff --git a/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java b/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java new file mode 100644 index 0000000000..afd7369d8d --- /dev/null +++ b/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java @@ -0,0 +1,23 @@ +package com.baeldung.streams; + +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class StreamToImmutableUnitTest { + + @Test + public void whenUsingStreamToList_thenReturnImmutableList() { + + List immutableList = Stream.of("a", "b", "c", "d") + .toList(); + + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + immutableList.add("e"); + }); + + } + +} diff --git a/core-java-modules/core-java-8-2/README.md b/core-java-modules/core-java-8-2/README.md index c1c09d2192..7f2245ccc0 100644 --- a/core-java-modules/core-java-8-2/README.md +++ b/core-java-modules/core-java-8-2/README.md @@ -7,4 +7,5 @@ This module contains articles about Java 8 core features - [Run a Java Application from the Command Line](https://www.baeldung.com/java-run-jar-with-arguments) - [Java 8 Stream skip() vs limit()](https://www.baeldung.com/java-stream-skip-vs-limit) - [Guide to Java BiFunction Interface](https://www.baeldung.com/java-bifunction-interface) +- [Interface With Default Methods vs Abstract Class](https://www.baeldung.com/java-interface-default-method-vs-abstract-class) - [[<-- Prev]](/core-java-modules/core-java-8) diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChidlCircleInterfaceImpl.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChidlCircleInterfaceImpl.java new file mode 100644 index 0000000000..2aadf2e2e8 --- /dev/null +++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChidlCircleInterfaceImpl.java @@ -0,0 +1,14 @@ +package com.baeldung.interfaceVsAbstractClass; + +public class ChidlCircleInterfaceImpl implements CircleInterface { + private String color; + + @Override + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } +} diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChildCircleClass.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChildCircleClass.java new file mode 100644 index 0000000000..8d26bda306 --- /dev/null +++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChildCircleClass.java @@ -0,0 +1,5 @@ +package com.baeldung.interfaceVsAbstractClass; + +public class ChildCircleClass extends CircleClass { + +} diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleClass.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleClass.java new file mode 100644 index 0000000000..048ffa4fe9 --- /dev/null +++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleClass.java @@ -0,0 +1,23 @@ +package com.baeldung.interfaceVsAbstractClass; + +import java.util.Arrays; +import java.util.List; + +public abstract class CircleClass { + + private String color; + private List allowedColors = Arrays.asList("RED", "GREEN", "BLUE"); + + public boolean isValid() { + return allowedColors.contains(getColor()); + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + +} diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleInterface.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleInterface.java new file mode 100644 index 0000000000..a0c3a6d9c6 --- /dev/null +++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleInterface.java @@ -0,0 +1,14 @@ +package com.baeldung.interfaceVsAbstractClass; + +import java.util.Arrays; +import java.util.List; + +public interface CircleInterface { + List allowedColors = Arrays.asList("RED", "GREEN", "BLUE"); + + String getColor(); + + public default boolean isValid() { + return allowedColors.contains(getColor()); + } +} diff --git a/core-java-modules/core-java-8-2/src/test/java/com/baeldung/interfaceVsAbstractClass/InterfaceVsAbstractClassUnitTest.java b/core-java-modules/core-java-8-2/src/test/java/com/baeldung/interfaceVsAbstractClass/InterfaceVsAbstractClassUnitTest.java new file mode 100644 index 0000000000..8cba13ddeb --- /dev/null +++ b/core-java-modules/core-java-8-2/src/test/java/com/baeldung/interfaceVsAbstractClass/InterfaceVsAbstractClassUnitTest.java @@ -0,0 +1,21 @@ +package com.baeldung.interfaceVsAbstractClass; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class InterfaceVsAbstractClassUnitTest { + @Test + public void givenAbstractClass_whenValidCircleUsed_thenPass() { + CircleClass redCircle = new ChildCircleClass(); + redCircle.setColor("RED"); + assertTrue(redCircle.isValid()); + } + + @Test + public void givenInterface_whenValidCircleWithoutStateUsed_thenPass() { + ChidlCircleInterfaceImpl redCircleWithoutState = new ChidlCircleInterfaceImpl(); + redCircleWithoutState.setColor("RED"); + assertTrue(redCircleWithoutState.isValid()); + } +} diff --git a/core-java-modules/core-java-9/README.md b/core-java-modules/core-java-9/README.md index cab02369cc..8203755417 100644 --- a/core-java-modules/core-java-9/README.md +++ b/core-java-modules/core-java-9/README.md @@ -10,3 +10,4 @@ This module contains articles about Java 9 core features - [Initialize a HashMap in Java](https://www.baeldung.com/java-initialize-hashmap) - [Immutable ArrayList in Java](https://www.baeldung.com/java-immutable-list) - [Easy Ways to Write a Java InputStream to an OutputStream](https://www.baeldung.com/java-inputstream-to-outputstream) +- [Private Methods in Java Interfaces](https://www.baeldung.com/java-interface-private-methods) diff --git a/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/CustomFoo.java b/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/CustomFoo.java new file mode 100644 index 0000000000..388927fbfe --- /dev/null +++ b/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/CustomFoo.java @@ -0,0 +1,10 @@ +package com.baeldung.java9.interfaces; + +public class CustomFoo implements Foo { + + public static void main(String... args) { + Foo customFoo = new CustomFoo(); + customFoo.bar(); // 'Hello world!' + Foo.buzz(); // 'Hello static world!' + } +} diff --git a/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/Foo.java b/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/Foo.java new file mode 100644 index 0000000000..8ccee83b7e --- /dev/null +++ b/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/Foo.java @@ -0,0 +1,22 @@ +package com.baeldung.java9.interfaces; + +public interface Foo { + + public default void bar() { + System.out.print("Hello"); + baz(); + } + + public static void buzz() { + System.out.print("Hello"); + staticBaz(); + } + + private void baz() { + System.out.print(" world!"); + } + + private static void staticBaz() { + System.out.print(" static world!"); + } +} diff --git a/core-java-modules/core-java-9/src/test/java/com/baeldung/java9/interfaces/CustomFooUnitTest.java b/core-java-modules/core-java-9/src/test/java/com/baeldung/java9/interfaces/CustomFooUnitTest.java new file mode 100644 index 0000000000..d107c091fb --- /dev/null +++ b/core-java-modules/core-java-9/src/test/java/com/baeldung/java9/interfaces/CustomFooUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.java9.interfaces; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.assertj.core.api.Assertions.assertThat; + +class CustomFooUnitTest { + private ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private PrintStream originalOut = System.out; + + @BeforeEach + void setup() { + System.setOut(new PrintStream(outContent)); + } + + @AfterEach + void tearDown() { + System.setOut(originalOut); + } + + @Test + void givenACustomFooObject_whenCallingDefaultMethodBar_thenExpectedStringIsWrittenToSystemOut() { + CustomFoo customFoo = new CustomFoo(); + customFoo.bar(); + assertThat(outContent.toString()).isEqualTo("Hello world!"); + } + + @Test + void givenAFooInterface_whenCallingStaticMethodBuzz_thenExpectedStringIsWrittenToSystemOut() { + Foo.buzz(); + assertThat(outContent.toString()).isEqualTo("Hello static world!"); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-annotations/README.md b/core-java-modules/core-java-annotations/README.md index 93da3aea62..18f5589771 100644 --- a/core-java-modules/core-java-annotations/README.md +++ b/core-java-modules/core-java-annotations/README.md @@ -10,3 +10,5 @@ - [Overview of Java Built-in Annotations](https://www.baeldung.com/java-default-annotations) - [Creating a Custom Annotation in Java](https://www.baeldung.com/java-custom-annotation) - [Efficient Word Frequency Calculator in Java](https://www.baeldung.com/java-word-frequency) +- [Why Missing Annotations Don’t Cause ClassNotFoundException](https://www.baeldung.com/classnotfoundexception-missing-annotation) +- [Valid @SuppressWarnings Warning Names](https://www.baeldung.com/java-suppresswarnings-valid-names) diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java new file mode 100644 index 0000000000..aad7cf49eb --- /dev/null +++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java @@ -0,0 +1,46 @@ +package com.baeldung.annotations; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings({"serial", "unchecked"}) +public class ClassWithSuppressWarningsNames implements Serializable { + +// private static final long serialVersionUID = -1166032307853492833L; + + @SuppressWarnings("unused") + public static void suppressBoxingWarning() { + int value = 5; + int unusedVar = 10; // no warning here + List list = new ArrayList<>(); + list.add(Integer.valueOf(value)); + } + + @SuppressWarnings("deprecated") + void suppressDeprecatedWarning() { + ClassWithSuppressWarningsNames cls = new ClassWithSuppressWarningsNames(); + cls.deprecatedMethod(); // no warning here + } + + @SuppressWarnings("fallthrough") + String suppressFallthroughWarning() { + int day = 5; + switch (day) { + case 5: + return "This is day 5"; +// break; // no warning here + case 10: + return "This is day 10"; +// break; + default: + return "This default day"; + } + } + + @Deprecated + String deprecatedMethod() { + return "deprecated method"; + } + +} diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java new file mode 100644 index 0000000000..daf9f60b96 --- /dev/null +++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java @@ -0,0 +1,8 @@ +package com.baeldung.missingannotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface A { +} diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java new file mode 100644 index 0000000000..a86b2f72ec --- /dev/null +++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java @@ -0,0 +1,10 @@ +package com.baeldung.missingannotation; + +@A +@C(D.class) +public class B { + + public static void main(String[] args) { + System.out.println("It worked"); + } +} diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java new file mode 100644 index 0000000000..2adf2b42fa --- /dev/null +++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java @@ -0,0 +1,9 @@ +package com.baeldung.missingannotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface C { + Class value(); +} diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java new file mode 100644 index 0000000000..7c534fa682 --- /dev/null +++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java @@ -0,0 +1,4 @@ +package com.baeldung.missingannotation; + +public class D { +} diff --git a/core-java-modules/core-java-collections-4/README.md b/core-java-modules/core-java-collections-4/README.md index 4fd77473d4..0e14f407c4 100644 --- a/core-java-modules/core-java-collections-4/README.md +++ b/core-java-modules/core-java-collections-4/README.md @@ -6,3 +6,4 @@ - [ArrayList vs. LinkedList vs. HashMap in Java](https://www.baeldung.com/java-arraylist-vs-linkedlist-vs-hashmap) - [Java Deque vs. Stack](https://www.baeldung.com/java-deque-vs-stack) +- [Collection.toArray(new T[0]) or .toArray(new T[size])](https://www.baeldung.com/java-collection-toarray-methods) diff --git a/core-java-modules/core-java-collections-4/pom.xml b/core-java-modules/core-java-collections-4/pom.xml index daf2588ece..7eb8222211 100644 --- a/core-java-modules/core-java-collections-4/pom.xml +++ b/core-java-modules/core-java-collections-4/pom.xml @@ -1,31 +1,31 @@ - 4.0.0 - core-java-collections-4 - 0.1.0-SNAPSHOT - core-java-collections-4 - jar - - - com.baeldung.core-java-modules - core-java-modules - 0.0.1-SNAPSHOT - ../pom.xml - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + core-java-collections-4 + 0.1.0-SNAPSHOT + core-java-collections-4 + jar - - - org.assertj - assertj-core - ${assertj.version} - test - - + + com.baeldung.core-java-modules + core-java-modules + 0.0.1-SNAPSHOT + ../pom.xml + - - 3.19.0 - + + + org.assertj + assertj-core + ${assertj.version} + test + + + + + 3.19.0 + \ No newline at end of file diff --git a/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/toarraycomparison/ToArrayBenchmark.java b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/toarraycomparison/ToArrayBenchmark.java new file mode 100644 index 0000000000..fe76a9ab3d --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/toarraycomparison/ToArrayBenchmark.java @@ -0,0 +1,72 @@ +package com.baeldung.collections.toarraycomparison; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.TreeSet; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(value = 3, jvmArgsAppend = { "-XX:+UseParallelGC", "-Xms4g", "-Xmx4g" }) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +public class ToArrayBenchmark { + + @Param({ "10", "10000", "10000000" }) + private int size; + + @Param({ "array-list", "tree-set" }) + private String type; + + private Collection collection; + + @Setup + public void setup() { + switch (type) { + case "array-list": + collection = new ArrayList(); + break; + case "tree-set": + collection = new TreeSet(); + break; + default: + throw new UnsupportedOperationException(); + } + for (int i = 0; i < size; i++) { + collection.add(String.valueOf(i)); + } + } + + @Benchmark + public String[] zero_sized() { + return collection.toArray(new String[0]); + } + + @Benchmark + public String[] pre_sized() { + return collection.toArray(new String[collection.size()]); + } + + + public static void main(String[] args) { + try { + org.openjdk.jmh.Main.main(args); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/core-java-modules/core-java-concurrency-2/pom.xml b/core-java-modules/core-java-concurrency-2/pom.xml index 3a29101d0f..71c73f5dff 100644 --- a/core-java-modules/core-java-concurrency-2/pom.xml +++ b/core-java-modules/core-java-concurrency-2/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-concurrency-2 jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-concurrency-advanced-2/pom.xml b/core-java-modules/core-java-concurrency-advanced-2/pom.xml index 0571598684..f975dd41a4 100644 --- a/core-java-modules/core-java-concurrency-advanced-2/pom.xml +++ b/core-java-modules/core-java-concurrency-advanced-2/pom.xml @@ -31,7 +31,6 @@ jmh-generator-annprocess ${jmh-generator.version} - org.assertj assertj-core diff --git a/core-java-modules/core-java-concurrency-advanced-4/README.md b/core-java-modules/core-java-concurrency-advanced-4/README.md index 5b93cec0dd..db856a2cd6 100644 --- a/core-java-modules/core-java-concurrency-advanced-4/README.md +++ b/core-java-modules/core-java-concurrency-advanced-4/README.md @@ -2,3 +2,4 @@ - [Binary Semaphore vs Reentrant Lock](https://www.baeldung.com/java-binary-semaphore-vs-reentrant-lock) - [Bad Practices With Synchronization](https://www.baeldung.com/java-synchronization-bad-practices) +- [Start Two Threads at the Exact Same Time in Java](https://www.baeldung.com/java-start-two-threads-at-same-time) diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java new file mode 100644 index 0000000000..3e90bd4399 --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java @@ -0,0 +1,86 @@ +package com.baeldung.threadsstartatsametime; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Phaser; + +public class ThreadsStartAtSameTime { + + public static void main(String[] args) throws BrokenBarrierException, InterruptedException { + usingCountDownLatch(); + + Thread.sleep(30); + + usingCyclicBarrier(); + + Thread.sleep(30); + + usingPhaser(); + + } + + private static void usingCountDownLatch() throws InterruptedException { + System.out.println("==============================================="); + System.out.println(" >>> Using CountDownLatch <<<<"); + System.out.println("==============================================="); + + CountDownLatch latch = new CountDownLatch(1); + + WorkerWithCountDownLatch worker1 = new WorkerWithCountDownLatch("Worker with latch 1", latch); + WorkerWithCountDownLatch worker2 = new WorkerWithCountDownLatch("Worker with latch 2", latch); + + worker1.start(); + worker2.start(); + + Thread.sleep(10);//simulation of some actual work + + System.out.println("-----------------------------------------------"); + System.out.println(" Now release the latch:"); + System.out.println("-----------------------------------------------"); + latch.countDown(); + } + + private static void usingCyclicBarrier() throws BrokenBarrierException, InterruptedException { + System.out.println("\n==============================================="); + System.out.println(" >>> Using CyclicBarrier <<<<"); + System.out.println("==============================================="); + + CyclicBarrier barrier = new CyclicBarrier(3); + + WorkerWithCyclicBarrier worker1 = new WorkerWithCyclicBarrier("Worker with barrier 1", barrier); + WorkerWithCyclicBarrier worker2 = new WorkerWithCyclicBarrier("Worker with barrier 2", barrier); + + worker1.start(); + worker2.start(); + + Thread.sleep(10);//simulation of some actual work + + System.out.println("-----------------------------------------------"); + System.out.println(" Now open the barrier:"); + System.out.println("-----------------------------------------------"); + barrier.await(); + } + + private static void usingPhaser() throws InterruptedException { + System.out.println("\n==============================================="); + System.out.println(" >>> Using Phaser <<<"); + System.out.println("==============================================="); + + Phaser phaser = new Phaser(); + phaser.register(); + + WorkerWithPhaser worker1 = new WorkerWithPhaser("Worker with phaser 1", phaser); + WorkerWithPhaser worker2 = new WorkerWithPhaser("Worker with phaser 2", phaser); + + worker1.start(); + worker2.start(); + + Thread.sleep(10);//simulation of some actual work + + System.out.println("-----------------------------------------------"); + System.out.println(" Now open the phaser barrier:"); + System.out.println("-----------------------------------------------"); + phaser.arriveAndAwaitAdvance(); + } +} diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java new file mode 100644 index 0000000000..44b95ec042 --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java @@ -0,0 +1,24 @@ +package com.baeldung.threadsstartatsametime; + +import java.time.Instant; +import java.util.concurrent.CountDownLatch; + +public class WorkerWithCountDownLatch extends Thread { + private CountDownLatch latch; + + public WorkerWithCountDownLatch(String name, CountDownLatch latch) { + this.latch = latch; + setName(name); + } + + @Override public void run() { + try { + System.out.printf("[ %s ] created, blocked by the latch\n", getName()); + latch.await(); + System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now()); + // do actual work here... + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java new file mode 100644 index 0000000000..99059e0725 --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java @@ -0,0 +1,25 @@ +package com.baeldung.threadsstartatsametime; + +import java.time.Instant; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +public class WorkerWithCyclicBarrier extends Thread { + private CyclicBarrier barrier; + + public WorkerWithCyclicBarrier(String name, CyclicBarrier barrier) { + this.barrier = barrier; + this.setName(name); + } + + @Override public void run() { + try { + System.out.printf("[ %s ] created, blocked by the barrier\n", getName()); + barrier.await(); + System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now()); + // do actual work here... + } catch (InterruptedException | BrokenBarrierException e) { + e.printStackTrace(); + } + } +} diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java new file mode 100644 index 0000000000..44994d1cd0 --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java @@ -0,0 +1,26 @@ +package com.baeldung.threadsstartatsametime; + +import java.time.Instant; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Phaser; + +public class WorkerWithPhaser extends Thread { + private Phaser phaser; + + public WorkerWithPhaser(String name, Phaser phaser) { + this.phaser = phaser; + phaser.register(); + setName(name); + } + + @Override public void run() { + try { + System.out.printf("[ %s ] created, blocked by the phaser\n", getName()); + phaser.arriveAndAwaitAdvance(); + System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now()); + // do actual work here... + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } +} diff --git a/core-java-modules/core-java-concurrency-advanced/pom.xml b/core-java-modules/core-java-concurrency-advanced/pom.xml index 7dae0b17aa..52a2e67e70 100644 --- a/core-java-modules/core-java-concurrency-advanced/pom.xml +++ b/core-java-modules/core-java-concurrency-advanced/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-concurrency-advanced jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-concurrency-basic-2/README.md b/core-java-modules/core-java-concurrency-basic-2/README.md index c4a6105c56..d2ff75ca95 100644 --- a/core-java-modules/core-java-concurrency-basic-2/README.md +++ b/core-java-modules/core-java-concurrency-basic-2/README.md @@ -13,4 +13,5 @@ This module contains articles about basic Java concurrency - [Why are Local Variables Thread-Safe in Java](https://www.baeldung.com/java-local-variables-thread-safe) - [How to Stop Execution After a Certain Time in Java](https://www.baeldung.com/java-stop-execution-after-certain-time) - [How to Handle InterruptedException in Java](https://www.baeldung.com/java-interrupted-exception) +- [How to Get the Number of Threads in a Java Process](https://www.baeldung.com/java-get-number-of-threads) - [[<-- Prev]](/core-java-modules/core-java-concurrency-basic) diff --git a/core-java-modules/core-java-concurrency-basic-2/pom.xml b/core-java-modules/core-java-concurrency-basic-2/pom.xml index 38c9c26c80..99c8cee0f2 100644 --- a/core-java-modules/core-java-concurrency-basic-2/pom.xml +++ b/core-java-modules/core-java-concurrency-basic-2/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-concurrency-basic-2 jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-concurrency-basic-2/src/main/java/com/baeldung/concurrent/threads/number/FindNumberOfThreads.java b/core-java-modules/core-java-concurrency-basic-2/src/main/java/com/baeldung/concurrent/threads/number/FindNumberOfThreads.java new file mode 100644 index 0000000000..8af2724fb6 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-2/src/main/java/com/baeldung/concurrent/threads/number/FindNumberOfThreads.java @@ -0,0 +1,14 @@ +package com.baeldung.concurrent.threads.number; + +import java.lang.management.ManagementFactory; + +public class FindNumberOfThreads { + + public static void main(String[] args) { + System.out.println("Number of threads " + Thread.activeCount()); + System.out.println("Current Thread Group - " + + Thread.currentThread().getThreadGroup().getName()); + System.out.println("Total Number of threads " + + ManagementFactory.getThreadMXBean().getThreadCount()); + } +} diff --git a/core-java-modules/core-java-concurrency-basic/pom.xml b/core-java-modules/core-java-concurrency-basic/pom.xml index 3f69d1a73b..5df379efb2 100644 --- a/core-java-modules/core-java-concurrency-basic/pom.xml +++ b/core-java-modules/core-java-concurrency-basic/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-concurrency-basic jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-concurrency-collections-2/pom.xml b/core-java-modules/core-java-concurrency-collections-2/pom.xml index 020586e892..0a31c64728 100644 --- a/core-java-modules/core-java-concurrency-collections-2/pom.xml +++ b/core-java-modules/core-java-concurrency-collections-2/pom.xml @@ -6,7 +6,7 @@ 0.1.0-SNAPSHOT core-java-concurrency-collections-2 jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-concurrency-collections/pom.xml b/core-java-modules/core-java-concurrency-collections/pom.xml index 8a7fc58bfd..edb5a71e39 100644 --- a/core-java-modules/core-java-concurrency-collections/pom.xml +++ b/core-java-modules/core-java-concurrency-collections/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-concurrency-collections jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-date-operations-1/pom.xml b/core-java-modules/core-java-date-operations-1/pom.xml index ded6f062cf..955cd910e9 100644 --- a/core-java-modules/core-java-date-operations-1/pom.xml +++ b/core-java-modules/core-java-date-operations-1/pom.xml @@ -49,7 +49,6 @@ true - org.apache.maven.plugins diff --git a/core-java-modules/core-java-date-operations-2/pom.xml b/core-java-modules/core-java-date-operations-2/pom.xml index 5766f39739..bb0435a92a 100644 --- a/core-java-modules/core-java-date-operations-2/pom.xml +++ b/core-java-modules/core-java-date-operations-2/pom.xml @@ -7,7 +7,7 @@ ${project.parent.version} core-java-date-operations-2 jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-datetime-conversion/pom.xml b/core-java-modules/core-java-datetime-conversion/pom.xml index b494b307d0..8f082e2793 100644 --- a/core-java-modules/core-java-datetime-conversion/pom.xml +++ b/core-java-modules/core-java-datetime-conversion/pom.xml @@ -48,7 +48,6 @@ true - org.apache.maven.plugins diff --git a/core-java-modules/core-java-function/pom.xml b/core-java-modules/core-java-function/pom.xml index 5db73d8796..1cdcfe47db 100644 --- a/core-java-modules/core-java-function/pom.xml +++ b/core-java-modules/core-java-function/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-function jar - + com.baeldung.core-java-modules core-java-modules @@ -40,4 +40,4 @@ 3.6.1 - + \ No newline at end of file diff --git a/core-java-modules/core-java-functional/pom.xml b/core-java-modules/core-java-functional/pom.xml index 87a08e1e3f..3eeec71a5a 100644 --- a/core-java-modules/core-java-functional/pom.xml +++ b/core-java-modules/core-java-functional/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-functional jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-io-2/pom.xml b/core-java-modules/core-java-io-2/pom.xml index a4168a8f1f..19f34d6da1 100644 --- a/core-java-modules/core-java-io-2/pom.xml +++ b/core-java-modules/core-java-io-2/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-io-2 jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-io-apis/pom.xml b/core-java-modules/core-java-io-apis/pom.xml index 0984296ea1..9d0f3196ab 100644 --- a/core-java-modules/core-java-io-apis/pom.xml +++ b/core-java-modules/core-java-io-apis/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-io-apis jar - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-io-conversions-2/pom.xml b/core-java-modules/core-java-io-conversions-2/pom.xml index e594666b55..dcb9d494dc 100644 --- a/core-java-modules/core-java-io-conversions-2/pom.xml +++ b/core-java-modules/core-java-io-conversions-2/pom.xml @@ -36,6 +36,16 @@ core-java-io-conversions-2 + + + org.apache.maven.plugins + maven-compiler-plugin + + ${source.version} + ${target.version} + + + src/main/resources @@ -45,6 +55,8 @@ + 11 + 11 20200518 4.1 diff --git a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java index c34c32891f..03f528766b 100644 --- a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java +++ b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java @@ -21,11 +21,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; -import java.io.StringReader; import java.io.StringWriter; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Scanner; import java.util.UUID; @@ -70,6 +70,16 @@ public class JavaInputStreamToXUnitTest { assertThat(text, equalTo(originalString)); } + @Test + public void givenUsingJava9_whenConvertingAnInputStreamToAString_thenCorrect() throws IOException { + final String originalString = randomAlphabetic(DEFAULT_SIZE); + final InputStream inputStream = new ByteArrayInputStream(originalString.getBytes()); + + final String text = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + + assertThat(text, equalTo(originalString)); + } + @Test public final void givenUsingJava7_whenConvertingAnInputStreamToAString_thenCorrect() throws IOException { final String originalString = randomAlphabetic(DEFAULT_SIZE); @@ -155,15 +165,13 @@ public class JavaInputStreamToXUnitTest { @Test public final void whenConvertingToFile_thenCorrect() throws IOException { - final InputStream initialStream = new FileInputStream(new File("src/test/resources/sample.txt")); - final byte[] buffer = new byte[initialStream.available()]; - initialStream.read(buffer); + final Path path = Paths.get("src/test/resources/sample.txt"); + final byte[] buffer = java.nio.file.Files.readAllBytes(path); final File targetFile = new File("src/test/resources/targetFile.tmp"); final OutputStream outStream = new FileOutputStream(targetFile); outStream.write(buffer); - IOUtils.closeQuietly(initialStream); IOUtils.closeQuietly(outStream); } diff --git a/core-java-modules/core-java-io-conversions/pom.xml b/core-java-modules/core-java-io-conversions/pom.xml index 14320f46b9..2dd21a80e8 100644 --- a/core-java-modules/core-java-io-conversions/pom.xml +++ b/core-java-modules/core-java-io-conversions/pom.xml @@ -7,6 +7,7 @@ 0.1.0-SNAPSHOT core-java-io-conversions jar + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-io/pom.xml b/core-java-modules/core-java-io/pom.xml index 67495f84f9..0cf6d6767e 100644 --- a/core-java-modules/core-java-io/pom.xml +++ b/core-java-modules/core-java-io/pom.xml @@ -7,7 +7,7 @@ 0.1.0-SNAPSHOT core-java-io jar - + com.baeldung.core-java-modules core-java-modules @@ -97,6 +97,7 @@ + integration diff --git a/core-java-modules/core-java-jar/pom.xml b/core-java-modules/core-java-jar/pom.xml index 4e4a7ee465..b638f81b0c 100644 --- a/core-java-modules/core-java-jar/pom.xml +++ b/core-java-modules/core-java-jar/pom.xml @@ -7,6 +7,7 @@ 0.1.0-SNAPSHOT core-java-jar jar + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-jpms/decoupling-pattern1/consumermodule1/pom.xml b/core-java-modules/core-java-jpms/decoupling-pattern1/consumermodule1/pom.xml index d0dce32c33..f82e72b85d 100644 --- a/core-java-modules/core-java-jpms/decoupling-pattern1/consumermodule1/pom.xml +++ b/core-java-modules/core-java-jpms/decoupling-pattern1/consumermodule1/pom.xml @@ -10,7 +10,7 @@ com.baeldung.decoupling-pattern1 decoupling-pattern1 - 1.0 + 1.0-SNAPSHOT @@ -26,6 +26,10 @@ org.apache.maven.plugins maven-compiler-plugin + + ${source.version} + ${target.version} + diff --git a/core-java-modules/core-java-jpms/decoupling-pattern1/servicemodule1/pom.xml b/core-java-modules/core-java-jpms/decoupling-pattern1/servicemodule1/pom.xml index 8e30fa9182..fc4b5854f9 100644 --- a/core-java-modules/core-java-jpms/decoupling-pattern1/servicemodule1/pom.xml +++ b/core-java-modules/core-java-jpms/decoupling-pattern1/servicemodule1/pom.xml @@ -5,12 +5,13 @@ 4.0.0 com.baeldung.servicemodule servicemodule1 + 1.0 jar com.baeldung.decoupling-pattern1 decoupling-pattern1 - 1.0 + 1.0-SNAPSHOT @@ -18,6 +19,10 @@ org.apache.maven.plugins maven-compiler-plugin + + ${source.version} + ${target.version} + diff --git a/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule2/pom.xml b/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule2/pom.xml index 774319a067..13d0b2d201 100644 --- a/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule2/pom.xml +++ b/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule2/pom.xml @@ -45,4 +45,4 @@ 1.0 - + \ No newline at end of file diff --git a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java index cf63a0ddda..a3ea77ec86 100644 --- a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java +++ b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java @@ -3,6 +3,9 @@ package com.baeldung.transientkw; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import java.io.File; + +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; class TransientUnitTest { @@ -31,5 +34,11 @@ class TransientUnitTest { assertEquals("Fiction", book2.getBookCategory()); } + + @AfterAll + public static void cleanup() { + File file = new File(BookSerDe.fileName); + file.deleteOnExit(); + } } diff --git a/core-java-modules/core-java-lang-4/README.md b/core-java-modules/core-java-lang-4/README.md index e1023513eb..562be950c0 100644 --- a/core-java-modules/core-java-lang-4/README.md +++ b/core-java-modules/core-java-lang-4/README.md @@ -6,3 +6,5 @@ This module contains articles about core features in the Java language - [The package-info.java File](https://www.baeldung.com/java-package-info) - [What are Compile-time Constants in Java?](https://www.baeldung.com/java-compile-time-constants) - [Java Objects.hash() vs Objects.hashCode()](https://www.baeldung.com/java-objects-hash-vs-objects-hashcode) +- [Referencing a Method in Javadoc Comments](https://www.baeldung.com/java-method-in-javadoc) +- [Tiered Compilation in JVM](https://www.baeldung.com/jvm-tiered-compilation) diff --git a/core-java-modules/core-java-lang-4/pom.xml b/core-java-modules/core-java-lang-4/pom.xml index 4e692ffa55..824194cc07 100644 --- a/core-java-modules/core-java-lang-4/pom.xml +++ b/core-java-modules/core-java-lang-4/pom.xml @@ -21,6 +21,16 @@ jmh-core ${jmh-core.version} + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson.version} + org.openjdk.jmh jmh-generator-annprocess @@ -38,4 +48,5 @@ + \ No newline at end of file diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Animal.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Animal.java new file mode 100644 index 0000000000..b26b869a60 --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Animal.java @@ -0,0 +1,12 @@ +package com.baeldung.javadocmemberreference; + +public class Animal { + + public void run() { + + } + + public void run(String direction) { + + } +} diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Person.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Person.java new file mode 100644 index 0000000000..a2792e0304 --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Person.java @@ -0,0 +1,49 @@ +package com.baeldung.javadocmemberreference; + +import com.baeldung.vehicle.Car; + +public class Person { + + Person() { + + } + + /** + * Also, check the {@link #move() Move} method for more movement details. + */ + public void walk() { + + } + + /** + * Check this {@link #move(String) Move} method for direction oriented movement. + */ + public void move() { + + } + + public void move(String direction) { + + } + + /** + * Additionally, check this {@link Animal#run(String) Run} method for direction based run. + */ + public void run() { + + } + + /** + * Also consider checking {@link com.baeldung.vehicle.Vehicle#Vehicle() Vehicle} constructor to initialize vehicle object. + */ + public void goToWork() { + + } + + /** + * Have a look at {@link Car#getNumberOfSeats() SeatsAvailability} method for checking the available seats needed for driving. + */ + public void drive() { + + } +} diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java new file mode 100644 index 0000000000..2eca40f055 --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java @@ -0,0 +1,21 @@ +package com.baeldung.tieredcompilation; + +public class Article { + + private String name; + private String author; + + public Article(String name, String author) { + this.name = name; + this.author = author; + } + + public String getName() { + return name; + } + + public String getAuthor() { + return author; + } + +} diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java new file mode 100644 index 0000000000..ee5d74d5fe --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java @@ -0,0 +1,7 @@ +package com.baeldung.tieredcompilation; + +public interface Formatter { + + String format(T object) throws Exception; + +} diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java new file mode 100644 index 0000000000..9951b477da --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java @@ -0,0 +1,15 @@ +package com.baeldung.tieredcompilation; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.json.JsonMapper; + +public class JsonFormatter implements Formatter { + + private static final JsonMapper mapper = new JsonMapper(); + + @Override + public String format(T object) throws JsonProcessingException { + return mapper.writeValueAsString(object); + } + +} diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java new file mode 100644 index 0000000000..ea7178051e --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java @@ -0,0 +1,17 @@ +package com.baeldung.tieredcompilation; + +public class TieredCompilation { + + public static void main(String[] args) throws Exception { + for (int i = 0; i < 1_000_000; i++) { + Formatter formatter; + if (i < 500_000) { + formatter = new JsonFormatter(); + } else { + formatter = new XmlFormatter(); + } + formatter.format(new Article("Tiered Compilation in JVM", "Baeldung")); + } + } + +} diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java new file mode 100644 index 0000000000..8b9823cd88 --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java @@ -0,0 +1,15 @@ +package com.baeldung.tieredcompilation; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +public class XmlFormatter implements Formatter { + + private static final XmlMapper mapper = new XmlMapper(); + + @Override + public String format(T object) throws JsonProcessingException { + return mapper.writeValueAsString(object); + } + +} diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Car.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Car.java new file mode 100644 index 0000000000..4bacab82d6 --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Car.java @@ -0,0 +1,14 @@ +package com.baeldung.vehicle; + +public class Car { + + public Car() { + + } + + public static int getNumberOfSeats() { + int availableSeats = 0; + // available seats calculation logic + return availableSeats; + } +} diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Vehicle.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Vehicle.java new file mode 100644 index 0000000000..e52ae7fc5c --- /dev/null +++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Vehicle.java @@ -0,0 +1,8 @@ +package com.baeldung.vehicle; + +public class Vehicle { + + public Vehicle() { + + } +} diff --git a/core-java-modules/core-java-lang-math-2/pom.xml b/core-java-modules/core-java-lang-math-2/pom.xml index 098d77b59a..c825ecdef8 100644 --- a/core-java-modules/core-java-lang-math-2/pom.xml +++ b/core-java-modules/core-java-lang-math-2/pom.xml @@ -6,7 +6,7 @@ core-java-lang-math-2 0.0.1-SNAPSHOT core-java-lang-math-2 - + com.baeldung.core-java-modules core-java-modules diff --git a/core-java-modules/core-java-lang-math-3/pom.xml b/core-java-modules/core-java-lang-math-3/pom.xml index 6779d0ecc6..e1a650a18f 100644 --- a/core-java-modules/core-java-lang-math-3/pom.xml +++ b/core-java-modules/core-java-lang-math-3/pom.xml @@ -27,4 +27,4 @@ - + \ No newline at end of file diff --git a/core-java-modules/core-java-lang-oop-constructors/README.md b/core-java-modules/core-java-lang-oop-constructors/README.md index 0082969807..4bec8db256 100644 --- a/core-java-modules/core-java-lang-oop-constructors/README.md +++ b/core-java-modules/core-java-lang-oop-constructors/README.md @@ -5,4 +5,5 @@ This module contains article about constructors in Java ### Relevant Articles: - [A Guide to Constructors in Java](https://www.baeldung.com/java-constructors) - [Java Copy Constructor](https://www.baeldung.com/java-copy-constructor) -- [Cannot Reference “X†Before Supertype Constructor Has Been Called](https://www.baeldung.com/java-cannot-reference-x-before-supertype-constructor-error) \ No newline at end of file +- [Cannot Reference “X†Before Supertype Constructor Has Been Called](https://www.baeldung.com/java-cannot-reference-x-before-supertype-constructor-error) +- [Private Constructors in Java](https://www.baeldung.com/java-private-constructors) diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Animal.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Animal.java new file mode 100644 index 0000000000..1927bc4455 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Animal.java @@ -0,0 +1,32 @@ +package com.baeldung.constructors.exception; + +import java.io.File; +import java.io.IOException; + +public class Animal { + + public Animal() throws InstantiationException { + throw new InstantiationException("Cannot be instantiated"); + } + + public Animal(String id, int age) { + if (id == null) + throw new NullPointerException("Id cannot be null"); + if (age < 0) + throw new IllegalArgumentException("Age cannot be negative"); + } + + public Animal(File file) throws SecurityException, IOException { + // Avoiding Path traversal attacks + if (file.isAbsolute()) { + throw new SecurityException("Traversal attack - absolute path not allowed"); + } + // Avoiding directory traversal + // a/../..b/ + if (!file.getCanonicalPath() + .equals(file.getAbsolutePath())) { + throw new SecurityException("Directory traversal attempt?"); + } + } + +} diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Bird.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Bird.java new file mode 100644 index 0000000000..6e6e6558ba --- /dev/null +++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Bird.java @@ -0,0 +1,13 @@ +package com.baeldung.constructors.exception; + +public class Bird extends Animal { + + // Note that we are throwing parent exception + public Bird() throws ReflectiveOperationException { + super(); + } + + public Bird(String id, int age) { + super(id, age); + } +} diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/Employee.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/Employee.java new file mode 100644 index 0000000000..96785b0fb0 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/Employee.java @@ -0,0 +1,39 @@ +package com.baeldung.privateconstructors; + +public class Employee { + + private final String name; + private final int age; + private final String department; + + private Employee(String name, int age, String department) { + this.name = name; + this.age = age; + this.department = department; + } + + public static class Builder { + private String name; + private int age; + private String department; + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Builder setAge(int age) { + this.age = age; + return this; + } + + public Builder setDepartment(String department) { + this.department = department; + return this; + } + + public Employee build() { + return new Employee(name, age, department); + } + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/PrivateConstructorClass.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/PrivateConstructorClass.java new file mode 100644 index 0000000000..5361799b88 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/PrivateConstructorClass.java @@ -0,0 +1,8 @@ +package com.baeldung.privateconstructors; + +public class PrivateConstructorClass { + + private PrivateConstructorClass() { + // in the private constructor + } +} diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/SingletonClass.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/SingletonClass.java new file mode 100644 index 0000000000..a02ef37f7e --- /dev/null +++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/SingletonClass.java @@ -0,0 +1,20 @@ +package com.baeldung.privateconstructors; + +public final class SingletonClass { + + private static SingletonClass INSTANCE; + private String info = "Initial info class"; + + private SingletonClass() { + } + + public static SingletonClass getInstance() { + if (INSTANCE == null) { + INSTANCE = new SingletonClass(); + } + + return INSTANCE; + } + + // getters and setters +} \ No newline at end of file diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/StringUtils.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/StringUtils.java new file mode 100644 index 0000000000..b151b6f618 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/StringUtils.java @@ -0,0 +1,16 @@ +package com.baeldung.privateconstructors; + +public class StringUtils { + + private StringUtils() { + // this class cannot be instantiated + } + + public static String toUpperCase(String s) { + return s.toUpperCase(); + } + + public static String toLowerCase(String s) { + return s.toLowerCase(); + } +} diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/ValueTypeClass.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/ValueTypeClass.java new file mode 100644 index 0000000000..ff449ea9e9 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/ValueTypeClass.java @@ -0,0 +1,22 @@ +package com.baeldung.privateconstructors; + +public class ValueTypeClass { + + private final String value; + private final String type; + + public ValueTypeClass(int x) { + this(Integer.toString(x), "int"); + } + + public ValueTypeClass(boolean x) { + this(Boolean.toString(x), "boolean"); + } + + private ValueTypeClass(String value, String type) { + this.value = value; + this.type = type; + } + + // getters and setters +} diff --git a/core-java-modules/core-java-lang-oop-constructors/src/test/java/com/baeldung/constructors/exception/AnimalUnitTest.java b/core-java-modules/core-java-lang-oop-constructors/src/test/java/com/baeldung/constructors/exception/AnimalUnitTest.java new file mode 100644 index 0000000000..8f7ed440f6 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-constructors/src/test/java/com/baeldung/constructors/exception/AnimalUnitTest.java @@ -0,0 +1,47 @@ +package com.baeldung.constructors.exception; + +import java.io.File; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +public class AnimalUnitTest { + + @Test + public void givenNoArgument_thenFails() { + Assertions.assertThatThrownBy(() -> { + new Animal(); + }) + .isInstanceOf(Exception.class); + } + + @Test + public void givenInvalidArg_thenFails() { + Assertions.assertThatThrownBy(() -> { + new Animal(null, 30); + }) + .isInstanceOf(NullPointerException.class); + } + + @Test(expected = Test.None.class) + public void givenValidArg_thenSuccess() { + new Animal("1234", 30); + } + + @Test + public void givenAbsolutePath_thenFails() { + Assertions.assertThatThrownBy(() -> { + new Animal(new File("temp.txt").getAbsoluteFile()); + }) + .isInstanceOf(SecurityException.class); + } + + @Test + public void givenDirectoryTraversalPath_thenFails() { + Assertions.assertThatThrownBy(() -> { + new Animal(new File(File.separator + ".." + File.separator + "temp.txt")); + }) + .isInstanceOf(SecurityException.class); + } + +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/README.md b/core-java-modules/core-java-lang-oop-modifiers/README.md index eef905fa0e..1f37cc903e 100644 --- a/core-java-modules/core-java-lang-oop-modifiers/README.md +++ b/core-java-modules/core-java-lang-oop-modifiers/README.md @@ -9,4 +9,5 @@ This module contains articles about modifiers in Java - [The “final†Keyword in Java](https://www.baeldung.com/java-final) - [A Guide to the Static Keyword in Java](https://www.baeldung.com/java-static) - [Static and Default Methods in Interfaces in Java](https://www.baeldung.com/java-static-default-methods) -- [The strictfp Keyword in Java](https://www.baeldung.com/java-strictfp) \ No newline at end of file +- [The strictfp Keyword in Java](https://www.baeldung.com/java-strictfp) +- [Static Classes Versus the Singleton Pattern in Java](https://www.baeldung.com/java-static-class-vs-singleton) diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java new file mode 100644 index 0000000000..d472e386eb --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java @@ -0,0 +1,30 @@ +package com.baeldung.staticsingletondifference; + +public class CachingSingleton implements SingletonInterface { + + private CachingSingleton() { + } + + private static class SingletonHolder { + public static final CachingSingleton instance = new CachingSingleton(); + } + + public static CachingSingleton getInstance() { + return SingletonHolder.instance; + } + + @Override + public String describeMe() { + return "Caching Responsibilities"; + } + + @Override + public String passOnLocks(MyLock lock) { + return lock.takeLock(1); + } + + @Override + public void increment() { + throw new UnsupportedOperationException("Not Supported Here"); + } +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java new file mode 100644 index 0000000000..84ea5d4d1a --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java @@ -0,0 +1,36 @@ +package com.baeldung.staticsingletondifference; + +public class FileSystemSingleton implements SingletonInterface{ + + private int filesWritten; + + private FileSystemSingleton() { + } + + private static class SingletonHolder { + public static final FileSystemSingleton instance = new FileSystemSingleton(); + } + + public static FileSystemSingleton getInstance() { + return SingletonHolder.instance; + } + + @Override + public String describeMe() { + return "File System Responsibilities"; + } + + @Override + public String passOnLocks(MyLock lock) { + return lock.takeLock(2); + } + + @Override + public void increment() { + this.filesWritten++; + } + + public int getFilesWritten() { + return filesWritten; + } +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java new file mode 100644 index 0000000000..3a124bd678 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java @@ -0,0 +1,8 @@ +package com.baeldung.staticsingletondifference; + +public class MyLock { + + protected String takeLock(int locks) { + return "Taken Specific Lock"; + } +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java new file mode 100644 index 0000000000..64a422f9ec --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java @@ -0,0 +1,49 @@ +package com.baeldung.staticsingletondifference; + +import java.io.ObjectStreamException; +import java.io.Serializable; + +public class SerializableCloneableSingleton implements SingletonInterface, Serializable, Cloneable { + + private static final long serialVersionUID = -1917003064592196223L; + + private int state; + + private SerializableCloneableSingleton() { + } + + private static class SingletonHolder { + public static final SerializableCloneableSingleton instance = new SerializableCloneableSingleton(); + } + + public static SerializableCloneableSingleton getInstance() { + return SingletonHolder.instance; + } + + @Override + public String describeMe() { + throw new UnsupportedOperationException("Not Supported Here"); + } + + @Override + public String passOnLocks(MyLock lock) { + throw new UnsupportedOperationException("Not Supported Here"); + } + + @Override + public void increment() { + this.state++; + } + + public int getState() { + return state; + } + + private Object readResolve() throws ObjectStreamException { + return SingletonHolder.instance; + } + + public Object cloneObject() throws CloneNotSupportedException { + return this.clone(); + } +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java new file mode 100644 index 0000000000..34e14c5cac --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java @@ -0,0 +1,10 @@ +package com.baeldung.staticsingletondifference; + +public interface SingletonInterface { + + public String describeMe(); + + public String passOnLocks(MyLock lock); + + public void increment(); +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java new file mode 100644 index 0000000000..35d9931597 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java @@ -0,0 +1,20 @@ +package com.baeldung.staticsingletondifference; + +public class SingletonLock extends MyLock { + + private SingletonLock() { + } + + private static class SingletonHolder { + public static final SingletonLock instance = new SingletonLock(); + } + + public static SingletonLock getInstance() { + return SingletonHolder.instance; + } + + @Override + public String takeLock(int locks) { + return "Taken Singleton Lock"; + } +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java new file mode 100644 index 0000000000..c9661cbf70 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java @@ -0,0 +1,8 @@ +package com.baeldung.staticsingletondifference; + +public class SubUtility extends SuperUtility { + + public static String echoIt(String data) { + return data; + } +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java new file mode 100644 index 0000000000..00011f4697 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java @@ -0,0 +1,8 @@ +package com.baeldung.staticsingletondifference; + +public class SuperUtility { + + public static String echoIt(String data) { + return "SUPER"; + } +} diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java b/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java new file mode 100644 index 0000000000..4b0b23b5ec --- /dev/null +++ b/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java @@ -0,0 +1,77 @@ +package com.baeldung.staticsingletondifference; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.stream.IntStream; + +import org.junit.Assert; +import org.junit.Test; + +public class ForSingletonsUnitTest { + + @Test + public void whenStaticUtilClassInheritance_thenOverridingFails() { + SuperUtility superUtility = new SubUtility(); + Assert.assertNotEquals("ECHO", superUtility.echoIt("ECHO")); + Assert.assertEquals("SUPER", superUtility.echoIt("ECHO")); + } + + @Test + public void whenSingletonDerivesBaseClass_thenRuntimePolymorphism() { + MyLock myLock = new MyLock(); + Assert.assertEquals("Taken Specific Lock", myLock.takeLock(10)); + myLock = SingletonLock.getInstance(); + Assert.assertEquals("Taken Singleton Lock", myLock.takeLock(10)); + } + + @Test + public void whenSingletonImplementsInterface_thenRuntimePolymorphism() { + SingletonInterface singleton = FileSystemSingleton.getInstance(); + Assert.assertEquals("File System Responsibilities", singleton.describeMe()); + singleton = CachingSingleton.getInstance(); + Assert.assertEquals("Caching Responsibilities", singleton.describeMe()); + } + + @Test + public void whenSingleton_thenPassAsArguments() { + SingletonInterface singleton = FileSystemSingleton.getInstance(); + Assert.assertEquals("Taken Singleton Lock", singleton.passOnLocks(SingletonLock.getInstance())); + } + + @Test + public void whenSingleton_thenAllowState() { + SingletonInterface singleton = FileSystemSingleton.getInstance(); + IntStream.range(0, 5) + .forEach(i -> singleton.increment()); + Assert.assertEquals(5, ((FileSystemSingleton) singleton).getFilesWritten()); + } + + @Test + public void whenSingleton_thenAllowSerializationDeserialization() { + SingletonInterface singleton = SerializableCloneableSingleton.getInstance(); + singleton.increment(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + new ObjectOutputStream(baos).writeObject(singleton); + SerializableCloneableSingleton singletonNew = (SerializableCloneableSingleton) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject(); + Assert.assertEquals(1, singletonNew.getState()); + Assert.assertEquals(singleton.hashCode(), singletonNew.hashCode()); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + } + + @Test + public void whenSingleton_thenAllowCloneable() { + SerializableCloneableSingleton singleton = SerializableCloneableSingleton.getInstance(); + singleton.increment(); + try { + Assert.assertEquals(2, ((SerializableCloneableSingleton) singleton.cloneObject()).getState()); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-lang-operators-2/README.md b/core-java-modules/core-java-lang-operators-2/README.md index 46c874f361..bc1809a4a7 100644 --- a/core-java-modules/core-java-lang-operators-2/README.md +++ b/core-java-modules/core-java-lang-operators-2/README.md @@ -3,3 +3,6 @@ This module contains articles about Java operators ## Relevant Articles: + +- [Logical vs Bitwise OR Operator](https://www.baeldung.com/java-logical-vs-bitwise-or-operator) +- [Bitmasking in Java with Bitwise Operators](https://www.baeldung.com/java-bitmasking) diff --git a/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java new file mode 100644 index 0000000000..9af59d7c1c --- /dev/null +++ b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java @@ -0,0 +1,120 @@ +package com.baeldung.oroperators; + +import org.junit.Test; + +import java.util.Arrays; + +import static org.junit.Assert.*; + +public class BitmaskingUnitTest { + + @Test + public void givenIntegerShouldPrintBinaryRepresentation() { + // given + int intRepresentation = 1094795523; + + // expected + String stringRepresentation = getStringRepresentation(intRepresentation); + assertEquals(stringRepresentation, "01000001010000010100000100000011"); + } + + private String getStringRepresentation(int intRepresentation) { + String binaryString = Integer.toBinaryString(intRepresentation); + return padWithZeros(binaryString); + } + + private String padWithZeros(String binaryString) { + return String.format("%" + 32 + "s", binaryString).replace(' ', '0'); + } + + @Test + public void givenBinaryRepresentationShouldReturnNumber() { + // given + String stringRepresentation = "01000001010000010100000100000011"; + + // expected + int intRepresentation = Integer.parseUnsignedInt(stringRepresentation, 2); + assertEquals(intRepresentation, 1094795523); + } + + @Test + public void givenBinaryRepresentationShouldReturnCharacter() { + // given + String stringRepresentation = "010000010100000101000001"; + + // expected + assertEquals(binaryToText(stringRepresentation), "AAA"); + } + + @Test + public void givenIntAndPositionNumberShouldReturnBitValue() { + // given + String stringRepresentation = "010000010100000101000001000000011"; + int intRepresentation = Integer.parseUnsignedInt(stringRepresentation, 2); + + // when + boolean value1 = extractValueAtPosition(intRepresentation, 1); + boolean value2 = extractValueAtPosition(intRepresentation, 2); + boolean value3 = extractValueAtPosition(intRepresentation, 3); + + // then + assertTrue(value1); + assertTrue(value2); + assertFalse(value3); + + } + + @Test + public void givenIntegerShouldExtractLastThreeBytes() { + // given + int intRepresentation = 1094795523; + + // when + int lastThreeBites = intRepresentation >> 8; + + // expected + String stringRepresentation = getStringRepresentation(lastThreeBites); + assertEquals(stringRepresentation, "00000000010000010100000101000001"); + assertEquals(binaryToText(stringRepresentation), "AAA"); + } + + @Test + public void givenIntegerShouldApplyMask() { + // given + int intRepresentation = Integer.parseUnsignedInt("00000000010000010100000101000001", 2); + int mask = Integer.parseUnsignedInt("00000000000000000000000000000011", 2); + int mask2 = Integer.parseUnsignedInt("00000000000000000000000000000001", 2); + + // when + int masked = intRepresentation & mask; + int masked2 = intRepresentation & mask2; + + // expected + assertEquals(getStringRepresentation(masked), "00000000000000000000000000000001"); + assertEquals(getStringRepresentation(masked2), "00000000000000000000000000000001"); + assertNotEquals(masked, mask); + assertEquals(masked2, mask2); + assertFalse((intRepresentation & mask) == mask); + } + + private boolean extractValueAtPosition(int intRepresentation, int position) { + String mask = getStringRepresentation(1 << (position - 1)); + System.out.println(getStringRepresentation(intRepresentation)); + System.out.println(mask); + System.out.println("-------------------------------- &"); + System.out.println(getStringRepresentation((intRepresentation) & (1 << (position - 1)))); + System.out.println(); + return ((intRepresentation) & (1 << (position - 1))) != 0; + } + + private static String binaryToText(String stringRepresentation) { + return Arrays.stream(stringRepresentation.split("(?<=\\G.{8})")) + .filter(eightBits -> !eightBits.equals("00000000")) + .map(eightBits -> (char)Integer.parseInt(eightBits, 2)) + .collect( + StringBuilder::new, + StringBuilder::append, + StringBuilder::append + ).toString(); + } +} diff --git a/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitwiseAndLogicalOROperatorUnitTest.java b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitwiseAndLogicalOROperatorUnitTest.java index c04638991d..8b95d19753 100644 --- a/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitwiseAndLogicalOROperatorUnitTest.java +++ b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitwiseAndLogicalOROperatorUnitTest.java @@ -1,4 +1,110 @@ package com.baeldung.oroperators; +import org.junit.Test; + +import static org.junit.Assert.*; + public class BitwiseAndLogicalOROperatorUnitTest { + + @Test + public void givenTwoTrueBooleans_whenLogicalOrOperator_thenTrue() { + boolean condition1 = true; + boolean condition2 = true; + boolean result = condition1 || condition2; + assertTrue(result); + } + + @Test + public void givenOneFalseAndOneTrueBooleans_whenLogicalOrOperator_thenTrue() { + boolean condition1 = true; + boolean condition2 = false; + boolean result = condition1 || condition2; + assertTrue(result); + } + + @Test + public void givenTwoFalseBooleans_whenLogicalOrOperator_thenFalse() { + boolean condition1 = false; + boolean condition2 = false; + boolean result = condition1 || condition2; + assertFalse(result); + } + + @Test + public void givenTwoExpressions_whenLogicalOrOperator_TrueOrFalse_thenShortCircuitTrue() { + + boolean shortCircuitResult = returnAndLog(true) || returnAndLog(false); + assertTrue(shortCircuitResult); + } + + @Test + public void givenTwoExpressions_whenLogicalOrOperator_FalseOrTrue_thenShortCircuitTrue() { + + boolean shortCircuitResult = returnAndLog(false) || returnAndLog(true); + assertTrue(shortCircuitResult); + } + + @Test + public void givenTwoTrueBooleans_whenBitwiseOrOperator_thenTrue() { + boolean condition1 = true; + boolean condition2 = true; + boolean result = condition1 | condition2; + assertTrue(result); + } + + @Test + public void givenOneFalseAndOneTrueBooleans_whenBitwiseOrOperator_thenFalse() { + boolean condition1 = true; + boolean condition2 = false; + boolean result = condition1 | condition2; + assertTrue(result); + } + + @Test + public void givenTwoFalseBooleans_whenBitwiseOrOperator_thenFalse() { + boolean condition1 = false; + boolean condition2 = false; + boolean result = condition1 | condition2; + assertFalse(result); + } + + @Test + public void givenTwoIntegers_whenBitwiseOrOperator_thenNewInteger() { + int four = 4; //0100 + int three = 3; //0011 + int fourORthree = four | three; + assertEquals(7, fourORthree); + } + + @Test + public void givenFourIntegers_whenBitwiseOrOperator_thenNewInteger() { + int result = 1 | 2 | 3 | 4; + assertEquals(7, result); + } + + @Test + public void givenExpression_whenLogicalORAndOtherOperators_thenEvaluateByPrecedence() { + + boolean result = 2 + 4 == 5 || 3 < 5; + assertTrue(result); + } + + @Test + public void givenExpression_whenBitwiseORAndOtherOperators_thenEvaluateByPrecedence() { + + int result = 1 + 2 | 5 - 1; + assertEquals(7, result); + } + + @Test + public void givenExpression_multipleLogicalOr_thenEvaluateByLeftToRightAssociativity() { + + boolean result = true || false || false; + assertTrue(result); + } + + boolean returnAndLog(boolean value) { + System.out.println("Returning " + value); + return value; + } } diff --git a/core-java-modules/core-java-lang-syntax-2/pom.xml b/core-java-modules/core-java-lang-syntax-2/pom.xml index b725089351..9ca06e8fba 100644 --- a/core-java-modules/core-java-lang-syntax-2/pom.xml +++ b/core-java-modules/core-java-lang-syntax-2/pom.xml @@ -25,4 +25,4 @@ - + \ No newline at end of file diff --git a/core-java-modules/core-java-lang/pom.xml b/core-java-modules/core-java-lang/pom.xml index 2a1a7a05b8..6a14cab242 100644 --- a/core-java-modules/core-java-lang/pom.xml +++ b/core-java-modules/core-java-lang/pom.xml @@ -25,4 +25,4 @@ - + \ No newline at end of file diff --git a/core-java-modules/core-java-networking-3/README.md b/core-java-modules/core-java-networking-3/README.md index 730231525f..2e76ab5d51 100644 --- a/core-java-modules/core-java-networking-3/README.md +++ b/core-java-modules/core-java-networking-3/README.md @@ -6,4 +6,6 @@ This module contains articles about networking in Java - [Finding a Free Port in Java](https://www.baeldung.com/java-free-port) - [Downloading Email Attachments in Java](https://www.baeldung.com/java-download-email-attachments) +- [Connection Timeout vs. Read Timeout for Java Sockets](https://www.baeldung.com/java-socket-connection-read-timeout) +- [Find Whether an IP Address Is in the Specified Range or Not in Java](https://www.baeldung.com/java-check-ip-address-range) - [[<-- Prev]](/core-java-modules/core-java-networking-2) diff --git a/core-java-modules/core-java-networking-3/pom.xml b/core-java-modules/core-java-networking-3/pom.xml index 0ad800e173..d3b2398b75 100644 --- a/core-java-modules/core-java-networking-3/pom.xml +++ b/core-java-modules/core-java-networking-3/pom.xml @@ -47,6 +47,24 @@ javax.mail 1.6.2 + + + com.github.seancfoley + ipaddress + ${seancfoley.ipaddress.version} + + + + com.github.jgonian + commons-ip-math + ${jgonian.commons-ip-math.version} + + + + com.googlecode.java-ipv6 + java-ipv6 + ${googlecode.ipv6.version} + @@ -66,6 +84,9 @@ 9.4.31.v20200723 10.0.0-M7 3.11.1 + 5.3.3 + 1.32 + 0.17 \ No newline at end of file diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java new file mode 100644 index 0000000000..634c88ccba --- /dev/null +++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java @@ -0,0 +1,81 @@ +package com.baeldung.ipingivenrange; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import inet.ipaddr.IPAddress; +import inet.ipaddr.IPAddressSeqRange; +import inet.ipaddr.IPAddressString; +import inet.ipaddr.AddressStringException; + +import com.github.jgonian.ipmath.Ipv4; +import com.github.jgonian.ipmath.Ipv4Range; +import com.github.jgonian.ipmath.Ipv6; +import com.github.jgonian.ipmath.Ipv6Range; +import com.googlecode.ipv6.IPv6Address; +import com.googlecode.ipv6.IPv6AddressRange; + +public class IPWithGivenRangeCheck { + + // using IPAddress library + public static boolean checkIPIsInGivenRange(String inputIP, String rangeStartIP, String rangeEndIP) throws AddressStringException { + IPAddress startIPAddress = new IPAddressString(rangeStartIP).getAddress(); + IPAddress endIPAddress = new IPAddressString(rangeEndIP).getAddress(); + IPAddressSeqRange ipRange = startIPAddress.toSequentialRange(endIPAddress); + IPAddress inputIPAddress = new IPAddressString(inputIP).toAddress(); + + return ipRange.contains(inputIPAddress); + } + + // using Commons IP Math library for IPv4 + public static boolean checkIPv4IsInRange(String inputIP, String rangeStartIP, String rangeEndIP) { + Ipv4 startIPAddress = Ipv4.of(rangeStartIP); + Ipv4 endIPAddress = Ipv4.of(rangeEndIP); + Ipv4Range ipRange = Ipv4Range.from(startIPAddress) + .to(endIPAddress); + Ipv4 inputIPAddress = Ipv4.of(inputIP); + + return ipRange.contains(inputIPAddress); + } + + // using Commons IP Math library for IPv6 + public static boolean checkIPv6IsInRange(String inputIP, String rangeStartIP, String rangeEndIP) { + Ipv6 startIPAddress = Ipv6.of(rangeStartIP); + Ipv6 endIPAddress = Ipv6.of(rangeEndIP); + Ipv6Range ipRange = Ipv6Range.from(startIPAddress) + .to(endIPAddress); + Ipv6 inputIPAddress = Ipv6.of(inputIP); + + return ipRange.contains(inputIPAddress); + } + + // checking IP is in range by converting it to an integer + public static boolean checkIPv4IsInRangeByConvertingToInt(String inputIP, String rangeStartIP, String rangeEndIP) throws UnknownHostException { + long startIPAddress = ipToLongInt(InetAddress.getByName(rangeStartIP)); + long endIPAddress = ipToLongInt(InetAddress.getByName(rangeEndIP)); + long inputIPAddress = ipToLongInt(InetAddress.getByName(inputIP)); + + return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress); + } + + private static long ipToLongInt(InetAddress ipAddress) { + long resultIP = 0; + byte[] ipAddressOctets = ipAddress.getAddress(); + + for (byte octet : ipAddressOctets) { + resultIP <<= 8; + resultIP |= octet & 0xFF; + } + return resultIP; + } + + // using Java IPv6 library (which internally uses two long integers to store ip address) + public static boolean checkIPv6IsInRangeByIPv6library(String inputIP, String rangeStartIP, String rangeEndIP) { + IPv6Address startIPAddress = IPv6Address.fromString(rangeStartIP); + IPv6Address endIPAddress = IPv6Address.fromString(rangeEndIP); + IPv6AddressRange ipRange = IPv6AddressRange.fromFirstAndLast(startIPAddress, endIPAddress); + IPv6Address inputIPAddress = IPv6Address.fromString(inputIP); + + return ipRange.contains(inputIPAddress); + } +} diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpClientSocket.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpClientSocket.java new file mode 100644 index 0000000000..a482db3543 --- /dev/null +++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpClientSocket.java @@ -0,0 +1,46 @@ +package com.baeldung.timeout; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.Socket; +import java.net.UnknownHostException; + +public class TcpClientSocket { + + private Socket socket; + private PrintStream out; + private BufferedReader in; + + public void connect(String host, int port) { + try { + socket = new Socket(host, port); + socket.setSoTimeout(30000); + System.out.println("connected to " + host + " on port " + port); + + out = new PrintStream(socket.getOutputStream(), true); + System.out.println("Sending message ... "); + out.println("Hello world"); + + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + out.println(in.readLine()); + + System.out.println("Closing connection !!! "); + in.close(); + out.close(); + socket.close(); + + } catch (UnknownHostException e) { + System.err.println(e); + } catch (IOException e) { + System.err.println(e); + } + } + + public static void main(String[] args) throws IOException { + TcpClientSocket client = new TcpClientSocket(); + client.connect("127.0.0.1", 5000); + } + +} diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpServerSocket.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpServerSocket.java new file mode 100644 index 0000000000..452aaac191 --- /dev/null +++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpServerSocket.java @@ -0,0 +1,43 @@ +package com.baeldung.timeout; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ServerSocket; +import java.net.Socket; + +public class TcpServerSocket { + + private Socket socket; + private ServerSocket serverSocket; + private BufferedReader in; + + public TcpServerSocket(int port) { + + try { + serverSocket = new ServerSocket(port); + System.out.println("Server is listening on port :: " + port); + System.out.println("Waiting for a client ..."); + + socket = serverSocket.accept(); + socket.setSoTimeout(40000); + System.out.println("Client connected !! "); + + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + String line = in.readLine(); + System.out.println(line); + + System.out.println("Closing connection !!! "); + + socket.close(); + in.close(); + } catch (IOException i) { + System.out.println(i); + } + } + + public static void main(String args[]) { + TcpServerSocket server = new TcpServerSocket(5000); + } +} diff --git a/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java new file mode 100644 index 0000000000..a9b54834d5 --- /dev/null +++ b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java @@ -0,0 +1,58 @@ +package com.baeldung.ipingivenrange; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +import com.baeldung.ipingivenrange.IPWithGivenRangeCheck; + +class IPWithGivenRangeCheckUnitTest { + + @Test + void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception { + // test for IPAddress library + assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.220.3.0", "192.210.0.0", "192.255.0.0")); + + // test for Common IP Math library + assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRange("192.220.3.0", "192.210.0.0", "192.255.0.0")); + + // test for IPv4 by converting it to an integer and checking if it falls under the specified range. + assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.220.3.0", "192.210.0.0", "192.255.0.0")); + } + + @Test + void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception { + // test for IPAddress library + assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.200.0.0", "192.210.0.0", "192.255.0.0")); + + // test for Common IP Math library + assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRange("192.200.0.0", "192.210.0.0", "192.255.0.0")); + + // test for IPv4 by converting it to an integer and checking if it falls under the specified range. + assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.200.0.0", "192.210.0.0", "192.255.0.0")); + } + + @Test + void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception { + // test for IPAddress library + assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange("2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334")); + + // test for Common IP Math library + assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRange("2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334")); + + // test for Java IPv6 library + assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library("fe80::226:2dff:fefa:dcba", "fe80::226:2dff:fefa:cd1f", "fe80::226:2dff:fefa:ffff")); + } + + @Test + void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception { + // test for IPAddress library + assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334")); + + // test for Common IP Math library + assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRange("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334")); + + // test for Java IPv6 library + assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334")); + } +} diff --git a/core-java-modules/core-java-nio-2/README.md b/core-java-modules/core-java-nio-2/README.md index ab54899501..52efa0330a 100644 --- a/core-java-modules/core-java-nio-2/README.md +++ b/core-java-modules/core-java-nio-2/README.md @@ -10,4 +10,5 @@ This module contains articles about core Java non-blocking input and output (IO) - [Using Java MappedByteBuffer](https://www.baeldung.com/java-mapped-byte-buffer) - [How to Lock a File in Java](https://www.baeldung.com/java-lock-files) - [Java NIO DatagramChannel](https://www.baeldung.com/java-nio-datagramchannel) +- [Java – Path vs File](https://www.baeldung.com/java-path-vs-file) - [[<-- Prev]](/core-java-modules/core-java-nio) diff --git a/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/pathvsfile/PathVsFileUnitTest.java b/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/pathvsfile/PathVsFileUnitTest.java new file mode 100644 index 0000000000..7c1e910db8 --- /dev/null +++ b/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/pathvsfile/PathVsFileUnitTest.java @@ -0,0 +1,98 @@ +package com.baeldung.pathvsfile; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.catchThrowable; + +public class PathVsFileUnitTest { + + @Test + public void whenCreateFileAndPath_thenShouldPointTheSame() { + File file = new File("baeldung/tutorial.txt"); + Path path = Paths.get("baeldung/tutorial.txt"); + + assertThat(file.toString()) + .isEqualTo(path.toString()); + assertThat(file.toPath()) + .isEqualTo(path); + assertThat(path.toFile()) + .isEqualTo(file); + assertThat(file) + .isEqualTo(new File("baeldung", "tutorial.txt")); + assertThat(path) + .isEqualTo(Paths.get("baeldung", "tutorial.txt" )) + .isEqualTo(Paths.get("baeldung").resolve("tutorial.txt")); + } + + @Test + public void givenNoFile_whenDelete_thenReportError() { + File file = new File("tutorial.txt"); + Path path = Paths.get("tutorial.txt"); + + assertThat(file.delete()).isFalse(); + assertThatThrownBy(() -> Files.delete(path)) + .isInstanceOf(IOException.class) + .isInstanceOf(NoSuchFileException.class); + } + + @Test + public void givenExistedFile_whenReadMetadata_thenShouldBeSame() throws IOException { + File file = new File("tutorial.txt"); + Path path = Paths.get("tutorial.txt"); + + Throwable throwable = catchThrowable(() -> Files.createFile(path)); + assertThat(throwable) + .isNull(); + + assertThat(file.exists()) + .isEqualTo(Files.exists(path)); + assertThat(file.isFile()) + .isEqualTo(Files.isRegularFile(path)); + assertThat(file.isDirectory()) + .isEqualTo(Files.isDirectory(path)); + assertThat(file.canRead()) + .isEqualTo(Files.isReadable(path)); + assertThat(file.canWrite()) + .isEqualTo(Files.isWritable(path)); + assertThat(file.canExecute()) + .isEqualTo(Files.isExecutable(path)); + assertThat(file.isHidden()) + .isEqualTo(Files.isHidden(path)); + } + + @Test + public void givenExistedFile_whenUriPathOperations_thenShouldBeSame() throws IOException { + File file = new File("tutorial.txt"); + Path path = Paths.get("tutorial.txt"); + + Throwable throwable = catchThrowable(() -> Files.createFile(path)); + assertThat(throwable) + .isNull(); + + assertThat(file.toURI()) + .isEqualTo(path.toUri()); + assertThat(file.getAbsolutePath()) + .isEqualTo(path.toAbsolutePath().toString()); + assertThat(file.getCanonicalPath()) + .isEqualTo(path.toRealPath().toString()) + .isEqualTo(path.toRealPath().normalize().toString()); + } + + @AfterEach + public void afterEach() throws IOException { + Path path = Paths.get("tutorial.txt"); + if (Files.exists(path)) { + Files.delete(path); + } + } +} diff --git a/core-java-modules/core-java-perf/pom.xml b/core-java-modules/core-java-perf/pom.xml index bc82e92e34..636de00d8f 100644 --- a/core-java-modules/core-java-perf/pom.xml +++ b/core-java-modules/core-java-perf/pom.xml @@ -15,4 +15,4 @@ ../ - + \ No newline at end of file diff --git a/core-java-modules/core-java-reflection-2/pom.xml b/core-java-modules/core-java-reflection-2/pom.xml index 347f986275..5b04b8e8db 100644 --- a/core-java-modules/core-java-reflection-2/pom.xml +++ b/core-java-modules/core-java-reflection-2/pom.xml @@ -62,4 +62,5 @@ 1.8 5.3.4 + \ No newline at end of file diff --git a/core-java-modules/core-java-regex-2/README.md b/core-java-modules/core-java-regex-2/README.md new file mode 100644 index 0000000000..25656c3cb1 --- /dev/null +++ b/core-java-modules/core-java-regex-2/README.md @@ -0,0 +1,5 @@ +### Relevant Articles: + +- [Non-Capturing Regex Groups in Java](https://www.baeldung.com/java-regex-non-capturing-groups) +- [Lookahead and Lookbehind in Java Regex](https://www.baeldung.com/java-regex-lookahead-lookbehind) +- [Converting Camel Case and Title Case to Words in Java](https://www.baeldung.com/java-camel-case-title-case-to-words) diff --git a/core-java-modules/core-java-regex-2/pom.xml b/core-java-modules/core-java-regex-2/pom.xml new file mode 100644 index 0000000000..9e081c2228 --- /dev/null +++ b/core-java-modules/core-java-regex-2/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + core-java-regex-2 + 0.1.0-SNAPSHOT + core-java-regex-2 + jar + + + com.baeldung.core-java-modules + core-java-modules + 0.0.1-SNAPSHOT + ../ + + + + + org.assertj + assertj-core + ${assertj-core.version} + test + + + + + 3.15.0 + + + \ No newline at end of file diff --git a/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java new file mode 100644 index 0000000000..ea6593495f --- /dev/null +++ b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java @@ -0,0 +1,27 @@ +package com.baeldung.regex.camelcasetowords; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Convert a string in camelCase or TitleCase into a list of words + */ +public class CamelCaseToWords { + private static final Pattern WORD_FINDER = Pattern.compile("(([A-Z]?[a-z]+)|([A-Z]))"); + + /** + * Find the words in mixed case string like ThisIsText or HereIsSomeText + * @param text the text to parse + * @return the list of words to process + */ + public static List findWordsInMixedCase(String text) { + Matcher matcher = WORD_FINDER.matcher(text); + List words = new ArrayList<>(); + while (matcher.find()) { + words.add(matcher.group(0)); + } + return words; + } +} diff --git a/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java new file mode 100644 index 0000000000..f87e59c6cc --- /dev/null +++ b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java @@ -0,0 +1,43 @@ +package com.baeldung.regex.camelcasetowords; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Recapitalize { + private static final Set STOP_WORDS = Stream.of("a", "an", "the", "and", + "but", "for", "at", "by", "to", "or") + .collect(Collectors.toSet()); + + public static String sentenceCase(List words) { + List capitalized = new ArrayList<>(); + for (int i = 0; i < words.size(); i++) { + String currentWord = words.get(i); + if (i == 0) { + capitalized.add(capitalizeFirst(currentWord)); + } else { + capitalized.add(currentWord.toLowerCase()); + } + } + return String.join(" ", capitalized) + "."; + } + + public static String capitalizeMyTitle(List words) { + List capitalized = new ArrayList<>(); + for (int i = 0; i < words.size(); i++) { + String currentWord = words.get(i); + if (i == 0 || !STOP_WORDS.contains(currentWord.toLowerCase())) { + capitalized.add(capitalizeFirst(currentWord)); + } else { + capitalized.add(currentWord.toLowerCase()); + } + } + return String.join(" ", capitalized); + } + + private static String capitalizeFirst(String word) { + return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase(); + } +} diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java new file mode 100644 index 0000000000..a843945bba --- /dev/null +++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java @@ -0,0 +1,39 @@ +package com.baeldung.regex.camelcasetowords; + +import org.junit.jupiter.api.Test; + +import static com.baeldung.regex.camelcasetowords.CamelCaseToWords.findWordsInMixedCase; +import static org.assertj.core.api.Assertions.assertThat; + +class CamelCaseToWordsUnitTest { + + @Test + void givenPlainStringWithNonLetters_thenFindsWords() { + assertThat(findWordsInMixedCase("some words")) + .containsExactly("some", "words"); + } + + @Test + void givenWordsInCamelCase_thenFindsWords() { + assertThat(findWordsInMixedCase("thisIsCamelCaseText")) + .containsExactly("this", "Is", "Camel", "Case", "Text"); + } + + @Test + void givenWordsInTitleCase_thenFindsWords() { + assertThat(findWordsInMixedCase("ThisIsTitleCaseText")) + .containsExactly("This", "Is", "Title", "Case", "Text"); + } + + @Test + void givenWordsAcrossMultipleTexts_thenFindsWords() { + assertThat(findWordsInMixedCase("ThisIsTitleCaseText --- andSoIsThis")) + .containsExactly("This", "Is", "Title", "Case", "Text", "and", "So", "Is", "This"); + } + + @Test + void givenCamelCaseHasASingleLetterWord_thenItCanBeSplit() { + assertThat(findWordsInMixedCase("thisHasASingleLetterWord")) + .containsExactly("this", "Has", "A", "Single", "Letter", "Word"); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java new file mode 100644 index 0000000000..32c8f39372 --- /dev/null +++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java @@ -0,0 +1,35 @@ +package com.baeldung.regex.camelcasetowords; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static com.baeldung.regex.camelcasetowords.Recapitalize.*; +import static org.assertj.core.api.Assertions.assertThat; + +class RecapitalizeUnitTest { + + @Test + void givenWords_thenCanComposeSentence() { + assertThat(sentenceCase(Arrays.asList("these", "Words", "Form", "A", "Sentence"))) + .isEqualTo("These words form a sentence."); + } + + @Test + void givenNonStopWords_thenTitleIsComposed() { + assertThat(capitalizeMyTitle(Arrays.asList("title", "words", "capitalize"))) + .isEqualTo("Title Words Capitalize"); + } + + @Test + void givenStopWords_thenTitleHasThemInLowerCase() { + assertThat(capitalizeMyTitle(Arrays.asList("this", "is", "A", "title", "with", "a", "stop", "word", "or", "two"))) + .isEqualTo("This Is a Title With a Stop Word or Two"); + } + + @Test + void givenStopWordIsFirstWord_thenTitleHasItCapitalized() { + assertThat(capitalizeMyTitle(Arrays.asList("a", "stop", "word", "first"))) + .isEqualTo("A Stop Word First"); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/lookaround/LookaroundUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/lookaround/LookaroundUnitTest.java new file mode 100644 index 0000000000..eab3793931 --- /dev/null +++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/lookaround/LookaroundUnitTest.java @@ -0,0 +1,61 @@ +package com.baeldung.regex.lookaround; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.junit.jupiter.api.Test; + +public class LookaroundUnitTest { + + @Test + void givenPositiveLookahead_thenSuccess() { + Pattern pattern = Pattern.compile("import (?=static.+).+"); + + Matcher matcher = pattern.matcher("import static org.junit.jupiter.api.Assertions.assertEquals;"); + assertTrue(matcher.find()); + assertEquals("import static org.junit.jupiter.api.Assertions.assertEquals;", matcher.group()); + + assertFalse(pattern.matcher("import java.util.regex.Matcher;") + .find()); + } + + @Test + void givenNegativeLookahead_thenSuccess() { + Pattern pattern = Pattern.compile("import (?!static.+).+"); + + Matcher matcher = pattern.matcher("import java.util.regex.Matcher;"); + assertTrue(matcher.find()); + assertEquals("import java.util.regex.Matcher;", matcher.group()); + + assertFalse(pattern.matcher("import static org.junit.jupiter.api.Assertions.assertEquals;") + .find()); + } + + @Test + void givenPositiveLookbehind_thenSuccess() { + Pattern pattern = Pattern.compile(".*(?<=jupiter).*assertEquals;"); + + Matcher matcher = pattern.matcher("import static org.junit.jupiter.api.Assertions.assertEquals;"); + assertTrue(matcher.find()); + assertEquals("import static org.junit.jupiter.api.Assertions.assertEquals;", matcher.group()); + + assertFalse(pattern.matcher("import static org.junit.Assert.assertEquals;") + .find()); + } + + @Test + void givenNegativeLookbehind_thenSuccess() { + Pattern pattern = Pattern.compile(".*(?[.a-z]+/?)+/ending-path"); + private static final Pattern INDEPENDENT_URL_PATTERN_WITH_ENDING_PATH_AND_BACKTRACKING = Pattern.compile("[^:]+://(?>(?:[.a-z]+/?)+/)ending-path"); + + @Test + void givenSimpleUrlPattern_whenValidUrlProvided_thenMatches() { + Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.microsoft.com/"); + + Assertions.assertThat(urlMatcher.matches()).isTrue(); + Assertions.assertThatThrownBy(() -> urlMatcher.group(1)).isInstanceOf(IndexOutOfBoundsException.class); + } + + @Test + void whenSimpleUrlProvidedWithPathProvided_thenMatches() { + Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.microsoft.com/live"); + + Assertions.assertThat(urlMatcher.matches()).isTrue(); + } + + @Test + void whenSimpleUrlProvidedWithPathEndingWithSlashProvided_thenMatches() { + Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.microsoft.com/live/"); + + Assertions.assertThat(urlMatcher.matches()).isTrue(); + } + + @Test + void givenSimpleUrlPattern_whenUrlWithMultiplePathSegmentsProvided_thenMatches() { + Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.microsoft.com/some/other/url/path"); + + Assertions.assertThat(urlMatcher.matches()).isTrue(); + } + + @Test + void whenUrlWithUppercaseCharactersProvided_thenDoesNotMatch() { + Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.Microsoft.com/"); + + Assertions.assertThat(urlMatcher.matches()).isFalse(); + } + + @Test + void givenPatternWithCaseInsensitiveGroup_whenUrlHasUppercaseCharactersInsideOfScope_thenMatches() { + Matcher urlMatcher = SCOPED_CASE_INSENSITIVE_URL_PATTERN.matcher("http://www.Microsoft.com/"); + + Assertions.assertThat(urlMatcher.matches()).isTrue(); + } + + @Test + void givenCaseInsensitivePattern_whenUrlHasUppercaseCharacters_thenMatches() { + Matcher urlMatcher = CASE_INSENSITIVE_URL_PATTERN.matcher("http://www.Microsoft.com/Ending-path"); + + Assertions.assertThat(urlMatcher.matches()).isTrue(); + Assertions.assertThat(urlMatcher.group(1)).isEqualTo("/Ending-path"); + } + + @Test + void givenPatternWithCaseInsensitiveGroup_whenUrlHasUppercaseCharactersOutsideOfScope_thenMatchFails() { + Matcher urlMatcher = SCOPED_CASE_INSENSTIIVE_URL_PATTERN_WITH_ENDING_PATH.matcher("http://www.Microsoft.com/Ending-path"); + + Assertions.assertThat(urlMatcher.matches()).isFalse(); + } + + @Test + void givenPatternAllowingBacktracking_whenUrlWithEndingPathCausingBacktrackingProvided_thenMatches() { + Matcher urlMatcher = SIMPLE_URL_PATTERN_WITH_SPECIFIC_ENDING_PATH.matcher("http://www.microsoft.com/ending-path"); + + Assertions.assertThat(urlMatcher.matches()).isTrue(); + } + + @Test + void givenPatternWithIndependentNonCapturingGroup_whenBacktrackingOccurs_thenDoesNotMatch() { + Matcher independentMatcher = INDEPENDENT_URL_PATTERN_WITH_ENDING_PATH.matcher("http://www.microsoft.com/ending-path"); + + Assertions.assertThat(independentMatcher.matches()).isFalse(); + } + + @Test + void givenPatternWithIndependentNonCapturingGroup_whenBacktrackingDoesNotOccur_thenMatches() { + Matcher independentMatcher = INDEPENDENT_URL_PATTERN_WITH_ENDING_PATH.matcher("http://www.microsoft.com//ending-path"); + + Assertions.assertThat(independentMatcher.matches()).isTrue(); + } + + @Test + void givenPatternWithIndependentNonCapturingGroup_whenBacktrackingOccursInsideGroup_thenMatches() { + Matcher independentMatcher = INDEPENDENT_URL_PATTERN_WITH_ENDING_PATH_AND_BACKTRACKING.matcher("http://www.microsoft.com/ending-path"); + + Assertions.assertThat(independentMatcher.matches()).isTrue(); + } + + @Test + void givenCaseInsensitivePatternWithCaseSensitivieSubPattern_whenUrlWithUppercaseCharactersOutsideOfScopeProvided_thenMatches() { + Matcher urlMatcher = SCOPED_CASE_SENSITIVE_URL_PATTERN.matcher("http://www.microsoft.com/ENDING-PATH"); + + Assertions.assertThat(urlMatcher.matches()).isTrue(); + } + + @Test + void givenCaseInsensitivePatternWithCaseSensitivieSubPattern_whenUrlWithUppercaseCharactersInsideOfScopeProvided_thenDoesNotMatch() { + Matcher urlMatcher = SCOPED_CASE_SENSITIVE_URL_PATTERN.matcher("http://www.Microsoft.com/ending-path"); + + Assertions.assertThat(urlMatcher.matches()).isFalse(); + } +} diff --git a/core-java-modules/core-java-regex/README.md b/core-java-modules/core-java-regex/README.md index bc28f4b732..4c78f64d75 100644 --- a/core-java-modules/core-java-regex/README.md +++ b/core-java-modules/core-java-regex/README.md @@ -14,3 +14,4 @@ - [Validate Phone Numbers With Java Regex](https://www.baeldung.com/java-regex-validate-phone-numbers) - [How to Count the Number of Matches for a Regex?](https://www.baeldung.com/java-count-regex-matches) - [Find All Numbers in a String in Java](https://www.baeldung.com/java-find-numbers-in-string) +- [Understanding the Pattern.quote Method](https://www.baeldung.com/java-pattern-quote) diff --git a/core-java-modules/core-java-regex/src/test/java/com/baeldung/ignore/pattern/metacharacters/IgnoringPatternMetacharactersUnitTest.java b/core-java-modules/core-java-regex/src/test/java/com/baeldung/ignore/pattern/metacharacters/IgnoringPatternMetacharactersUnitTest.java index 921876c0d5..8c07282716 100644 --- a/core-java-modules/core-java-regex/src/test/java/com/baeldung/ignore/pattern/metacharacters/IgnoringPatternMetacharactersUnitTest.java +++ b/core-java-modules/core-java-regex/src/test/java/com/baeldung/ignore/pattern/metacharacters/IgnoringPatternMetacharactersUnitTest.java @@ -12,7 +12,7 @@ public class IgnoringPatternMetacharactersUnitTest { private static final String patternStr = "$100.50"; @Test - public void givenPatternStringHasMetacharacters_whenPatternMatchedWithoutEscapingMetacharacters_thenNoMatchesFound() { + public void whenMetacharactersNotEscaped_thenNoMatchesFound() { Pattern pattern = Pattern.compile(patternStr); Matcher matcher = pattern.matcher(dollarAmounts); @@ -25,7 +25,7 @@ public class IgnoringPatternMetacharactersUnitTest { } @Test - public void givenPatternStringHasMetacharacters_whenPatternCompiledUsingManuallyMetaEscapedPattern_thenMatchingSuccessful() { + public void whenMetacharactersManuallyEscaped_thenMatchingSuccessful() { String metaEscapedPatternStr = "\\Q" + patternStr + "\\E"; Pattern pattern = Pattern.compile(metaEscapedPatternStr); Matcher matcher = pattern.matcher(dollarAmounts); @@ -39,7 +39,7 @@ public class IgnoringPatternMetacharactersUnitTest { } @Test - public void givenPatternStringHasMetacharacters_whenPatternCompiledUsingLiteralPatternFromQuote_thenMatchingSuccessful() { + public void whenMetacharactersEscapedUsingPatternQuote_thenMatchingSuccessful() { String literalPatternStr = Pattern.quote(patternStr); Pattern pattern = Pattern.compile(literalPatternStr); Matcher matcher = pattern.matcher(dollarAmounts); diff --git a/core-java-modules/core-java-security-2/README.md b/core-java-modules/core-java-security-2/README.md index 684f2504cc..680a7de925 100644 --- a/core-java-modules/core-java-security-2/README.md +++ b/core-java-modules/core-java-security-2/README.md @@ -17,4 +17,5 @@ This module contains articles about core Java Security - [InvalidAlgorithmParameterException: Wrong IV Length](https://www.baeldung.com/java-invalidalgorithmparameter-exception) - [The java.security.egd JVM Option](https://www.baeldung.com/java-security-egd) - [RSA in Java](https://www.baeldung.com/java-rsa) +- [3DES in Java](https://www.baeldung.com/java-3des) - More articles: [[<-- prev]](/core-java-modules/core-java-security) diff --git a/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java new file mode 100644 index 0000000000..6e5df8e619 --- /dev/null +++ b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java @@ -0,0 +1,90 @@ +package com.baeldung.des; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +public class TripleDESUnitTest { + + @Test + public void given3DesKey_whenEncryptAndDecryptString_thenCompareResults() throws Exception { + byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes(); + byte[] iv = "a76nb5h9".getBytes(); + + String secretMessage = "Baeldung secret message"; + + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); + byte[] secretMessagesBytes = secretMessage.getBytes(StandardCharsets.UTF_8); + byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessagesBytes); + + Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); + byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes); + String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8); + + Assertions.assertEquals(secretMessage, decryptedMessage); + } + + @Test + public void given3DesKey_whenEncryptAndDecryptFile_thenCompareResults() throws Exception { + byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes(); + byte[] iv = "a76nb5h9".getBytes(); + + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + String originalContent = "some secret message"; + Path tempFile = Files.createTempFile("temp", "txt"); + writeString(tempFile, originalContent); + + byte[] fileBytes = Files.readAllBytes(tempFile); + Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); + byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(encryptedFileBytes); + } + + encryptedFileBytes = Files.readAllBytes(tempFile); + Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); + byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes); + try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) { + stream.write(decryptedFileBytes); + } + + String fileContent = readString(tempFile); + + Assertions.assertEquals(originalContent, fileContent); + } + + private void writeString(Path path, String content) throws Exception { + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write(content); + } + } + + private String readString(Path path) throws Exception { + StringBuilder resultStringBuilder = new StringBuilder(); + try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) { + String line; + while ((line = br.readLine()) != null) { + resultStringBuilder.append(line); + } + } + return resultStringBuilder.toString(); + } +} diff --git a/core-java-modules/core-java-security-3/pom.xml b/core-java-modules/core-java-security-3/pom.xml index 2520cee7f8..69d0ffb917 100644 --- a/core-java-modules/core-java-security-3/pom.xml +++ b/core-java-modules/core-java-security-3/pom.xml @@ -3,9 +3,9 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - core-java-security-2 + core-java-security-3 0.1.0-SNAPSHOT - core-java-security-2 + core-java-security-3 jar diff --git a/core-java-modules/core-java-streams-4/.gitignore b/core-java-modules/core-java-streams-4/.gitignore new file mode 100644 index 0000000000..3de4cc647e --- /dev/null +++ b/core-java-modules/core-java-streams-4/.gitignore @@ -0,0 +1,26 @@ +*.class + +0.* + +#folders# +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* +.resourceCache + +# Packaged files # +*.jar +*.war +*.ear + +# Files generated by integration tests +*.txt +backup-pom.xml +/bin/ +/temp + +#IntelliJ specific +.idea/ +*.iml \ No newline at end of file diff --git a/core-java-modules/core-java-streams-4/README.md b/core-java-modules/core-java-streams-4/README.md new file mode 100644 index 0000000000..6eeee943aa --- /dev/null +++ b/core-java-modules/core-java-streams-4/README.md @@ -0,0 +1,3 @@ +## Relevant Articles: + +- [Count Occurrences Using Java groupingBy Collector](https://www.baeldung.com/java-groupingby-count) diff --git a/core-java-modules/core-java-streams-4/pom.xml b/core-java-modules/core-java-streams-4/pom.xml new file mode 100644 index 0000000000..8cb3786776 --- /dev/null +++ b/core-java-modules/core-java-streams-4/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + core-java-streams-4 + 0.1.0-SNAPSHOT + core-java-streams-4 + jar + + + com.baeldung.core-java-modules + core-java-modules + 0.0.1-SNAPSHOT + ../ + + + + + log4j + log4j + ${log4j.version} + + + org.junit + junit-bom + ${junit-jupiter.version} + pom + import + + + + + core-java-streams-4 + + + src/main + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source} + ${maven.compiler.target} + -parameters + + + + + + + + 3.1 + 1.8 + 1.8 + + + \ No newline at end of file diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java new file mode 100644 index 0000000000..c50d73a638 --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java @@ -0,0 +1,84 @@ +package com.baeldung.streamcollectors; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.junit.Assert; +import org.junit.Test; + +public class StreamGroupingByCollectorUnitTest { + @Test + public void givenListOfStrings_whenGroupingEqualStrings_thenUseCollectorsGroupingByToGroupEqualStringsAndCountOfOccurrences() { + + List list = new ArrayList<>(Arrays.asList("Foo", "Bar", "Bar", "Foo", "Bar")); + + Map result = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); + Assert.assertEquals(new Long(2), result.get("Foo")); + Assert.assertEquals(new Long(3), result.get("Bar")); + + } + + @Test + public void givenListOfStrings_whenGroupingEqualLengthStrings_thenUseCollectorsGroupingByConcurrentToGroupEqualLengthStringsAndCountOfOccurrences() { + + List list = new ArrayList<>(Arrays.asList("Adam", "Bill", "Jack", "Joe", "Ian")); + + Map result = list.stream().collect(Collectors.groupingByConcurrent(String::length, Collectors.counting())); + Assert.assertEquals(new Long(2), result.get(3)); + Assert.assertEquals(new Long(3), result.get(4)); + + } + + @Test + public void givenListOfEmployees_whenGroupingDepartmentId_thenUseCollectorsGroupingByDepartmentIdAndCountNumberOfEmployeesWithinEveryDepartment() { + + List list = new ArrayList<>(Arrays.asList(new Employee(1, "Joe", 1), new Employee(2, "Josh", 1), new Employee(3, "Jamie", 2), new Employee(4, "Jim", 2), new Employee(5, "Jack", 2))); + + Map result = list.stream().collect(Collectors.groupingBy(Employee::getDepartmentId, Collectors.counting())); + Assert.assertEquals(new Long(2), result.get(1)); + Assert.assertEquals(new Long(3), result.get(2)); + + } + + static class Employee { + + Integer employeeId; + String employeeName; + Integer departmentId; + + Employee(Integer employeeId, String employeeName, Integer departmentId) { + this.employeeId = employeeId; + this.employeeName = employeeName; + this.departmentId = departmentId; + } + + public Integer getEmployeeId() { + return employeeId; + } + + public void setEmployeeId(Integer employeeId) { + this.employeeId = employeeId; + } + + public String getEmployeeName() { + return employeeName; + } + + public void setEmployeeName(String employeeName) { + this.employeeName = employeeName; + } + + public Integer getDepartmentId() { + return departmentId; + } + + public void setDepartmentId(Integer departmentId) { + this.departmentId = departmentId; + } + } + +} diff --git a/core-java-modules/core-java-streams/pom.xml b/core-java-modules/core-java-streams/pom.xml index f02ba1c69a..3d5de7cebe 100644 --- a/core-java-modules/core-java-streams/pom.xml +++ b/core-java-modules/core-java-streams/pom.xml @@ -91,7 +91,6 @@ true - org.apache.maven.plugins diff --git a/core-java-modules/core-java-string-algorithms-3/pom.xml b/core-java-modules/core-java-string-algorithms-3/pom.xml index 1048b796bc..c8243258c1 100644 --- a/core-java-modules/core-java-string-algorithms-3/pom.xml +++ b/core-java-modules/core-java-string-algorithms-3/pom.xml @@ -27,7 +27,6 @@ guava ${guava.version} - org.junit.jupiter junit-jupiter diff --git a/core-java-modules/core-java-string-conversions-2/README.md b/core-java-modules/core-java-string-conversions-2/README.md index 3bd3ba927e..46eb783a27 100644 --- a/core-java-modules/core-java-string-conversions-2/README.md +++ b/core-java-modules/core-java-string-conversions-2/README.md @@ -7,4 +7,6 @@ This module contains articles about string conversions from/to another type. - [Convert String to Byte Array and Reverse in Java](https://www.baeldung.com/java-string-to-byte-array) - [Convert Character Array to String in Java](https://www.baeldung.com/java-char-array-to-string) - [Converting String to BigDecimal in Java](https://www.baeldung.com/java-string-to-bigdecimal) +- [Converting String to BigInteger in Java](https://www.baeldung.com/java-string-to-biginteger) +- [Convert a String to Camel Case](https://www.baeldung.com/java-string-to-camel-case) - More articles: [[<-- prev]](/core-java-string-conversions) diff --git a/core-java-modules/core-java-string-conversions-2/pom.xml b/core-java-modules/core-java-string-conversions-2/pom.xml index 8a222a6b5d..700b4394c6 100644 --- a/core-java-modules/core-java-string-conversions-2/pom.xml +++ b/core-java-modules/core-java-string-conversions-2/pom.xml @@ -33,8 +33,36 @@ ${hamcrest.version} test + + com.ibm.icu + icu4j + ${icu.version} + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + org.apache.commons + commons-text + ${commons-text.version} + + + org.assertj + assertj-core + ${assertj.version} + test + + + 64.2 + 3.12.2 + 1.9 + 30.1.1-jre + + core-java-string-conversions-2 diff --git a/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java b/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java new file mode 100644 index 0000000000..7a4369ac4a --- /dev/null +++ b/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java @@ -0,0 +1,109 @@ +package com.baeldung.stringtocamelcase; + +import com.google.common.base.CaseFormat; +import com.ibm.icu.lang.UCharacter; +import com.ibm.icu.text.BreakIterator; +import org.apache.commons.text.CaseUtils; +import org.apache.commons.text.WordUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class StringToCamelCase { + + public static String toCamelCaseByIteration(String text, char delimiter) { + if (text == null || text.isEmpty()) { + return text; + } + boolean shouldConvertNextCharToLower = true; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < text.length(); i++) { + char currentChar = text.charAt(i); + if (currentChar == delimiter) { + shouldConvertNextCharToLower = false; + } else if (shouldConvertNextCharToLower) { + builder.append(Character.toLowerCase(currentChar)); + } else { + builder.append(Character.toUpperCase(currentChar)); + shouldConvertNextCharToLower = true; + } + } + return builder.toString(); + } + + public static String toCamelCaseBySplitting(String text, String delimiter) { + if (text == null || text.isEmpty()) { + return text; + } + String[] words = text.split(delimiter); + StringBuilder builder = new StringBuilder(); + for (int i = 0, wordsLength = words.length; i < wordsLength; i++) { + String word = words[i]; + if (i == 0) { + //Make the first word all lowercase + word = word.isEmpty() ? word : word.toLowerCase(); + } else { + //Convert the first character to Uppercase and others to lowercase + // e.g sTRING =====> String + word = word.isEmpty() ? word : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase(); + } + builder.append(word); + } + return builder.toString(); + } + + public static String toCamelCaseBySplittingUsingStreams(String text, String delimiter) { + if (text == null || text.isEmpty()) { + return text; + } + String[] words = text.split(delimiter); + //Convert the first word to lowercase and then every + //other word to Title Case. + String firstWord = words[0].toLowerCase(); + String otherWords = Arrays.stream(words, 1, words.length) + .filter(word -> !word.isEmpty()) + .map(word -> Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase()) + .collect(Collectors.joining("")); + + return firstWord + otherWords; + } + + public static String toCamelCaseByRegex(String text) { + StringBuilder builder = new StringBuilder(); + String[] words = text.split("[\\W_]+"); + for (int i = 0; i < words.length; i++) { + String word = words[i]; + if (i == 0) { + word = word.isEmpty() ? word : word.toLowerCase(); + } else { + word = word.isEmpty() ? word : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase(); + } + builder.append(word); + } + return builder.toString(); + } + + //Third-Party Libraries + public static String toCamelCaseUsingICU4J(String text, String delimiter) { + if (text == null || text.isEmpty()) { + return text; + } + text = UCharacter.toTitleCase(text, BreakIterator.getTitleInstance()).replaceAll(delimiter, ""); + StringBuilder builder = new StringBuilder(text); + builder.setCharAt(0, Character.toLowerCase(text.charAt(0))); + return builder.toString(); + } + + public static String toCamelCaseUsingGuava(String text, String delimiter) { + String toUpperUnderscore = text.toUpperCase().replaceAll(delimiter, "_"); + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, toUpperUnderscore); + } + + public static String toCamelCaseUsingApacheCommons(String text, char delimiter) { + text = WordUtils.capitalizeFully(text, delimiter).replaceAll(String.valueOf(delimiter), ""); + StringBuilder builder = new StringBuilder(text); + builder.setCharAt(0, Character.toLowerCase(text.charAt(0))); + return builder.toString(); + } + +} diff --git a/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java new file mode 100644 index 0000000000..82ffe96d84 --- /dev/null +++ b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java @@ -0,0 +1,44 @@ +package com.baeldung.stringtobybiginteger; + +import static org.junit.Assert.assertEquals; + +import java.math.BigInteger; + +import org.junit.Test; + +public class StringToBigIntegerUnitTest { + + @Test + public void whenGetStringWithOutRadix_thenOK() { + String inputString = "878"; + BigInteger result = new BigInteger(inputString); + assertEquals("878", result.toString()); + } + + @Test + public void whenGetStringWithRadix_thenOK() { + String inputString = "290f98"; + BigInteger result = new BigInteger(inputString, 16); + assertEquals("2690968", result.toString()); + } + + @Test(expected = NumberFormatException.class) + public void whenGetStringWithOutRadix_thenThrowError() { + String inputString = "290f98"; + new BigInteger(inputString); + } + + @Test(expected = NumberFormatException.class) + public void whenGetStringWithRadix_thenThrowError() { + String inputString = "290f98"; + new BigInteger(inputString, 7); + } + + @Test + public void whenGetStringUsingByte_thenOk() { + String inputString = "290f98"; + byte[] inputStringBytes = inputString.getBytes(); + BigInteger result = new BigInteger(inputStringBytes); + assertEquals("290f98", new String(result.toByteArray())); + } +} diff --git a/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java new file mode 100644 index 0000000000..3adc4434a9 --- /dev/null +++ b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java @@ -0,0 +1,111 @@ +package com.baeldung.stringtocamelcase; + + +import com.google.common.base.CaseFormat; +import org.apache.commons.text.CaseUtils; +import org.junit.Test; + +import static com.baeldung.stringtocamelcase.StringToCamelCase.*; +import static org.assertj.core.api.Assertions.*; + + +public class StringToCamelCaseUnitTest { + + @Test + public void givenStringWithWhiteSpaces_WhenToCamelCaseByIteration_ThenReturnCamelCase() { + assertThat(toCamelCaseByIteration("THIS STRING SHOULD BE IN CAMEL CASE", ' ')).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithUnderscores_WhenToCamelCaseByIteration_ThenReturnCamelCase() { + assertThat(toCamelCaseByIteration("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", '_')).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithWhiteSpaces_WhenToCamelCaseBySplitting_ThenReturnCamelCase() { + assertThat(toCamelCaseBySplitting("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithUnderscores_WhenToCamelCaseBySplitting_ThenReturnCamelCase() { + assertThat(toCamelCaseBySplitting("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithWhiteSpaces_WhenToCamelCaseBySplittingUsingStreams_ThenReturnCamelCase() { + assertThat(toCamelCaseBySplittingUsingStreams("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithUnderscores_WhenToCamelCaseBySplittingUsingStreams_ThenReturnCamelCase() { + assertThat(toCamelCaseBySplittingUsingStreams("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingApacheCommonsText_ThenReturnCamelCase() { + assertThat(CaseUtils.toCamelCase("THIS STRING SHOULD BE IN CAMEL CASE", false, ' ')) + .isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithUnderscores_WhenToCamelCaseUsingApacheCommonsText_ThenReturnCamelCase() { + assertThat(CaseUtils.toCamelCase("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", false, '_')) + .isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingICU4J_ThenReturnCamelCase() { + assertThat(toCamelCaseUsingICU4J("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithUnderscores_WhenToCamelCaseUsingICU4J_ThenReturnCamelCase() { + assertThat(toCamelCaseUsingICU4J("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingGuava_ThenReturnCamelCase() { + assertThat(toCamelCaseUsingGuava("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithUnderscores_WhenToCamelCaseUsingGuava_ThenReturnCamelCase() { + assertThat(toCamelCaseUsingGuava("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingApacheCommons_ThenReturnCamelCase() { + assertThat(toCamelCaseUsingApacheCommons("THIS STRING SHOULD BE IN CAMEL CASE", ' ')).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithUnderscores_WhenToCamelCaseUsingApacheCommons_ThenReturnCamelCase() { + assertThat(toCamelCaseUsingApacheCommons("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", '_')).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithWhiteSpaces_WhenToCamelCaseByRegex_ThenReturnCamelCase() { + assertThat(toCamelCaseByRegex("THIS STRING SHOULD BE IN CAMEL CASE")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenStringWithWhiteUnderscores_WhenToCamelCaseByRegex_ThenReturnCamelCase() { + assertThat(toCamelCaseByRegex("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE")).isEqualTo("thisStringShouldBeInCamelCase"); + } + + @Test + public void givenRandomString_WhenToCamelCaseByRegex_ThenReturnCamelCase() { + assertThat(toCamelCaseByRegex("Please Turn this56738 to camel Case")).isEqualTo("pleaseTurnThis56738ToCamelCase"); + } + + @Test + public void givenRandomStringWithDifferentDelimiters_WhenToCamelCaseByRegex_ThenReturnCamelCase() { + assertThat(toCamelCaseByRegex("Please Turn this56738 to camel Case This should-be_in;camel-case")).isEqualTo("pleaseTurnThis56738ToCamelCaseThisShouldBeInCamelCase"); + } + + @Test + public void givenUppercaseWordWithUnderscores_WhenCaseFormatToLowerCamel_ThenReturnCamelCase() { + assertThat(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "THIS_STRING_SHOULD_BE_IN_CAMEL_CASE")).isEqualTo("thisStringShouldBeInCamelCase"); + } + +} diff --git a/core-java-modules/core-java-string-operations-3/pom.xml b/core-java-modules/core-java-string-operations-3/pom.xml index aa9fe38cca..b73851f90c 100644 --- a/core-java-modules/core-java-string-operations-3/pom.xml +++ b/core-java-modules/core-java-string-operations-3/pom.xml @@ -44,13 +44,18 @@ - - 3.6.1 - 3.6.3 - 6.1.1 - 2.11.1 - 3.1.0 - + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + @@ -58,4 +63,15 @@ https://repo.gradle.org/gradle/libs-releases-local/ + + + 11 + 11 + 3.6.1 + 3.6.3 + 6.1.1 + 2.11.1 + 3.1.0 + + \ No newline at end of file diff --git a/core-java-modules/core-java-string-operations-3/src/main/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUtils.java b/core-java-modules/core-java-string-operations-3/src/main/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUtils.java new file mode 100644 index 0000000000..1a86edd45a --- /dev/null +++ b/core-java-modules/core-java-string-operations-3/src/main/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUtils.java @@ -0,0 +1,61 @@ +package com.baeldung.stringfilenamevalidaiton; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Arrays; + +public class StringFilenameValidationUtils { + + public static final Character[] INVALID_WINDOWS_SPECIFIC_CHARS = {'"', '*', ':', '<', '>', '?', '\\', '|', 0x7F}; + public static final Character[] INVALID_UNIX_SPECIFIC_CHARS = {'\000'}; + + public static final String REGEX_PATTERN = "^[A-za-z0-9.]{1,255}$"; + + private StringFilenameValidationUtils() { + } + + public static boolean validateStringFilenameUsingIO(String filename) throws IOException { + File file = new File(filename); + boolean created = false; + try { + created = file.createNewFile(); + return created; + } finally { + if (created) { + file.delete(); + } + } + } + + public static boolean validateStringFilenameUsingNIO2(String filename) { + Paths.get(filename); + return true; + } + + public static boolean validateStringFilenameUsingContains(String filename) { + if (filename == null || filename.isEmpty() || filename.length() > 255) { + return false; + } + return Arrays.stream(getInvalidCharsByOS()) + .noneMatch(ch -> filename.contains(ch.toString())); + } + + public static boolean validateStringFilenameUsingRegex(String filename) { + if (filename == null) { + return false; + } + return filename.matches(REGEX_PATTERN); + } + + private static Character[] getInvalidCharsByOS() { + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + return INVALID_WINDOWS_SPECIFIC_CHARS; + } else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) { + return INVALID_UNIX_SPECIFIC_CHARS; + } else { + return new Character[]{}; + } + } +} diff --git a/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/splitstringbynewline/SplitStringByNewLineUnitTest.java b/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/splitstringbynewline/SplitStringByNewLineUnitTest.java index 9afba62947..2e6613b0be 100644 --- a/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/splitstringbynewline/SplitStringByNewLineUnitTest.java +++ b/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/splitstringbynewline/SplitStringByNewLineUnitTest.java @@ -1,9 +1,13 @@ package com.baeldung.splitstringbynewline; import org.junit.Test; + +import java.util.regex.Pattern; + import static org.assertj.core.api.Assertions.assertThat; public class SplitStringByNewLineUnitTest { + @Test public void givenString_whenSplitByNewLineUsingSystemLineSeparator_thenReturnsArray() { assertThat("Line1\nLine2\nLine3".split(System.lineSeparator())).containsExactly("Line1", "Line2", "Line3"); @@ -26,4 +30,20 @@ public class SplitStringByNewLineUnitTest { assertThat("Line1\r\nLine2\r\nLine3".split("\\R")).containsExactly("Line1", "Line2", "Line3"); } + + @Test + public void givenString_whenSplitByNewLineUsingJava8PatternClass_thenReturnsStream() { + Pattern pattern = Pattern.compile("\\R"); + + assertThat(pattern.splitAsStream("Line1\nLine2\nLine3")).containsExactly("Line1", "Line2", "Line3"); + + assertThat(pattern.splitAsStream("Line1\rLine2\rLine3")).containsExactly("Line1", "Line2", "Line3"); + + assertThat(pattern.splitAsStream("Line1\r\nLine2\r\nLine3")).containsExactly("Line1", "Line2", "Line3"); + } + + @Test + public void givenString_whenSplitByNewLineUsingJava11Lines_thenReturnsStream() { + assertThat("Line1\nLine2\rLine3\r\nLine4".lines()).containsExactly("Line1", "Line2", "Line3", "Line4"); + } } diff --git a/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUnitTest.java b/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUnitTest.java new file mode 100644 index 0000000000..3e787f08be --- /dev/null +++ b/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUnitTest.java @@ -0,0 +1,130 @@ +package com.baeldung.stringfilenamevalidaiton; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.RandomUtils; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; + +import java.io.IOException; +import java.nio.file.InvalidPathException; +import java.util.Arrays; +import java.util.stream.Stream; + +import static com.baeldung.stringfilenamevalidaiton.StringFilenameValidationUtils.validateStringFilenameUsingContains; +import static com.baeldung.stringfilenamevalidaiton.StringFilenameValidationUtils.validateStringFilenameUsingIO; +import static com.baeldung.stringfilenamevalidaiton.StringFilenameValidationUtils.validateStringFilenameUsingNIO2; +import static com.baeldung.stringfilenamevalidaiton.StringFilenameValidationUtils.validateStringFilenameUsingRegex; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class StringFilenameValidationUnitTest { + + private static final String CORRECT_FILENAME_PATTERN = "baeldung.txt"; + + @ParameterizedTest + @MethodSource("correctAlphanumericFilenamesProvider") + public void givenCorrectAlphanumericRandomFilenameString_whenValidateUsingIO_thenReturnTrue(String filename) throws IOException { + assertThat(validateStringFilenameUsingIO(filename)).isTrue(); + assertThat(validateStringFilenameUsingNIO2(filename)).isTrue(); + assertThat(validateStringFilenameUsingContains(filename)).isTrue(); + assertThat(validateStringFilenameUsingRegex(filename)).isTrue(); + } + + @Test + public void givenTooLongFileNameString_whenValidate_thenIOAndCustomFailsNIO2Succeed() { + String filename = RandomStringUtils.randomAlphabetic(500); + assertThatThrownBy(() -> validateStringFilenameUsingIO(filename)) + .isInstanceOf(IOException.class) + .hasMessageContaining("File name too long"); + assertThat(validateStringFilenameUsingNIO2(filename)).isTrue(); + assertThat(validateStringFilenameUsingContains(filename)).isFalse(); + assertThat(validateStringFilenameUsingRegex(filename)).isFalse(); + } + + @ParameterizedTest + @NullSource + public void givenNullString_whenValidate_thenFails(String filename) { + assertThatThrownBy(() -> validateStringFilenameUsingIO(filename)) + .isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> validateStringFilenameUsingNIO2(filename)) + .isInstanceOf(NullPointerException.class); + assertThat(validateStringFilenameUsingContains(filename)).isFalse(); + assertThat(validateStringFilenameUsingRegex(filename)).isFalse(); + } + + @ParameterizedTest + @EmptySource + public void givenEmptyString_whenValidate_thenIOAndCustomFailsNIO2Succeed(String filename) { + assertThatThrownBy(() -> validateStringFilenameUsingIO(filename)) + .isInstanceOf(IOException.class); + assertThat(validateStringFilenameUsingNIO2(filename)).isTrue(); + assertThat(validateStringFilenameUsingContains(filename)).isFalse(); + assertThat(validateStringFilenameUsingRegex(filename)).isFalse(); + } + + @ParameterizedTest + @EnabledOnOs({OS.LINUX, OS.MAC}) + @MethodSource("filenamesWithInvalidWindowsChars") + public void givenFilenameStringWithInvalidWindowsCharAndIsUnix_whenValidateUsingIO_thenReturnTrue(String filename) throws IOException { + assertThat(validateStringFilenameUsingIO(filename)).isTrue(); + assertThat(validateStringFilenameUsingNIO2(filename)).isTrue(); + assertThat(validateStringFilenameUsingContains(filename)).isTrue(); + } + + @ParameterizedTest + @EnabledOnOs(OS.WINDOWS) + @MethodSource("filenamesWithInvalidWindowsChars") + public void givenFilenameStringWithInvalidWindowsCharAndIsWindows_whenValidateUsingIO_thenRaiseException(String filename) { + assertThatThrownBy(() -> validateStringFilenameUsingIO(filename)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Invalid file path"); + + assertThatThrownBy(() -> validateStringFilenameUsingNIO2(filename)) + .isInstanceOf(InvalidPathException.class) + .hasMessage("character not allowed"); + + assertThat(validateStringFilenameUsingContains(filename)).isFalse(); + } + + @ParameterizedTest + @EnabledOnOs({OS.LINUX, OS.MAC}) + @MethodSource("filenamesWithInvalidUnixChars") + public void givenFilenameStringWithInvalidUnixCharAndIsUnix_whenValidate_thenRaiseException(String filename) { + assertThatThrownBy(() -> validateStringFilenameUsingIO(filename)) + .isInstanceOf(IOException.class) + .hasMessageContaining("Invalid file path"); + + assertThatThrownBy(() -> validateStringFilenameUsingNIO2(filename)) + .isInstanceOf(InvalidPathException.class) + .hasMessageContaining("character not allowed"); + + assertThat(validateStringFilenameUsingContains(filename)).isFalse(); + } + + + private static Stream correctAlphanumericFilenamesProvider() { + return Stream.generate(() -> RandomStringUtils.randomAlphanumeric(1, 10) + "." + RandomStringUtils.randomAlphabetic(3, 5)).limit(10); + } + + private static Stream filenamesWithInvalidWindowsChars() { + return Arrays.stream(StringFilenameValidationUtils.INVALID_WINDOWS_SPECIFIC_CHARS) + .map(character -> { + int idx = RandomUtils.nextInt(0, CORRECT_FILENAME_PATTERN.length()); + return CORRECT_FILENAME_PATTERN.substring(0, idx) + character + CORRECT_FILENAME_PATTERN.substring(idx); + }); + } + + private static Stream filenamesWithInvalidUnixChars() { + return Arrays.stream(StringFilenameValidationUtils.INVALID_UNIX_SPECIFIC_CHARS) + .map(character -> { + int idx = RandomUtils.nextInt(0, CORRECT_FILENAME_PATTERN.length()); + return CORRECT_FILENAME_PATTERN.substring(0, idx) + character + CORRECT_FILENAME_PATTERN.substring(idx); + }); + } + +} diff --git a/core-java-modules/core-java/README.md b/core-java-modules/core-java/README.md index 14857d5d87..f1cf2d8c09 100644 --- a/core-java-modules/core-java/README.md +++ b/core-java-modules/core-java/README.md @@ -12,3 +12,4 @@ - [A Guide to the ResourceBundle](http://www.baeldung.com/java-resourcebundle) - [Merging java.util.Properties Objects](https://www.baeldung.com/java-merging-properties) - [Deserialization Vulnerabilities in Java](https://www.baeldung.com/java-deserialization-vulnerabilities) +- [Generating Alphanumeric UUID String in Java](https://www.baeldung.com/java-generate-alphanumeric-uuid) diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java b/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java index cb43a26929..54de1f95c6 100644 --- a/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java +++ b/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java @@ -1,6 +1,8 @@ package com.baeldung.uuid; import java.io.UnsupportedEncodingException; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Duration; @@ -132,21 +134,21 @@ public class UUIDGenerator { } private static byte[] bytesFromUUID(String uuidHexString) { - String normalizedUUIDHexString = uuidHexString.replace("-",""); + String normalizedUUIDHexString = uuidHexString.replace("-", ""); assert normalizedUUIDHexString.length() == 32; byte[] bytes = new byte[16]; for (int i = 0; i < 16; i++) { - byte b = hexToByte(normalizedUUIDHexString.substring(i*2, i*2+2)); + byte b = hexToByte(normalizedUUIDHexString.substring(i * 2, i * 2 + 2)); bytes[i] = b; } return bytes; } public static byte hexToByte(String hexString) { - int firstDigit = Character.digit(hexString.charAt(0),16); - int secondDigit = Character.digit(hexString.charAt(1),16); + int firstDigit = Character.digit(hexString.charAt(0), 16); + int secondDigit = Character.digit(hexString.charAt(1), 16); return (byte) ((firstDigit << 4) + secondDigit); } @@ -154,14 +156,47 @@ public class UUIDGenerator { int finalLength = byteArray1.length + byteArray2.length; byte[] result = new byte[finalLength]; - for(int i = 0; i < byteArray1.length; i++) { + for (int i = 0; i < byteArray1.length; i++) { result[i] = byteArray1[i]; } - for(int i = 0; i < byteArray2.length; i++) { - result[byteArray1.length+i] = byteArray2[i]; + for (int i = 0; i < byteArray2.length; i++) { + result[byteArray1.length + i] = byteArray2[i]; } return result; } + + public static UUID generateType5UUID(String name) { + + try { + + byte[] bytes = name.getBytes(StandardCharsets.UTF_8); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + byte[] hash = md.digest(bytes); + + long msb = getLeastAndMostSignificantBitsVersion5(hash, 0); + long lsb = getLeastAndMostSignificantBitsVersion5(hash, 8); + // Set the version field + msb &= ~(0xfL << 12); + msb |= ((long) 5) << 12; + // Set the variant field to 2 + lsb &= ~(0x3L << 62); + lsb |= 2L << 62; + return new UUID(msb, lsb); + + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private static long getLeastAndMostSignificantBitsVersion5(final byte[] src, final int offset) { + long ans = 0; + for (int i = offset + 7; i >= offset; i -= 1) { + ans <<= 8; + ans |= src[i] & 0xffL; + } + return ans; + } } \ No newline at end of file diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java b/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java index 9e08363a63..cf0d7656ff 100644 --- a/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java +++ b/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java @@ -61,4 +61,14 @@ class UUIDGeneratorUnitTest { assertEquals(5, uuid.version()); assertEquals(2, uuid.variant()); } + + @Test + public void version_5_UUID_is_correctly_generated_for_domain_baeldung_name() { + + UUID uuid = UUIDGenerator.generateType5UUID("baeldung.com"); + + assertEquals("efd5462b-b07a-52a3-94ea-bf575c0e0e75", uuid.toString()); + assertEquals(5, uuid.version()); + assertEquals(2, uuid.variant()); + } } \ No newline at end of file diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index b801d44a08..13dd20b5da 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -48,7 +48,6 @@ core-java-concurrency-collections core-java-concurrency-collections-2 core-java-console - core-java-8-datetime-2 core-java-date-operations-2 core-java-8-datetime @@ -63,7 +62,6 @@ core-java-io-4 core-java-io-apis core-java-io-conversions - core-java-io-conversions-2 core-java-jar core-java-jndi core-java-jvm @@ -103,6 +101,7 @@ core-java-streams core-java-streams-2 core-java-streams-3 + core-java-streams-4 core-java-string-algorithms core-java-string-algorithms-2 core-java-string-algorithms-3 @@ -111,9 +110,9 @@ core-java-string-conversions-2 core-java-string-operations core-java-string-operations-2 - core-java-string-operations-3 core-java-sun core-java-regex + core-java-regex-2 pre-jpms diff --git a/data-structures/README.md b/data-structures/README.md index 0a29bd8964..cfcfbc6a0a 100644 --- a/data-structures/README.md +++ b/data-structures/README.md @@ -12,3 +12,5 @@ This module contains articles about data structures in Java - [Guide to AVL Trees in Java](https://www.baeldung.com/java-avl-trees) - [Graphs in Java](https://www.baeldung.com/java-graphs) - [Implementing a Ring Buffer in Java](https://www.baeldung.com/java-ring-buffer) +- [How to Implement Min-Max Heap In Java](https://www.baeldung.com/java-min-max-heap) +- [How to Implement LRU Cache in Java](https://www.baeldung.com/java-lru-cache) diff --git a/data-structures/src/main/java/com/baeldung/lrucache/Cache.java b/data-structures/src/main/java/com/baeldung/lrucache/Cache.java new file mode 100644 index 0000000000..f070f66191 --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/lrucache/Cache.java @@ -0,0 +1,15 @@ +package com.baeldung.lrucache; + +import java.util.Optional; + +public interface Cache { + boolean put(K key, V value); + + Optional get(K key); + + int size(); + + boolean isEmpty(); + + void clear(); +} diff --git a/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java b/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java new file mode 100644 index 0000000000..7d260c4531 --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java @@ -0,0 +1,31 @@ +package com.baeldung.lrucache; + +/** + * Created by arash on 09.07.21. + */ + +public class CacheElement { + private K key; + private V value; + + public CacheElement(K key, V value) { + this.value = value; + this.key = key; + } + + public K getKey() { + return key; + } + + public void setKey(K key) { + this.key = key; + } + + public V getValue() { + return value; + } + + public void setValue(V value) { + this.value = value; + } +} diff --git a/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java b/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java new file mode 100644 index 0000000000..5850d6c13d --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java @@ -0,0 +1,163 @@ +package com.baeldung.lrucache; + +import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class DoublyLinkedList { + + private DummyNode dummyNode; + private LinkedListNode head; + private LinkedListNode tail; + private AtomicInteger size; + private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public DoublyLinkedList() { + this.dummyNode = new DummyNode(this); + clear(); + } + + public void clear() { + this.lock.writeLock().lock(); + try { + head = dummyNode; + tail = dummyNode; + size = new AtomicInteger(0); + } finally { + this.lock.writeLock().unlock(); + } + } + + public int size() { + this.lock.readLock().lock(); + try { + return size.get(); + } finally { + this.lock.readLock().unlock(); + } + } + + public boolean isEmpty() { + this.lock.readLock().lock(); + try { + return head.isEmpty(); + } finally { + this.lock.readLock().unlock(); + } + } + + public boolean contains(T value) { + this.lock.readLock().lock(); + try { + return search(value).hasElement(); + } finally { + this.lock.readLock().unlock(); + } + } + + public LinkedListNode search(T value) { + this.lock.readLock().lock(); + try { + return head.search(value); + } finally { + this.lock.readLock().unlock(); + } + } + + public LinkedListNode add(T value) { + this.lock.writeLock().lock(); + try { + head = new Node(value, head, this); + if (tail.isEmpty()) { + tail = head; + } + size.incrementAndGet(); + return head; + } finally { + this.lock.writeLock().unlock(); + } + } + + public boolean addAll(Collection values) { + this.lock.writeLock().lock(); + try { + for (T value : values) { + if (add(value).isEmpty()) { + return false; + } + } + return true; + } finally { + this.lock.writeLock().unlock(); + } + } + + public LinkedListNode remove(T value) { + this.lock.writeLock().lock(); + try { + LinkedListNode linkedListNode = head.search(value); + if (!linkedListNode.isEmpty()) { + if (linkedListNode == tail) { + tail = tail.getPrev(); + } + if (linkedListNode == head) { + head = head.getNext(); + } + linkedListNode.detach(); + size.decrementAndGet(); + } + return linkedListNode; + } finally { + this.lock.writeLock().unlock(); + } + } + + public LinkedListNode removeTail() { + this.lock.writeLock().lock(); + try { + LinkedListNode oldTail = tail; + if (oldTail == head) { + tail = head = dummyNode; + } else { + tail = tail.getPrev(); + oldTail.detach(); + } + if (!oldTail.isEmpty()) { + size.decrementAndGet(); + } + return oldTail; + } finally { + this.lock.writeLock().unlock(); + } + } + + public LinkedListNode moveToFront(LinkedListNode node) { + return node.isEmpty() ? dummyNode : updateAndMoveToFront(node, node.getElement()); + } + + public LinkedListNode updateAndMoveToFront(LinkedListNode node, T newValue) { + this.lock.writeLock().lock(); + try { + if (node.isEmpty() || (this != (node.getListReference()))) { + return dummyNode; + } + detach(node); + add(newValue); + return head; + } finally { + this.lock.writeLock().unlock(); + } + } + + private void detach(LinkedListNode node) { + if (node != tail) { + node.detach(); + if (node == head) { + head = head.getNext(); + } + size.decrementAndGet(); + } else { + removeTail(); + } + } +} diff --git a/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java b/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java new file mode 100644 index 0000000000..4965b70bcf --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java @@ -0,0 +1,62 @@ +package com.baeldung.lrucache; + +/** + * Created by arash on 09.07.21. + */ +public class DummyNode implements LinkedListNode { + private DoublyLinkedList list; + + public DummyNode(DoublyLinkedList list) { + this.list = list; + } + + @Override + public boolean hasElement() { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public T getElement() throws NullPointerException { + throw new NullPointerException(); + } + + @Override + public void detach() { + return; + } + + @Override + public DoublyLinkedList getListReference() { + return list; + } + + @Override + public LinkedListNode setPrev(LinkedListNode next) { + return next; + } + + @Override + public LinkedListNode setNext(LinkedListNode prev) { + return prev; + } + + @Override + public LinkedListNode getPrev() { + return this; + } + + @Override + public LinkedListNode getNext() { + return this; + } + + @Override + public LinkedListNode search(T value) { + return this; + } +} diff --git a/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java b/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java new file mode 100644 index 0000000000..3032a90d8e --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java @@ -0,0 +1,100 @@ +package com.baeldung.lrucache; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class LRUCache implements Cache { + private int size; + private Map>> linkedListNodeMap; + private DoublyLinkedList> doublyLinkedList; + private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public LRUCache(int size) { + this.size = size; + this.linkedListNodeMap = new ConcurrentHashMap<>(size); + this.doublyLinkedList = new DoublyLinkedList<>(); + } + + @Override + public boolean put(K key, V value) { + this.lock.writeLock().lock(); + try { + CacheElement item = new CacheElement(key, value); + LinkedListNode> newNode; + if (this.linkedListNodeMap.containsKey(key)) { + LinkedListNode> node = this.linkedListNodeMap.get(key); + newNode = doublyLinkedList.updateAndMoveToFront(node, item); + } else { + if (this.size() >= this.size) { + this.evictElement(); + } + newNode = this.doublyLinkedList.add(item); + } + if (newNode.isEmpty()) { + return false; + } + this.linkedListNodeMap.put(key, newNode); + return true; + } finally { + this.lock.writeLock().unlock(); + } + } + + @Override + public Optional get(K key) { + this.lock.readLock().lock(); + try { + LinkedListNode> linkedListNode = this.linkedListNodeMap.get(key); + if (linkedListNode != null && !linkedListNode.isEmpty()) { + linkedListNodeMap.put(key, this.doublyLinkedList.moveToFront(linkedListNode)); + return Optional.of(linkedListNode.getElement().getValue()); + } + return Optional.empty(); + } finally { + this.lock.readLock().unlock(); + } + } + + @Override + public int size() { + this.lock.readLock().lock(); + try { + return doublyLinkedList.size(); + } finally { + this.lock.readLock().unlock(); + } + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public void clear() { + this.lock.writeLock().lock(); + try { + linkedListNodeMap.clear(); + doublyLinkedList.clear(); + } finally { + this.lock.writeLock().unlock(); + } + } + + + private boolean evictElement() { + this.lock.writeLock().lock(); + try { + LinkedListNode> linkedListNode = doublyLinkedList.removeTail(); + if (linkedListNode.isEmpty()) { + return false; + } + linkedListNodeMap.remove(linkedListNode.getElement().getKey()); + return true; + } finally { + this.lock.writeLock().unlock(); + } + } +} diff --git a/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java b/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java new file mode 100644 index 0000000000..219ee13496 --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java @@ -0,0 +1,23 @@ +package com.baeldung.lrucache; + +public interface LinkedListNode { + boolean hasElement(); + + boolean isEmpty(); + + V getElement() throws NullPointerException; + + void detach(); + + DoublyLinkedList getListReference(); + + LinkedListNode setPrev(LinkedListNode prev); + + LinkedListNode setNext(LinkedListNode next); + + LinkedListNode getPrev(); + + LinkedListNode getNext(); + + LinkedListNode search(V value); +} diff --git a/data-structures/src/main/java/com/baeldung/lrucache/Node.java b/data-structures/src/main/java/com/baeldung/lrucache/Node.java new file mode 100644 index 0000000000..340bb1dd82 --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/lrucache/Node.java @@ -0,0 +1,71 @@ +package com.baeldung.lrucache; + +/** + * Created by arash on 09.07.21. + */ +public class Node implements LinkedListNode { + private T value; + private DoublyLinkedList list; + private LinkedListNode next; + private LinkedListNode prev; + + public Node(T value, LinkedListNode next, DoublyLinkedList list) { + this.value = value; + this.next = next; + this.setPrev(next.getPrev()); + this.prev.setNext(this); + this.next.setPrev(this); + this.list = list; + } + + @Override + public boolean hasElement() { + return true; + } + + @Override + public boolean isEmpty() { + return false; + } + + public T getElement() { + return value; + } + + public void detach() { + this.prev.setNext(this.getNext()); + this.next.setPrev(this.getPrev()); + } + + @Override + public DoublyLinkedList getListReference() { + return this.list; + } + + @Override + public LinkedListNode setPrev(LinkedListNode prev) { + this.prev = prev; + return this; + } + + @Override + public LinkedListNode setNext(LinkedListNode next) { + this.next = next; + return this; + } + + @Override + public LinkedListNode getPrev() { + return this.prev; + } + + @Override + public LinkedListNode getNext() { + return this.next; + } + + @Override + public LinkedListNode search(T value) { + return this.getElement() == value ? this : this.getNext().search(value); + } +} diff --git a/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java b/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java new file mode 100644 index 0000000000..124183afe6 --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java @@ -0,0 +1,344 @@ +package com.baeldung.minmaxheap; + +import java.util.*; + +/** + * Created by arash on 15.06.21. + */ + +public class MinMaxHeap> { + private List array; + private int capacity; + private int indicator; + + MinMaxHeap(int capacity) { + array = new ArrayList<>(); + this.capacity = capacity; + indicator = 1; + } + + MinMaxHeap(List array) { + this.array = array; + this.capacity = array.size(); + this.indicator = array.size() + 1; + } + + public List getMinMaxHeap() { + return array; + } + + public List create() { + for (int i = Math.floorDiv(array.size(), 2); i >= 1; i--) { + pushDown(array, i); + } + return array; + } + + private void pushDown(List array, int i) { + if (isEvenLevel(i)) { + pushDownMin(array, i); + } else { + pushDownMax(array, i); + } + } + + private void pushDownMin(List h, int i) { + while (getLeftChildIndex(i) < indicator) { + int indexOfSmallest = getIndexOfSmallestChildOrGrandChild(h, i); + if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) { + if (getParentIndex(getParentIndex(indexOfSmallest)) == i) { + if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) { + swap(indexOfSmallest - 1, i - 1, h); + if (h.get(indexOfSmallest - 1).compareTo(h.get(getParentIndex(indexOfSmallest) - 1)) > 0) { + swap(indexOfSmallest - 1, getParentIndex(indexOfSmallest) - 1, h); + } + } + } else if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) { + swap(indexOfSmallest - 1, i - 1, h); + } + } else { + break; + } + i = indexOfSmallest; + } + } + + private void pushDownMax(List h, int i) { + while (getLeftChildIndex(i) < indicator) { + int indexOfGreatest = getIndexOfGreatestChildOrGrandChild(h, i); + if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) { + if (getParentIndex(getParentIndex(indexOfGreatest)) == i) { + if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) { + swap(indexOfGreatest - 1, i - 1, h); + if (h.get(indexOfGreatest - 1).compareTo(h.get(getParentIndex(indexOfGreatest) - 1)) < 0) { + swap(indexOfGreatest - 1, getParentIndex(indexOfGreatest) - 1, h); + } + } + } else if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) { + swap(indexOfGreatest - 1, i - 1, h); + } + } else { + break; + } + i = indexOfGreatest; + } + } + + private void swap(int i, int j, List h) { //switch data at x with data at y + T temp = h.get(i); + h.set(i, h.get(j)); + h.set(j, temp); + } + + private int getLeftChildIndex(int i) { + return 2 * i; + } + + private int getRightChildIndex(int i) { + return ((2 * i) + 1); + } + + private int getParentIndex(int i) { + return i / 2; + } + + private int getGrandparentIndex(int i) { + return i / 4; + } + + private boolean isEvenLevel(int i) { + return logBase2(i) % 2 == 0; + } + + private int logBase2(int num) { + return (int) (Math.log(num) / Math.log(2)); + } + + private int getMinChildIndex(int i) { + return array.get(getLeftChildIndex(i)).compareTo(array.get(getRightChildIndex(i))) < 0 ? getLeftChildIndex(i) : getRightChildIndex(i); + } + + private int getMaxChildIndex(int i) { + return array.get(getLeftChildIndex(i)).compareTo(array.get(getRightChildIndex(i))) > 0 ? getLeftChildIndex(i) : getRightChildIndex(i); + } + + private int getIndexOfSmallestChildOrGrandChild(List h, int i) { + int minIndex = getLeftChildIndex(i); + T minValue = h.get(minIndex - 1); + + if (getRightChildIndex(i) < indicator) { + if (h.get(getRightChildIndex(i) - 1).compareTo(minValue) < 0) { + minValue = h.get(getRightChildIndex(i)); + minIndex = getRightChildIndex(i); + } + } else { + return minIndex; + } + + if (getLeftChildIndex(getLeftChildIndex(i)) < indicator) { + if (h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1).compareTo(minValue) < 0) { + minValue = h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1); + minIndex = getLeftChildIndex(getLeftChildIndex(i)); + } + } else { + return minIndex; //if no leftmost grandchild + } + + if (getRightChildIndex(getLeftChildIndex(i)) < indicator) { + if (h.get(getRightChildIndex(getLeftChildIndex(i)) - 1).compareTo(minValue) < 0) { + minValue = h.get(getRightChildIndex(getLeftChildIndex(i)) - 1); + minIndex = getRightChildIndex(getLeftChildIndex(i)); + } + } else { + return minIndex; //if no left-right grandchild + } + + if (getLeftChildIndex(getRightChildIndex(i)) < indicator) { + if (h.get(getLeftChildIndex(getRightChildIndex(i)) - 1).compareTo(minValue) < 0) { + minValue = h.get(getLeftChildIndex(getRightChildIndex(i)) - 1); + minIndex = getLeftChildIndex(getRightChildIndex(i)); + } + } else { + return minIndex; //if no right-left grandchild + } + + if (getRightChildIndex(getRightChildIndex(i)) < indicator) { + if (h.get(getRightChildIndex(getRightChildIndex(i)) - 1).compareTo(minValue) < 0) { + minValue = h.get(getRightChildIndex(getRightChildIndex(i)) - 1); + minIndex = getRightChildIndex(getRightChildIndex(i)); + } + } else { + return minIndex; + } + + return minIndex; + } + + private int getIndexOfGreatestChildOrGrandChild(List h, int i) { + int maxIndex = getLeftChildIndex(i); //we know left child exists + T maxValue = h.get(maxIndex - 1); + + if (getRightChildIndex(i) < indicator) { + if (h.get(getRightChildIndex(i) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getRightChildIndex(i) - 1); + maxIndex = getRightChildIndex(i); + } + } else { + return maxIndex; + } + + if (getLeftChildIndex(getLeftChildIndex(i)) < indicator) { + if (h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1); + maxIndex = getLeftChildIndex(getLeftChildIndex(i)); + } + } else { + return maxIndex; //if no leftmost grandchild + } + + if (getRightChildIndex(getLeftChildIndex(i)) < indicator) { + if (h.get(getRightChildIndex(getLeftChildIndex(i)) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getRightChildIndex(getLeftChildIndex(i)) - 1); + maxIndex = getRightChildIndex(getLeftChildIndex(i)); + } + } else { + return maxIndex; //if no left-right grandchild + } + + if (getLeftChildIndex(getRightChildIndex(i)) < indicator) { + if (h.get(getLeftChildIndex(getRightChildIndex(i)) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getLeftChildIndex(getRightChildIndex(i)) - 1); + maxIndex = getLeftChildIndex(getRightChildIndex(i)); + } + } else { + return maxIndex; + } + + if (getRightChildIndex(getRightChildIndex(i)) < indicator) { + if (h.get(getRightChildIndex(getRightChildIndex(i)) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getRightChildIndex(getRightChildIndex(i)) - 1); + maxIndex = getRightChildIndex(getRightChildIndex(i)); + } + } else { + return maxIndex; + } + + return maxIndex; + } + + + public boolean isFull() { + return indicator == this.capacity + 1; + } + + public boolean isEmpty() { + return indicator == 1; + } + + public void insert(T item) { + if (isEmpty()) { + array.add(item); + indicator++; + } else if (!isFull()) { + array.add(item); + pushUp(array, indicator); + indicator++; + } else { + throw new RuntimeException("invalid operation !!!"); + } + } + + private void pushUpMin(List h, int i) { + while (hasGrandparent(i) && h.get(i - 1).compareTo(h.get(getGrandparentIndex(i) - 1)) < 0) { + swap(i - 1, getGrandparentIndex(i) - 1, h); + i = getGrandparentIndex(i); + } + } + + private void pushUpMax(List h, int i) { + while (hasGrandparent(i) && h.get(i - 1).compareTo(h.get(getGrandparentIndex(i) - 1)) > 0) { + swap(i - 1, getGrandparentIndex(i) - 1, h); + i = getGrandparentIndex(i); + } + } + + private boolean hasGrandparent(int i) { + return getParentIndex(i) > 1; + } + + private void pushUp(List h, int i) { + if (i != 1) { + if (isEvenLevel(i)) { + if (h.get(i - 1).compareTo(h.get(getParentIndex(i) - 1)) < 0) { + pushUpMin(h, i); + } else { + swap(i - 1, getParentIndex(i) - 1, h); + i = getParentIndex(i); + pushUpMax(h, i); + } + } else if (h.get(i - 1).compareTo(h.get(getParentIndex(i) - 1)) > 0) { + pushUpMax(h, i); + } else { + swap(i - 1, getParentIndex(i) - 1, h); + i = getParentIndex(i); + pushUpMin(h, i); + } + } + } + + public T min() { + if (!isEmpty()) { + return array.get(0); + } + return null; + } + + public T max() { + if (!isEmpty()) { + if (indicator == 2) { + return array.get(0); + } + if (indicator == 3) { + return array.get(1); + } + return array.get(1).compareTo(array.get(2)) < 0 ? array.get(2) : array.get(1); + } + return null; + } + + public T removeMin() { + T min = min(); + if (min != null) { + if (indicator == 2) { + array.remove(indicator--); + return min; + } + array.set(0, array.get(--indicator - 1)); + array.remove(indicator - 1); + pushDown(array, 1); + } + return min; + } + + public T removeMax() { + T max = max(); + if (max != null) { + int maxIndex; + if (indicator == 2) { + maxIndex = 0; + array.remove(--indicator - 1); + return max; + } else if (indicator == 3) { + maxIndex = 1; + array.remove(--indicator - 1); + return max; + } else { + maxIndex = array.get(1).compareTo(array.get(2)) < 0 ? 2 : 1; + } + array.set(maxIndex, array.get(--indicator - 1)); + array.remove(indicator - 1); + pushDown(array, maxIndex + 1); + } + return max; + } +} diff --git a/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java b/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java new file mode 100644 index 0000000000..e64085ae4c --- /dev/null +++ b/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.lrucache; + +import org.junit.Test; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.IntStream; +import static org.junit.Assert.*; + +public class LRUCacheUnitTest { + + @Test + public void addSomeDataToCache_WhenGetData_ThenIsEqualWithCacheElement() { + LRUCache lruCache = new LRUCache<>(3); + lruCache.put("1", "test1"); + lruCache.put("2", "test2"); + lruCache.put("3", "test3"); + assertEquals("test1", lruCache.get("1").get()); + assertEquals("test2", lruCache.get("2").get()); + assertEquals("test3", lruCache.get("3").get()); + } + + @Test + public void addDataToCacheToTheNumberOfSize_WhenAddOneMoreData_ThenLeastRecentlyDataWillEvict() { + LRUCache lruCache = new LRUCache<>(3); + lruCache.put("1", "test1"); + lruCache.put("2", "test2"); + lruCache.put("3", "test3"); + lruCache.put("4", "test4"); + assertFalse(lruCache.get("1").isPresent()); + } + + @Test + public void runMultiThreadTask_WhenPutDataInConcurrentToCache_ThenNoDataLost() throws Exception { + final int size = 50; + final ExecutorService executorService = Executors.newFixedThreadPool(5); + Cache cache = new LRUCache<>(size); + CountDownLatch countDownLatch = new CountDownLatch(size); + try { + IntStream.range(0, size).mapToObj(key -> () -> { + cache.put(key, "value" + key); + countDownLatch.countDown(); + }).forEach(executorService::submit); + countDownLatch.await(); + } finally { + executorService.shutdown(); + } + assertEquals(cache.size(), size); + IntStream.range(0, size).forEach(i -> assertEquals("value" + i, cache.get(i).get())); + } +} diff --git a/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java b/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java new file mode 100644 index 0000000000..1682db63be --- /dev/null +++ b/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java @@ -0,0 +1,32 @@ +package com.baeldung.minmaxheap; + +import org.junit.Assert; +import org.junit.Test; +import java.util.Arrays; +import java.util.List; + + +public class MinMaxHeapUnitTest { + + @Test + public void givenUnOrderedArray_WhenCreateMinMaxHeap_ThenIsEqualWithMinMaxHeapOrdered() { + List list = Arrays.asList(34, 12, 28, 9, 30, 19, 1, 40); + MinMaxHeap minMaxHeap = new MinMaxHeap<>(list); + minMaxHeap.create(); + Assert.assertEquals(Arrays.asList(1, 40, 34, 9, 30, 19, 28, 12), list); + } + + @Test + public void givenNewElement_WhenInserted_ThenIsEqualWithMinMaxHeapOrdered() { + MinMaxHeap minMaxHeap = new MinMaxHeap(8); + minMaxHeap.insert(34); + minMaxHeap.insert(12); + minMaxHeap.insert(28); + minMaxHeap.insert(9); + minMaxHeap.insert(30); + minMaxHeap.insert(19); + minMaxHeap.insert(1); + minMaxHeap.insert(40); + Assert.assertEquals(Arrays.asList(1, 40, 28, 12, 30, 19, 9, 34), minMaxHeap.getMinMaxHeap()); + } +} diff --git a/ddd/pom.xml b/ddd/pom.xml index 77cf1f5341..cc5a173e85 100644 --- a/ddd/pom.xml +++ b/ddd/pom.xml @@ -101,4 +101,5 @@ 1.0.1 + \ No newline at end of file diff --git a/docker/heap-sizing/pom.xml b/docker/heap-sizing/pom.xml index a86a67fdcd..2cc354f6cf 100644 --- a/docker/heap-sizing/pom.xml +++ b/docker/heap-sizing/pom.xml @@ -1,21 +1,21 @@ - + 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.4.2 - - com.baeldung.docker heap-sizing 0.0.1-SNAPSHOT heap-sizing Demo project for Spring Boot - - 11 - + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + org.springframework.boot @@ -58,4 +58,8 @@ + + 11 + + diff --git a/flyway-cdi-extension/pom.xml b/flyway-cdi-extension/pom.xml index 757d1e0a7f..dbd21374e2 100644 --- a/flyway-cdi-extension/pom.xml +++ b/flyway-cdi-extension/pom.xml @@ -56,4 +56,5 @@ 8.5.33 1.3.2 + \ No newline at end of file diff --git a/geotools/pom.xml b/geotools/pom.xml index 46913daa69..b9a6a7c91f 100644 --- a/geotools/pom.xml +++ b/geotools/pom.xml @@ -1,6 +1,5 @@ - 4.0.0 @@ -36,9 +35,9 @@ - osgeo - Open Source Geospatial Foundation Repository - http://download.osgeo.org/webdav/geotools/ + osgeo-release + OSGeo Repository + https://repo.osgeo.org/repository/release/ @@ -48,4 +47,4 @@ 15.2 - + \ No newline at end of file diff --git a/grpc/pom.xml b/grpc/pom.xml index c3e4996c29..915777f3bd 100644 --- a/grpc/pom.xml +++ b/grpc/pom.xml @@ -77,4 +77,5 @@ 1.6.1 0.6.1 + \ No newline at end of file diff --git a/guava-modules/guava-concurrency/README.md b/guava-modules/guava-concurrency/README.md new file mode 100644 index 0000000000..12fca9a1a5 --- /dev/null +++ b/guava-modules/guava-concurrency/README.md @@ -0,0 +1,3 @@ +## Relevant Articles: + +- [Guava’s Futures and ListenableFuture](https://www.baeldung.com/guava-futures-listenablefuture) diff --git a/guava-modules/guava-concurrency/pom.xml b/guava-modules/guava-concurrency/pom.xml new file mode 100644 index 0000000000..a9ff19e6bd --- /dev/null +++ b/guava-modules/guava-concurrency/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + guava-concurrency + + + guava-modules + com.baeldung + 0.0.1-SNAPSHOT + + + \ No newline at end of file diff --git a/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java new file mode 100644 index 0000000000..b6620bd1e2 --- /dev/null +++ b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java @@ -0,0 +1,105 @@ +package com.baeldung.guava.future; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import com.baeldung.guava.future.exception.ListenableFutureException; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListenableFutureTask; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +public class ListenableFutureService { + + private final ListeningExecutorService lExecService; + + public ListenableFutureService() { + this.lExecService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + } + + public ListenableFutureService(ListeningExecutorService lExecService) { + this.lExecService = lExecService; + } + + public ListenableFuture fetchConfig(String configKey) { + return lExecService.submit(() -> { + TimeUnit.MILLISECONDS.sleep(500); + return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE)); + }); + } + + public FutureTask fetchConfigTask(String configKey) { + return new FutureTask<>(() -> { + TimeUnit.MILLISECONDS.sleep(500); + return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE)); + }); + } + + public ListenableFutureTask fetchConfigListenableTask(String configKey) { + return ListenableFutureTask.create(() -> { + TimeUnit.MILLISECONDS.sleep(500); + return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE)); + }); + } + + public ListenableFuture succeedingTask() { + return Futures.immediateFuture(new Random().nextInt(Integer.MAX_VALUE)); + } + + public ListenableFuture failingTask() { + return Futures.immediateFailedFuture(new ListenableFutureException()); + } + + public ListenableFuture getCartId() { + return lExecService.submit(() -> { + TimeUnit.MILLISECONDS.sleep(500); + return new Random().nextInt(Integer.MAX_VALUE); + }); + } + + public ListenableFuture getCustomerName() { + String[] names = new String[] { "Mark", "Jane", "June" }; + return lExecService.submit(() -> { + TimeUnit.MILLISECONDS.sleep(500); + return names[new Random().nextInt(names.length)]; + }); + } + + public ListenableFuture> getCartItems() { + String[] items = new String[] { "Apple", "Orange", "Mango", "Pineapple" }; + return lExecService.submit(() -> { + TimeUnit.MILLISECONDS.sleep(500); + + int noOfItems = new Random().nextInt(items.length); + if (noOfItems == 0) ++noOfItems; + + return Arrays.stream(items, 0, noOfItems).collect(Collectors.toList()); + }); + } + + public ListenableFuture generateUsername(String firstName) { + return lExecService.submit(() -> { + TimeUnit.MILLISECONDS.sleep(500); + return firstName.replaceAll("[^a-zA-Z]+","") + .concat("@service.com"); + }); + } + + public ListenableFuture generatePassword(String username) { + return lExecService.submit(() -> { + TimeUnit.MILLISECONDS.sleep(500); + if (username.contains("@")) { + String[] parts = username.split("@"); + return parts[0] + "123@" + parts[1]; + } else { + return username + "123"; + } + }); + } +} \ No newline at end of file diff --git a/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java new file mode 100644 index 0000000000..921c02b54a --- /dev/null +++ b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java @@ -0,0 +1,4 @@ +package com.baeldung.guava.future.exception; + +public class ListenableFutureException extends Exception { +} diff --git a/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java new file mode 100644 index 0000000000..27a1cc6592 --- /dev/null +++ b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java @@ -0,0 +1,277 @@ +package com.baeldung.guava.future; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.UnaryOperator; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.junit.jupiter.api.Test; + +import com.baeldung.guava.future.exception.ListenableFutureException; +import com.google.common.base.Function; +import com.google.common.util.concurrent.AsyncCallable; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +public class ListenableFutureComplexUnitTest { + + @Test + public void givenAllSucceedingTasks_whenAllAsList_thenAllSuccess() { + final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService(); + final ListenableFutureService service = new ListenableFutureService(listeningExecService); + + ListenableFuture task1 = service.fetchConfig("config.0"); + ListenableFuture task2 = service.fetchConfig("config.1"); + ListenableFuture task3 = service.fetchConfig("config.2"); + + ListenableFuture> configsTask = Futures.allAsList(task1, task2, task3); + Futures.addCallback(configsTask, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List configResults) { + assertNotNull(configResults); + assertEquals(3, configResults.size()); + for (int i = 0; i < 3; i++) { + assertTrue(configResults.get(i) + .contains("config." + i)); + } + } + + @Override + public void onFailure(Throwable t) { + fail("Unexpected failure detected", t); + } + }, listeningExecService); + } + + @Test + public void givenOneFailingTask_whenAllAsList_thenFailure() { + final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService(); + final ListenableFutureService service = new ListenableFutureService(listeningExecService); + + ListenableFuture task1 = service.fetchConfig("config.0"); + ListenableFuture task2 = service.failingTask(); + ListenableFuture task3 = service.fetchConfig("config.2"); + + ListenableFuture> configsTask = Futures.allAsList(task1, task2, task3); + Futures.addCallback(configsTask, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List configResults) { + fail("Expected a failed future"); + } + + @Override + public void onFailure(Throwable t) { + assertTrue(t instanceof ListenableFutureException); + } + }, listeningExecService); + } + + @Test + public void givenOneFailingTask_whenSuccessfulAsList_thenSomeSuccess() { + final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService(); + final ListenableFutureService service = new ListenableFutureService(listeningExecService); + + ListenableFuture task1 = service.fetchConfig("config.0"); + ListenableFuture task2 = service.failingTask(); + ListenableFuture task3 = service.fetchConfig("config.2"); + + ListenableFuture> configsTask = Futures.successfulAsList(task1, task2, task3); + + Futures.addCallback(configsTask, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List configResults) { + assertNotNull(configResults); + assertTrue(configResults.get(0).contains("config.0")); + assertNull(configResults.get(1)); + assertTrue(configResults.get(2).contains("config.2")); + } + + @Override + public void onFailure(Throwable t) { + fail("Unexpected failure detected", t); + } + }, listeningExecService); + } + + @Test + public void givenAllSucceedingTasks_whenAllSucceed_thenSuccess() { + ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService(); + ListenableFutureService service = new ListenableFutureService(listeningExecService); + + ListenableFuture cartIdTask = service.getCartId(); + ListenableFuture customerNameTask = service.getCustomerName(); + ListenableFuture> cartItemsTask = service.getCartItems(); + + ListenableFuture cartInfoTask = Futures.whenAllSucceed(cartIdTask, customerNameTask, cartItemsTask) + .call(() -> { + int cartId = Futures.getDone(cartIdTask); + String customerName = Futures.getDone(customerNameTask); + List cartItems = Futures.getDone(cartItemsTask); + return new CartInfo(cartId, customerName, cartItems); + }, listeningExecService); + + Futures.addCallback(cartInfoTask, new FutureCallback() { + @Override + public void onSuccess(@Nullable CartInfo result) { + assertNotNull(result); + assertTrue(result.cartId >= 0); + assertFalse(result.customerName.isEmpty()); + assertFalse(result.cartItems.isEmpty()); + } + + @Override + public void onFailure(Throwable t) { + fail("Unexpected failure detected", t); + } + }, listeningExecService); + } + + @Test + public void givenAllSucceedingTasks_whenAllComplete_thenSomeSuccess() { + ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService(); + ListenableFutureService service = new ListenableFutureService(listeningExecService); + + ListenableFuture cartIdTask = service.getCartId(); + ListenableFuture customerNameTask = service.failingTask(); + ListenableFuture> cartItemsTask = service.getCartItems(); + + ListenableFuture cartInfoTask = Futures.whenAllComplete(cartIdTask, customerNameTask, cartItemsTask) + .call(() -> { + Integer cartId = getOrNull(cartIdTask); + String customerName = getOrNull(customerNameTask); + List cartItems = getOrNull(cartItemsTask); + return new CartInfo(cartId, customerName, cartItems); + }, listeningExecService); + + Futures.addCallback(cartInfoTask, new FutureCallback() { + @Override + public void onSuccess(@Nullable CartInfo result) { + assertNotNull(result); + assertTrue(result.cartId >= 0); + assertNull(result.customerName); + assertFalse(result.cartItems.isEmpty()); + } + + @Override + public void onFailure(Throwable t) { + fail("Unexpected failure detected", t); + } + }, listeningExecService); + } + + @Test + public void whenTransform_thenTransformSuccess() { + ListeningExecutorService listenExecService = MoreExecutors.newDirectExecutorService(); + ListenableFutureService service = new ListenableFutureService(listenExecService); + + ListenableFuture> cartItemsTask = service.getCartItems(); + + Function, Integer> itemCountFunc = cartItems -> { + assertNotNull(cartItems); + return cartItems.size(); + }; + + ListenableFuture itemCountTask = Futures.transform(cartItemsTask, itemCountFunc, listenExecService); + + Futures.addCallback(itemCountTask, new FutureCallback() { + @Override + public void onSuccess(@Nullable Integer cartItemCount) { + assertNotNull(cartItemCount); + assertTrue(cartItemCount > 0); + } + + @Override + public void onFailure(Throwable t) { + fail("Unexpected failure detected", t); + } + }, listenExecService); + } + + @Test + public void whenSubmitAsync_thenSuccess() { + ListeningExecutorService executor = MoreExecutors.newDirectExecutorService(); + ListenableFutureService service = new ListenableFutureService(executor); + + AsyncCallable asyncConfigTask = () -> { + ListenableFuture configTask = service.fetchConfig("config.a"); + TimeUnit.MILLISECONDS.sleep(500); //some long running task + return configTask; + }; + + ListenableFuture configTask = Futures.submitAsync(asyncConfigTask, executor); + + Futures.addCallback(configTask, new FutureCallback() { + @Override + public void onSuccess(@Nullable String result) { + assertNotNull(result); + assertTrue(result.contains("config.a")); + } + + @Override + public void onFailure(Throwable t) { + fail("Unexpected failure detected", t); + } + }, executor); + } + + @Test + public void whenAsyncTransform_thenSuccess() { + ListeningExecutorService executor = MoreExecutors.newDirectExecutorService(); + ListenableFutureService service = new ListenableFutureService(executor); + + ListenableFuture usernameTask = service.generateUsername("john"); + AsyncFunction passwordFunc = username -> { + ListenableFuture generatePasswordTask = service.generatePassword(username); + TimeUnit.MILLISECONDS.sleep(500); // some long running task + return generatePasswordTask; + }; + + ListenableFuture passwordTask = Futures.transformAsync(usernameTask, passwordFunc, executor); + + Futures.addCallback(passwordTask, new FutureCallback() { + @Override + public void onSuccess(@Nullable String password) { + assertNotNull(password); + assertTrue(password.contains("john")); + assertTrue(password.contains("@")); + } + + @Override + public void onFailure(Throwable t) { + fail("Unexpected failure detected", t); + } + }, executor); + } + + private static T getOrNull(ListenableFuture future) { + try { + return Futures.getDone(future); + } catch (ExecutionException e) { + return null; + } + } + + static class CartInfo { + Integer cartId; + String customerName; + List cartItems; + + public CartInfo(Integer cartId, String customerName, List cartItems) { + this.cartId = cartId; + this.customerName = customerName; + this.cartItems = cartItems; + } + } +} \ No newline at end of file diff --git a/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java new file mode 100644 index 0000000000..7dce11a33f --- /dev/null +++ b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java @@ -0,0 +1,122 @@ +package com.baeldung.guava.future; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +import com.baeldung.guava.future.exception.ListenableFutureException; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListenableFutureTask; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +public class ListenableFutureSimpleUnitTest { + + @Test + public void whenSubmitToListeningExecutor_thenSuccess() throws ExecutionException, InterruptedException { + ExecutorService execService = Executors.newSingleThreadExecutor(); + ListeningExecutorService listeningExecService = MoreExecutors.listeningDecorator(execService); + + ListenableFuture asyncTask = listeningExecService.submit(() -> { + TimeUnit.MILLISECONDS.sleep(500); // long running task + return 5; + }); + + assertEquals(5, asyncTask.get()); + } + + @Test + public void + givenJavaExecutor_whenSubmitListeningTask_thenSuccess() throws ExecutionException, InterruptedException { + Executor executor = Executors.newSingleThreadExecutor(); + ListenableFutureService service = new ListenableFutureService(); + + FutureTask configFuture = service.fetchConfigTask("future.value"); + executor.execute(configFuture); + assertTrue(configFuture.get().contains("future.value")); + + ListenableFutureTask configListenableFuture = + service.fetchConfigListenableTask("listenable.value"); + executor.execute(configListenableFuture); + assertTrue(configListenableFuture.get().contains("listenable.value")); + } + + @Test + public void givenNonFailingTask_whenCallbackListen_thenSuccess() { + Executor listeningExecutor = MoreExecutors.directExecutor(); + + ListenableFuture succeedingTask = new ListenableFutureService().succeedingTask(); + Futures.addCallback(succeedingTask, new FutureCallback() { + @Override + public void onSuccess(Integer result) { + assertNotNull(result); + assertTrue(result >= 0); + } + + @Override + public void onFailure(Throwable t) { + fail("Succeeding task cannot failed", t); + } + }, listeningExecutor); + } + + @Test + public void givenFailingTask_whenCallbackListen_thenThrows() { + Executor listeningExecutor = MoreExecutors.directExecutor(); + + ListenableFuture failingTask = new ListenableFutureService().failingTask(); + Futures.addCallback(failingTask, new FutureCallback() { + @Override + public void onSuccess(Integer result) { + fail("Failing task cannot succeed"); + } + + @Override + public void onFailure(Throwable t) { + assertTrue(t instanceof ListenableFutureException); + } + }, listeningExecutor); + } + + @Test + public void givenNonFailingTask_whenDirectListen_thenListenerExecutes() { + Executor listeningExecutor = MoreExecutors.directExecutor(); + + int nextTask = 1; + Set runningTasks = ConcurrentHashMap.newKeySet(); + runningTasks.add(nextTask); + + ListenableFuture nonFailingTask = new ListenableFutureService().succeedingTask(); + nonFailingTask.addListener(() -> runningTasks.remove(nextTask), listeningExecutor); + + assertTrue(runningTasks.isEmpty()); + } + + @Test + public void givenFailingTask_whenDirectListen_thenListenerExecutes() { + final Executor listeningExecutor = MoreExecutors.directExecutor(); + + int nextTask = 1; + Set runningTasks = ConcurrentHashMap.newKeySet(); + runningTasks.add(nextTask); + + final ListenableFuture failingTask = new ListenableFutureService().failingTask(); + failingTask.addListener(() -> runningTasks.remove(nextTask),listeningExecutor); + + assertTrue(runningTasks.isEmpty()); + } +} \ No newline at end of file diff --git a/guava-modules/pom.xml b/guava-modules/pom.xml index 957b8ad166..8ffac98b51 100644 --- a/guava-modules/pom.xml +++ b/guava-modules/pom.xml @@ -24,6 +24,7 @@ guava-collections-list guava-collections-map guava-collections-set + guava-concurrency guava-io diff --git a/httpclient-2/README.md b/httpclient-2/README.md index 9d7a9683cd..7c2d5862bd 100644 --- a/httpclient-2/README.md +++ b/httpclient-2/README.md @@ -11,4 +11,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [How to Set TLS Version in Apache HttpClient](https://www.baeldung.com/apache-httpclient-tls) - [Reading an HTTP Response Body as a String in Java](https://www.baeldung.com/java-http-response-body-as-string) - [How To Get Cookies From the Apache HttpClient Response](https://www.baeldung.com/java-apache-httpclient-cookies) +- [Enabling Logging for Apache HttpClient](https://www.baeldung.com/java-httpclient-enable-logging) - More articles: [[<-- prev]](../httpclient) diff --git a/httpclient-2/pom.xml b/httpclient-2/pom.xml index 881b0407c3..85fc1d87e7 100644 --- a/httpclient-2/pom.xml +++ b/httpclient-2/pom.xml @@ -26,6 +26,11 @@ + + org.apache.httpcomponents.client5 + httpclient5 + ${httpclient5.version} + org.springframework.boot @@ -38,6 +43,12 @@ ${spring-boot.version} test + + + org.springframework.boot + spring-boot-starter-webflux + ${spring-boot.version} + @@ -63,6 +74,7 @@ 4.5.8 + 5.1 11 11 2.1.7.RELEASE diff --git a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java new file mode 100644 index 0000000000..35b21789ef --- /dev/null +++ b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java @@ -0,0 +1,29 @@ +package com.baeldung.httpclient.readresponsebodystring; + +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class ApacheHttpClient5UnitTest { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + public static final String DUMMY_URL = "https://postman-echo.com/get"; + + @Test + public void whenUseApacheHttpClient_thenCorrect() throws IOException, ParseException { + HttpGet request = new HttpGet(DUMMY_URL); + + try (CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(request)) { + HttpEntity entity = response.getEntity(); + logger.debug("Response -> {}", EntityUtils.toString(entity)); + } + } +} \ No newline at end of file diff --git a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java index 5a638b2bd5..7625b415f2 100644 --- a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java +++ b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java @@ -7,10 +7,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; public class ApacheHttpClientUnitTest { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); public static final String DUMMY_URL = "https://postman-echo.com/get"; @Test @@ -19,8 +22,7 @@ public class ApacheHttpClientUnitTest { try (CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(request)) { HttpEntity entity = response.getEntity(); - String result = EntityUtils.toString(entity); - System.out.println("Response -> " + result); + logger.debug("Response -> {}", EntityUtils.toString(entity)); } } } diff --git a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/SpringWebClientUnitTest.java b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/SpringWebClientUnitTest.java new file mode 100644 index 0000000000..9bd2f825ad --- /dev/null +++ b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/SpringWebClientUnitTest.java @@ -0,0 +1,17 @@ +package com.baeldung.httpclient.readresponsebodystring; + +import org.junit.Test; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +public class SpringWebClientUnitTest { + public static final String DUMMY_URL = "https://postman-echo.com/get"; + + @Test + public void whenUseWebClientRetrieve_thenCorrect() { + WebClient webClient = WebClient.create(DUMMY_URL); + Mono body = webClient.get().retrieve().bodyToMono(String.class); + String s = body.block(); + System.out.println(s); + } +} diff --git a/httpclient-2/src/test/resources/logback.xml b/httpclient-2/src/test/resources/logback.xml new file mode 100644 index 0000000000..366a94e86e --- /dev/null +++ b/httpclient-2/src/test/resources/logback.xml @@ -0,0 +1,15 @@ + + + + %date [%level] %logger - %msg %n + + + + + + + + + + + \ No newline at end of file diff --git a/image-processing/README.md b/image-processing/README.md index eba23f83eb..12b061bf41 100644 --- a/image-processing/README.md +++ b/image-processing/README.md @@ -8,3 +8,4 @@ This module contains articles about image processing. - [Optical Character Recognition with Tesseract](https://www.baeldung.com/java-ocr-tesseract) - [How Can I Resize an Image Using Java?](https://www.baeldung.com/java-resize-image) - [Adding Text to an Image in Java](https://www.baeldung.com/java-add-text-to-image) +- [Capturing Image From Webcam In Java](https://www.baeldung.com/java-capture-image-from-webcam) diff --git a/image-processing/pom.xml b/image-processing/pom.xml index ea218356ce..3780ecfd33 100644 --- a/image-processing/pom.xml +++ b/image-processing/pom.xml @@ -82,6 +82,16 @@ MarvinPlugins ${marvin-version} + + org.bytedeco + javacv-platform + ${javacv-platform.version} + + + com.github.sarxos + webcam-capture + ${webcam-capture.version} + @@ -91,6 +101,8 @@ 4.5.1 4.1.0-1.5.2 3.4.2-0 + 1.5.5 + 0.3.12 4.2 0.4.11 1.5.5 diff --git a/image-processing/src/main/java/com/baeldung/imagefromwebcam/MarvinExample.java b/image-processing/src/main/java/com/baeldung/imagefromwebcam/MarvinExample.java new file mode 100644 index 0000000000..67a46d9d59 --- /dev/null +++ b/image-processing/src/main/java/com/baeldung/imagefromwebcam/MarvinExample.java @@ -0,0 +1,31 @@ +package com.baeldung.imagefromwebcam; + +import marvin.gui.MarvinImagePanel; +import marvin.image.MarvinImage; +import marvin.io.MarvinImageIO; +import marvin.video.MarvinJavaCVAdapter; +import marvin.video.MarvinVideoInterface; +import marvin.video.MarvinVideoInterfaceException; + +public class MarvinExample { + + public static void main(String[] args) throws MarvinVideoInterfaceException { + MarvinVideoInterface videoAdapter = new MarvinJavaCVAdapter(); + videoAdapter.connect(0); + MarvinImage image = videoAdapter.getFrame(); + MarvinImageIO.saveImage(image, "selfie.jpg"); + } + + public void captureWithPanel() throws MarvinVideoInterfaceException { + MarvinVideoInterface videoAdapter = new MarvinJavaCVAdapter(); + videoAdapter.connect(0); + MarvinImage image = videoAdapter.getFrame(); + + MarvinImagePanel imagePanel = new MarvinImagePanel(); + imagePanel.setImage(image); + + imagePanel.setSize(800,600); + imagePanel.setVisible(true); + } + +} diff --git a/image-processing/src/main/java/com/baeldung/imagefromwebcam/OpenCVExample.java b/image-processing/src/main/java/com/baeldung/imagefromwebcam/OpenCVExample.java new file mode 100644 index 0000000000..3d9d1bcb00 --- /dev/null +++ b/image-processing/src/main/java/com/baeldung/imagefromwebcam/OpenCVExample.java @@ -0,0 +1,31 @@ +package com.baeldung.imagefromwebcam; + +import org.bytedeco.javacv.*; +import org.bytedeco.opencv.opencv_core.IplImage; +import java.awt.event.WindowEvent; +import javax.swing.JFrame; +import static org.bytedeco.opencv.helper.opencv_imgcodecs.cvSaveImage; + +public class OpenCVExample { + + public static void main(String[] args) throws Exception { + CanvasFrame canvas = new CanvasFrame("Web Cam"); + canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + FrameGrabber grabber = new OpenCVFrameGrabber(0); + OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage(); + + grabber.start(); + Frame frame = grabber.grab(); + + IplImage img = converter.convert(frame); + cvSaveImage("selfie.jpg", img); + + canvas.showImage(frame); + + Thread.sleep(2000); + + canvas.dispatchEvent(new WindowEvent(canvas, WindowEvent.WINDOW_CLOSING)); + } + +} diff --git a/image-processing/src/main/java/com/baeldung/imagefromwebcam/WebcamCaptureExample.java b/image-processing/src/main/java/com/baeldung/imagefromwebcam/WebcamCaptureExample.java new file mode 100644 index 0000000000..8edde2a637 --- /dev/null +++ b/image-processing/src/main/java/com/baeldung/imagefromwebcam/WebcamCaptureExample.java @@ -0,0 +1,41 @@ +package com.baeldung.imagefromwebcam; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JFrame; + +import com.github.sarxos.webcam.Webcam; +import com.github.sarxos.webcam.WebcamPanel; +import com.github.sarxos.webcam.WebcamResolution; +import com.github.sarxos.webcam.util.ImageUtils; + +public class WebcamCaptureExample { + + public static void main(String[] args) throws IOException, Exception { + Webcam webcam = Webcam.getDefault(); + webcam.open(); + + BufferedImage image = webcam.getImage(); + + ImageIO.write(image, ImageUtils.FORMAT_JPG, new File("selfie.jpg")); + } + + public void captureWithPanel() { + Webcam webcam = Webcam.getDefault(); + webcam.setViewSize(WebcamResolution.VGA.getSize()); + + WebcamPanel panel = new WebcamPanel(webcam); + panel.setImageSizeDisplayed(true); + + JFrame window = new JFrame("Webcam"); + window.add(panel); + window.setResizable(true); + window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + window.pack(); + window.setVisible(true); + } + +} diff --git a/java-collections-conversions-2/README.md b/java-collections-conversions-2/README.md index 628421b0e2..83b3ec8786 100644 --- a/java-collections-conversions-2/README.md +++ b/java-collections-conversions-2/README.md @@ -8,4 +8,5 @@ This module contains articles about conversions among Collection types and array - [Mapping Lists with ModelMapper](https://www.baeldung.com/java-modelmapper-lists) - [Converting List to Map With a Custom Supplier](https://www.baeldung.com/list-to-map-supplier) - [Arrays.asList vs new ArrayList(Arrays.asList())](https://www.baeldung.com/java-arrays-aslist-vs-new-arraylist) +- [Iterate Over a Set in Java](https://www.baeldung.com/java-iterate-set) - More articles: [[<-- prev]](../java-collections-conversions) diff --git a/java-collections-conversions-2/pom.xml b/java-collections-conversions-2/pom.xml index 7845e0d934..a81b245b33 100644 --- a/java-collections-conversions-2/pom.xml +++ b/java-collections-conversions-2/pom.xml @@ -44,6 +44,11 @@ ${hamcrest.version} test + + io.vavr + vavr + 0.10.3 + diff --git a/java-collections-conversions-2/src/test/java/com/baeldung/setiteration/SetIteration.java b/java-collections-conversions-2/src/test/java/com/baeldung/setiteration/SetIteration.java new file mode 100644 index 0000000000..ee0943ec1c --- /dev/null +++ b/java-collections-conversions-2/src/test/java/com/baeldung/setiteration/SetIteration.java @@ -0,0 +1,74 @@ +package com.baeldung.setiteration; + +import com.google.common.collect.Sets; +import io.vavr.collection.Stream; +import org.junit.jupiter.api.Test; + +import java.util.Iterator; +import java.util.Set; +import java.util.stream.Collectors; + +class SetIteration { + + @Test + void givenSet_whenIteratorUsed_shouldIterateOverElements() { + // given + Set names = Sets.newHashSet("Tom", "Jane", "Karen"); + + // when + Iterator namesIterator1 = names.iterator(); + Iterator namesIterator2 = names.iterator(); + + // then + namesIterator1.forEachRemaining(System.out::println); + while(namesIterator2.hasNext()) { + System.out.println(namesIterator2.next()); + } + } + + @Test + void givenSet_whenStreamUsed_shouldIterateOverElements() { + // given + Set names = Sets.newHashSet("Tom", "Jane", "Karen"); + + // when & then + String namesJoined = names.stream() + .map(String::toUpperCase) + .peek(System.out::println) + .collect(Collectors.joining()); + } + + @Test + void givenSet_whenEnhancedLoopUsed_shouldIterateOverElements() { + // given + Set names = Sets.newHashSet("Tom", "Jane", "Karen"); + + // when & then + for (String name : names) { + System.out.println(name); + } + } + + @Test + void givenSet_whenMappedToArray_shouldIterateOverElements() { + // given + Set names = Sets.newHashSet("Tom", "Jane", "Karen"); + + // when & then + Object[] namesArray = names.toArray(); + for (int i = 0; i < namesArray.length; i++) { + System.out.println(i + ": " + namesArray[i]); + } + } + + @Test + void givenSet_whenZippedWithIndex_shouldIterateOverElements() { + // given + Set names = Sets.newHashSet("Tom", "Jane", "Karen"); + + // when & then + Stream.ofAll(names) + .zipWithIndex() + .forEach(t -> System.out.println(t._2() + ": " + t._1())); + } +} diff --git a/java-collections-conversions/pom.xml b/java-collections-conversions/pom.xml index 92fc66b480..e76181c9af 100644 --- a/java-collections-conversions/pom.xml +++ b/java-collections-conversions/pom.xml @@ -41,5 +41,5 @@ 4.1 - + \ No newline at end of file diff --git a/java-collections-maps-3/pom.xml b/java-collections-maps-3/pom.xml index 37a0f617d0..0cdf24e31b 100644 --- a/java-collections-maps-3/pom.xml +++ b/java-collections-maps-3/pom.xml @@ -40,4 +40,5 @@ 3.6.1 5.2.5.RELEASE + \ No newline at end of file diff --git a/java-jdi/pom.xml b/java-jdi/pom.xml index 978ede604b..a8716de4ee 100644 --- a/java-jdi/pom.xml +++ b/java-jdi/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 java-jdi 0.1.0-SNAPSHOT @@ -39,4 +39,4 @@ 1.8 - + \ No newline at end of file diff --git a/java-lite/pom.xml b/java-lite/pom.xml index 7ff50a8fd3..c422e9a421 100644 --- a/java-lite/pom.xml +++ b/java-lite/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 org.baeldung java-lite diff --git a/java-numbers-4/README.md b/java-numbers-4/README.md index cdd53692e0..f053a82b80 100644 --- a/java-numbers-4/README.md +++ b/java-numbers-4/README.md @@ -3,3 +3,4 @@ - [Probability in Java](https://www.baeldung.com/java-probability) - [Understanding the & 0xff Value in Java](https://www.baeldung.com/java-and-0xff) - [Determine if an Integer’s Square Root Is an Integer in Java](https://www.baeldung.com/java-find-if-square-root-is-integer) +- [Guide to Java BigInteger](https://www.baeldung.com/java-biginteger) diff --git a/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java b/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java new file mode 100644 index 0000000000..4ac185984d --- /dev/null +++ b/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java @@ -0,0 +1,76 @@ +package com.baeldung.biginteger; + +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.nio.ByteBuffer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BigIntegerUnitTest { + + @Test + void givenPositiveAndNegativeAndZeroBigInteger_whenGetSigNumValue_shouldReturnOneAndMinusOneAndZero() { + assertEquals(1, BigInteger.TEN.signum()); + assertEquals(-1, BigInteger.TEN.negate().signum()); + assertEquals(0, BigInteger.ZERO.signum()); + } + + @Test + void givenByteArrays_whenCreateBigInteger_shouldTranslateToTwosComplementBinary() { + assertEquals(new BigInteger("1"), new BigInteger(new byte[]{0b1})); + assertEquals(new BigInteger("2"), new BigInteger(new byte[]{0b10})); + assertEquals(new BigInteger("4"), new BigInteger(new byte[]{0b100})); + } + + @Test + void givenSingleByte_whenCreateBigIntegerAndDifferentSigNum_thenOppositeValues() { + byte[] bytes = { (byte) -128 }; // 0b1000_0000 + + BigInteger positive = new BigInteger(1, bytes); + BigInteger negative = new BigInteger(-1, bytes); + + assertEquals(new BigInteger("128"), positive); + assertEquals("10000000", positive.toString(2)); + + assertEquals(new BigInteger("-128"), negative); + assertEquals("-10000000", negative.toString(2)); + } + + @Test + void givenZeroBigInteger_whenCheckMagnitude_thenIsEmpty() { + assertEquals(0, BigInteger.ZERO.bitCount()); + assertEquals(BigInteger.ZERO, new BigInteger(0, new byte[]{})); + } + + @Test + void given63rdBitSet_whenCreateBigInteger_thenIsLargerThanLongByOne() { + // first + BigInteger bi1 = BigInteger.ZERO.setBit(63); + String str1 = bi1.toString(2); + + // second + byte[] bytes = ByteBuffer.allocate(Long.BYTES).putLong(Long.MIN_VALUE).array(); + BigInteger bi2 = new BigInteger(1, bytes); + String str2 = bi2.toString(2); + + largerThanLongAssertionSet(bi1, str1); + + assertEquals(bi1, bi2); + + largerThanLongAssertionSet(bi2, str2); + + } + + private static void largerThanLongAssertionSet(BigInteger bi, String str) + { + assertEquals(64, bi.bitLength()); + assertEquals(1, bi.signum()); + assertEquals("9223372036854775808", bi.toString()); + assertEquals(BigInteger.ONE, bi.subtract(BigInteger.valueOf(Long.MAX_VALUE))); + + assertEquals(64, str.length()); + assertTrue(str.matches("^10{63}$")); // 1000 0000 + } +} diff --git a/jee-7/pom.xml b/jee-7/pom.xml index b68c8b9801..eb23a3e150 100644 --- a/jee-7/pom.xml +++ b/jee-7/pom.xml @@ -485,23 +485,9 @@ - - - bintray-mvc-spec-maven - bintray - http://dl.bintray.com/mvc-spec/maven - - true - - - false - - - - 1.0.0 - 20160715 + 1.0-edr2 1.8 3.0.0 7.0 @@ -523,7 +509,7 @@ 2.1.0.Final 2.8 2.2 - 20160715 + 1.0.0-m02 1.0.0.Final 1.0.2.Final 1.0.0.Final diff --git a/jenkins/plugins/pom.xml b/jenkins/plugins/pom.xml index c4caf5567f..3662d59980 100644 --- a/jenkins/plugins/pom.xml +++ b/jenkins/plugins/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 plugins 1.0-SNAPSHOT @@ -12,7 +13,7 @@ org.jenkins-ci.plugins plugin 2.33 - + @@ -80,9 +81,9 @@ - + 2.7.3 - 1.7 2.12 2.39 @@ -93,4 +94,4 @@ 2.14 - + \ No newline at end of file diff --git a/json-2/pom.xml b/json-2/pom.xml index fbae40b6a3..733fbc6668 100644 --- a/json-2/pom.xml +++ b/json-2/pom.xml @@ -14,6 +14,11 @@ + + org.jsonschema2pojo + jsonschema2pojo-core + 1.1.1 + com.jsoniter jsoniter @@ -46,7 +51,6 @@ commons-lang3 ${commons-lang3.version} - com.fasterxml.jackson.core jackson-annotations diff --git a/json-2/src/main/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversion.java b/json-2/src/main/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversion.java new file mode 100644 index 0000000000..65eecd0d38 --- /dev/null +++ b/json-2/src/main/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversion.java @@ -0,0 +1,42 @@ +package com.baeldung.jsontojavaclass; + +import java.io.File; +import java.io.IOException; +import java.net.URL; + +import org.jsonschema2pojo.DefaultGenerationConfig; +import org.jsonschema2pojo.GenerationConfig; +import org.jsonschema2pojo.Jackson2Annotator; +import org.jsonschema2pojo.SchemaGenerator; +import org.jsonschema2pojo.SchemaMapper; +import org.jsonschema2pojo.SchemaStore; +import org.jsonschema2pojo.SourceType; +import org.jsonschema2pojo.rules.RuleFactory; + +import com.sun.codemodel.JCodeModel; + +public class JsonToJavaClassConversion { + + public Object convertJsonToJavaClass(URL inputJson, File outputJavaClassDirectory, String packageName, String className) throws IOException { + JCodeModel jcodeModel = new JCodeModel(); + + GenerationConfig config = new DefaultGenerationConfig() { + @Override + public boolean isGenerateBuilders() { + return true; + } + + @Override + public SourceType getSourceType() { + return SourceType.JSON; + } + }; + + SchemaMapper mapper = new SchemaMapper(new RuleFactory(config, new Jackson2Annotator(config), new SchemaStore()), new SchemaGenerator()); + mapper.generate(jcodeModel, className, packageName, inputJson); + + jcodeModel.build(outputJavaClassDirectory); + return mapper; + } + +} diff --git a/json-2/src/main/java/com/baeldung/jsontojavaclass/pojo/SamplePojo.java b/json-2/src/main/java/com/baeldung/jsontojavaclass/pojo/SamplePojo.java new file mode 100644 index 0000000000..4c8a719de0 --- /dev/null +++ b/json-2/src/main/java/com/baeldung/jsontojavaclass/pojo/SamplePojo.java @@ -0,0 +1,214 @@ + +package com.baeldung.jsontojavaclass.pojo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Generated; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ "name", "area", "author", "id", "salary", "topics" }) +@Generated("jsonschema2pojo") +public class SamplePojo { + + @JsonProperty("name") + private String name; + @JsonProperty("area") + private String area; + @JsonProperty("author") + private String author; + @JsonProperty("id") + private Integer id; + @JsonProperty("salary") + private Integer salary; + @JsonProperty("topics") + private List topics = new ArrayList(); + @JsonIgnore + private Map additionalProperties = new HashMap(); + + @JsonProperty("name") + public String getName() { + return name; + } + + @JsonProperty("name") + public void setName(String name) { + this.name = name; + } + + public SamplePojo withName(String name) { + this.name = name; + return this; + } + + @JsonProperty("area") + public String getArea() { + return area; + } + + @JsonProperty("area") + public void setArea(String area) { + this.area = area; + } + + public SamplePojo withArea(String area) { + this.area = area; + return this; + } + + @JsonProperty("author") + public String getAuthor() { + return author; + } + + @JsonProperty("author") + public void setAuthor(String author) { + this.author = author; + } + + public SamplePojo withAuthor(String author) { + this.author = author; + return this; + } + + @JsonProperty("id") + public Integer getId() { + return id; + } + + @JsonProperty("id") + public void setId(Integer id) { + this.id = id; + } + + public SamplePojo withId(Integer id) { + this.id = id; + return this; + } + + @JsonProperty("salary") + public Integer getSalary() { + return salary; + } + + @JsonProperty("salary") + public void setSalary(Integer salary) { + this.salary = salary; + } + + public SamplePojo withSalary(Integer salary) { + this.salary = salary; + return this; + } + + @JsonProperty("topics") + public List getTopics() { + return topics; + } + + @JsonProperty("topics") + public void setTopics(List topics) { + this.topics = topics; + } + + public SamplePojo withTopics(List topics) { + this.topics = topics; + return this; + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + + public SamplePojo withAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(SamplePojo.class.getName()) + .append('@') + .append(Integer.toHexString(System.identityHashCode(this))) + .append('['); + sb.append("name"); + sb.append('='); + sb.append(((this.name == null) ? "" : this.name)); + sb.append(','); + sb.append("area"); + sb.append('='); + sb.append(((this.area == null) ? "" : this.area)); + sb.append(','); + sb.append("author"); + sb.append('='); + sb.append(((this.author == null) ? "" : this.author)); + sb.append(','); + sb.append("id"); + sb.append('='); + sb.append(((this.id == null) ? "" : this.id)); + sb.append(','); + sb.append("salary"); + sb.append('='); + sb.append(((this.salary == null) ? "" : this.salary)); + sb.append(','); + sb.append("topics"); + sb.append('='); + sb.append(((this.topics == null) ? "" : this.topics)); + sb.append(','); + sb.append("additionalProperties"); + sb.append('='); + sb.append(((this.additionalProperties == null) ? "" : this.additionalProperties)); + sb.append(','); + if (sb.charAt((sb.length() - 1)) == ',') { + sb.setCharAt((sb.length() - 1), ']'); + } else { + sb.append(']'); + } + return sb.toString(); + } + + @Override + public int hashCode() { + int result = 1; + result = ((result * 31) + ((this.area == null) ? 0 : this.area.hashCode())); + result = ((result * 31) + ((this.author == null) ? 0 : this.author.hashCode())); + result = ((result * 31) + ((this.topics == null) ? 0 : this.topics.hashCode())); + result = ((result * 31) + ((this.name == null) ? 0 : this.name.hashCode())); + result = ((result * 31) + ((this.id == null) ? 0 : this.id.hashCode())); + result = ((result * 31) + ((this.additionalProperties == null) ? 0 : this.additionalProperties.hashCode())); + result = ((result * 31) + ((this.salary == null) ? 0 : this.salary.hashCode())); + return result; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if ((other instanceof SamplePojo) == false) { + return false; + } + SamplePojo rhs = ((SamplePojo) other); + return ((((((((this.area == rhs.area) || ((this.area != null) && this.area.equals(rhs.area))) && ((this.author == rhs.author) || ((this.author != null) && this.author.equals(rhs.author)))) + && ((this.topics == rhs.topics) || ((this.topics != null) && this.topics.equals(rhs.topics)))) && ((this.name == rhs.name) || ((this.name != null) && this.name.equals(rhs.name)))) + && ((this.id == rhs.id) || ((this.id != null) && this.id.equals(rhs.id)))) && ((this.additionalProperties == rhs.additionalProperties) || ((this.additionalProperties != null) && this.additionalProperties.equals(rhs.additionalProperties)))) + && ((this.salary == rhs.salary) || ((this.salary != null) && this.salary.equals(rhs.salary)))); + } + +} diff --git a/json-2/src/test/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversionUnitTest.java b/json-2/src/test/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversionUnitTest.java new file mode 100644 index 0000000000..8dbfb14b45 --- /dev/null +++ b/json-2/src/test/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversionUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.jsontojavaclass; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class JsonToJavaClassConversionUnitTest { + + private JsonToJavaClassConversion jsonToJavaConversion = new JsonToJavaClassConversion(); + + @Test + void whenProvideInputJSON_thenGenerateJavaClass() throws MalformedURLException, IOException { + + String packageName = "com.baeldung.jsontojavaclass.pojo"; + + // load input JSON file + String jsonPath = "src/test/resources/"; + File inputJson = new File(jsonPath + "sample_input.json"); + + // create the local directory for generating the Java Class file + String outputPath = "src/main/java/"; + File outputJavaClassDirectory = new File(outputPath); + outputJavaClassDirectory.mkdirs(); + + String className = "SamplePojo"; + + Object object = jsonToJavaConversion.convertJsonToJavaClass(inputJson.toURI() + .toURL(), outputJavaClassDirectory, packageName, className); + System.out.println(object); + + Assertions.assertNotNull(object); + + } + +} diff --git a/json-2/src/test/resources/sample_input.json b/json-2/src/test/resources/sample_input.json new file mode 100644 index 0000000000..b6e90da357 --- /dev/null +++ b/json-2/src/test/resources/sample_input.json @@ -0,0 +1,13 @@ +{ + "name": "Baeldung", + "area": "tech blogs", + "author": "Eugen", + "id": 32134, + "salary": 70000, + "topics": [ + "java", + "kotlin", + "cs", + "linux" + ] +} \ No newline at end of file diff --git a/jta/pom.xml b/jta/pom.xml index b8b5570226..1937f55a20 100644 --- a/jta/pom.xml +++ b/jta/pom.xml @@ -36,4 +36,8 @@ + + 2.4.7 + + \ No newline at end of file diff --git a/ksqldb/pom.xml b/ksqldb/pom.xml new file mode 100644 index 0000000000..13867b16e3 --- /dev/null +++ b/ksqldb/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + ksqldb-app + 0.0.1-SNAPSHOT + ksqldb + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + ../pom.xml + + + + + confluent + confluent-repo + http://packages.confluent.io/maven/ + + + + + + io.confluent.ksql + ksqldb-api-client + ${ksqldb.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + org.awaitility + awaitility + ${awaitility.version} + test + + + + org.assertj + assertj-core + ${assertj.version} + test + + + + org.testcontainers + testcontainers + ${testcontainers.version} + test + + + + org.testcontainers + junit-jupiter + ${testcontainers.version} + test + + + + + 6.2.0 + 3.20.2 + 4.1.0 + 1.15.3 + + + diff --git a/ksqldb/src/main/java/com/baeldung/ksqldb/Alert.java b/ksqldb/src/main/java/com/baeldung/ksqldb/Alert.java new file mode 100644 index 0000000000..badb00f114 --- /dev/null +++ b/ksqldb/src/main/java/com/baeldung/ksqldb/Alert.java @@ -0,0 +1,29 @@ +package com.baeldung.ksqldb; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class Alert { + + @JsonProperty(value = "SENSOR_ID") + private String sensorId; + + @JsonProperty(value = "START_PERIOD") + private String startPeriod; + + @JsonProperty(value = "END_PERIOD") + private String endPeriod; + + @JsonProperty(value = "AVERAGE_READING") + private double averageReading; + +} diff --git a/ksqldb/src/main/java/com/baeldung/ksqldb/KsqlDBApplication.java b/ksqldb/src/main/java/com/baeldung/ksqldb/KsqlDBApplication.java new file mode 100644 index 0000000000..35ad3ebbb0 --- /dev/null +++ b/ksqldb/src/main/java/com/baeldung/ksqldb/KsqlDBApplication.java @@ -0,0 +1,75 @@ +package com.baeldung.ksqldb; + +import io.confluent.ksql.api.client.Client; +import io.confluent.ksql.api.client.ExecuteStatementResult; +import io.confluent.ksql.api.client.KsqlObject; +import io.confluent.ksql.api.client.Row; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Subscriber; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +@AllArgsConstructor +@Slf4j +public class KsqlDBApplication { + + private static final String CREATE_READINGS_STREAM = "" + + " CREATE STREAM readings (sensor_id VARCHAR KEY, timestamp VARCHAR, reading INT)" + + " WITH (KAFKA_TOPIC = 'readings'," + + " VALUE_FORMAT = 'JSON'," + + " TIMESTAMP = 'timestamp'," + + " TIMESTAMP_FORMAT = 'yyyy-MM-dd HH:mm:ss'," + + " PARTITIONS = 1);"; + + private static final String CREATE_ALERTS_TABLE = "" + + " CREATE TABLE alerts AS" + + " SELECT" + + " sensor_id," + + " TIMESTAMPTOSTRING(WINDOWSTART, 'yyyy-MM-dd HH:mm:ss', 'UTC') AS start_period," + + " TIMESTAMPTOSTRING(WINDOWEND, 'yyyy-MM-dd HH:mm:ss', 'UTC') AS end_period," + + " AVG(reading) AS average_reading" + + " FROM readings" + + " WINDOW TUMBLING (SIZE 30 MINUTES)" + + " GROUP BY sensor_id" + + " HAVING AVG(reading) > 25" + + " EMIT CHANGES;"; + + private static final String ALERTS_QUERY = "SELECT * FROM alerts EMIT CHANGES;"; + + private static final String READINGS_STREAM = "readings"; + + private static final Map PROPERTIES = Collections.singletonMap("auto.offset.reset", "earliest"); + + private final Client client; + + public CompletableFuture createReadingsStream() { + return client.executeStatement(CREATE_READINGS_STREAM, PROPERTIES); + } + + public CompletableFuture createAlertsTable() { + return client.executeStatement(CREATE_ALERTS_TABLE, PROPERTIES); + } + + public CompletableFuture insert(Collection rows) { + return CompletableFuture.allOf( + rows.stream() + .map(row -> client.insertInto(READINGS_STREAM, row)) + .toArray(CompletableFuture[]::new) + ); + } + + public CompletableFuture subscribeOnAlerts(Subscriber subscriber) { + return client.streamQuery(ALERTS_QUERY, PROPERTIES) + .thenAccept(streamedQueryResult -> streamedQueryResult.subscribe(subscriber)) + .whenComplete((result, ex) -> { + if (ex != null) { + log.error("Alerts push query failed", ex); + } + }); + } + +} diff --git a/ksqldb/src/main/java/com/baeldung/ksqldb/Reading.java b/ksqldb/src/main/java/com/baeldung/ksqldb/Reading.java new file mode 100644 index 0000000000..8964ff5801 --- /dev/null +++ b/ksqldb/src/main/java/com/baeldung/ksqldb/Reading.java @@ -0,0 +1,10 @@ +package com.baeldung.ksqldb; + +import lombok.Data; + +@Data +public class Reading { + private String id; + private String timestamp; + private int reading; +} diff --git a/ksqldb/src/main/java/com/baeldung/ksqldb/RowSubscriber.java b/ksqldb/src/main/java/com/baeldung/ksqldb/RowSubscriber.java new file mode 100644 index 0000000000..7d09583a39 --- /dev/null +++ b/ksqldb/src/main/java/com/baeldung/ksqldb/RowSubscriber.java @@ -0,0 +1,60 @@ +package com.baeldung.ksqldb; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.confluent.ksql.api.client.Row; +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class RowSubscriber implements Subscriber { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final Class clazz; + + private Subscription subscription; + + public List consumedItems = new ArrayList<>(); + + public RowSubscriber(Class clazz) { + this.clazz = clazz; + } + + @Override + public synchronized void onSubscribe(Subscription subscription) { + log.info("Subscriber is subscribed."); + this.subscription = subscription; + subscription.request(1); + } + + @Override + public synchronized void onNext(Row row) { + String jsonString = row.asObject().toJsonString(); + log.info("Row JSON: {}", jsonString); + try { + T item = OBJECT_MAPPER.readValue(jsonString, this.clazz); + log.info("Item: {}", item); + consumedItems.add(item); + } catch (JsonProcessingException e) { + log.error("Unable to parse json", e); + } + + // Request the next row + subscription.request(1); + } + + @Override + public synchronized void onError(Throwable t) { + log.error("Received an error", t); + } + + @Override + public synchronized void onComplete() { + log.info("Query has ended."); + } +} diff --git a/ksqldb/src/test/java/com/baeldung/ksqldb/KsqlDBApplicationLiveTest.java b/ksqldb/src/test/java/com/baeldung/ksqldb/KsqlDBApplicationLiveTest.java new file mode 100644 index 0000000000..f13f418048 --- /dev/null +++ b/ksqldb/src/test/java/com/baeldung/ksqldb/KsqlDBApplicationLiveTest.java @@ -0,0 +1,160 @@ +package com.baeldung.ksqldb; + +import io.confluent.ksql.api.client.Client; +import io.confluent.ksql.api.client.ClientOptions; +import io.confluent.ksql.api.client.KsqlObject; +import io.confluent.ksql.api.client.QueryInfo; +import io.confluent.ksql.api.client.QueryInfo.QueryType; +import io.confluent.ksql.api.client.Row; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.io.File; +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.awaitility.Awaitility.given; + +@Testcontainers +class KsqlDBApplicationLiveTest { + + private static final File KSQLDB_COMPOSE_FILE = new File("src/test/resources/docker/docker-compose.yml"); + + private static final Map PROPERTIES = Collections.singletonMap("auto.offset.reset", "earliest"); + + private static final String KSQLDB_SERVER_HOST = "localhost"; + private static final int KSQLDB_SERVER_PORT = 8088; + + @Container + public static DockerComposeContainer dockerComposeContainer = + new DockerComposeContainer<>(KSQLDB_COMPOSE_FILE) + .withServices("zookeeper", "broker", "ksqldb-server") + .withExposedService("ksqldb-server", 8088, + Wait.forHealthcheck().withStartupTimeout(Duration.ofMinutes(5))) + .withLocalCompose(true); + + private KsqlDBApplication ksqlDBApplication; + + private Client client; + + @BeforeEach + void setup() { + ClientOptions options = ClientOptions.create() + .setHost(KSQLDB_SERVER_HOST) + .setPort(KSQLDB_SERVER_PORT); + client = Client.create(options); + + ksqlDBApplication = new KsqlDBApplication(client); + } + + @AfterEach + void tearDown() { + deleteAlerts(); + } + + @Test + void givenSensorReadings_whenSubscribedToAlerts_thenAlertsAreConsumed() { + createAlertsMaterializedView(); + RowSubscriber alertSubscriber = new RowSubscriber<>(Alert.class); + + CompletableFuture result = ksqlDBApplication.subscribeOnAlerts(alertSubscriber); + insertSampleData(); + + assertThat(result).isNotNull(); + await().atMost(Duration.ofMinutes(3)).untilAsserted(() -> + assertThat(alertSubscriber.consumedItems) + .containsOnly( + expectedAlert("sensor-1", "2021-08-01 09:30:00", "2021-08-01 10:00:00", 28.0), + expectedAlert("sensor-2", "2021-08-01 10:00:00", "2021-08-01 10:30:00", 26.0) + ) + ); + } + + @Test + void givenSensorReadings_whenPullQueryForRow_thenRowIsReturned() { + createAlertsMaterializedView(); + insertSampleData(); + + String pullQuery = "SELECT * FROM alerts WHERE sensor_id = 'sensor-2';"; + + given().ignoreExceptions() + .await().atMost(Duration.ofMinutes(1)) + .untilAsserted(() -> { + // it may be possible that the materialized view is not updated with sample data yet + // so ignore TimeoutException and try again + List rows = client.executeQuery(pullQuery, PROPERTIES) + .get(10, TimeUnit.SECONDS); + + assertThat(rows).hasSize(1); + + Row row = rows.get(0); + assertThat(row.getString("SENSOR_ID")).isEqualTo("sensor-2"); + assertThat(row.getString("START_PERIOD")).isEqualTo("2021-08-01 10:00:00"); + assertThat(row.getString("END_PERIOD")).isEqualTo("2021-08-01 10:30:00"); + assertThat(row.getDouble("AVERAGE_READING")).isEqualTo(26.0); + }); + } + + private void createAlertsMaterializedView() { + ksqlDBApplication.createReadingsStream().join(); + ksqlDBApplication.createAlertsTable().join(); + } + + private void insertSampleData() { + ksqlDBApplication.insert( + Arrays.asList( + new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:00:00").put("reading", 22), + new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:10:00").put("reading", 20), + new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:20:00").put("reading", 20), + + // these reading will exceed the alert threshold (sensor-1) + new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:30:00").put("reading", 24), + new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:40:00").put("reading", 30), + new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:50:00").put("reading", 30), + + new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 10:00:00").put("reading", 24), + + // these reading will exceed the alert threshold (sensor-2) + new KsqlObject().put("sensor_id", "sensor-2").put("timestamp", "2021-08-01 10:00:00").put("reading", 26), + new KsqlObject().put("sensor_id", "sensor-2").put("timestamp", "2021-08-01 10:10:00").put("reading", 26), + new KsqlObject().put("sensor_id", "sensor-2").put("timestamp", "2021-08-01 10:20:00").put("reading", 26), + + new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 10:30:00").put("reading", 24) + ) + ).join(); + } + + private void deleteAlerts() { + client.listQueries() + .thenApply(queryInfos -> queryInfos.stream() + .filter(queryInfo -> queryInfo.getQueryType() == QueryType.PERSISTENT) + .map(QueryInfo::getId) + .findFirst() + .orElseThrow(() -> new RuntimeException("Persistent query not found"))) + .thenCompose(id -> client.executeStatement("TERMINATE " + id + ";")) + .thenCompose(result -> client.executeStatement("DROP TABLE alerts DELETE TOPIC;")) + .thenCompose(result -> client.executeStatement("DROP STREAM readings DELETE TOPIC;")) + .join(); + } + + private Alert expectedAlert(String sensorId, String startPeriod, String endPeriod, double average) { + return Alert.builder() + .sensorId(sensorId) + .startPeriod(startPeriod) + .endPeriod(endPeriod) + .averageReading(average) + .build(); + } +} diff --git a/ksqldb/src/test/resources/docker/docker-compose.yml b/ksqldb/src/test/resources/docker/docker-compose.yml new file mode 100644 index 0000000000..c90fe85e45 --- /dev/null +++ b/ksqldb/src/test/resources/docker/docker-compose.yml @@ -0,0 +1,49 @@ +--- +version: '3' + +services: + zookeeper: + image: confluentinc/cp-zookeeper:6.2.0 + hostname: zookeeper + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + + broker: + image: confluentinc/cp-kafka:6.2.0 + hostname: broker + depends_on: + - zookeeper + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:9092,PLAINTEXT_HOST://localhost:29092 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + + ksqldb-server: + image: confluentinc/ksqldb-server:0.19.0 + hostname: ksqldb-server + depends_on: + - broker + ports: + - "8088:8088" + healthcheck: + test: curl -f http://ksqldb-server:8088/ || exit 1 + environment: + KSQL_LISTENERS: http://0.0.0.0:8088 + KSQL_BOOTSTRAP_SERVERS: broker:9092 + KSQL_KSQL_LOGGING_PROCESSING_STREAM_AUTO_CREATE: "true" + KSQL_KSQL_LOGGING_PROCESSING_TOPIC_AUTO_CREATE: "true" + + ksqldb-cli: + image: confluentinc/ksqldb-cli:0.19.0 + hostname: ksqldb-cli + depends_on: + - broker + - ksqldb-server + entrypoint: /bin/sh + tty: true diff --git a/ksqldb/src/test/resources/log4j.properties b/ksqldb/src/test/resources/log4j.properties new file mode 100644 index 0000000000..31a98608fb --- /dev/null +++ b/ksqldb/src/test/resources/log4j.properties @@ -0,0 +1,6 @@ +log4j.rootLogger=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n \ No newline at end of file diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml new file mode 100644 index 0000000000..d463dd6dee --- /dev/null +++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web-app-deployment +spec: + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 2 + maxUnavailable: 1 + selector: + matchLabels: + app: web-app + replicas: 3 + template: + metadata: + labels: + app: web-app + spec: + containers: + - name: web-app + image: hello-world:nanoserver-1809 + volumeMounts: + - name: counter + mountPath: /app/ + volumes: + - name: counter + persistentVolumeClaim: + claimName: counter +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: counter +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 50Mi + storageClassName: default +--- +apiVersion: v1 +kind: Service +metadata: + name: web-app-service +spec: + ports: + - name: http + port: 80 + nodePort: 30080 + selector: + name: web-app + type: NodePort diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml new file mode 100644 index 0000000000..99bbb5d555 --- /dev/null +++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: counter +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 50Mi + storageClassName: default diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml new file mode 100644 index 0000000000..eabb12c0e0 --- /dev/null +++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: web-app-service +spec: + ports: + - name: http + port: 80 + nodePort: 30080 + selector: + name: web-app + type: NodePort diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml new file mode 100644 index 0000000000..59f137cff1 --- /dev/null +++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: myclaim +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 5Gi diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml new file mode 100644 index 0000000000..80e5b50665 --- /dev/null +++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: nginx + labels: + app: nginx +spec: + ports: + - port: 80 + name: web + clusterIP: None + selector: + app: nginx diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml new file mode 100644 index 0000000000..4ff4baca8c --- /dev/null +++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml @@ -0,0 +1,28 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: web +spec: + selector: + matchLabels: + app: nginx + serviceName: "nginx" + replicas: 3 + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx + ports: + - containerPort: 80 + name: web + volumeMounts: + - name: www + mountPath: /usr/share/nginx/html + volumes: + - name: www + persistentVolumeClaim: + claimName: myclaim diff --git a/kubernetes/k8s-admission-controller/Dockerfile b/kubernetes/k8s-admission-controller/Dockerfile new file mode 100644 index 0000000000..f8939ee7c8 --- /dev/null +++ b/kubernetes/k8s-admission-controller/Dockerfile @@ -0,0 +1,11 @@ +FROM adoptopenjdk:11-jre-hotspot as builder +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} application.jar +RUN java -Djarmode=layertools -jar application.jar extract + +FROM adoptopenjdk:11-jre-hotspot +COPY --from=builder dependencies/ ./ +COPY --from=builder snapshot-dependencies/ ./ +COPY --from=builder spring-boot-loader/ ./ +COPY --from=builder application/ ./ +ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"] \ No newline at end of file diff --git a/kubernetes/k8s-admission-controller/README.md b/kubernetes/k8s-admission-controller/README.md new file mode 100644 index 0000000000..c446ab403d --- /dev/null +++ b/kubernetes/k8s-admission-controller/README.md @@ -0,0 +1,3 @@ +## Relevant Articles: + +- [Creating a Kubertes Admission Controller in Java](https://www.baeldung.com/java-kubernetes-admission-controller) diff --git a/kubernetes/k8s-admission-controller/pom.xml b/kubernetes/k8s-admission-controller/pom.xml new file mode 100644 index 0000000000..18bf98a830 --- /dev/null +++ b/kubernetes/k8s-admission-controller/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + k8s-admission-controller + k8s-admission-controller + Demo project for Spring Boot + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ./../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + org.projectlombok + lombok + + + com.baeldung.kubernetes.admission.Application + + true + + + + + + + \ No newline at end of file diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java new file mode 100644 index 0000000000..1260ec0af9 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java @@ -0,0 +1,17 @@ +package com.baeldung.kubernetes.admission; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties; + +@SpringBootApplication +@EnableConfigurationProperties(AdmissionControllerProperties.class) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java new file mode 100644 index 0000000000..f9103d85ba --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java @@ -0,0 +1,18 @@ +/** + * + */ +package com.baeldung.kubernetes.admission.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import lombok.Data; + +@ConfigurationProperties(prefix = "admission-controller") +@Data +public class AdmissionControllerProperties { + + private boolean disabled; + private String annotation = "com.baeldung/wait-for-it"; + private String waitForItImage = "willwill/wait-for-it"; + +} diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java new file mode 100644 index 0000000000..21434535f9 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java @@ -0,0 +1,27 @@ +/** + * + */ +package com.baeldung.kubernetes.admission.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse; +import com.baeldung.kubernetes.admission.service.AdmissionService; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import lombok.RequiredArgsConstructor; +import reactor.core.publisher.Mono; + +@RestController +@RequiredArgsConstructor +public class AdmissionReviewController { + + private final AdmissionService admissionService; + + @PostMapping(path = "/mutate") + public Mono processAdmissionReviewRequest(@RequestBody Mono request) { + return request.map((body) -> admissionService.processAdmission(body)); + } +} diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java new file mode 100644 index 0000000000..6d590ff408 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java @@ -0,0 +1,31 @@ +/** + * + */ +package com.baeldung.kubernetes.admission.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import lombok.Builder; +import lombok.Data; + +/** + * Result sent to the API server after reviewing and, possibly + * modifying the incoming request + */ +@Builder +@Data +public class AdmissionReviewData { + + final String uid; + final boolean allowed; + + @JsonInclude(Include.NON_NULL) + final String patchType; + + @JsonInclude(Include.NON_NULL) + final String patch; + + @JsonInclude(Include.NON_NULL) + final AdmissionStatus status; +} diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java new file mode 100644 index 0000000000..07a3fa5b7a --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java @@ -0,0 +1,23 @@ +package com.baeldung.kubernetes.admission.dto; + +public class AdmissionReviewException extends RuntimeException { + + private static final long serialVersionUID = 1L; + private final int code; + + public AdmissionReviewException(int code, String message) { + super(message); + this.code = code; + } + + public AdmissionReviewException(String message) { + super(message); + this.code = 400; + + } + + public int getCode() { + return code; + } + +} diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java new file mode 100644 index 0000000000..1780f52540 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java @@ -0,0 +1,25 @@ +/** + * + */ +package com.baeldung.kubernetes.admission.dto; + +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Data; + +/** + * Response "envelope" sent back to the API Server + */ +@Builder +@Data +public class AdmissionReviewResponse { + + @Default + final String apiVersion = "admission.k8s.io/v1"; + + @Default + final String kind = "AdmissionReview"; + + final AdmissionReviewData response; + +} diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java new file mode 100644 index 0000000000..ccab7ac958 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java @@ -0,0 +1,13 @@ +package com.baeldung.kubernetes.admission.dto; + +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class AdmissionStatus { + + int code; + String message; + +} diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java new file mode 100644 index 0000000000..814bafbae7 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java @@ -0,0 +1,218 @@ +/** + * + */ +package com.baeldung.kubernetes.admission.service; + +import java.util.Base64; +import java.util.UUID; + +import org.springframework.stereotype.Component; + +import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties; +import com.baeldung.kubernetes.admission.dto.AdmissionReviewData; +import com.baeldung.kubernetes.admission.dto.AdmissionReviewException; +import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse; +import com.baeldung.kubernetes.admission.dto.AdmissionStatus; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * Process an incoming admission request and add the "wait-for-it" init container + * if there's an appropriate annotation + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class AdmissionService { + + private final AdmissionControllerProperties admissionControllerProperties; + private final ObjectMapper om; + + public AdmissionReviewResponse processAdmission(ObjectNode body) { + + String uid = body.path("request") + .required("uid") + .asText(); + + log.info("[I42] processAdmission: uid={}",uid); + if ( log.isDebugEnabled()) { + log.debug("processAdmission: body={}", body.toPrettyString()); + } + + // Get request annotations + JsonNode annotations = body.path("request") + .path("object") + .path("metadata") + .path("annotations"); + log.info("processAdmision: annotations={}", annotations.toString()); + + AdmissionReviewData data; + try { + if (admissionControllerProperties.isDisabled()) { + log.info("[I58] 'disabled' option in effect. No changes to current request will be made"); + data = createSimpleAllowedReview(body); + } else if (annotations.isMissingNode()) { + log.info("[I68] No annotations found in request. No changes will be made"); + data = createSimpleAllowedReview(body); + } else { + data = processAnnotations(body, annotations); + } + + log.info("[I65] Review result: isAllowed=" + data.isAllowed()); + log.info("[I64] AdmissionReviewData= {}", data); + + return AdmissionReviewResponse.builder() + .apiVersion(body.required("apiVersion").asText()) + .kind(body.required("kind").asText()) + .response(data) + .build(); + } catch (AdmissionReviewException ex) { + log.error("[E72] Error processing AdmissionRequest: code={}, message={}", ex.getCode(), ex.getMessage()); + data = createRejectedAdmissionReview(body, ex.getCode(), ex.getMessage()); + + return AdmissionReviewResponse.builder() + .apiVersion(body.required("apiVersion").asText()) + .kind(body.required("kind").asText()) + .response(data) + .build(); + } catch (Exception ex) { + log.error("[E72] Unable to process AdmissionRequest: " + ex.getMessage(), ex); + data = createRejectedAdmissionReview(body, 500, ex.getMessage()); + return AdmissionReviewResponse.builder() + .apiVersion(body.required("apiVersion").asText()) + .kind(body.required("kind").asText()) + .response(data) + .build(); + } + } + + /** + * @param body + * @return + */ + protected AdmissionReviewData createSimpleAllowedReview(ObjectNode body) { + AdmissionReviewData data; + String requestId = body.path("request") + .required("uid") + .asText(); + + data = AdmissionReviewData.builder() + .allowed(true) + .uid(requestId) + .build(); + + return data; + + } + + /** + * @param body + * @return + */ + protected AdmissionReviewData createRejectedAdmissionReview(ObjectNode body, int code, String message) { + AdmissionReviewData data; + String requestId = body.path("request") + .required("uid") + .asText(); + + AdmissionStatus status = AdmissionStatus.builder() + .code(code) + .message(message) + .build(); + + data = AdmissionReviewData.builder() + .allowed(false) + .uid(requestId) + .status(status) + .build(); + + return data; + + } + + /** + * Processa anotações incluídas no deployment + * @param annotations + * @return + */ + protected AdmissionReviewData processAnnotations(ObjectNode body, JsonNode annotations) { + + if (annotations.path(admissionControllerProperties.getAnnotation()) + .isMissingNode()) { + log.info("[I78] processAnnotations: Annotation {} not found in deployment deployment.", admissionControllerProperties.getAnnotation()); + return createSimpleAllowedReview(body); + } + else { + log.info("[I163] annotation found: {}", annotations.path(admissionControllerProperties.getAnnotation())); + } + + // Get wait-for-it arguments from the annotation + String waitForArgs = annotations.path(admissionControllerProperties.getAnnotation()) + .asText(); + + log.info("[I169] waitForArgs={}", waitForArgs); + // Create a PATCH object + String patch = injectInitContainer(body, waitForArgs); + + return AdmissionReviewData.builder() + .allowed(true) + .uid(body.path("request") + .required("uid") + .asText()) + .patch(Base64.getEncoder() + .encodeToString(patch.getBytes())) + .patchType("JSONPatch") + .build(); + + } + + /** + * Creates the JSONPatch to be included in the admission response + * @param body + * @param waitForArgs + * @return JSONPatch string + */ + protected String injectInitContainer(ObjectNode body, String waitForArgs) { + + // Recover original init containers from the request + JsonNode originalSpec = body.path("request") + .path("object") + .path("spec") + .path("template") + .path("spec") + .require(); + + JsonNode maybeInitContainers = originalSpec.path("initContainers"); + ArrayNode initContainers = + maybeInitContainers.isMissingNode()? + om.createArrayNode():(ArrayNode) maybeInitContainers; + + // Create the patch array + ArrayNode patchArray = om.createArrayNode(); + ObjectNode addNode = patchArray.addObject(); + + addNode.put("op", "add"); + addNode.put("path", "/spec/template/spec/initContainers"); + ArrayNode values = addNode.putArray("value"); + + // Preserve original init containers + values.addAll(initContainers); + + // append the "wait-for-it" container + ObjectNode wfi = values.addObject(); + wfi.put("name", "wait-for-it-" + UUID.randomUUID()); // Create an unique name, JIC + wfi.put("image", admissionControllerProperties.getWaitForItImage()); + + ArrayNode args = wfi.putArray("args"); + for (String s : waitForArgs.split("\\s")) { + args.add(s); + } + + return patchArray.toString(); + } +} diff --git a/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java b/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java new file mode 100644 index 0000000000..544b48a9d4 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java @@ -0,0 +1,65 @@ +package com.baeldung.kubernetes.admission.service; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Base64; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties; +import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +@SpringBootTest +@ActiveProfiles("test") +@EnableConfigurationProperties(AdmissionControllerProperties.class) +class AdmissionServiceUnitTest { + + @Autowired + private ObjectMapper mapper; + + @Autowired + private AdmissionService admissionService; + + @Test + void whenAnnotationPresent_thenAddContainer() throws Exception { + + InputStream is = this.getClass() + .getClassLoader() + .getResourceAsStream("test1.json"); + JsonNode body = mapper.readTree(is); + AdmissionReviewResponse response = admissionService.processAdmission((ObjectNode) body); + assertNotNull(response); + assertNotNull(response.getResponse()); + assertNotNull(response.getResponse()); + assertTrue(response.getResponse() + .isAllowed()); + + String jsonResponse = mapper.writeValueAsString(response); + System.out.println(jsonResponse); + + // Decode Patch data + String b64patch = response.getResponse() + .getPatch(); + assertNotNull(b64patch); + byte[] patch = Base64.getDecoder() + .decode(b64patch); + + JsonNode root = mapper.reader() + .readTree(new ByteArrayInputStream(patch)); + assertTrue(root instanceof ArrayNode); + + assertEquals(1, ((ArrayNode) root).size()); + + } + +} diff --git a/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml b/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml new file mode 100644 index 0000000000..0c601660d1 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend + labels: + app: nginx + annotations: + com.baeldung/wait-for-it: "www.google.com:80" +spec: + replicas: 1 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test1.json b/kubernetes/k8s-admission-controller/src/test/resources/test1.json new file mode 100644 index 0000000000..0ad42ae133 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/resources/test1.json @@ -0,0 +1,84 @@ +{ + "kind": "AdmissionReview", + "apiVersion": "admission.k8s.io/v1", + "request": { + "uid": "c46a6607-129d-425b-af2f-c6f87a0756da", + "kind": { + "group": "apps", + "version": "v1", + "kind": "Deployment" + }, + "resource": { + "group": "apps", + "version": "v1", + "resource": "deployments" + }, + "requestKind": { + "group": "apps", + "version": "v1", + "kind": "Deployment" + }, + "requestResource": { + "group": "apps", + "version": "v1", + "resource": "deployments" + }, + "name": "test-deployment", + "namespace": "test-namespace", + "operation": "CREATE", + "object": { + "kind": "Deployment", + "apiVersion": "apps/v1", + "metadata": { + "name": "test-deployment", + "namespace": "test-namespace", + "annotations": { + "com.baeldung/wait-for-it": "www.google.com:80" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "app": "test-app" + } + }, + "template": { + "metadata": { + "name": "test-app", + "creationTimestamp": null, + "labels": { + "app": "test-app" + } + }, + "spec": { + "containers": [ + { + "name": "app", + "image": "test-app-image:latest", + "ports": [ + { + "name": "http", + "containerPort": 8080, + "protocol": "TCP" + } + ], + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ] + } + } + }, + "status": {} + }, + "oldObject": null, + "dryRun": false, + "options": { + "kind": "CreateOptions", + "apiVersion": "meta.k8s.io/v1" + } + } +} \ No newline at end of file diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test2.json b/kubernetes/k8s-admission-controller/src/test/resources/test2.json new file mode 100644 index 0000000000..5b8a6ca243 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/resources/test2.json @@ -0,0 +1,85 @@ +{ + "kind": "AdmissionReview", + "apiVersion": "admission.k8s.io/v1", + "request": { + "uid": "c46a6607-129d-425b-af2f-c6f87a0756da", + "kind": { + "group": "apps", + "version": "v1", + "kind": "Deployment" + }, + "resource": { + "group": "apps", + "version": "v1", + "resource": "deployments" + }, + "requestKind": { + "group": "apps", + "version": "v1", + "kind": "Deployment" + }, + "requestResource": { + "group": "apps", + "version": "v1", + "resource": "deployments" + }, + "name": "test-deployment", + "namespace": "test-namespace", + "operation": "CREATE", + "object": { + "kind": "Deployment", + "apiVersion": "apps/v1", + "metadata": { + "name": "test-deployment", + "namespace": "test-namespace", + "annotations": { + "com.baeldung/wait-for-it": "www.google.com:80" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "app": "test-app" + } + }, + "template": { + "metadata": { + "name": "test-app", + "creationTimestamp": null, + "labels": { + "app": "test-app" + } + }, + "spec": { + "initContainers": [], + "containers": [ + { + "name": "app", + "image": "test-app-image:latest", + "ports": [ + { + "name": "http", + "containerPort": 8080, + "protocol": "TCP" + } + ], + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ] + } + } + }, + "status": {} + }, + "oldObject": null, + "dryRun": false, + "options": { + "kind": "CreateOptions", + "apiVersion": "meta.k8s.io/v1" + } + } +} \ No newline at end of file diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test3.json b/kubernetes/k8s-admission-controller/src/test/resources/test3.json new file mode 100644 index 0000000000..9cb4d516f9 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/resources/test3.json @@ -0,0 +1,94 @@ +{ + "kind": "AdmissionReview", + "apiVersion": "admission.k8s.io/v1", + "request": { + "uid": "c46a6607-129d-425b-af2f-c6f87a0756da", + "kind": { + "group": "apps", + "version": "v1", + "kind": "Deployment" + }, + "resource": { + "group": "apps", + "version": "v1", + "resource": "deployments" + }, + "requestKind": { + "group": "apps", + "version": "v1", + "kind": "Deployment" + }, + "requestResource": { + "group": "apps", + "version": "v1", + "resource": "deployments" + }, + "name": "test-deployment", + "namespace": "test-namespace", + "operation": "CREATE", + "object": { + "kind": "Deployment", + "apiVersion": "apps/v1", + "metadata": { + "name": "test-deployment", + "namespace": "test-namespace", + "annotations": { + "com.baeldung/wait-for-it": "www.google.com:80" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "app": "test-app" + } + }, + "template": { + "metadata": { + "name": "test-app", + "creationTimestamp": null, + "labels": { + "app": "test-app" + } + }, + "spec": { + "initContainers": [ + { + "name": "init1", + "image": "test-app-image:latest", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ], + "containers": [ + { + "name": "app", + "image": "test-app-image:latest", + "ports": [ + { + "name": "http", + "containerPort": 8080, + "protocol": "TCP" + } + ], + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ] + } + } + }, + "status": {} + }, + "oldObject": null, + "dryRun": false, + "options": { + "kind": "CreateOptions", + "apiVersion": "meta.k8s.io/v1" + } + } +} \ No newline at end of file diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test4.json b/kubernetes/k8s-admission-controller/src/test/resources/test4.json new file mode 100644 index 0000000000..1e3be90a93 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/resources/test4.json @@ -0,0 +1,48 @@ +{ + "kind": "AdmissionReview", + "apiVersion": "admission.k8s.io/v1", + "request": { + "uid": "26beb334-739a-48d2-b04d-25f6e5e7c106", + "kind": { + "group": "apps", + "version": "v1", + "kind": "Deployment" + }, + "resource": {}, + "f:type": {} + }, + "f:template": { + "f:metadata": { + "f:labels": { + ".": {}, + "f:app": {} + } + }, + "f:spec": { + "f:containers": { + "k:{\"name\":\"nginx\"}": { + ".": {}, + "f:image": {}, + "f:imagePullPolicy": {}, + "f:name": {}, + "f:ports": { + ".": {}, + "k:{\"containerPort\":80,\"protocol\":\"TCP\"}": { + ".": {}, + "f:containerPort": {}, + "f:protocol": {} + } + }, + "f:resources": {}, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {} + } + }, + "f:dnsPolicy": {}, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:securityContext": {}, + "f:terminationGracePeriodSeconds": {} + } + } +} \ No newline at end of file diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore b/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore new file mode 100644 index 0000000000..4c305a080a --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore @@ -0,0 +1,3 @@ +.terraform +terraform.tfstate +terraform.tfstate.backup diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl b/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl new file mode 100644 index 0000000000..e13c37b4ec --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl @@ -0,0 +1,57 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.3.2" + constraints = "2.3.2" + hashes = [ + "h1:D8HWX3vouTPI3Jicq43xOQyoYWtSsVua92cBVrJ3ZMs=", + "zh:10f71c170be13538374a4b9553fcb3d98a6036bcd1ca5901877773116c3f828e", + "zh:11d2230e531b7480317e988207a73cb67b332f225b0892304983b19b6014ebe0", + "zh:3317387a9a6cc27fd7536b8f3cad4b8a9285e9461f125c5a15d192cef3281856", + "zh:458a9858362900fbe97e00432ae8a5bef212a4dacf97a57ede7534c164730da4", + "zh:50ea297007d9fe53e5411577f87a4b13f3877ce732089b42f938430e6aadff0d", + "zh:56705c959e4cbea3b115782d04c62c68ac75128c5c44ee7aa4043df253ffbfe3", + "zh:7eb3722f7f036e224824470c3e0d941f1f268fcd5fa2f8203e0eee425d0e1484", + "zh:9f408a6df4d74089e6ce18f9206b06b8107ddb57e2bc9b958a6b7dc352c62980", + "zh:aadd25ccc3021040808feb2645779962f638766eb583f586806e59f24dde81bb", + "zh:b101c3456e4309b09aab129b0118561178c92cb4be5d96dec553189c3084dca1", + "zh:ec08478573b4953764099fbfd670fae81dc24b60e467fb3b023e6fab50b70a9e", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.1.0" + hashes = [ + "h1:SFT7X3zY18CLWjoH2GfQyapxsRv6GDKsy9cF1aRwncc=", + "zh:02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2", + "zh:53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515", + "zh:5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521", + "zh:9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2", + "zh:a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e", + "zh:a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53", + "zh:c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d", + "zh:cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8", + "zh:e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70", + "zh:fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b", + "zh:fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e", + ] +} + +provider "registry.terraform.io/hashicorp/tls" { + version = "3.1.0" + hashes = [ + "h1:ekOxs6MjdIElt8h9crEVaOwWbEqtfUUfArtA13Jkk6A=", + "zh:3d46616b41fea215566f4a957b6d3a1aa43f1f75c26776d72a98bdba79439db6", + "zh:623a203817a6dafa86f1b4141b645159e07ec418c82fe40acd4d2a27543cbaa2", + "zh:668217e78b210a6572e7b0ecb4134a6781cc4d738f4f5d09eb756085b082592e", + "zh:95354df03710691773c8f50a32e31fca25f124b7f3d6078265fdf3c4e1384dca", + "zh:9f97ab190380430d57392303e3f36f4f7835c74ea83276baa98d6b9a997c3698", + "zh:a16f0bab665f8d933e95ca055b9c8d5707f1a0dd8c8ecca6c13091f40dc1e99d", + "zh:be274d5008c24dc0d6540c19e22dbb31ee6bfdd0b2cddd4d97f3cd8a8d657841", + "zh:d5faa9dce0a5fc9d26b2463cea5be35f8586ab75030e7fa4d4920cd73ee26989", + "zh:e9b672210b7fb410780e7b429975adcc76dd557738ecc7c890ea18942eb321a5", + "zh:eb1f8368573d2370605d6dbf60f9aaa5b64e55741d96b5fb026dbfe91de67c0d", + "zh:fc1e12b713837b85daf6c3bb703d7795eaf1c5177aebae1afcf811dd7009f4b0", + ] +} diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars b/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars new file mode 100644 index 0000000000..eb1cdaea53 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars @@ -0,0 +1,10 @@ +# +# Sample variable values. +# +namespace="default" +deployment_name="wait-for-it-admission-controller" +replicas=1 +image="psevestre/wait-for-it-admission-controller" +image_prefix="" +image_version="latest" +k8s_config_context="minikube" diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/main.tf b/kubernetes/k8s-admission-controller/src/test/terraform/main.tf new file mode 100644 index 0000000000..a0717c4013 --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/terraform/main.tf @@ -0,0 +1,277 @@ + +locals { + prefix = var.image_prefix != "" ? "${var.image_prefix}/":"" + image = "${local.prefix}${var.image}:${var.image_version}" + cloud_sdk_image = "${local.prefix}frapsoft/openssl" + ns = data.kubernetes_namespace.ns.metadata[0].name + + # Spring SSL Configuration + webhook_config_json = jsonencode({ + server = { + port = 443 + ssl = { + "key-store" = "/shared-config/webhook.p12" + "key-store-type" = "PKCS12" + "key-alias" = "webhook" + "key-store-password" = "" + } + } + + admission-controller = { + disabled = false + image-prefix = "gcr.io/sandboxbv-01" + } + }) + +} + + +# Resource namespace +data "kubernetes_namespace" "ns" { + metadata { + name = var.namespace + } +} + +# TLS Key +resource "tls_private_key" "tls" { + algorithm = "RSA" +} + +# CSR +resource "tls_cert_request" "tls" { + key_algorithm = "RSA" + private_key_pem = tls_private_key.tls.private_key_pem + subject { + common_name = "${var.deployment_name}.${var.namespace}.svc" + } + + dns_names = [ + var.deployment_name, + "${var.deployment_name}.${var.namespace}", + "${var.deployment_name}.${var.namespace}.svc", + "${var.deployment_name}.${var.namespace}.svc.cluster.local" + ] + +} + +# HTTPS Certificate +resource "tls_self_signed_cert" "tls" { + key_algorithm = tls_private_key.tls.algorithm + private_key_pem = tls_private_key.tls.private_key_pem + + subject { + common_name = "${var.deployment_name}.${local.ns}" + } + + validity_period_hours = 24*365*20 + + dns_names = [ + var.deployment_name, + "${var.deployment_name}.${var.namespace}", + "${var.deployment_name}.${var.namespace}.svc", + "${var.deployment_name}.${var.namespace}.svc.cluster.local" + ] + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth" + ] +} + +# Certificado +# Obs: Desativado pois o certificado fica preso no estado "Issued" +resource "kubernetes_certificate_signing_request" "tls" { + count = 0 + metadata { + name = "${var.deployment_name}.${var.namespace}" + } + + auto_approve = true + + spec { + usages = [ + "key encipherment", + "digital signature", + "server auth" + ] + + signer_name = "kubernetes.io/kubelet-serving" + + request = tls_cert_request.tls.cert_request_pem + } + +} + +# Secret to store TLS key/cert +resource "kubernetes_secret" "tls" { + metadata { + namespace = local.ns + name = var.deployment_name + } + + data = { + "webhook-key.pem" = tls_private_key.tls.private_key_pem + "webhook-cert.pem" = tls_self_signed_cert.tls.cert_pem + } + +} + +# Deployment +resource "kubernetes_deployment" "main" { + metadata { + name = var.deployment_name + namespace = local.ns + } + + spec { + replicas = var.replicas + selector { + match_labels = { + app = var.deployment_name + } + } + + template { + metadata { + labels = { + app = var.deployment_name + } + } + + spec { + container { + image = local.image + name = var.deployment_name + volume_mount { + mount_path = "/shared-config" + name = "shared-config" + } + + env { + name = "SPRING_APPLICATION_JSON" + value = local.webhook_config_json + } + + } + + init_container { + name = "setup-keystore" + image = local.cloud_sdk_image + + args = [ + "pkcs12", "-export", + "-in", "/secret/webhook-cert.pem", + "-inkey", "/secret/webhook-key.pem", + "-name", "webhook", + "-out", "/shared-config/webhook.p12", + "-passout", "pass:" + ] + + volume_mount { + mount_path = "/shared-config" + name = "shared-config" + } + + volume_mount { + mount_path = "/secret/webhook-cert.pem" + name = "webhook-secret" + sub_path = "webhook-cert.pem" + } + + volume_mount { + mount_path = "/secret/webhook-key.pem" + name = "webhook-secret" + sub_path = "webhook-key.pem" + } + + } + + volume { + name = "shared-config" + empty_dir {} + } + + volume { + name = "webhook-secret" + secret { + secret_name = kubernetes_secret.tls.metadata[0].name + items { + key = "webhook-cert.pem" + path = "webhook-cert.pem" + } + items { + key = "webhook-key.pem" + path = "webhook-key.pem" + } + } + } + + } + } + } +} + +# Service +resource "kubernetes_service" "svc" { + metadata { + name = var.deployment_name + namespace = local.ns + } + + spec { + selector = { + "app" = var.deployment_name + } + + port { + name = "https" + port = 443 + protocol = "TCP" + target_port = 443 + } + + type = "ClusterIP" + } +} + +# Admission Controller +resource "kubernetes_mutating_webhook_configuration" "waitforit" { + metadata { + name = var.deployment_name + } + + webhook { + name = var.admission_controller_name + admission_review_versions = [ "v1", "v1beta1" ] + + #failure_policy = "Ignore" # + + client_config { + + service { + name = kubernetes_service.svc.metadata[0].name + namespace = local.ns + path = "/mutate" + port = 443 + } + + # IMPORTANT: CA_BUNDLE must be Base64-encoded + ca_bundle = tls_self_signed_cert.tls.cert_pem + } + + rule { + api_groups = [ "*" ] + api_versions = [ "*" ] + operations = [ "CREATE", "UPDATE" ] + resources = [ "deployments", "statefulsets" ] + } + + side_effects = "None" # + } + + depends_on = [ + kubernetes_deployment.main + ] +} diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf b/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf new file mode 100644 index 0000000000..eb095caa0e --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf @@ -0,0 +1,14 @@ +terraform { + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.3.2" + } + } +} + +# Use standard kubectl environment to get connection details +provider "kubernetes" { + config_context = var.k8s_config_context + config_path = var.k8s_config_path +} \ No newline at end of file diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf b/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf new file mode 100644 index 0000000000..33ca735f0d --- /dev/null +++ b/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf @@ -0,0 +1,50 @@ +variable "namespace" { + type = string + description = "Namespace where the Admission Controller will be deploymed" +} + +variable "deployment_name" { + type = string + description = "Admission Controller Deployment Name" +} + +variable "replicas" { + type = number + description = "Number of replicas used in the deployment" + default = 3 +} + +variable "image" { + type = string + description = "Admission Controller image name" +} + +variable "image_version" { + type = string + description = "Admission Controller image version name" + default = "latest" +} + +variable "image_prefix" { + type = string + description = "Image repository prefix" + default = "gcr.io/baeldung" +} + +variable "admission_controller_name" { + type = string + description = "Admission Controller name" + default = "wait-for-it.service.local" +} + +variable "k8s_config_context" { + type = string + description = "Name of the K8S config context" +} + +variable "k8s_config_path" { + type = string + description = "Location of the standard K8S configuration" + default = "~/.kube/config" + +} \ No newline at end of file diff --git a/kubernetes/k8s-intro/README.md b/kubernetes/k8s-intro/README.md index 6ac593452c..8c11f4d53e 100644 --- a/kubernetes/k8s-intro/README.md +++ b/kubernetes/k8s-intro/README.md @@ -17,3 +17,4 @@ If you get a valid response, then you're good to go. - [Paging and Async Calls with the Kubernetes API](https://www.baeldung.com/java-kubernetes-paging-async) - [Using Watch with the Kubernetes API](https://www.baeldung.com/java-kubernetes-watch) - [Using Namespaces and Selectors With the Kubernetes Java API](https://www.baeldung.com/java-kubernetes-namespaces-selectors) +- [Creating, Updating and Deleting Resources with the Java Kubernetes API](https://www.baeldung.com/java-kubernetes-api-crud) diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 72cb1577a4..2927d449d8 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -1,3 +1,4 @@ + @@ -13,5 +14,7 @@ k8s-intro + k8s-admission-controller + \ No newline at end of file diff --git a/libraries-2/pom.xml b/libraries-2/pom.xml index 28a7fb62fd..104d3cbabc 100644 --- a/libraries-2/pom.xml +++ b/libraries-2/pom.xml @@ -130,7 +130,7 @@ 0.28.3 1.1.0 4.1.2 - 6.6.0 + 6.17.0 5.1.9.RELEASE 2.5.0 diff --git a/libraries-3/pom.xml b/libraries-3/pom.xml index 079628ffaa..0ff89b046f 100644 --- a/libraries-3/pom.xml +++ b/libraries-3/pom.xml @@ -230,4 +230,5 @@ 2.8 2.1.3 + \ No newline at end of file diff --git a/libraries-4/pom.xml b/libraries-4/pom.xml index 756bfbd3a8..b16d1f216f 100644 --- a/libraries-4/pom.xml +++ b/libraries-4/pom.xml @@ -26,6 +26,17 @@ com.haulmont.yarg yarg ${yarg.version} + + + org.olap4j + olap4j + + + + + org.olap4j + olap4j + ${olap4j.version} net.engio @@ -112,6 +123,7 @@ 0.6.5 3.0.0 2.2.4 + 1.2.0 \ No newline at end of file diff --git a/libraries-6/pom.xml b/libraries-6/pom.xml index 289597adc9..8d05d2013c 100644 --- a/libraries-6/pom.xml +++ b/libraries-6/pom.xml @@ -123,9 +123,9 @@ - bedatadriven - bedatadriven public repo - https://nexus.bedatadriven.com/content/groups/public/ + mulesoft + Mulesoft Repository + https://repository.mulesoft.org/nexus/content/repositories/public/ diff --git a/libraries-6/src/test/java/com/baeldung/kafkastreams/KafkaStreamsLiveTest.java b/libraries-6/src/test/java/com/baeldung/kafkastreams/KafkaStreamsLiveTest.java deleted file mode 100644 index e61f4158a7..0000000000 --- a/libraries-6/src/test/java/com/baeldung/kafkastreams/KafkaStreamsLiveTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.baeldung.kafkastreams; - -import org.apache.kafka.clients.consumer.ConsumerConfig; -import org.apache.kafka.common.serialization.Serde; -import org.apache.kafka.common.serialization.Serdes; -import org.apache.kafka.streams.KafkaStreams; -import org.apache.kafka.streams.StreamsBuilder; -import org.apache.kafka.streams.StreamsConfig; -import org.apache.kafka.streams.Topology; -import org.apache.kafka.streams.kstream.KStream; -import org.apache.kafka.streams.kstream.KTable; -import org.apache.kafka.streams.kstream.Produced; -import org.apache.kafka.test.TestUtils; -import org.junit.Ignore; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Properties; -import java.util.regex.Pattern; - -public class KafkaStreamsLiveTest { - private String bootstrapServers = "localhost:9092"; - - @Test - @Ignore("it needs to have kafka broker running on local") - public void shouldTestKafkaStreams() throws InterruptedException { - // given - String inputTopic = "inputTopic"; - - Properties streamsConfiguration = new Properties(); - streamsConfiguration.put(StreamsConfig.APPLICATION_ID_CONFIG, "wordcount-live-test"); - streamsConfiguration.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); - streamsConfiguration.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName()); - streamsConfiguration.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName()); - streamsConfiguration.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 1000); - streamsConfiguration.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); - // Use a temporary directory for storing state, which will be automatically removed after the test. - streamsConfiguration.put(StreamsConfig.STATE_DIR_CONFIG, TestUtils.tempDirectory().getAbsolutePath()); - - // when - StreamsBuilder builder = new StreamsBuilder(); - KStream textLines = builder.stream(inputTopic); - Pattern pattern = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS); - - KTable wordCounts = textLines.flatMapValues(value -> Arrays.asList(pattern.split(value.toLowerCase()))).groupBy((key, word) -> word).count(); - - textLines.foreach((word, count) -> System.out.println("word: " + word + " -> " + count)); - - String outputTopic = "outputTopic"; - final Serde stringSerde = Serdes.String(); - final Serde longSerde = Serdes.String(); - textLines.to(outputTopic, Produced.with(stringSerde,longSerde)); - - KafkaStreams streams = new KafkaStreams(new Topology(), streamsConfiguration); - streams.start(); - - // then - Thread.sleep(30000); - streams.close(); - } -} diff --git a/libraries-data-3/README.md b/libraries-data-3/README.md index fffdf65252..d3bfb2c80c 100644 --- a/libraries-data-3/README.md +++ b/libraries-data-3/README.md @@ -3,7 +3,8 @@ This module contains articles about libraries for data processing in Java. ### Relevant articles -- [Kafka Streams vs Kafka Consumer]() +- [Kafka Streams vs Kafka Consumer](https://www.baeldung.com/java-kafka-streams-vs-kafka-consumer) +- [Kafka Topic Creation Using Java](https://www.baeldung.com/kafka-topic-creation) - More articles: [[<-- prev]](/../libraries-data-2) ##### Building the project diff --git a/libraries-data-3/pom.xml b/libraries-data-3/pom.xml index bfc39e537e..37d5c7ca0d 100644 --- a/libraries-data-3/pom.xml +++ b/libraries-data-3/pom.xml @@ -17,8 +17,6 @@ org.apache.kafka kafka-clients ${kafka.version} - test - test org.apache.kafka @@ -47,6 +45,12 @@ ${testcontainers-kafka.version} test + + org.testcontainers + junit-jupiter + ${testcontainers-jupiter.version} + test + @@ -54,6 +58,7 @@ 1.7.25 2.8.0 1.15.3 + 1.15.3 \ No newline at end of file diff --git a/libraries-data-3/src/main/java/com/baeldung/kafka/admin/KafkaTopicApplication.java b/libraries-data-3/src/main/java/com/baeldung/kafka/admin/KafkaTopicApplication.java new file mode 100644 index 0000000000..0d74e27d4e --- /dev/null +++ b/libraries-data-3/src/main/java/com/baeldung/kafka/admin/KafkaTopicApplication.java @@ -0,0 +1,87 @@ +package com.baeldung.kafka.admin; + +import org.apache.kafka.clients.admin.Admin; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.CreateTopicsOptions; +import org.apache.kafka.clients.admin.CreateTopicsResult; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.common.KafkaFuture; +import org.apache.kafka.common.config.TopicConfig; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class KafkaTopicApplication { + + private final Properties properties; + + public KafkaTopicApplication(Properties properties) { + this.properties = properties; + } + + public void createTopic(String topicName) throws Exception { + try (Admin admin = Admin.create(properties)) { + int partitions = 1; + short replicationFactor = 1; + NewTopic newTopic = new NewTopic(topicName, partitions, replicationFactor); + + CreateTopicsResult result = admin.createTopics( + Collections.singleton(newTopic)); + + // get the async result for the new topic creation + KafkaFuture future = result.values().get(topicName); + + // call get() to block until topic creation has completed or failed + future.get(); + } + } + + public void createTopicWithOptions(String topicName) throws Exception { + Properties props = new Properties(); + props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + + try (Admin admin = Admin.create(props)) { + int partitions = 1; + short replicationFactor = 1; + NewTopic newTopic = new NewTopic(topicName, partitions, replicationFactor); + + CreateTopicsOptions topicOptions = new CreateTopicsOptions() + .validateOnly(true) + .retryOnQuotaViolation(true); + + CreateTopicsResult result = admin.createTopics( + Collections.singleton(newTopic), topicOptions + ); + + KafkaFuture future = result.values().get(topicName); + future.get(); + } + } + + public void createCompactedTopicWithCompression(String topicName) throws Exception { + Properties props = new Properties(); + props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + + try (Admin admin = Admin.create(props)) { + int partitions = 1; + short replicationFactor = 1; + + // Create a compacted topic with 'lz4' compression codec + Map newTopicConfig = new HashMap<>(); + newTopicConfig.put(TopicConfig.CLEANUP_POLICY_CONFIG, TopicConfig.CLEANUP_POLICY_COMPACT); + newTopicConfig.put(TopicConfig.COMPRESSION_TYPE_CONFIG, "lz4"); + NewTopic newTopic = new NewTopic(topicName, partitions, replicationFactor) + .configs(newTopicConfig); + + CreateTopicsResult result = admin.createTopics( + Collections.singleton(newTopic) + ); + + KafkaFuture future = result.values().get(topicName); + future.get(); + } + } + +} diff --git a/libraries-data-3/src/test/java/com/baeldung/kafka/admin/KafkaTopicApplicationIntegrationTest.java b/libraries-data-3/src/test/java/com/baeldung/kafka/admin/KafkaTopicApplicationIntegrationTest.java new file mode 100644 index 0000000000..d79f6af7c1 --- /dev/null +++ b/libraries-data-3/src/test/java/com/baeldung/kafka/admin/KafkaTopicApplicationIntegrationTest.java @@ -0,0 +1,40 @@ +package com.baeldung.kafka.admin; + +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +@Testcontainers +class KafkaTopicApplicationIntegrationTest { + + @Container + private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")); + + private KafkaTopicApplication kafkaTopicApplication; + + @BeforeEach + void setup() { + Properties properties = new Properties(); + properties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + kafkaTopicApplication = new KafkaTopicApplication(properties); + } + + @Test + void givenTopicName_whenCreateNewTopic_thenTopicIsCreated() throws Exception { + kafkaTopicApplication.createTopic("test-topic"); + + String topicCommand = "/usr/bin/kafka-topics --bootstrap-server=localhost:9092 --list"; + String stdout = KAFKA_CONTAINER.execInContainer("/bin/sh", "-c", topicCommand) + .getStdout(); + + assertThat(stdout).contains("test-topic"); + } +} diff --git a/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java b/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java index 4b35a94eb5..cce8b78808 100644 --- a/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java +++ b/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java @@ -21,7 +21,7 @@ public class CustomerService { final ObjectMapper mapper = new ObjectMapper(); final Customer customer = mapper.convertValue(customerData, Customer.class); - if (Operation.DELETE.name().equals(operation.name())) { + if (Operation.DELETE == operation) { customerRepository.deleteById(customer.getId()); } else { customerRepository.save(customer); diff --git a/libraries-data-io/README.md b/libraries-data-io/README.md index 3e68334ec9..16c7cc66eb 100644 --- a/libraries-data-io/README.md +++ b/libraries-data-io/README.md @@ -10,3 +10,4 @@ This module contains articles about IO data processing libraries. - [Interact with Google Sheets from Java](https://www.baeldung.com/google-sheets-java-client) - [Introduction To Docx4J](https://www.baeldung.com/docx4j) - [Breaking YAML Strings Over Multiple Lines](https://www.baeldung.com/yaml-multi-line) +- [Different Serialization Approaches for Java](https://www.baeldung.com/java-serialization-approaches) diff --git a/libraries-data-io/pom.xml b/libraries-data-io/pom.xml index 58bfde9aa0..1335ba54d1 100644 --- a/libraries-data-io/pom.xml +++ b/libraries-data-io/pom.xml @@ -55,6 +55,46 @@ docx4j ${docx4j.version} + + + com.google.code.gson + gson + ${gson.version} + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + + com.esotericsoftware.yamlbeans + yamlbeans + ${yamlbeans.version} + + + + org.apache.thrift + libthrift + ${apache-thrift.version} + + + + com.google.protobuf + protobuf-java + ${google-protobuf.version} + org.assertj assertj-core @@ -73,6 +113,11 @@ 3.9.0 3.3.5 2.1 + 2.8.7 + 2.12.3 + 1.15 + 0.14.2 + 3.17.3 \ No newline at end of file diff --git a/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java new file mode 100644 index 0000000000..27eadfb5f6 --- /dev/null +++ b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java @@ -0,0 +1,32 @@ +package com.baeldung.serialization.protocols; + +import java.io.Serializable; + +public class User implements Serializable { + + private static final long serialVersionUID = 1L; + + private int id; + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "User [id=" + id + ", name=" + name + "]"; + } +} \ No newline at end of file diff --git a/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java new file mode 100644 index 0000000000..adefabb7e8 --- /dev/null +++ b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java @@ -0,0 +1,636 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: user.proto + +package com.baeldung.serialization.protocols; + +public final class UserProtos { + private UserProtos() { + } + + public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry); + } + + public interface UserOrBuilder extends + // @@protoc_insertion_point(interface_extends:protobuf.User) + com.google.protobuf.MessageOrBuilder { + + /** + * int32 id = 1; + * @return The id. + */ + int getId(); + + /** + * string name = 2; + * @return The name. + */ + java.lang.String getName(); + + /** + * string name = 2; + * @return The bytes for name. + */ + com.google.protobuf.ByteString getNameBytes(); + } + + /** + * Protobuf type {@code protobuf.User} + */ + public static final class User extends com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:protobuf.User) + UserOrBuilder { + private static final long serialVersionUID = 0L; + + // Use User.newBuilder() to construct. + private User(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private User() { + name_ = ""; + } + + @java.lang.Override + @SuppressWarnings({ "unused" }) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new User(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet getUnknownFields() { + return this.unknownFields; + } + + private User(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + id_ = input.readInt32(); + break; + } + case 18: { + java.lang.String s = input.readStringRequireUtf8(); + + name_ = s; + break; + } + default: { + if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { + return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_fieldAccessorTable.ensureFieldAccessorsInitialized(com.baeldung.serialization.protocols.UserProtos.User.class, + com.baeldung.serialization.protocols.UserProtos.User.Builder.class); + } + + public static final int ID_FIELD_NUMBER = 1; + private int id_; + + /** + * int32 id = 1; + * @return The id. + */ + @java.lang.Override + public int getId() { + return id_; + } + + public static final int NAME_FIELD_NUMBER = 2; + private volatile java.lang.Object name_; + + /** + * string name = 2; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + + /** + * string name = 2; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) + return true; + if (isInitialized == 0) + return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (id_ != 0) { + output.writeInt32(1, id_); + } + if (!getNameBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, name_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) + return size; + + size = 0; + if (id_ != 0) { + size += com.google.protobuf.CodedOutputStream.computeInt32Size(1, id_); + } + if (!getNameBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, name_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.baeldung.serialization.protocols.UserProtos.User)) { + return super.equals(obj); + } + com.baeldung.serialization.protocols.UserProtos.User other = (com.baeldung.serialization.protocols.UserProtos.User) obj; + + if (getId() != other.getId()) + return false; + if (!getName().equals(other.getName())) + return false; + if (!unknownFields.equals(other.unknownFields)) + return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseDelimitedFrom(java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(com.baeldung.serialization.protocols.UserProtos.User prototype) { + return DEFAULT_INSTANCE.toBuilder() + .mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + + /** + * Protobuf type {@code protobuf.User} + */ + public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:protobuf.User) + com.baeldung.serialization.protocols.UserProtos.UserOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { + return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_fieldAccessorTable.ensureFieldAccessorsInitialized(com.baeldung.serialization.protocols.UserProtos.User.class, + com.baeldung.serialization.protocols.UserProtos.User.Builder.class); + } + + // Construct using com.baeldung.serialization.compare.UserProtos.User.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { + } + } + + @java.lang.Override + public Builder clear() { + super.clear(); + id_ = 0; + + name_ = ""; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor; + } + + @java.lang.Override + public com.baeldung.serialization.protocols.UserProtos.User getDefaultInstanceForType() { + return com.baeldung.serialization.protocols.UserProtos.User.getDefaultInstance(); + } + + @java.lang.Override + public com.baeldung.serialization.protocols.UserProtos.User build() { + com.baeldung.serialization.protocols.UserProtos.User result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.baeldung.serialization.protocols.UserProtos.User buildPartial() { + com.baeldung.serialization.protocols.UserProtos.User result = new com.baeldung.serialization.protocols.UserProtos.User(this); + result.id_ = id_; + result.name_ = name_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.baeldung.serialization.protocols.UserProtos.User) { + return mergeFrom((com.baeldung.serialization.protocols.UserProtos.User) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.baeldung.serialization.protocols.UserProtos.User other) { + if (other == com.baeldung.serialization.protocols.UserProtos.User.getDefaultInstance()) + return this; + if (other.getId() != 0) { + setId(other.getId()); + } + if (!other.getName() + .isEmpty()) { + name_ = other.name_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { + com.baeldung.serialization.protocols.UserProtos.User parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (com.baeldung.serialization.protocols.UserProtos.User) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private int id_; + + /** + * int32 id = 1; + * @return The id. + */ + @java.lang.Override + public int getId() { + return id_; + } + + /** + * int32 id = 1; + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId(int value) { + + id_ = value; + onChanged(); + return this; + } + + /** + * int32 id = 1; + * @return This builder for chaining. + */ + public Builder clearId() { + + id_ = 0; + onChanged(); + return this; + } + + private java.lang.Object name_ = ""; + + /** + * string name = 2; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + + /** + * string name = 2; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + /** + * string name = 2; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + name_ = value; + onChanged(); + return this; + } + + /** + * string name = 2; + * @return This builder for chaining. + */ + public Builder clearName() { + + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + + /** + * string name = 2; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + name_ = value; + onChanged(); + return this; + } + + @java.lang.Override + public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:protobuf.User) + } + + // @@protoc_insertion_point(class_scope:protobuf.User) + private static final com.baeldung.serialization.protocols.UserProtos.User DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new com.baeldung.serialization.protocols.UserProtos.User(); + } + + public static com.baeldung.serialization.protocols.UserProtos.User getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public User parsePartialFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { + return new User(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.baeldung.serialization.protocols.UserProtos.User getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + } + + private static final com.google.protobuf.Descriptors.Descriptor internal_static_protobuf_User_descriptor; + private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_protobuf_User_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + return descriptor; + } + + private static com.google.protobuf.Descriptors.FileDescriptor descriptor; + static { + java.lang.String[] descriptorData = { "\n\nuser.proto\022\010protobuf\" \n\004User\022\n\n\002id\030\001 \001" + "(\005\022\014\n\004name\030\002 \001(\tB0\n\"com.baeldung.seriali" + "zation.compareB\nUserProtosb\006proto3" }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {}); + internal_static_protobuf_User_descriptor = getDescriptor().getMessageTypes() + .get(0); + internal_static_protobuf_User_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(internal_static_protobuf_User_descriptor, new java.lang.String[] { "Id", "Name", }); + } + // @@protoc_insertion_point(outer_class_scope) +} \ No newline at end of file diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java new file mode 100644 index 0000000000..7b6aa0a24b --- /dev/null +++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java @@ -0,0 +1,32 @@ +package com.baeldung.serialization.protocols; + +import static org.junit.Assert.assertEquals; + +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TMemoryBuffer; +import org.junit.jupiter.api.Test; + +public class ApacheThriftSerializationUnitTest { + + @Test + public void whenUsingThriftForSerialization_ThenDataIsSameAfterDeserialization() throws TException { + + User user = new User(); + user.setId(2); + user.setName("Greg"); + + TMemoryBuffer trans = new TMemoryBuffer(4096); + TProtocol proto = new TBinaryProtocol(trans); + + proto.writeI32(user.getId()); + proto.writeString(user.getName()); + + int userId = proto.readI32(); + String userName = proto.readString(); + + assertEquals(2, userId); + assertEquals("Greg", userName); + } +} \ No newline at end of file diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java new file mode 100644 index 0000000000..46d66049d4 --- /dev/null +++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java @@ -0,0 +1,27 @@ +package com.baeldung.serialization.protocols; + +import static org.junit.Assert.assertEquals; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +public class GoogleProtocolBufferUnitTest { + + @Test + public void whenUsingProtocolBuffersSerialization_ThenObjectIsTheSameAfterDeserialization() throws IOException { + + String filePath = "src/test/resources/protocols/usersproto"; + + UserProtos.User user = UserProtos.User.newBuilder().setId(1234).setName("John Doe").build(); + FileOutputStream fos = new FileOutputStream(filePath); + user.writeTo(fos); + + UserProtos.User deserializedUser = UserProtos.User.newBuilder().mergeFrom(new FileInputStream(filePath)).build(); + + assertEquals(1234, deserializedUser.getId()); + assertEquals("John Doe", deserializedUser.getName()); + } +} \ No newline at end of file diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java new file mode 100644 index 0000000000..513dcf6ce0 --- /dev/null +++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.serialization.protocols; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.junit.Assert; +import org.junit.jupiter.api.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class GsonSerializationUnitTest { + + @Test + public void whenSerializedUsingGson_ThenObjectIsSameAfterDeserialization() { + + User user = new User(); + user.setId(1); + user.setName("Mark"); + + String filePath = "src/test/resources/protocols/gson_user.json"; + + try (Writer writer = new FileWriter(filePath)) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + gson.toJson(user, writer); + + assertTrue(Files.exists(Paths.get(filePath))); + + } catch (IOException e) { + e.printStackTrace(); + } + + try { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + User deserializedUser = gson.fromJson(new FileReader(filePath), User.class); + + assertEquals(1, deserializedUser.getId()); + assertEquals("Mark", deserializedUser.getName()); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java new file mode 100644 index 0000000000..4bfb88b290 --- /dev/null +++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java @@ -0,0 +1,61 @@ +package com.baeldung.serialization.protocols; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JacksonSerializationUnitTest { + + @Test + public void whenUsingJacksonAPIForJSONSerialization_thenDeserializeCorrectObject() throws IOException { + + User user = new User(); + user.setId(1); + user.setName("Mark Jonson"); + + String filePath = "src/test/resources/protocols/jackson_user.json"; + + File file = new File(filePath); + + ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(file, user); + + User deserializedUser = mapper.readValue(new File(filePath), User.class); + + assertEquals(1, deserializedUser.getId()); + assertEquals("Mark Jonson", deserializedUser.getName()); + } + + public static List populateListOfUsers() { + User user1 = new User(); + user1.setId(1); + user1.setName("Mark Jonson"); + + User user2 = new User(); + user2.setId(2); + user2.setName("Johny Beth"); + + User user3 = new User(); + user3.setId(3); + user3.setName("Eliza Green"); + + User user4 = new User(); + user4.setId(4); + user4.setName("Monica Doe"); + + List users = new ArrayList<>(); + users.add(user1); + users.add(user2); + users.add(user3); + users.add(user4); + + return users; + } +} \ No newline at end of file diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java new file mode 100644 index 0000000000..bb4cc0df0e --- /dev/null +++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.serialization.protocols; + +import static org.junit.Assert.assertEquals; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.junit.jupiter.api.Test; + +public class JavaNativeSerializationUnitTest { + + @Test + public void whenUsingJavaNativeSerialization_ThenObjectIsTheSameAfterDeserialization() throws IOException, ClassNotFoundException { + + User user = new User(); + user.setId(1); + user.setName("Mark"); + + String filePath = "src/test/resources/protocols/user.txt"; + + FileOutputStream fileOutputStream = new FileOutputStream(filePath); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); + objectOutputStream.writeObject(user); + objectOutputStream.flush(); + objectOutputStream.close(); + + FileInputStream fileInputStream = new FileInputStream(filePath); + ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); + User deserializedUser = (User) objectInputStream.readObject(); + objectInputStream.close(); + + assertEquals(1, deserializedUser.getId()); + assertEquals("Mark", deserializedUser.getName()); + } +} \ No newline at end of file diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java new file mode 100644 index 0000000000..7ef3f88511 --- /dev/null +++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java @@ -0,0 +1,66 @@ +package com.baeldung.serialization.protocols; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.esotericsoftware.yamlbeans.YamlReader; +import com.esotericsoftware.yamlbeans.YamlWriter; + +public class YAMLSerializationUnitTest { + + @Test + public void whenUsingYAMLBeansForSerialization_thenDeserializeCorrectMap() throws IOException { + + String filePath = "src/test/resources/protocols/yamlbeans_user.yaml"; + + YamlWriter writer = new YamlWriter(new FileWriter(filePath)); + writer.write(populateUserMap()); + writer.close(); + + YamlReader reader = new YamlReader(new FileReader(filePath)); + Object object = reader.read(); + reader.close(); + + assertTrue(object instanceof Map); + Map deserializedUsers = (Map) object; + + assertEquals(4, deserializedUsers.size()); + assertEquals("Mark Jonson", (deserializedUsers.get("User1").getName())); + assertEquals(1, (deserializedUsers.get("User1").getId())); + } + + private Map populateUserMap() { + + User user1 = new User(); + user1.setId(1); + user1.setName("Mark Jonson"); + + User user2 = new User(); + user2.setId(2); + user2.setName("Johny Beth"); + + User user3 = new User(); + user3.setId(3); + user3.setName("Eliza Green"); + + User user4 = new User(); + user4.setId(4); + user4.setName("Monica Doe"); + + Map users = new LinkedHashMap<>(); + users.put("User1", user1); + users.put("User2", user2); + users.put("User3", user3); + users.put("User4", user4); + + return users; + } +} \ No newline at end of file diff --git a/libraries-data-io/src/test/resources/protocols/gson_user.json b/libraries-data-io/src/test/resources/protocols/gson_user.json new file mode 100644 index 0000000000..5440a774f7 --- /dev/null +++ b/libraries-data-io/src/test/resources/protocols/gson_user.json @@ -0,0 +1,4 @@ +{ + "id": 1, + "name": "Mark" +} \ No newline at end of file diff --git a/libraries-data-io/src/test/resources/protocols/jackson_user.json b/libraries-data-io/src/test/resources/protocols/jackson_user.json new file mode 100644 index 0000000000..ce10cca7ea --- /dev/null +++ b/libraries-data-io/src/test/resources/protocols/jackson_user.json @@ -0,0 +1 @@ +{"id":1,"name":"Mark Jonson"} \ No newline at end of file diff --git a/libraries-data-io/src/test/resources/protocols/user.proto b/libraries-data-io/src/test/resources/protocols/user.proto new file mode 100644 index 0000000000..d4a231d6ec --- /dev/null +++ b/libraries-data-io/src/test/resources/protocols/user.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package protobuf; + +option java_package = "com.baeldung.serialization.protocols"; +option java_outer_classname = "UserProtos"; + +message User { + int32 id = 1; + string name = 2; + } \ No newline at end of file diff --git a/libraries-data-io/src/test/resources/protocols/user.txt b/libraries-data-io/src/test/resources/protocols/user.txt new file mode 100644 index 0000000000..c9f53bfd49 Binary files /dev/null and b/libraries-data-io/src/test/resources/protocols/user.txt differ diff --git a/libraries-data-io/src/test/resources/protocols/usersproto b/libraries-data-io/src/test/resources/protocols/usersproto new file mode 100644 index 0000000000..83dd650be6 --- /dev/null +++ b/libraries-data-io/src/test/resources/protocols/usersproto @@ -0,0 +1 @@ +Ò John Doe \ No newline at end of file diff --git a/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml b/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml new file mode 100644 index 0000000000..83726897b9 --- /dev/null +++ b/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml @@ -0,0 +1,13 @@ +!java.util.LinkedHashMap +User1: !com.baeldung.serialization.protocols.User + id: 1 + name: Mark Jonson +User2: !com.baeldung.serialization.protocols.User + id: 2 + name: Johny Beth +User3: !com.baeldung.serialization.protocols.User + id: 3 + name: Eliza Green +User4: !com.baeldung.serialization.protocols.User + id: 4 + name: Monica Doe diff --git a/libraries-data/pom.xml b/libraries-data/pom.xml index 3c6bedba90..717ee802db 100644 --- a/libraries-data/pom.xml +++ b/libraries-data/pom.xml @@ -73,7 +73,7 @@ commons-cli commons-cli - ${commons.cli.version} + ${commons-cli.version} provided @@ -163,7 +163,6 @@ 2.3 - 1.2 3.0.1 1.2.2 1.0.0 diff --git a/libraries-http-2/README.md b/libraries-http-2/README.md index 4085d98ea5..fdad2b3e0a 100644 --- a/libraries-http-2/README.md +++ b/libraries-http-2/README.md @@ -9,5 +9,7 @@ This module contains articles about HTTP libraries. - [Retrofit 2 – Dynamic URL](https://www.baeldung.com/retrofit-dynamic-url) - [Adding Interceptors in OkHTTP](https://www.baeldung.com/java-okhttp-interceptors) - [A Guide to Events in OkHTTP](https://www.baeldung.com/java-okhttp-events) +- [Download a Binary File Using OkHttp](https://www.baeldung.com/java-okhttp-download-binary-file) +- [Trusting a Self-Signed Certificate in OkHttp](https://www.baeldung.com/okhttp-self-signed-cert) - More articles [[<-- prev]](/libraries-http) diff --git a/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileDownloader.java b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileDownloader.java new file mode 100644 index 0000000000..a7ef60f124 --- /dev/null +++ b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileDownloader.java @@ -0,0 +1,38 @@ +package com.baeldung.okhttp.download; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +import java.io.IOException; +import java.util.Objects; + +import static org.springframework.http.HttpHeaders.CONTENT_LENGTH; + +public class BinaryFileDownloader implements AutoCloseable { + + private final OkHttpClient client; + private final BinaryFileWriter writer; + + public BinaryFileDownloader(OkHttpClient client, BinaryFileWriter writer) { + this.client = client; + this.writer = writer; + } + + public long download(String url) throws IOException { + Request request = new Request.Builder().url(url).build(); + Response response = client.newCall(request).execute(); + ResponseBody responseBody = response.body(); + if (responseBody == null) { + throw new IllegalStateException("Response doesn't contain a file"); + } + double length = Double.parseDouble(Objects.requireNonNull(response.header(CONTENT_LENGTH, "1"))); + return writer.write(responseBody.byteStream(), length); + } + + @Override + public void close() throws Exception { + writer.close(); + } +} diff --git a/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileWriter.java b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileWriter.java new file mode 100644 index 0000000000..fb3664c08e --- /dev/null +++ b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileWriter.java @@ -0,0 +1,37 @@ +package com.baeldung.okhttp.download; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class BinaryFileWriter implements AutoCloseable { + + private static final int CHUNK_SIZE = 1024; + private final OutputStream outputStream; + private final ProgressCallback progressCallback; + + public BinaryFileWriter(OutputStream outputStream, ProgressCallback progressCallback) { + this.outputStream = outputStream; + this.progressCallback = progressCallback; + } + + public long write(InputStream inputStream, double length) throws IOException { + try (BufferedInputStream input = new BufferedInputStream(inputStream)) { + byte[] dataBuffer = new byte[CHUNK_SIZE]; + int readBytes; + long totalBytes = 0; + while ((readBytes = input.read(dataBuffer)) != -1) { + totalBytes += readBytes; + outputStream.write(dataBuffer, 0, readBytes); + progressCallback.onProgress(totalBytes / length * 100.0); + } + return totalBytes; + } + } + + @Override + public void close() throws IOException { + outputStream.close(); + } +} diff --git a/libraries-http-2/src/main/java/com/baeldung/okhttp/download/ProgressCallback.java b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/ProgressCallback.java new file mode 100644 index 0000000000..b57cabf854 --- /dev/null +++ b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/ProgressCallback.java @@ -0,0 +1,7 @@ +package com.baeldung.okhttp.download; + +public interface ProgressCallback { + + void onProgress(double progress); + +} diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java new file mode 100644 index 0000000000..3e0c47f793 --- /dev/null +++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java @@ -0,0 +1,5 @@ +package com.baeldung.okhttp; + +public interface Consts { + int SSL_APPLICATION_PORT = 8443; +} \ No newline at end of file diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderIntegrationTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderIntegrationTest.java new file mode 100644 index 0000000000..6f0f705350 --- /dev/null +++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderIntegrationTest.java @@ -0,0 +1,39 @@ +package com.baeldung.okhttp.download; + +import okhttp3.OkHttpClient; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.FileOutputStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class BinaryFileDownloaderIntegrationTest { + + @Rule + public MockWebServer server = new MockWebServer(); + + @Test + public void givenATextFile_whenDownload_thenExpectFileDownloaded() { + String body = "Hello Baeldung Readers!"; + server.enqueue(new MockResponse().setBody(body)); + String fileName = "download.txt"; + + ProgressCallback progressCallback = progress -> assertEquals(100.0, progress, .0); + try (BinaryFileWriter writer = new BinaryFileWriter(new FileOutputStream(fileName), progressCallback); BinaryFileDownloader tested = new BinaryFileDownloader(new OkHttpClient(), writer)) { + long downloaded = tested.download(server.url("/greetings").toString()); + assertEquals(body.length(), downloaded); + File downloadedFile = new File(fileName); + assertTrue(downloadedFile.isFile()); + assertTrue(downloadedFile.delete()); + } catch (Exception e) { + fail("An unexpected exception has occurred: " + e); + } + } + +} diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderUnitTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderUnitTest.java new file mode 100644 index 0000000000..15dda3a471 --- /dev/null +++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderUnitTest.java @@ -0,0 +1,74 @@ +package com.baeldung.okhttp.download; + +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.InputStream; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class BinaryFileDownloaderUnitTest { + + @Mock + private OkHttpClient client; + @Mock + private BinaryFileWriter writer; + @InjectMocks + private BinaryFileDownloader tested; + + @Test + public void givenUrlAndResponse_whenDownload_thenExpectFileWritten() throws Exception { + String url = "http://example.com/file"; + Call call = mock(Call.class); + when(client.newCall(any(Request.class))).thenReturn(call); + ResponseBody body = ResponseBody.create("BODY", MediaType.get("application/text")); + Response response = createResponse(url, body); + when(call.execute()).thenReturn(response); + when(writer.write(any(), anyDouble())).thenReturn(1L); + + try (BinaryFileDownloader tested = new BinaryFileDownloader(client, writer)) { + long size = tested.download(url); + assertEquals(1L, size); + verify(writer).write(any(InputStream.class), anyDouble()); + } + verify(writer).close(); + } + + @Test(expected = IllegalStateException.class) + public void givenUrlAndResponseWithNullBody_whenDownload_thenExpectIllegalStateException() throws Exception { + String url = "http://example.com/file"; + Call call = mock(Call.class); + when(client.newCall(any(Request.class))).thenReturn(call); + Response response = createResponse(url, null); + when(call.execute()).thenReturn(response); + + tested.download(url); + + verify(writer, times(0)).write(any(InputStream.class), anyDouble()); + } + + @NotNull + private Response createResponse(String url, ResponseBody body) { + Request request = new Request.Builder().url(url).build(); + return new Response.Builder().code(200).request(request).protocol(Protocol.HTTP_2).message("Message").body(body).build(); + } + +} \ No newline at end of file diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileWriterUnitTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileWriterUnitTest.java new file mode 100644 index 0000000000..2b3f0a1313 --- /dev/null +++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileWriterUnitTest.java @@ -0,0 +1,55 @@ +package com.baeldung.okhttp.download; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.InputStream; +import java.io.OutputStream; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class BinaryFileWriterUnitTest { + + @Mock + private OutputStream outputStream; + + @Test + public void givenInputStream_whenWrite_thenExpectWritten() throws Exception { + InputStream inputStream = mock(InputStream.class); + when(inputStream.read(any(), anyInt(), anyInt())).thenReturn(10, -1); + + try (BinaryFileWriter tested = new BinaryFileWriter(outputStream, progress -> assertEquals(100.0, progress, .0))) { + long result = tested.write(inputStream, 10); + + assertEquals(10, result); + verify(outputStream).write(any(), eq(0), eq(10)); + verify(inputStream).close(); + } + verify(outputStream).close(); + } + + @Test + public void givenInputStreamEmpty_whenWrite_thenExpectNotWritten() throws Exception { + InputStream inputStream = mock(InputStream.class); + + try (BinaryFileWriter tested = new BinaryFileWriter(outputStream, progress -> assertEquals(100.0, progress, .0))) { + long result = tested.write(inputStream, 1); + + assertEquals(0, result); + verify(outputStream, times(0)).write(any(), anyInt(), anyInt()); + verify(inputStream).close(); + } + verify(outputStream).close(); + } + +} \ No newline at end of file diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java new file mode 100644 index 0000000000..3e7fad2a29 --- /dev/null +++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java @@ -0,0 +1,108 @@ +package com.baeldung.okhttp.ssl; + +import static com.baeldung.okhttp.Consts.SSL_APPLICATION_PORT; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.cert.CertificateException; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.junit.Before; +import org.junit.Test; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +/** + * Execute spring-security-web-boot-2 module before running this live test + * @see com.baeldung.ssl.HttpsEnabledApplication + */ +public class OkHttpSSLSelfSignedCertLiveTest { + + private final String HTTPS_WELCOME_URL = "https://localhost:" + SSL_APPLICATION_PORT + "/welcome"; + + private OkHttpClient.Builder builder; + + @Before + public void init() { + builder = new OkHttpClient.Builder(); + } + + @Test(expected = SSLHandshakeException.class) + public void whenHTTPSSelfSignedCertGET_thenException() throws IOException { + builder.build() + .newCall(new Request.Builder().url(HTTPS_WELCOME_URL) + .build()) + .execute(); + } + + @Test(expected = SSLPeerUnverifiedException.class) + public void givenTrustAllCerts_whenHTTPSSelfSignedCertGET_thenException() throws GeneralSecurityException, IOException { + final TrustManager TRUST_ALL_CERTS = new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[] {}; + } + }; + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, new TrustManager[] { TRUST_ALL_CERTS }, new java.security.SecureRandom()); + builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) TRUST_ALL_CERTS); + builder.build() + .newCall(new Request.Builder().url(HTTPS_WELCOME_URL) + .build()) + .execute(); + } + + @Test + public void givenTrustAllCertsSkipHostnameVerification_whenHTTPSSelfSignedCertGET_then200OK() throws GeneralSecurityException, IOException { + final TrustManager TRUST_ALL_CERTS = new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[] {}; + } + }; + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, new TrustManager[] { TRUST_ALL_CERTS }, new java.security.SecureRandom()); + builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) TRUST_ALL_CERTS); + builder.hostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + Response response = builder.build() + .newCall(new Request.Builder().url(HTTPS_WELCOME_URL) + .build()) + .execute(); + assertEquals(200, response.code()); + assertNotNull(response.body()); + assertEquals("

Welcome to Secured Site

", response.body() + .string()); + } +} diff --git a/libraries-io/README.md b/libraries-io/README.md index 90095a2f23..6cfe978d91 100644 --- a/libraries-io/README.md +++ b/libraries-io/README.md @@ -2,4 +2,5 @@ ### Relevant Articles: - [Transferring a File Through SFTP in Java](https://www.baeldung.com/java-file-sftp) +- [How to Create Password-Protected Zip Files and Unzip Them in Java](https://www.baeldung.com/java-password-protected-zip-unzip) diff --git a/libraries-io/pom.xml b/libraries-io/pom.xml index 2f65fd989b..0cea9ae9ac 100644 --- a/libraries-io/pom.xml +++ b/libraries-io/pom.xml @@ -29,6 +29,11 @@ commons-vfs2 ${vfs.version} + + net.lingala.zip4j + zip4j + 2.9.0 + diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/CreateSplitZipFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/CreateSplitZipFile.java new file mode 100644 index 0000000000..cc39bc9dd2 --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/CreateSplitZipFile.java @@ -0,0 +1,22 @@ +package com.baeldung.java.io.zip4j; + +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; +import net.lingala.zip4j.model.ZipParameters; +import net.lingala.zip4j.model.enums.EncryptionMethod; + +import java.io.File; +import java.util.Arrays; + +public class CreateSplitZipFile { + + public static void main(String[] args) throws ZipException { + ZipParameters zipParameters = new ZipParameters(); + zipParameters.setEncryptFiles(true); + zipParameters.setEncryptionMethod(EncryptionMethod.AES); + ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray()); + int splitLength = 1024 * 1024 * 10; //10MB + zipFile.createSplitZipFile(Arrays.asList(new File("aFile.txt")), zipParameters, true, splitLength); + zipFile.createSplitZipFileFromFolder(new File("/users/folder_to_add"), zipParameters, true, splitLength); + } +} diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractAllFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractAllFile.java new file mode 100644 index 0000000000..10e7ddd339 --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractAllFile.java @@ -0,0 +1,12 @@ +package com.baeldung.java.io.zip4j; + +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; + +public class ExtractAllFile { + + public static void main(String[] args) throws ZipException { + ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray()); + zipFile.extractAll("/destination_directory"); + } +} diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractSingleFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractSingleFile.java new file mode 100644 index 0000000000..4cf466d02b --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractSingleFile.java @@ -0,0 +1,12 @@ +package com.baeldung.java.io.zip4j; + +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; + +public class ExtractSingleFile { + + public static void main(String[] args) throws ZipException { + ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray()); + zipFile.extractFile("aFile.txt", "/destination_directory"); + } +} diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipFolder.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipFolder.java new file mode 100644 index 0000000000..4d89e8665f --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipFolder.java @@ -0,0 +1,19 @@ +package com.baeldung.java.io.zip4j; + +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; +import net.lingala.zip4j.model.ZipParameters; +import net.lingala.zip4j.model.enums.EncryptionMethod; + +import java.io.File; + +public class ZipFolder { + + public static void main(String[] args) throws ZipException { + ZipParameters zipParameters = new ZipParameters(); + zipParameters.setEncryptFiles(true); + zipParameters.setEncryptionMethod(EncryptionMethod.AES); + ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray()); + zipFile.addFolder(new File("/users/folder_to_add"), zipParameters); + } +} diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipMultiFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipMultiFile.java new file mode 100644 index 0000000000..dcb860ef92 --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipMultiFile.java @@ -0,0 +1,27 @@ +package com.baeldung.java.io.zip4j; + +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; +import net.lingala.zip4j.model.ZipParameters; +import net.lingala.zip4j.model.enums.EncryptionMethod; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +public class ZipMultiFile { + + public static void main(String[] args) throws ZipException { + ZipParameters zipParameters = new ZipParameters(); + zipParameters.setEncryptFiles(true); + zipParameters.setEncryptionMethod(EncryptionMethod.AES); + + List filesToAdd = Arrays.asList( + new File("aFile.txt"), + new File("bFile.txt") + ); + + ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray()); + zipFile.addFiles(filesToAdd, zipParameters); + } +} diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java new file mode 100644 index 0000000000..a5f600df47 --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java @@ -0,0 +1,21 @@ +package com.baeldung.java.io.zip4j; + +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; +import net.lingala.zip4j.model.ZipParameters; +import net.lingala.zip4j.model.enums.CompressionLevel; +import net.lingala.zip4j.model.enums.EncryptionMethod; + +import java.io.File; + +public class ZipSingleFile { + + public static void main(String[] args) throws ZipException { + ZipParameters zipParameters = new ZipParameters(); + zipParameters.setEncryptFiles(true); + zipParameters.setCompressionLevel(CompressionLevel.HIGHER); + zipParameters.setEncryptionMethod(EncryptionMethod.AES); + ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray()); + zipFile.addFile(new File("aFile.txt")); + } +} diff --git a/libraries-security/pom.xml b/libraries-security/pom.xml index 6ee6b7c358..0031b7bc06 100644 --- a/libraries-security/pom.xml +++ b/libraries-security/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 libraries-security libraries-security diff --git a/libraries-server/pom.xml b/libraries-server/pom.xml index e36ed88f8f..b283164daa 100644 --- a/libraries-server/pom.xml +++ b/libraries-server/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 libraries-server 0.0.1-SNAPSHOT @@ -99,7 +100,7 @@ ${nanohttpd.version} - + 3.6.2 4.5.3 diff --git a/libraries-testing/README.md b/libraries-testing/README.md index 447f3f32b9..5498c73094 100644 --- a/libraries-testing/README.md +++ b/libraries-testing/README.md @@ -13,3 +13,4 @@ This module contains articles about test libraries. - [Testing with Hamcrest](https://www.baeldung.com/java-junit-hamcrest-guide) - [Introduction To DBUnit](https://www.baeldung.com/java-dbunit) - [Introduction to ArchUnit](https://www.baeldung.com/java-archunit-intro) +- [Guide to the ModelAssert Library for JSON](https://www.baeldung.com/json-modelassert) diff --git a/libraries-testing/pom.xml b/libraries-testing/pom.xml index 6d7e0c01e1..d7b4a88369 100644 --- a/libraries-testing/pom.xml +++ b/libraries-testing/pom.xml @@ -84,6 +84,12 @@ jsonassert ${jsonassert.version} + + uk.org.webcompere + model-assert + ${modelassert.version} + test + org.awaitility awaitility @@ -209,6 +215,7 @@ 1.8 3.8.1 0.14.1 + 1.0.0 \ No newline at end of file diff --git a/libraries-testing/src/test/java/com/baeldung/modelassert/ModelAssertUnitTest.java b/libraries-testing/src/test/java/com/baeldung/modelassert/ModelAssertUnitTest.java new file mode 100644 index 0000000000..b32afdb6d0 --- /dev/null +++ b/libraries-testing/src/test/java/com/baeldung/modelassert/ModelAssertUnitTest.java @@ -0,0 +1,289 @@ +package com.baeldung.modelassert; + +import org.hamcrest.Matcher; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; +import uk.org.webcompere.modelassert.json.dsl.nodespecific.tree.WhereDsl; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static uk.org.webcompere.modelassert.json.JsonAssertions.*; +import static uk.org.webcompere.modelassert.json.PathWildCard.ANY; +import static uk.org.webcompere.modelassert.json.PathWildCard.ANY_SUBTREE; +import static uk.org.webcompere.modelassert.json.Patterns.GUID_PATTERN; + +class ModelAssertUnitTest { + private static final String ACTUAL_JSON = "{" + + "\"name\": \"Baeldung\"," + + "\"isOnline\": true," + + "\"topics\": [ \"Java\", \"Spring\", \"Kotlin\", \"Scala\", \"Linux\" ]" + + "}"; + + private static final Path PATH_TO_EXPECTED = Paths.get("src", "test", "resources", "modelassert", "baeldung.json"); + + public interface DataService { + + boolean isUserLoggedIn(String userDetails); + } + + private DataService mockDataService = mock(DataService.class); + + @Test + void givenJson_thenNameIsBaeldung() { + assertJson(ACTUAL_JSON) + .at("/name").isText("Baeldung"); + } + + @Test + void givenJson_thenSecondTopicIsSpring() { + assertJson(ACTUAL_JSON) + .at("/topics/1").isText("Spring"); + } + + @Test + void givenJson_thenCanMakeMultipleAssertions() { + assertJson(ACTUAL_JSON) + .at("/name").isText("Baeldung") + .at("/topics/1").isText("Spring"); + } + + @Test + void givenCompareByString_thenFailsOnFormatting() throws IOException { + String expected = String.join("\n", Files.readAllLines(PATH_TO_EXPECTED)); + + assertThatThrownBy(() -> assertThat(ACTUAL_JSON).isEqualTo(expected)) + .isInstanceOf(AssertionFailedError.class); + } + + @Test + void givenTwoIdenticalTreesInDifferentFormat_thenPassesWithIsEqualTo() { + assertJson(ACTUAL_JSON) + .isEqualTo(PATH_TO_EXPECTED); + } + + @Test + void givenMap_thenCanCompareToYaml() { + Map map = new HashMap<>(); + map.put("name", "baeldung"); + + assertJson(map) + .isEqualToYaml("name: baeldung"); + } + + @Test + void givenYaml_thenCanCompareToMap() { + Map map = new HashMap<>(); + map.put("name", "baeldung"); + + assertYaml("name: baeldung") + .isEqualTo(map); + } + + @Test + void canProduceHamcrestMatcher() { + Matcher matcher = json().at("/name").hasValue("Baeldung"); + + MatcherAssert.assertThat(ACTUAL_JSON, matcher); + } + + @Test + void givenJson_thenCanAssertWithMatcherAssert() { + + MatcherAssert.assertThat(ACTUAL_JSON, json() + .at("/name").hasValue("Baeldung") + .at("/topics/1").isText("Spring")); + } + + @Test + void givenUserIsOnline_thenIsLoggedIn() { + given(mockDataService.isUserLoggedIn(argThat(json() + .at("/isOnline").isTrue() + .toArgumentMatcher()))) + .willReturn(true); + + assertThat(mockDataService.isUserLoggedIn(ACTUAL_JSON)) + .isTrue(); + + verify(mockDataService) + .isUserLoggedIn(argThat(json() + .at("/name").isText("Baeldung") + .toArgumentMatcher())); + } + + @Test + void givenDocument_canMakeSeveralRootNodeAssertions() { + assertJson(ACTUAL_JSON) + .isNotNull() + .isNotNumber() + .isObject() + .containsKey("name"); + } + + @Test + void givenDocument_canAssertArray() { + assertJson(ACTUAL_JSON) + .at("/topics").hasSize(5); + } + + @Test + void givenDocument_canAssertBooleanByType() { + assertJson(ACTUAL_JSON) + .at("/isOnline").booleanNode().isTrue(); + } + + @Test + void givenTextNode_canAssertContents() { + assertJson(ACTUAL_JSON) + .at("/name").textContains("ael"); + } + + @Test + void givenTextNode_canMatchWithRegex() { + assertJson(ACTUAL_JSON) + .at("/name").matches("[A-Z].+"); + } + + @Test + void givenNumberNode_canTestRange() { + assertJson("{count: 12}") + .at("/count").isBetween(1, 25); + } + + @Test + void givenNumberNode_canTestDouble() { + assertJson("{height: 6.3}") + .at("/height").isGreaterThanDouble(6.0); + } + + @Test + void givenNumberNode_canTestDoubleEquals() { + assertJson("{height: 6.3}") + .at("/height").isNumberEqualTo(6.3); + } + + @Test + void givenArrayNode_canTestContains() { + assertJson(ACTUAL_JSON) + .at("/topics").isArrayContaining("Scala", "Spring"); + } + + @Test + void givenArrayNode_canTestContainsExactlyInAnyOrder() { + assertJson(ACTUAL_JSON) + .at("/topics").isArrayContainingExactlyInAnyOrder("Scala", "Spring", "Java", "Linux", "Kotlin"); + } + + @Test + void givenArrayNode_canTestContainsExactly() { + assertJson(ACTUAL_JSON) + .at("/topics").isArrayContainingExactly("Java", "Spring", "Kotlin", "Scala", "Linux"); + } + + @Test + void givenArrayNode_thenCanAssertBySubtree() { + assertJson(ACTUAL_JSON) + .at("/topics").isEqualTo("[ \"Java\", \"Spring\", \"Kotlin\", \"Scala\", \"Linux\" ]"); + } + + @Test + void givenKeyOrderIsDifferent_canStillBeEqual() { + String actualJson = "{a:{d:3, c:2, b:1}}"; + String expectedJson = "{a:{b:1, c:2, d:3}}"; + + assertJson(actualJson) + .where().keysInAnyOrder() + .isEqualTo(expectedJson); + } + + @Test + void givenKeyOrderIsDifferentInA_canStillBeEqual() { + String actualJson = "{a:{d:3, c:2, b:1}}"; + String expectedJson = "{a:{b:1, c:2, d:3}}"; + + assertJson(actualJson) + .where() + .at("/a").keysInAnyOrder() + .isEqualTo(expectedJson); + } + + @Test + void givenElementOrderIsDifferent_canStillBeEqual() { + String actualJson = "{a:[1, 2, 3, 4, 5]}"; + String expectedJson = "{a:[5, 4, 3, 2, 1]}"; + + assertJson(actualJson) + .where().arrayInAnyOrder() + .isEqualTo(expectedJson); + } + + @Test + void givenActualHasExtraValue_thenIgnoreIt() { + String actualJson = "{user:{name: \"Baeldung\", url:\"http://www.baeldung.com\"}}"; + String expectedJson = "{user:{name: \"Baeldung\"}}"; + + assertJson(actualJson) + .where().at("/user/url").isIgnored() + .isEqualTo(expectedJson); + } + + @Test + void givenActualHasIdGUIDS_thenIgnoreThem() { + String actualJson = "{user:{credentials:[" + + "{id:\"a7dc2567-3340-4a3b-b1ab-9ce1778f265d\",role:\"Admin\"}," + + "{id:\"09da84ba-19c2-4674-974f-fd5afff3a0e5\",role:\"Sales\"}]}}"; + String expectedJson = "{user:{credentials:" + + "[{id:\"???\",role:\"Admin\"}," + + "{id:\"???\",role:\"Sales\"}]}}"; + + assertJson(actualJson) + .where() + .path("user","credentials", ANY, "id").isIgnored() + .isEqualTo(expectedJson); + } + + @Test + void givenActualHasIdGUIDS_thenMatchThemByRegexAndSubtree() { + String actualJson = "{user:{credentials:[" + + "{id:\"a7dc2567-3340-4a3b-b1ab-9ce1778f265d\",role:\"Admin\"}," + + "{id:\"09da84ba-19c2-4674-974f-fd5afff3a0e5\",role:\"Sales\"}]}}"; + String expectedJson = "{user:{credentials:" + + "[{id:\"???\",role:\"Admin\"}," + + "{id:\"???\",role:\"Sales\"}]}}"; + + assertJson(actualJson) + .where() + .path(ANY_SUBTREE, "id").matches(GUID_PATTERN) + .isEqualTo(expectedJson); + } + + @Test + void givenActualHasIdGUIDS_thenMatchThemByRegexAndSubtreeUsingStandardPattern() { + String actualJson = "{user:{credentials:[" + + "{id:\"a7dc2567-3340-4a3b-b1ab-9ce1778f265d\",role:\"Admin\"}," + + "{id:\"09da84ba-19c2-4674-974f-fd5afff3a0e5\",role:\"Sales\"}]}}"; + String expectedJson = "{user:{credentials:" + + "[{id:\"???\",role:\"Admin\"}," + + "{id:\"???\",role:\"Sales\"}]}}"; + + assertJson(actualJson) + .where() + .configuredBy(where -> idsAreGuids(where)) + .isEqualTo(expectedJson); + } + + private static WhereDsl idsAreGuids(WhereDsl where) { + return where.path(ANY_SUBTREE, "id").matches(GUID_PATTERN); + } +} diff --git a/libraries-testing/src/test/resources/modelassert/baeldung.json b/libraries-testing/src/test/resources/modelassert/baeldung.json new file mode 100644 index 0000000000..2eb8ed3859 --- /dev/null +++ b/libraries-testing/src/test/resources/modelassert/baeldung.json @@ -0,0 +1,5 @@ +{ + "name": "Baeldung", + "isOnline": true, + "topics": [ "Java", "Spring", "Kotlin", "Scala", "Linux" ] +} diff --git a/libraries/pom.xml b/libraries/pom.xml index 40cc1b4671..ffb2b09699 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -243,14 +243,6 @@ - - - false - - bintray-cuba-platform-main - bintray - http://dl.bintray.com/cuba-platform/main - nm-repo Numerical Method's Maven Repository diff --git a/logging-modules/log4j/README.md b/logging-modules/log4j/README.md index a7a7ea9643..371d0246ce 100644 --- a/logging-modules/log4j/README.md +++ b/logging-modules/log4j/README.md @@ -3,3 +3,4 @@ - [Introduction to SLF4J](http://www.baeldung.com/slf4j-with-log4j2-logback) - [A Guide to Rolling File Appenders](http://www.baeldung.com/java-logging-rolling-file-appenders) - [Logging Exceptions Using SLF4J](https://www.baeldung.com/slf4j-log-exceptions) +- [Log4j Warning: "No Appenders Could Be Found for Logger"](https://www.baeldung.com/log4j-no-appenders-found) diff --git a/logging-modules/logback/README.md b/logging-modules/logback/README.md index 05a4ab9308..c9e5450cb0 100644 --- a/logging-modules/logback/README.md +++ b/logging-modules/logback/README.md @@ -3,3 +3,4 @@ - [Get Log Output in JSON](https://www.baeldung.com/java-log-json-output) - [SLF4J Warning: Class Path Contains Multiple SLF4J Bindings](https://www.baeldung.com/slf4j-classpath-multiple-bindings) - [Sending Emails with Logback](https://www.baeldung.com/logback-send-email) +- [Mask Sensitive Data in Logs With Logback](https://www.baeldung.com/logback-mask-sensitive-data) diff --git a/logging-modules/logback/pom.xml b/logging-modules/logback/pom.xml index c11f20955b..6d32025d94 100644 --- a/logging-modules/logback/pom.xml +++ b/logging-modules/logback/pom.xml @@ -15,6 +15,11 @@ + + org.json + json + 20180130 + ch.qos.logback logback-classic diff --git a/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayout.java b/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayout.java new file mode 100644 index 0000000000..04321e9066 --- /dev/null +++ b/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayout.java @@ -0,0 +1,46 @@ +package com.baeldung.logback; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; + +public class MaskingPatternLayout extends PatternLayout { + + private Pattern multilinePattern; + private List maskPatterns = new ArrayList<>(); + + // invoked for every single entry in the xml + public void addMaskPattern(String maskPattern) { + maskPatterns.add(maskPattern); + multilinePattern = Pattern.compile(maskPatterns.stream().collect(Collectors.joining("|")), Pattern.MULTILINE); + } + + @Override + public String doLayout(ILoggingEvent event) { + return maskMessage(super.doLayout(event)); + } + + private String maskMessage(String message) { + if (multilinePattern == null) { + return message; + } + StringBuilder sb = new StringBuilder(message); + Matcher matcher = multilinePattern.matcher(sb); + while (matcher.find()) { + IntStream.rangeClosed(1, matcher.groupCount()).forEach(group -> { + if (matcher.group(group) != null) { + // replace each character with asterisk + IntStream.range(matcher.start(group), matcher.end(group)).forEach(i -> sb.setCharAt(i, '*')); + } + }); + } + return sb.toString(); + } + +} \ No newline at end of file diff --git a/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayoutExample.java b/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayoutExample.java new file mode 100644 index 0000000000..cbcd0209ab --- /dev/null +++ b/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayoutExample.java @@ -0,0 +1,27 @@ +package com.baeldung.logback; + +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MaskingPatternLayoutExample { + + private static final Logger logger = LoggerFactory.getLogger(MaskingPatternLayoutExample.class); + + public static void main(String[] args) { + Map user = new HashMap(); + user.put("user_id", "87656"); + user.put("SSN", "786445563"); + user.put("address", "22 Street"); + user.put("city", "Chicago"); + user.put("Country", "U.S."); + user.put("ip_address", "192.168.1.1"); + user.put("email_id", "spring@baeldung.com"); + JSONObject userDetails = new JSONObject(user); + + logger.info("MaskingPatternExample log from {}" + userDetails, MaskingPatternLayoutExample.class.getSimpleName()); + } +} diff --git a/logging-modules/logback/src/main/resources/logback.xml b/logging-modules/logback/src/main/resources/logback.xml index 76ddc2e3ee..2d56c110e0 100644 --- a/logging-modules/logback/src/main/resources/logback.xml +++ b/logging-modules/logback/src/main/resources/logback.xml @@ -47,10 +47,23 @@ + + + + \"SSN\"\s*:\s*\"(.*?)\" + \"address\"\s*:\s*\"(.*?)\" + (\d+\.\d+\.\d+\.\d+) + (\w+@\w+\.\w+) + %-5p [%d{ISO8601,UTC}] [%thread] %c: %m%n%rootException + + + + + \ No newline at end of file diff --git a/logging-modules/pom.xml b/logging-modules/pom.xml index 6d036d5648..d7b820040a 100644 --- a/logging-modules/pom.xml +++ b/logging-modules/pom.xml @@ -26,4 +26,4 @@ 5.6.2 - + \ No newline at end of file diff --git a/lombok-custom/pom.xml b/lombok-custom/pom.xml index 220367bfe9..dc7f0dfec6 100644 --- a/lombok-custom/pom.xml +++ b/lombok-custom/pom.xml @@ -1,11 +1,11 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 lombok-custom - lombok-custom 0.1-SNAPSHOT + lombok-custom com.baeldung @@ -20,13 +20,11 @@ ${lombok.version} provided - org.kohsuke.metainf-services metainf-services ${metainf-services.version} - org.eclipse.jdt core @@ -62,4 +60,4 @@ 3.3.0-v_771 - + \ No newline at end of file diff --git a/mapstruct/pom.xml b/mapstruct/pom.xml index 1434db0d54..1e7ce6cbfc 100644 --- a/mapstruct/pom.xml +++ b/mapstruct/pom.xml @@ -34,7 +34,12 @@ org.projectlombok lombok - ${org.projectlombok.version} + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + ${lombok.mapstruct.binding.version} org.assertj @@ -63,7 +68,12 @@ org.projectlombok lombok - ${org.projectlombok.version} + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + ${lombok.mapstruct.binding.version} @@ -72,11 +82,11 @@ - 1.3.1.Final + 1.4.2.Final 4.3.4.RELEASE 1.8 1.8 - 1.18.4 + 0.2.0 3.16.1 diff --git a/maven-modules/host-maven-repo-example/README.md b/maven-modules/host-maven-repo-example/README.md new file mode 100644 index 0000000000..032be2416c --- /dev/null +++ b/maven-modules/host-maven-repo-example/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Hosting a Maven Repository on GitHub](https://www.baeldung.com/maven-repo-github) diff --git a/maven-modules/host-maven-repo-example/pom.xml b/maven-modules/host-maven-repo-example/pom.xml new file mode 100644 index 0000000000..9434e4a3b3 --- /dev/null +++ b/maven-modules/host-maven-repo-example/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + com.baeldung.maven.plugin + host-maven-repo-example + 1.0-SNAPSHOT + + https://github.com/sgrverma23/host-maven-repo-example.git + + + github + 8 + 8 + + + + https://github.com/sgrverma23/host-maven-repo-example.git + scm:git:git@github.com:sgrverma23/host-maven-repo-example.git + scm:git:git@github.com:sgrverma23/host-maven-repo-example.git + + + + + + internal.repo + Temporary Staging Repository + file://${project.build.directory}/mvn-artifact + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.1.0 + + + attach-sources + + jar + + + + + + com.github.github + site-maven-plugin + 0.12 + + Maven artifacts for ${project.version} + true + ${project.build.directory} + refs/heads/main + + **/* + + true + host-maven-repo-example + sgrverma23 + github + + + + + site + + deploy + + + + + maven-deploy-plugin + 2.8.2 + + + internal.repo::default::file://${project.build.directory}/mvn-artifact + + + + + org.apache.maven.plugins + maven-source-plugin + 3.1.0 + + + attach-sources + + jar + + + + + + + + + PROJECT-REPO-URL + https://github.com/sgrverma23/host-maven-repo-example/main + + true + always + + + + \ No newline at end of file diff --git a/maven-modules/host-maven-repo-example/src/java/com/baeldung/maven/plugin/MainApp.java b/maven-modules/host-maven-repo-example/src/java/com/baeldung/maven/plugin/MainApp.java new file mode 100644 index 0000000000..521606313c --- /dev/null +++ b/maven-modules/host-maven-repo-example/src/java/com/baeldung/maven/plugin/MainApp.java @@ -0,0 +1,7 @@ +package com.baeldung.maven.plugin; + +public class MainApp { + public static void main(String[] args){ + System.out.println("Maven Repo Add"); + } +} diff --git a/maven-modules/maven-builder-plugin/README.md b/maven-modules/maven-builder-plugin/README.md new file mode 100644 index 0000000000..47cd99d281 --- /dev/null +++ b/maven-modules/maven-builder-plugin/README.md @@ -0,0 +1,3 @@ +## Relevant Articles: + +- [Additional Source Directories in Maven](https://www.baeldung.com/maven-add-src-directories) diff --git a/maven-modules/maven-builder-plugin/pom.xml b/maven-modules/maven-builder-plugin/pom.xml new file mode 100644 index 0000000000..2fedee06cc --- /dev/null +++ b/maven-modules/maven-builder-plugin/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + org.example + maven-builder-plugin + 1.0-SNAPSHOT + + + 1.8 + 1.8 + UTF-8 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + + add-source + generate-sources + + add-source + + + + src/main/newsrc/ + + + + + + + + + \ No newline at end of file diff --git a/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java b/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java new file mode 100644 index 0000000000..176951e21a --- /dev/null +++ b/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java @@ -0,0 +1,12 @@ +package com.baeldung.maven.plugin; + +import com.baeldung.database.DataConnection; + +public class MainApp { + + public static void main(String args[]){ + + System.out.println(DataConnection.temp()); + + } +} diff --git a/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java b/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java new file mode 100644 index 0000000000..8ab05a5658 --- /dev/null +++ b/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java @@ -0,0 +1,7 @@ +package com.baeldung.database; +public class DataConnection { + + public static String temp(){ + return "secondary source directory"; + } +} diff --git a/maven-modules/maven-copy-files/copy-rename-maven-plugin/pom.xml b/maven-modules/maven-copy-files/copy-rename-maven-plugin/pom.xml index 24a7c48499..e2f0d57e3a 100644 --- a/maven-modules/maven-copy-files/copy-rename-maven-plugin/pom.xml +++ b/maven-modules/maven-copy-files/copy-rename-maven-plugin/pom.xml @@ -1,20 +1,19 @@ - + 4.0.0 + org.baeldung + copy-rename-maven-plugin + 1.0-SNAPSHOT + copy-rename-maven-plugin + maven-copy-files com.baeldung 1.0-SNAPSHOT - org.baeldung - copy-rename-maven-plugin - 1.0-SNAPSHOT - copy-rename-maven-plugin - - UTF-8 - 1.7 - 1.7 - + junit @@ -23,6 +22,7 @@ test + @@ -89,4 +89,11 @@ - + + + UTF-8 + 1.7 + 1.7 + + + \ No newline at end of file diff --git a/maven-modules/maven-copy-files/maven-antrun-plugin/pom.xml b/maven-modules/maven-copy-files/maven-antrun-plugin/pom.xml index 61017dd18a..97e714d9ff 100644 --- a/maven-modules/maven-copy-files/maven-antrun-plugin/pom.xml +++ b/maven-modules/maven-copy-files/maven-antrun-plugin/pom.xml @@ -1,20 +1,19 @@ - + 4.0.0 + org.baeldung + maven-antrun-plugin + 1.0-SNAPSHOT + maven-antrun-plugin + maven-copy-files com.baeldung 1.0-SNAPSHOT - org.baeldung - maven-antrun-plugin - 1.0-SNAPSHOT - maven-antrun-plugin - - UTF-8 - 1.7 - 1.7 - + junit @@ -23,6 +22,7 @@ test + @@ -35,7 +35,8 @@ - + @@ -91,4 +92,11 @@ - + + + UTF-8 + 1.7 + 1.7 + + + \ No newline at end of file diff --git a/maven-modules/maven-copy-files/maven-resources-plugin/pom.xml b/maven-modules/maven-copy-files/maven-resources-plugin/pom.xml index 7dbe851f95..eed40565da 100644 --- a/maven-modules/maven-copy-files/maven-resources-plugin/pom.xml +++ b/maven-modules/maven-copy-files/maven-resources-plugin/pom.xml @@ -1,20 +1,19 @@ - + 4.0.0 + org.baeldung + maven-resources-plugin + 1.0-SNAPSHOT + maven-resoures-plugin + maven-copy-files com.baeldung 1.0-SNAPSHOT - org.baeldung - maven-resources-plugin - 1.0-SNAPSHOT - maven-resoures-plugin - - UTF-8 - 1.7 - 1.7 - + junit @@ -23,6 +22,7 @@ test + @@ -89,4 +89,11 @@ - + + + UTF-8 + 1.7 + 1.7 + + + \ No newline at end of file diff --git a/maven-modules/maven-copy-files/pom.xml b/maven-modules/maven-copy-files/pom.xml index b7b67286bc..d2208d68eb 100644 --- a/maven-modules/maven-copy-files/pom.xml +++ b/maven-modules/maven-copy-files/pom.xml @@ -1,23 +1,28 @@ - + 4.0.0 + com.baeldung + maven-copy-files + 1.0-SNAPSHOT + maven-copy-files + pom + + http://www.example.com + maven-modules com.baeldung 0.0.1-SNAPSHOT - com.baeldung - maven-copy-files - 1.0-SNAPSHOT - pom - maven-copy-files - - http://www.example.com - - UTF-8 - 1.7 - 1.7 - + + + maven-resources-plugin + maven-antrun-plugin + copy-rename-maven-plugin + + junit @@ -26,6 +31,7 @@ test + @@ -72,9 +78,11 @@ - - maven-resources-plugin - maven-antrun-plugin - copy-rename-maven-plugin - - + + + UTF-8 + 1.7 + 1.7 + + + \ No newline at end of file diff --git a/maven-modules/maven-custom-plugin/counter-maven-plugin/pom.xml b/maven-modules/maven-custom-plugin/counter-maven-plugin/pom.xml index 7ddf5b739c..9648464102 100644 --- a/maven-modules/maven-custom-plugin/counter-maven-plugin/pom.xml +++ b/maven-modules/maven-custom-plugin/counter-maven-plugin/pom.xml @@ -1,12 +1,12 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 com.baeldung counter-maven-plugin 0.0.1-SNAPSHOT - counter-maven-plugin Maven Mojo + counter-maven-plugin maven-plugin http://maven.apache.org diff --git a/maven-modules/maven-custom-plugin/usage-example/pom.xml b/maven-modules/maven-custom-plugin/usage-example/pom.xml index f512fc104d..bf5a69146b 100644 --- a/maven-modules/maven-custom-plugin/usage-example/pom.xml +++ b/maven-modules/maven-custom-plugin/usage-example/pom.xml @@ -1,6 +1,5 @@ - 4.0.0 @@ -48,4 +47,4 @@ 4.12 - + \ No newline at end of file diff --git a/maven-modules/maven-integration-test/README.md b/maven-modules/maven-integration-test/README.md index e73a73e61e..708cb3bf23 100644 --- a/maven-modules/maven-integration-test/README.md +++ b/maven-modules/maven-integration-test/README.md @@ -7,4 +7,5 @@ This module contains articles about Integration Testing with Maven and related p - [Integration Testing with Maven](https://www.baeldung.com/maven-integration-test) - [Build a Jar with Maven and Ignore the Test Results](https://www.baeldung.com/maven-ignore-test-results) - [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) \ No newline at end of file +- [The Maven Failsafe Plugin](https://www.baeldung.com/maven-failsafe-plugin) +- [Difference Between Maven Surefire and Failsafe Plugins](https://www.baeldung.com/maven-surefire-vs-failsafe) diff --git a/maven-modules/maven-integration-test/pom.xml b/maven-modules/maven-integration-test/pom.xml index 4ab8de783d..692751366d 100644 --- a/maven-modules/maven-integration-test/pom.xml +++ b/maven-modules/maven-integration-test/pom.xml @@ -6,6 +6,7 @@ maven-integration-test 0.0.1-SNAPSHOT maven-integration-test + war com.baeldung @@ -116,7 +117,9 @@ verify - + + **/*IntegrationTest + diff --git a/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java b/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java new file mode 100644 index 0000000000..a50fa16394 --- /dev/null +++ b/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java @@ -0,0 +1,13 @@ +package com.baeldung.maven.it; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FailsafeBuildPhaseIntegrationTest { + + @Test + public void whenTestExecutes_thenPreAndPostIntegrationBuildPhasesAreExecuted() { + assertTrue(true); + } +} diff --git a/maven-modules/maven-pom-types/pom.xml b/maven-modules/maven-pom-types/pom.xml index 98fbc828a0..8b174ea08e 100644 --- a/maven-modules/maven-pom-types/pom.xml +++ b/maven-modules/maven-pom-types/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung maven-pom-types diff --git a/maven-modules/maven-printing-plugins/pom.xml b/maven-modules/maven-printing-plugins/pom.xml index 805c3c1633..b68c88dbee 100644 --- a/maven-modules/maven-printing-plugins/pom.xml +++ b/maven-modules/maven-printing-plugins/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 maven-printing-plugins maven-printing-plugins @@ -27,11 +27,12 @@ - - - + + + + message="Save to file!" /> @@ -84,4 +85,5 @@ - + + \ No newline at end of file diff --git a/maven-modules/maven-properties/README.md b/maven-modules/maven-properties/README.md index 52ac8506b3..75ae24d215 100644 --- a/maven-modules/maven-properties/README.md +++ b/maven-modules/maven-properties/README.md @@ -6,3 +6,4 @@ have their own dedicated modules. ### Relevant Articles - [Accessing Maven Properties in Java](https://www.baeldung.com/java-accessing-maven-properties) +- [Default Values for Maven Properties](https://www.baeldung.com/maven-properties-defaults) diff --git a/maven-modules/plugin-management/README.md b/maven-modules/plugin-management/README.md new file mode 100644 index 0000000000..dec3a71cfd --- /dev/null +++ b/maven-modules/plugin-management/README.md @@ -0,0 +1,3 @@ +## Relevant Articles: + +- [Plugin Management in Maven](https://www.baeldung.com/maven-plugin-management) diff --git a/maven-modules/plugin-management/pom.xml b/maven-modules/plugin-management/pom.xml new file mode 100644 index 0000000000..cd75e5dbd0 --- /dev/null +++ b/maven-modules/plugin-management/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + plugin-management + 0.0.1-SNAPSHOT + pom + + + maven-modules + com.baeldung + 0.0.1-SNAPSHOT + + + + submodule-1 + submodule-2 + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${maven.bulid.helper.plugin} + + + add-resource + generate-resources + + add-resource + + + + + src/resources + json + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin} + + 1.8 + 1.8 + + + + + + + + 3.8.1 + 3.2.0 + + + \ No newline at end of file diff --git a/maven-modules/plugin-management/submodule-1/pom.xml b/maven-modules/plugin-management/submodule-1/pom.xml new file mode 100644 index 0000000000..c36e092254 --- /dev/null +++ b/maven-modules/plugin-management/submodule-1/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + submodule-1 + + + plugin-management + com.baeldung + 0.0.1-SNAPSHOT + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + + \ No newline at end of file diff --git a/maven-modules/plugin-management/submodule-1/src/resources/include.json b/maven-modules/plugin-management/submodule-1/src/resources/include.json new file mode 100644 index 0000000000..d5df76e7e0 --- /dev/null +++ b/maven-modules/plugin-management/submodule-1/src/resources/include.json @@ -0,0 +1,3 @@ +{ + "key": "value" +} \ No newline at end of file diff --git a/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java b/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java new file mode 100644 index 0000000000..ab6b781790 --- /dev/null +++ b/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java @@ -0,0 +1,19 @@ +package com.baeldung; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.net.URL; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CopiesAdditionalResourcesUnitTest { + + @Test + void givenAdditionalResource_whenCopyingFromSourceToDestination_thenShouldBeInDestination() { + URL resource = getClass().getClassLoader().getResource("json/include.json"); + File destinationFile = new File(resource.getFile()); + + assertTrue(destinationFile.exists()); + } +} diff --git a/maven-modules/plugin-management/submodule-2/pom.xml b/maven-modules/plugin-management/submodule-2/pom.xml new file mode 100644 index 0000000000..e50d3cc26d --- /dev/null +++ b/maven-modules/plugin-management/submodule-2/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + submodule-2 + + + plugin-management + com.baeldung + 0.0.1-SNAPSHOT + + + \ No newline at end of file diff --git a/maven-modules/pom.xml b/maven-modules/pom.xml index 0f146e26da..5b4b567ae6 100644 --- a/maven-modules/pom.xml +++ b/maven-modules/pom.xml @@ -16,7 +16,7 @@ - maven-copy-files + maven-copy-files maven-custom-plugin maven-exec-plugin maven-integration-test @@ -32,6 +32,9 @@ version-overriding-plugins versions-maven-plugin maven-printing-plugins + maven-builder-plugin + host-maven-repo-example + plugin-management - + \ No newline at end of file diff --git a/metrics/pom.xml b/metrics/pom.xml index 2020cd28cf..0ba590ceec 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -81,13 +81,18 @@ ${assertj-core.version} test + + com.netflix.spectator + spectator-api + 0.132.0 + 3.1.2 3.1.0 0.12.17 - 0.12.0.RELEASE + 1.7.1 2.0.7.RELEASE 3.11.1 diff --git a/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java b/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java index 1d738085ce..8fdb87cfe7 100644 --- a/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java +++ b/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java @@ -1,10 +1,10 @@ package com.baeldung.metrics.micrometer; +import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import io.micrometer.core.instrument.binder.JvmThreadMetrics; @SpringBootApplication public class MicrometerApp { @@ -14,7 +14,7 @@ public class MicrometerApp { return new JvmThreadMetrics(); } - public static void main(String[] args) throws Exception { + public static void main(String[] args) { SpringApplication.run(MicrometerApp.class, args); } diff --git a/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasIntegrationTest.java b/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasIntegrationTest.java index 689e23ad6d..02ef926794 100644 --- a/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasIntegrationTest.java +++ b/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasIntegrationTest.java @@ -1,11 +1,7 @@ package com.baeldung.metrics.micrometer; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.within; -import static org.assertj.core.api.Assertions.withinPercentage; -import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.collection.IsMapContaining.hasEntry; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -16,29 +12,25 @@ import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.Measurement; -import io.micrometer.core.instrument.Meter.Type; -import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Metrics; -import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; +import io.micrometer.core.instrument.distribution.HistogramSnapshot; +import io.micrometer.core.instrument.distribution.ValueAtPercentile; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -import io.micrometer.core.instrument.stats.hist.Histogram; -import io.micrometer.core.instrument.stats.quantile.WindowSketchQuantiles; import java.time.Duration; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Arrays; import java.util.Map; +import java.util.TreeMap; import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import org.assertj.core.data.Percentage; import org.junit.Before; import org.junit.Test; -import org.junit.jupiter.api.Assertions; import com.netflix.spectator.atlas.AtlasConfig; @@ -55,7 +47,7 @@ public class MicrometerAtlasIntegrationTest { @Override public Duration step() { - return Duration.ofSeconds(1); + return Duration.ofSeconds(10); } @Override @@ -77,9 +69,9 @@ public class MicrometerAtlasIntegrationTest { compositeRegistry.gauge("baeldung.heat", 90); - Optional oneGauge = oneSimpleMeter + Optional oneGauge = Optional.ofNullable(oneSimpleMeter .find("baeldung.heat") - .gauge(); + .gauge()); assertTrue(oneGauge.isPresent()); Iterator measurements = oneGauge .get() @@ -91,9 +83,9 @@ public class MicrometerAtlasIntegrationTest { .next() .getValue(), equalTo(90.00)); - Optional atlasGauge = atlasMeterRegistry + Optional atlasGauge = Optional.ofNullable(atlasMeterRegistry .find("baeldung.heat") - .gauge(); + .gauge()); assertTrue(atlasGauge.isPresent()); Iterator anotherMeasurements = atlasGauge .get() @@ -122,14 +114,14 @@ public class MicrometerAtlasIntegrationTest { .increment(); new CountedObject(); - Optional counterOptional = Metrics.globalRegistry + Optional counterOptional = Optional.ofNullable(Metrics.globalRegistry .find("objects.instance") - .counter(); + .counter()); assertTrue(counterOptional.isPresent()); - assertTrue(counterOptional + assertEquals(counterOptional .get() - .count() == 2.0); + .count() , 2.0, 0.0); } @Test @@ -142,10 +134,10 @@ public class MicrometerAtlasIntegrationTest { .register(registry); counter.increment(2.0); - assertTrue(counter.count() == 2); + assertEquals(counter.count(), 2, 0); counter.increment(-1); - assertTrue(counter.count() == 2); + assertEquals(counter.count(), 1, 0); } @Test @@ -161,8 +153,8 @@ public class MicrometerAtlasIntegrationTest { timer.record(30, TimeUnit.MILLISECONDS); - assertTrue(2 == timer.count()); - + assertEquals(2, timer.count(), 0); + assertThat(timer.totalTime(TimeUnit.MILLISECONDS)).isBetween(40.0, 55.0); } @@ -173,12 +165,12 @@ public class MicrometerAtlasIntegrationTest { .builder("3rdPartyService") .register(registry); - long currentTaskId = longTaskTimer.start(); + LongTaskTimer.Sample currentTaskId = longTaskTimer.start(); try { TimeUnit.MILLISECONDS.sleep(2); } catch (InterruptedException ignored) { } - long timeElapsed = longTaskTimer.stop(currentTaskId); + long timeElapsed = currentTaskId.stop(); assertEquals(2L, timeElapsed/((int) 1e6),1L); } @@ -191,10 +183,10 @@ public class MicrometerAtlasIntegrationTest { .builder("cache.size", list, List::size) .register(registry); - assertTrue(gauge.value() == 0.0); + assertEquals(gauge.value(), 0.0, 0.0); list.add("1"); - assertTrue(gauge.value() == 1.0); + assertEquals(gauge.value(), 1.0, 0.0); } @Test @@ -208,18 +200,17 @@ public class MicrometerAtlasIntegrationTest { distributionSummary.record(4); distributionSummary.record(5); - assertTrue(3 == distributionSummary.count()); - assertTrue(12 == distributionSummary.totalAmount()); + assertEquals(3, distributionSummary.count(), 0); + assertEquals(12, distributionSummary.totalAmount(), 0); } @Test - public void givenTimer_whenEnrichWithQuantile_thenQuantilesComputed() { + public void givenTimer_whenEnrichWithPercentile_thenPercentilesComputed() { SimpleMeterRegistry registry = new SimpleMeterRegistry(); Timer timer = Timer .builder("test.timer") - .quantiles(WindowSketchQuantiles - .quantiles(0.3, 0.5, 0.95) - .create()) + .publishPercentiles(0.3, 0.5, 0.95) + .publishPercentileHistogram() .register(registry); timer.record(2, TimeUnit.SECONDS); @@ -229,27 +220,18 @@ public class MicrometerAtlasIntegrationTest { timer.record(8, TimeUnit.SECONDS); timer.record(13, TimeUnit.SECONDS); - Map quantileMap = extractTagValueMap(registry, Type.Gauge, 1e9); - assertThat(quantileMap, allOf(hasEntry("quantile=0.3", 2), hasEntry("quantile=0.5", 3), hasEntry("quantile=0.95", 8))); - } + Map expectedMicrometer = new TreeMap<>(); + expectedMicrometer.put(0.3, 1946.157056); + expectedMicrometer.put(0.5, 3019.89888); + expectedMicrometer.put(0.95, 13354.663936); - private Map extractTagValueMap(MeterRegistry registry, Type meterType, double valueDivisor) { - return registry - .getMeters() - .stream() - .filter(meter -> meter.getType() == meterType) - .collect(Collectors.toMap(meter -> { - Tag tag = meter - .getId() - .getTags() - .iterator() - .next(); - return tag.getKey() + "=" + tag.getValue(); - }, meter -> (int) (meter - .measure() - .iterator() - .next() - .getValue() / valueDivisor))); + Map actualMicrometer = new TreeMap<>(); + ValueAtPercentile[] percentiles = timer.takeSnapshot().percentileValues(); + for (ValueAtPercentile percentile : percentiles) { + actualMicrometer.put(percentile.percentile(), percentile.value(TimeUnit.MILLISECONDS)); + } + + assertEquals(expectedMicrometer, actualMicrometer); } @Test @@ -257,7 +239,7 @@ public class MicrometerAtlasIntegrationTest { SimpleMeterRegistry registry = new SimpleMeterRegistry(); DistributionSummary hist = DistributionSummary .builder("summary") - .histogram(Histogram.linear(0, 10, 5)) + .serviceLevelObjectives(1, 10, 5) .register(registry); hist.record(3); @@ -267,17 +249,28 @@ public class MicrometerAtlasIntegrationTest { hist.record(13); hist.record(26); - Map histograms = extractTagValueMap(registry, Type.Counter, 1.0); + Map expectedMicrometer = new TreeMap<>(); + expectedMicrometer.put(1,0D); + expectedMicrometer.put(10,2D); + expectedMicrometer.put(5,1D); - assertThat(histograms, allOf(hasEntry("bucket=0.0", 0), hasEntry("bucket=10.0", 2), hasEntry("bucket=20.0", 2), hasEntry("bucket=30.0", 1), hasEntry("bucket=40.0", 1), hasEntry("bucket=Infinity", 0))); + Map actualMicrometer = new TreeMap<>(); + HistogramSnapshot snapshot = hist.takeSnapshot(); + Arrays.stream(snapshot.histogramCounts()).forEach(p -> { + actualMicrometer.put((Integer.valueOf((int) p.bucket())), p.count()); + }); + + assertEquals(expectedMicrometer, actualMicrometer); } @Test public void givenTimer_whenEnrichWithTimescaleHistogram_thenTimeScaleDataCollected() { SimpleMeterRegistry registry = new SimpleMeterRegistry(); + Duration[] durations = {Duration.ofMillis(25), Duration.ofMillis(300), Duration.ofMillis(600)}; Timer timer = Timer .builder("timer") - .histogram(Histogram.linearTime(TimeUnit.MILLISECONDS, 0, 200, 3)) + .sla(durations) + .publishPercentileHistogram() .register(registry); timer.record(1000, TimeUnit.MILLISECONDS); @@ -286,10 +279,18 @@ public class MicrometerAtlasIntegrationTest { timer.record(341, TimeUnit.MILLISECONDS); timer.record(500, TimeUnit.MILLISECONDS); - Map histograms = extractTagValueMap(registry, Type.Counter, 1.0); + Map expectedMicrometer = new TreeMap<>(); + expectedMicrometer.put(2.5E7,1D); + expectedMicrometer.put(3.0E8,1D); + expectedMicrometer.put(6.0E8,4D); - assertThat(histograms, allOf(hasEntry("bucket=0.0", 0), hasEntry("bucket=2.0E8", 1), hasEntry("bucket=4.0E8", 1), hasEntry("bucket=Infinity", 3))); + Map actualMicrometer = new TreeMap<>(); + HistogramSnapshot snapshot = timer.takeSnapshot(); + Arrays.stream(snapshot.histogramCounts()).forEach(p -> { + actualMicrometer.put((Double.valueOf((int) p.bucket())), p.count()); + }); + assertEquals(expectedMicrometer, actualMicrometer); } } diff --git a/netflix-modules/mantis/pom.xml b/netflix-modules/mantis/pom.xml index 1f8b377b94..0bab944b8a 100644 --- a/netflix-modules/mantis/pom.xml +++ b/netflix-modules/mantis/pom.xml @@ -55,9 +55,10 @@ test + - jcenter + jcenter https://jcenter.bintray.com/ diff --git a/open-liberty/pom.xml b/open-liberty/pom.xml index aff951cfd8..268b65c07e 100644 --- a/open-liberty/pom.xml +++ b/open-liberty/pom.xml @@ -111,4 +111,5 @@ 9443 7070 + \ No newline at end of file diff --git a/parent-boot-1/pom.xml b/parent-boot-1/pom.xml index 96f4b1cbe3..7029d96a7e 100644 --- a/parent-boot-1/pom.xml +++ b/parent-boot-1/pom.xml @@ -60,4 +60,4 @@ 1.5.22.RELEASE - + \ No newline at end of file diff --git a/parent-boot-2/pom.xml b/parent-boot-2/pom.xml index c67842e313..accbc2df96 100644 --- a/parent-boot-2/pom.xml +++ b/parent-boot-2/pom.xml @@ -81,10 +81,10 @@ 3.3.0 1.0.22.RELEASE - 2.4.3 + 2.5.1 1.9.1 3.4.0 - + \ No newline at end of file diff --git a/parent-java/pom.xml b/parent-java/pom.xml index c1abe79b2e..09d7f4c96d 100644 --- a/parent-java/pom.xml +++ b/parent-java/pom.xml @@ -46,4 +46,4 @@ 2.2 - + \ No newline at end of file diff --git a/patterns/design-patterns-architectural/README.md b/patterns/design-patterns-architectural/README.md index ae6781c66c..a8a5a98b88 100644 --- a/patterns/design-patterns-architectural/README.md +++ b/patterns/design-patterns-architectural/README.md @@ -2,3 +2,4 @@ - [Service Locator Pattern and Java Implementation](https://www.baeldung.com/java-service-locator-pattern) - [The DAO Pattern in Java](https://www.baeldung.com/java-dao-pattern) - [DAO vs Repository Patterns](https://www.baeldung.com/java-dao-vs-repository) +- [Difference Between MVC and MVP Patterns](https://www.baeldung.com/mvc-vs-mvp-pattern) diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/MvcMainClass.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/MvcMainClass.java new file mode 100644 index 0000000000..9ccd4ee74c --- /dev/null +++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/MvcMainClass.java @@ -0,0 +1,24 @@ +package com.baeldung.mvc_mvp.mvc; + +public class MvcMainClass { + + public static void main(String[] args) { + + Product model = retrieveProductFromDatabase(); + ProductView view = new ProductView(); + model.setView(view); + model.showProduct(); + + ProductController controller = new ProductController(model); + controller.setName("SmartPhone"); + model.showProduct(); + } + + private static Product retrieveProductFromDatabase() { + Product product = new Product(); + product.setName("Mobile"); + product.setDescription("New Brand"); + product.setPrice(1000.0); + return product; + } +} diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/Product.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/Product.java new file mode 100644 index 0000000000..5c44d4475a --- /dev/null +++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/Product.java @@ -0,0 +1,45 @@ +package com.baeldung.mvc_mvp.mvc; + +public class Product { + private String name; + private String description; + private Double price; + private ProductView view; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public ProductView getView() { + return view; + } + + public void setView(ProductView view) { + this.view = view; + } + + public void showProduct() { + view.printProductDetails(name, description, price); + } + +} diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductController.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductController.java new file mode 100644 index 0000000000..62cdb76f23 --- /dev/null +++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductController.java @@ -0,0 +1,34 @@ +package com.baeldung.mvc_mvp.mvc; + +public class ProductController { + private final Product product; + + public ProductController(Product product) { + this.product = product; + } + + public String getName() { + return product.getName(); + } + + public void setName(String name) { + product.setName(name); + } + + public String getDescription() { + return product.getDescription(); + } + + public void setDescription(String description) { + product.setDescription(description); + } + + public Double getPrice() { + return product.getPrice(); + } + + public void setPrice(Double price) { + product.setPrice(price); + } + +} diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductView.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductView.java new file mode 100644 index 0000000000..43f45b6df7 --- /dev/null +++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductView.java @@ -0,0 +1,15 @@ +package com.baeldung.mvc_mvp.mvc; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProductView { + private static Logger log = LoggerFactory.getLogger(ProductView.class); + + public void printProductDetails(String name, String description, Double price) { + log.info("Product details:"); + log.info("product Name: " + name); + log.info("product Description: " + description); + log.info("product price: " + price); + } +} diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/MvpMainClass.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/MvpMainClass.java new file mode 100644 index 0000000000..7d12a0142c --- /dev/null +++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/MvpMainClass.java @@ -0,0 +1,22 @@ +package com.baeldung.mvc_mvp.mvp; + +public class MvpMainClass { + + public static void main(String[] args) { + + Product model = retrieveProductFromDatabase(); + ProductView view = new ProductView(); + ProductPresenter presenter = new ProductPresenter(model, view); + presenter.showProduct(); + presenter.setName("SmartPhone"); + presenter.showProduct(); + } + + private static Product retrieveProductFromDatabase() { + Product product = new Product(); + product.setName("Mobile"); + product.setDescription("New Brand"); + product.setPrice(1000.0); + return product; + } +} diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/Product.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/Product.java new file mode 100644 index 0000000000..315e6e69b6 --- /dev/null +++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/Product.java @@ -0,0 +1,32 @@ +package com.baeldung.mvc_mvp.mvp; + +public class Product { + private String name; + private String description; + private Double price; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + +} diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductPresenter.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductPresenter.java new file mode 100644 index 0000000000..2b87bdbbb5 --- /dev/null +++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductPresenter.java @@ -0,0 +1,40 @@ +package com.baeldung.mvc_mvp.mvp; + +public class ProductPresenter { + private final Product product; + private final ProductView view; + + public ProductPresenter(Product product, ProductView view) { + this.product = product; + this.view = view; + } + + public String getName() { + return product.getName(); + } + + public void setName(String name) { + product.setName(name); + } + + public String getDescription() { + return product.getDescription(); + } + + public void setDescription(String description) { + product.setDescription(description); + } + + public Double getProductPrice() { + return product.getPrice(); + } + + public void setPrice(Double price) { + product.setPrice(price); + } + + public void showProduct() { + view.printProductDetails(product.getName(), product.getDescription(), product.getPrice()); + } + +} diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductView.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductView.java new file mode 100644 index 0000000000..27ccc6743d --- /dev/null +++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductView.java @@ -0,0 +1,15 @@ +package com.baeldung.mvc_mvp.mvp; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProductView { + private static Logger log = LoggerFactory.getLogger(ProductView.class); + + public void printProductDetails(String name, String description, Double price) { + log.info("Product details:"); + log.info("product Name: " + name); + log.info("product Description: " + description); + log.info("product price: " + price); + } +} diff --git a/patterns/enterprise-patterns/pom.xml b/patterns/enterprise-patterns/pom.xml index 3637072c58..5b952b9743 100644 --- a/patterns/enterprise-patterns/pom.xml +++ b/patterns/enterprise-patterns/pom.xml @@ -1,62 +1,67 @@ - 4.0.0 - - com.baeldung - patterns - 1.0.0-SNAPSHOT - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + enterprise-patterns + pom - enterprise-patterns - pom + + com.baeldung + patterns + 1.0.0-SNAPSHOT + - - 3.7.4 - + + wire-tap + - - - org.apache.camel.springboot - camel-spring-boot-starter - - - org.apache.camel.springboot - camel-activemq-starter - + + + org.apache.camel.springboot + camel-spring-boot-starter + + + org.apache.camel.springboot + camel-activemq-starter + + + + org.springframework.boot + spring-boot-starter-test + 2.2.2.RELEASE + test + + + org.apache.camel + camel-test-spring-junit5 + test + + - - - org.springframework.boot - spring-boot-starter-test - 2.2.2.RELEASE - - - org.apache.camel - camel-test-spring-junit5 - test - - + + + + org.apache.camel.springboot + camel-spring-boot-dependencies + ${camel.version} + pom + import + + + - - - - org.apache.camel.springboot - camel-spring-boot-dependencies - ${camel.version} - pom - import - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + - - - - org.springframework.boot - spring-boot-maven-plugin - - - - + + 3.7.4 + + + \ No newline at end of file diff --git a/patterns/enterprise-patterns/wire-tap/README.md b/patterns/enterprise-patterns/wire-tap/README.md index 5f2aaf0d5a..95cd1d585f 100644 --- a/patterns/enterprise-patterns/wire-tap/README.md +++ b/patterns/enterprise-patterns/wire-tap/README.md @@ -31,4 +31,5 @@ For more details, check out the AmqApplicationUnitTest.class. - [Wire tap (Enterprise Integration Pattern)](https://drafts.baeldung.com/?p=103346&preview=true) - [Intro to Apache camel](https://www.baeldung.com/apache-camel-intro) +- [Wire Tap Enterprise Integration Pattern](https://www.baeldung.com/wiretap-pattern) diff --git a/patterns/enterprise-patterns/wire-tap/pom.xml b/patterns/enterprise-patterns/wire-tap/pom.xml index 9169c4ac91..e7959e17f0 100644 --- a/patterns/enterprise-patterns/wire-tap/pom.xml +++ b/patterns/enterprise-patterns/wire-tap/pom.xml @@ -1,29 +1,25 @@ - 4.0.0 - wire-tap - 1.0 - jar + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + wire-tap + 1.0 + jar - - enterprise-patterns - com.baeldung - 1.0.0-SNAPSHOT - + + enterprise-patterns + com.baeldung + 1.0.0-SNAPSHOT + - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - + \ No newline at end of file diff --git a/patterns/hexagonal-architecture/pom.xml b/patterns/hexagonal-architecture/pom.xml index f8d4524514..9dfc113d03 100644 --- a/patterns/hexagonal-architecture/pom.xml +++ b/patterns/hexagonal-architecture/pom.xml @@ -21,7 +21,6 @@ org.springframework.boot spring-boot-starter-web - org.springframework.boot spring-boot-starter-data-mongodb diff --git a/patterns/simplehexagonalexample/pom.xml b/patterns/simplehexagonalexample/pom.xml new file mode 100644 index 0000000000..b73e81be44 --- /dev/null +++ b/patterns/simplehexagonalexample/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + 1.0.0-SNAPSHOT + simple-hexagonal-example + simpleHexagonalExample + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/DailyQuoteMain.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/DailyQuoteMain.java new file mode 100644 index 0000000000..de8b2f4793 --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/DailyQuoteMain.java @@ -0,0 +1,32 @@ +package com.baeldung.simplehexagonalex; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.baeldung.simplehexagonalex.controller.QuoteCliController; +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; + +@SpringBootApplication +public class DailyQuoteMain implements CommandLineRunner { + + private static final Logger LOG = LoggerFactory.getLogger(DailyQuoteMain.class); + @Autowired + private QuoteCliController quoteCliController; + + public static void main(final String[] args) { + + SpringApplication.run(DailyQuoteMain.class, args); + } + + @Override + public void run(String... args) throws Exception { + + QuoteOfTheDay quoteOfTheDay = quoteCliController.getQuote("cliController"); + + LOG.info(quoteOfTheDay.toString()); + } +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteCliController.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteCliController.java new file mode 100644 index 0000000000..9ecb2bf822 --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteCliController.java @@ -0,0 +1,24 @@ +package com.baeldung.simplehexagonalex.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; +import com.baeldung.simplehexagonalex.domain.service.QuoteService; + +@Component +public class QuoteCliController { + + private static final Logger LOG = LoggerFactory.getLogger(QuoteCliController.class); + + @Autowired + private QuoteService quoteService; + + public QuoteOfTheDay getQuote(String userId) { + + LOG.info("Getting quote"); + return quoteService.getQuote(userId); + } +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteRestController.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteRestController.java new file mode 100644 index 0000000000..061cff8cec --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteRestController.java @@ -0,0 +1,24 @@ +package com.baeldung.simplehexagonalex.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; +import com.baeldung.simplehexagonalex.domain.service.QuoteService; + +@RestController +@RequestMapping("/quote") +public class QuoteRestController { + + @Autowired + private QuoteService quoteService; + + @GetMapping(path = "/{userId}") + public QuoteOfTheDay getEmployee(@PathVariable("userId") String userId) { + return quoteService.getQuote(userId); + } + +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/QuoteOfTheDay.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/QuoteOfTheDay.java new file mode 100644 index 0000000000..c74c224c70 --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/QuoteOfTheDay.java @@ -0,0 +1,56 @@ +package com.baeldung.simplehexagonalex.domain; + +import java.util.Objects; + +public class QuoteOfTheDay { + + private String userName; + private String quote; + private String provider; + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getQuote() { + return quote; + } + + public void setQuote(String quote) { + this.quote = quote; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + @Override + public int hashCode() { + return Objects.hash(provider, quote, userName); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + QuoteOfTheDay other = (QuoteOfTheDay) obj; + return Objects.equals(provider, other.provider) && Objects.equals(quote, other.quote) && Objects.equals(userName, other.userName); + } + + @Override + public String toString() { + return "QuoteOfTheDay [userName=" + userName + ", quote=" + quote + ", provider=" + provider + "]"; + } +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/repository/QuoteOfTheDayFromProvider.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/repository/QuoteOfTheDayFromProvider.java new file mode 100644 index 0000000000..7e70cebd12 --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/repository/QuoteOfTheDayFromProvider.java @@ -0,0 +1,8 @@ +package com.baeldung.simplehexagonalex.domain.repository; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; + +public interface QuoteOfTheDayFromProvider { + + QuoteOfTheDay getQuote(); +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteAggregator.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteAggregator.java new file mode 100644 index 0000000000..16f7015ace --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteAggregator.java @@ -0,0 +1,22 @@ +package com.baeldung.simplehexagonalex.domain.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; +import com.baeldung.simplehexagonalex.domain.repository.QuoteOfTheDayFromProvider; + +@Service +public class QuoteAggregator implements QuoteService { + + @Autowired + private QuoteOfTheDayFromProvider quoteOfTheDayFromProvider; + + @Override + public QuoteOfTheDay getQuote(String userName) { + + QuoteOfTheDay quoteOfTheDay = quoteOfTheDayFromProvider.getQuote(); + quoteOfTheDay.setUserName(userName); + return quoteOfTheDay; + } +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteService.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteService.java new file mode 100644 index 0000000000..300387a41d --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteService.java @@ -0,0 +1,8 @@ +package com.baeldung.simplehexagonalex.domain.service; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; + +public interface QuoteService { + + QuoteOfTheDay getQuote(String userName); +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuote.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuote.java new file mode 100644 index 0000000000..0cbbc60726 --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuote.java @@ -0,0 +1,109 @@ +package com.baeldung.simplehexagonalex.repository.primary.quoteadapter; + +import java.util.List; +import java.util.Objects; + +public class ProviderQuote { + + private String quote; + private String length; + private String author; + private List tags; + private String category; + private String language; + private String date; + private String permalink; + private String id; + private String background; + private String title; + + public ProviderQuote() { + + } + + public ProviderQuote(String quote, String length, String author, List tags, String category, String language, String date, String permalink, String id, String background, String title) { + super(); + this.quote = quote; + this.length = length; + this.author = author; + this.tags = tags; + this.category = category; + this.language = language; + this.date = date; + this.permalink = permalink; + this.id = id; + this.background = background; + this.title = title; + } + + public String getQuote() { + return quote; + } + + public String getLength() { + return length; + } + + public String getAuthor() { + return author; + } + + public List getTags() { + return tags; + } + + public String getCategory() { + return category; + } + + public String getLanguage() { + return language; + } + + public String getDate() { + return date; + } + + public String getPermalink() { + return permalink; + } + + public String getId() { + return id; + } + + public String getBackground() { + return background; + } + + public String getTitle() { + return title; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ProviderQuote other = (ProviderQuote) obj; + return Objects.equals(id, other.id); + } + + @Override + public String toString() { + return "TheysaysoQuote [quote=" + quote + ", length=" + length + ", " + "author=" + author + ", tags=" + tags + ", category=" + category + ", language=" + language + ", date=" + date + ", permalink=" + permalink + ", id=" + id + ", background=" + + background + ", title=" + title + "]"; + } +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteAdapter.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteAdapter.java new file mode 100644 index 0000000000..ded08f7b18 --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteAdapter.java @@ -0,0 +1,54 @@ +package com.baeldung.simplehexagonalex.repository.primary.quoteadapter; + +import java.net.URI; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; +import com.baeldung.simplehexagonalex.domain.repository.QuoteOfTheDayFromProvider; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Service("providerQuoteAdapter") +@Primary +public class ProviderQuoteAdapter implements QuoteOfTheDayFromProvider { + + @Autowired + private Environment env; + + @Override + public QuoteOfTheDay getQuote() { + + QuoteOfTheDay quoteOfTheDay = new QuoteOfTheDay(); + ProviderQuoteEnvelope providerQuote; + try { + providerQuote = getProviderQuote(); + quoteOfTheDay.setQuote(providerQuote.getContents() + .getQuotes() + .get(0) + .getQuote()); + quoteOfTheDay.setProvider(providerQuote.getCopyright() + .getUrl()); + } catch (Exception e) { + quoteOfTheDay.setQuote("Unable to get the quote"); + quoteOfTheDay.setProvider("none"); + } + return quoteOfTheDay; + } + + private ProviderQuoteEnvelope getProviderQuote() throws Exception { + + HttpGet request = new HttpGet(URI.create(env.getProperty("theysayso.quote.provider.url"))); + CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = client.execute(request); + ProviderQuoteEnvelope providersQuote = new ObjectMapper().readValue(response.getEntity() + .getContent(), ProviderQuoteEnvelope.class); + return providersQuote; + } +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteEnvelope.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteEnvelope.java new file mode 100644 index 0000000000..611549f23d --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteEnvelope.java @@ -0,0 +1,68 @@ +package com.baeldung.simplehexagonalex.repository.primary.quoteadapter; + +import java.util.List; + +public class ProviderQuoteEnvelope { + + private Success success; + private Contents contents; + private String baseurl; + private Copyright copyright; + + public ProviderQuoteEnvelope() { + + } + + public ProviderQuoteEnvelope(Success success, Contents contents, String baseurl, Copyright copyright) { + super(); + this.success = success; + this.contents = contents; + this.baseurl = baseurl; + this.copyright = copyright; + } + + public Success getSuccess() { + return success; + } + + public Contents getContents() { + return contents; + } + + public String getBaseurl() { + return baseurl; + } + + public Copyright getCopyright() { + return copyright; + } + + public class Contents { + private List quotes; + + public List getQuotes() { + return quotes; + } + } + + public class Copyright { + private int year; + private String url; + + public int getYear() { + return year; + } + + public String getUrl() { + return url; + } + } + + public class Success { + private int total; + + public int getTotal() { + return total; + } + } +} diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/reposity/mock/quoteadapter/MockQuoteAdapter.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/reposity/mock/quoteadapter/MockQuoteAdapter.java new file mode 100644 index 0000000000..54c28fc94e --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/reposity/mock/quoteadapter/MockQuoteAdapter.java @@ -0,0 +1,20 @@ +package com.baeldung.simplehexagonalex.reposity.mock.quoteadapter; + +import org.springframework.stereotype.Service; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; +import com.baeldung.simplehexagonalex.domain.repository.QuoteOfTheDayFromProvider; + +@Service +public class MockQuoteAdapter implements QuoteOfTheDayFromProvider { + + @Override + public QuoteOfTheDay getQuote() { + + QuoteOfTheDay quoteOfTheDay = new QuoteOfTheDay(); + quoteOfTheDay.setQuote("Mock quote of the day"); + quoteOfTheDay.setProvider("Mock Provider"); + + return quoteOfTheDay; + } +} diff --git a/patterns/simplehexagonalexample/src/main/resources/application.properties b/patterns/simplehexagonalexample/src/main/resources/application.properties new file mode 100644 index 0000000000..dd9413bfd5 --- /dev/null +++ b/patterns/simplehexagonalexample/src/main/resources/application.properties @@ -0,0 +1 @@ +theysayso.quote.provider.url=https://quotes.rest/qod?language=en \ No newline at end of file diff --git a/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/MockAccessProviderUnitTest.java b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/MockAccessProviderUnitTest.java new file mode 100644 index 0000000000..602f7ea5d4 --- /dev/null +++ b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/MockAccessProviderUnitTest.java @@ -0,0 +1,22 @@ +package com.baeldung.simplehexagonalex.repository.primaryQuoteProvider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; +import com.baeldung.simplehexagonalex.reposity.mock.quoteadapter.MockQuoteAdapter; + +public class MockAccessProviderUnitTest { + + @Test + public void givenProvider_whenConnect_thenResponse() throws Exception { + + MockQuoteAdapter provider = new MockQuoteAdapter(); + QuoteOfTheDay quote = provider.getQuote(); + assertNotNull(quote); + assertEquals("Mock quote of the day", quote.getQuote()); + assertEquals("Mock Provider", quote.getProvider()); + } +} diff --git a/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/PrimaryAccessProviderIntegrationTest.java b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/PrimaryAccessProviderIntegrationTest.java new file mode 100644 index 0000000000..47e1dde136 --- /dev/null +++ b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/PrimaryAccessProviderIntegrationTest.java @@ -0,0 +1,28 @@ +package com.baeldung.simplehexagonalex.repository.primaryQuoteProvider; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; + +import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay; +import com.baeldung.simplehexagonalex.domain.repository.QuoteOfTheDayFromProvider; + +@SpringBootTest +public class PrimaryAccessProviderIntegrationTest { + + @Autowired + @Qualifier("providerQuoteAdapter") + QuoteOfTheDayFromProvider provider; + + @Test + public void whenQuoteProvider_thenResponse() throws Exception { + + QuoteOfTheDay quote = provider.getQuote(); + assertNotNull(quote); + assertThat(quote.getProvider()).contains("theysaidso"); + } +} diff --git a/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/QuoteRequestIntegrationTest.java b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/QuoteRequestIntegrationTest.java new file mode 100644 index 0000000000..552b5d51c2 --- /dev/null +++ b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/QuoteRequestIntegrationTest.java @@ -0,0 +1,36 @@ +package com.baeldung.simplehexagonalex.repository.primaryQuoteProvider; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +@SpringBootTest +@AutoConfigureMockMvc +public class QuoteRequestIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void whenRestQuoteRequest_thenResponse() throws Exception { + + MvcResult result = this.mockMvc.perform(get("/quote/tester")) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + + String content = result.getResponse() + .getContentAsString(); + + assertThat(content).contains("tester"); + assertThat(content).contains("theysaidso"); + } +} \ No newline at end of file diff --git a/patterns/simplehexagonalexample/src/test/resources/application.properties b/patterns/simplehexagonalexample/src/test/resources/application.properties new file mode 100644 index 0000000000..dd9413bfd5 --- /dev/null +++ b/patterns/simplehexagonalexample/src/test/resources/application.properties @@ -0,0 +1 @@ +theysayso.quote.provider.url=https://quotes.rest/qod?language=en \ No newline at end of file diff --git a/persistence-modules/core-java-persistence-2/README.md b/persistence-modules/core-java-persistence-2/README.md index cebd81388d..7b215bfef1 100644 --- a/persistence-modules/core-java-persistence-2/README.md +++ b/persistence-modules/core-java-persistence-2/README.md @@ -3,3 +3,5 @@ - [Getting Database URL From JDBC Connection Object](https://www.baeldung.com/jdbc-get-url-from-connection) - [JDBC URL Format For Different Databases](https://www.baeldung.com/java-jdbc-url-format) - [How to Check if a Database Table Exists with JDBC](https://www.baeldung.com/jdbc-check-table-exists) +- [Inserting Null Into an Integer Column Using JDBC](https://www.baeldung.com/jdbc-insert-null-into-integer-column) +- [A Guide to Auto-Commit in JDBC](https://www.baeldung.com/java-jdbc-auto-commit) diff --git a/persistence-modules/core-java-persistence-2/pom.xml b/persistence-modules/core-java-persistence-2/pom.xml index 092900b628..15676bf03e 100644 --- a/persistence-modules/core-java-persistence-2/pom.xml +++ b/persistence-modules/core-java-persistence-2/pom.xml @@ -45,7 +45,6 @@ 1.4.200 - 42.2.5.jre7 8.4.1.jre11 10.2.0.4.0 8.0.22 diff --git a/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/DBConfig.java b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/DBConfig.java new file mode 100644 index 0000000000..3907434239 --- /dev/null +++ b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/DBConfig.java @@ -0,0 +1,26 @@ +package com.baeldung.insertnull; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +public class DBConfig { + + private static Connection INSTANCE; + + public static Connection getConnection() throws SQLException { + if (INSTANCE == null) { + INSTANCE = DriverManager.getConnection("jdbc:h2:mem:insertnull", "user", "password"); + createPersonTable(); + } + return INSTANCE; + } + + private static void createPersonTable() throws SQLException { + try(Statement statement = INSTANCE.createStatement()) { + String sql = "CREATE TABLE Person (id INTEGER not null, name VARCHAR(50), lastName VARCHAR(50), age INTEGER, PRIMARY KEY (id))"; + statement.executeUpdate(sql); + } + } +} diff --git a/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/Person.java b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/Person.java new file mode 100644 index 0000000000..e261a7d74c --- /dev/null +++ b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/Person.java @@ -0,0 +1,48 @@ +package com.baeldung.insertnull; + +public class Person { + + private Integer id; + private String name; + private String lastName; + private Integer age; + + public Person(Integer id, String name, String lastName, Integer age) { + this.id = id; + this.name = name; + this.lastName = lastName; + this.age = age; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/jdbcautocommit/Person.java b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/jdbcautocommit/Person.java new file mode 100644 index 0000000000..81c60a0db1 --- /dev/null +++ b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/jdbcautocommit/Person.java @@ -0,0 +1,51 @@ +package com.baeldung.jdbcautocommit; + +public class Person { + + private Integer id; + private String name; + private String lastName; + private Integer age; + + public Person() { + } + + public Person(Integer id, String name, String lastName, Integer age) { + this.id = id; + this.name = name; + this.lastName = lastName; + this.age = age; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/insertnull/InsertNullUnitTest.java b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/insertnull/InsertNullUnitTest.java new file mode 100644 index 0000000000..508fa98e8f --- /dev/null +++ b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/insertnull/InsertNullUnitTest.java @@ -0,0 +1,50 @@ +package com.baeldung.insertnull; + +import org.junit.jupiter.api.Test; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +public class InsertNullUnitTest { + + private final String SQL = "INSERT INTO Person VALUES(?,?,?,?)"; + + @Test + public void givenNewPerson_whenSetNullIsUsed_thenNewRecordIsCreated() throws SQLException { + Person person = new Person(1, "John", "Doe", null); + + try (PreparedStatement preparedStatement = DBConfig.getConnection().prepareStatement(SQL)) { + preparedStatement.setInt(1, person.getId()); + preparedStatement.setString(2, person.getName()); + preparedStatement.setString(3, person.getLastName()); + if (person.getAge() == null) { + preparedStatement.setNull(4, Types.INTEGER); + } + else { + preparedStatement.setInt(4, person.getAge()); + } + int noOfRows = preparedStatement.executeUpdate(); + + assertThat(noOfRows, equalTo(1)); + } + } + + @Test + public void givenNewPerson_whenSetObjectIsUsed_thenNewRecordIsCreated() throws SQLException { + Person person = new Person(2, "John", "Doe", null); + + try (PreparedStatement preparedStatement = DBConfig.getConnection().prepareStatement(SQL)) { + preparedStatement.setInt(1, person.getId()); + preparedStatement.setString(2, person.getName()); + preparedStatement.setString(3, person.getLastName()); + preparedStatement.setObject(4, person.getAge(), Types.INTEGER); + int noOfRows = preparedStatement.executeUpdate(); + + assertThat(noOfRows, equalTo(1)); + } + } +} diff --git a/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcautocommit/AutoCommitUnitTest.java b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcautocommit/AutoCommitUnitTest.java new file mode 100644 index 0000000000..a3e3d8f34c --- /dev/null +++ b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcautocommit/AutoCommitUnitTest.java @@ -0,0 +1,259 @@ +package com.baeldung.jdbcautocommit; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class AutoCommitUnitTest { + + private static final String INSERT_PERSON_SQL = "INSERT INTO Person VALUES (?,?,?,?)"; + private static final String SELECT_ALL_PEOPLE_SQL = "SELECT * FROM Person"; + private static final String UPDATE_PERSON_AGE_BY_ID_SQL = "UPDATE Person SET age = ? WHERE id = ?"; + private static final String DELETE_ALL_PEOPLE_SQL = "DELETE FROM Person"; + private static final String UPDATE_PERSON_AGE_BY_NAME_SQL + = "UPDATE Person SET age = ? WHERE id = (SELECT id FROM Person WHERE name = ?)"; + private static final String CREATE_PERSON_TABLE_SQL + = "CREATE TABLE Person (id INTEGER not null, name VARCHAR(50), lastName VARCHAR(50), age INTEGER, PRIMARY KEY (id))"; + + private static Connection connection1; + private static Connection connection2; + + @BeforeAll + static void setup() throws SQLException { + connection1 = DriverManager.getConnection("jdbc:h2:mem:autocommit", "sa", ""); + createPersonTable(connection1); + + connection2 = DriverManager.getConnection("jdbc:h2:mem:autocommit", "sa", ""); + } + + @Test + void givenAutoCommitTrue_whenInsertAndUpdateRecord_thenDataPersistedAfterEachWithoutCommit() throws SQLException { + + connection1.setAutoCommit(true); + + Person person = new Person(1, "John", "Doe", 45); + insertPerson(connection1, person); + + // no explicit commit needed here when auto-commit true + + // viewed from different connection, connection2 : assert person has been persisted into + // the database + List people = selectAllPeople(connection2); + assertThat("person record inserted OK into empty table", people.size(), is(equalTo(1))); + Person personInserted = people.iterator().next(); + assertThat("id correct", personInserted.getId(), is(equalTo(1))); + + // update age for person in database + updatePersonAgeById(connection1, 1, 65); + + // no explicit commit needed here + + // viewed from connection2 : assert person's age has been updated to database + people = selectAllPeople(connection2); + Person personUpdated = people.iterator().next(); + assertThat("updated age correct", personUpdated.getAge(), is(equalTo(65))); + } + + @Test + void givenAutoCommitFalse_whenInsertCommitAndUpdateCommit_thenDataViewableAfterEachCommit() throws SQLException { + + connection1.setAutoCommit(false); + + Person person = new Person(1, "John", "Doe", 45); + insertPerson(connection1, person); + + // viewed from different connection, connection2 : assert that person has not yet been + // persisted to database before issuing commit + List people = selectAllPeople(connection2); + assertThat("No people have been inserted into database yet", people.size(), is(equalTo(0))); + + connection1.commit(); + + // viewed from connection2 : assert that person has been persisted to database after + // issuing commit + people = selectAllPeople(connection2); + assertThat("Person has been inserted into empty table after commit", people.size(), is(equalTo(1))); + Person personInserted = people.iterator().next(); + assertThat("id correct", personInserted.getId(), is(equalTo(1))); + + updatePersonAgeById(connection1, 1, 65); + + // assert that person's age has not been updated before issuing commit + people = selectAllPeople(connection2); + Person personUpdated = people.iterator().next(); + assertThat("person's age still 45, not updated", personUpdated.getAge(), is(equalTo(45))); + + connection1.commit(); + + // viewed from connection2 : assert that person's age has been updated after issuing commit + people = selectAllPeople(connection2); + personUpdated = people.iterator().next(); + assertThat("person's age updated to 65", personUpdated.getAge(), is(equalTo(65))); + } + + @Test + void givenAutoCommitFalse_whenInsertAndUpdateWithCommitOnlyAtEnd_thenDataOnlyViewableAfterCommit() throws SQLException { + + connection1.setAutoCommit(false); + + Person person = new Person(1, "John", "Doe", 45); + insertPerson(connection1, person); + + // viewed from different connection, connection2 : assert that person has not yet been + // persisted to database before issuing commit + List people = selectAllPeople(connection2); + assertThat("No people have been inserted into database yet", people.size(), is(equalTo(0))); + + updatePersonAgeById(connection1, 1, 65); + + // viewed from connection2 : assert that person has still not yet been + // persisted to database before issuing commit + people = selectAllPeople(connection2); + assertThat("No people have been inserted into database yet", people.size(), is(equalTo(0))); + + connection1.commit(); + + // viewed from connection2 : assert that person record has been persisted to + // database and person's age has been updated after issuing commit + people = selectAllPeople(connection2); + Person personUpdated = people.iterator().next(); + assertThat("person's age updated to 65", personUpdated.getAge(), is(equalTo(65))); + } + + @Test + void givenAutoCommitTrue_whenUpdateWithNestedSelect_thenUpdatePersistedWithoutCommit() throws SQLException { + + connection1.setAutoCommit(true); + + Person person = new Person(1, "John", "Doe", 45); + insertPerson(connection1, person); + + updatePersonAgeByName(connection1, "John", 77); + + // viewed from connection2 : assert person's age has been updated correctly to database + // without issuing commit + List people = selectAllPeople(connection2); + Person personUpdated = people.iterator().next(); + assertThat("updated age correct", personUpdated.getAge(), is(equalTo(77))); + } + + @Test + void givenAutoCommitFalse_whenModeChangedToTrueAfterSQLUpdate_thenUpdatePersistedWithoutCommit() throws SQLException { + + connection1.setAutoCommit(false); + + Person person = new Person(1, "John", "Doe", 45); + insertPerson(connection1, person); + updatePersonAgeByName(connection1, "John", 77); + + connection1.setAutoCommit(true); + + // viewed from connection2 : assert record inserted and person's age has been updated + // correctly to database after auto-commit changed false -> true ... without explicit commit needed + List people = selectAllPeople(connection2); + Person personUpdated = people.iterator().next(); + assertThat("updated age correct", personUpdated.getAge(), is(equalTo(77))); + } + + @AfterEach + void deleteAllRecords() throws SQLException { + + if (connection1.getAutoCommit() == false) { + connection1.setAutoCommit(true); + } + + deleteAllPeople(connection1); + } + + @AfterAll + static void closeConnections() throws SQLException { + + connection1.close(); + connection2.close(); + } + + private static void createPersonTable(Connection connection) throws SQLException { + try(Statement statement = connection.createStatement()) { + statement.executeUpdate(CREATE_PERSON_TABLE_SQL); + } + } + + private static int insertPerson(Connection connection, Person person) throws SQLException { + + try (PreparedStatement preparedStatement = connection.prepareStatement(INSERT_PERSON_SQL)) { + + preparedStatement.setInt(1, person.getId()); + preparedStatement.setString(2, person.getName()); + preparedStatement.setString(3, person.getLastName()); + preparedStatement.setInt(4, person.getAge()); + + // execute statement and return the number of rows inserted + return preparedStatement.executeUpdate(); + } + } + + private static void deleteAllPeople(Connection connection) throws SQLException { + try (Statement statement = connection.createStatement()) { + statement.execute(DELETE_ALL_PEOPLE_SQL); + } + } + + private static List selectAllPeople(Connection connection) throws SQLException { + + List people = null; + + try (Statement statement = connection.createStatement()) { + + people = new ArrayList<>(); + ResultSet resultSet = statement.executeQuery(SELECT_ALL_PEOPLE_SQL); + + while (resultSet.next()) { + Person person = new Person(); + + person.setId(resultSet.getInt("id")); + person.setName(resultSet.getString("name")); + person.setLastName(resultSet.getString("lastName")); + person.setAge(resultSet.getInt("age")); + + people.add(person); + } + } + + return people; + } + + private static void updatePersonAgeById(Connection connection, int id, int newAge) throws SQLException { + + try (PreparedStatement preparedStatement = connection.prepareStatement(UPDATE_PERSON_AGE_BY_ID_SQL)) { + + preparedStatement.setInt(1, newAge); + preparedStatement.setInt(2, id); + + preparedStatement.executeUpdate(); + } + } + + private static void updatePersonAgeByName(Connection connection, String name, int newAge) throws SQLException { + try (PreparedStatement preparedStatement = connection.prepareStatement(UPDATE_PERSON_AGE_BY_NAME_SQL)) { + + preparedStatement.setInt(1, newAge); + preparedStatement.setString(2, name); + + preparedStatement.executeUpdate(); + } + } +} diff --git a/persistence-modules/core-java-persistence/pom.xml b/persistence-modules/core-java-persistence/pom.xml index f1d779cf2f..96f8cef310 100644 --- a/persistence-modules/core-java-persistence/pom.xml +++ b/persistence-modules/core-java-persistence/pom.xml @@ -62,7 +62,6 @@ 1.4.200 - 42.2.5.jre7 3.10.0 2.4.0 3.2.0 diff --git a/persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3poDataSource.java b/persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3p0DataSource.java similarity index 91% rename from persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3poDataSource.java rename to persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3p0DataSource.java index 78642459d5..431427d332 100644 --- a/persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3poDataSource.java +++ b/persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3p0DataSource.java @@ -5,7 +5,7 @@ import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; -public class C3poDataSource { +public class C3p0DataSource { private static final ComboPooledDataSource cpds = new ComboPooledDataSource(); @@ -24,5 +24,5 @@ public class C3poDataSource { return cpds.getConnection(); } - private C3poDataSource(){} + private C3p0DataSource(){} } \ No newline at end of file diff --git a/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3p0DataSourceUnitTest.java b/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3p0DataSourceUnitTest.java new file mode 100644 index 0000000000..1095214e24 --- /dev/null +++ b/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3p0DataSourceUnitTest.java @@ -0,0 +1,13 @@ +package com.baeldung.connectionpool; + +import java.sql.SQLException; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class C3p0DataSourceUnitTest { + + @Test + public void givenC3p0DataSourceClass_whenCallGetConnection_thenCorrect() throws SQLException { + assertTrue(C3p0DataSource.getConnection().isValid(1)); + } +} \ No newline at end of file diff --git a/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java b/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java deleted file mode 100644 index acad9fe5e4..0000000000 --- a/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.baeldung.connectionpool; - -import java.sql.SQLException; -import static org.junit.Assert.assertTrue; -import org.junit.Test; - -public class C3poDataSourceUnitTest { - - @Test - public void givenC3poDataSourceClass_whenCalledgetConnection_thenCorrect() throws SQLException { - assertTrue(C3poDataSource.getConnection().isValid(1)); - } -} \ No newline at end of file diff --git a/persistence-modules/hibernate-jpa/README.md b/persistence-modules/hibernate-jpa/README.md index 64ec9dcae3..bb079b1705 100644 --- a/persistence-modules/hibernate-jpa/README.md +++ b/persistence-modules/hibernate-jpa/README.md @@ -13,3 +13,5 @@ This module contains articles specific to use of Hibernate as a JPA implementati - [Enabling Transaction Locks in Spring Data JPA](https://www.baeldung.com/java-jpa-transaction-locks) - [JPA/Hibernate Persistence Context](https://www.baeldung.com/jpa-hibernate-persistence-context) - [Quick Guide to EntityManager#getReference()](https://www.baeldung.com/jpa-entity-manager-get-reference) +- [JPA Entities and the Serializable Interface](https://www.baeldung.com/jpa-entities-serializable) +- [EntityNotFoundException in Hibernate](https://www.baeldung.com/hibernate-entitynotfoundexception) diff --git a/persistence-modules/hibernate-jpa/pom.xml b/persistence-modules/hibernate-jpa/pom.xml index 6d300c620b..85bfdac07f 100644 --- a/persistence-modules/hibernate-jpa/pom.xml +++ b/persistence-modules/hibernate-jpa/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 hibernate-jpa 0.0.1-SNAPSHOT @@ -91,7 +91,6 @@ 5.3.7.Final 8.0.13 - 42.2.11 2.2.3 3.8.0 2.1.7.RELEASE diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Category.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Category.java new file mode 100644 index 0000000000..25d31d50c7 --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Category.java @@ -0,0 +1,42 @@ +package com.baeldung.hibernate.entitynotfoundexception; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Category implements Serializable { + + @Id + @Column(unique = true, nullable = false) + private long id; + private String name; + @OneToMany + @JoinColumn(name = "category_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) + private List items = new ArrayList<>(); + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } +} diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Item.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Item.java new file mode 100644 index 0000000000..3abed00eb8 --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Item.java @@ -0,0 +1,43 @@ +package com.baeldung.hibernate.entitynotfoundexception; + +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +import javax.persistence.*; +import java.io.Serializable; + +@Entity +public class Item implements Serializable { + + @Id + @Column(unique = true, nullable = false) + private long id; + private String name; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "category_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) + private Category category; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } +} diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/User.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/User.java new file mode 100644 index 0000000000..d89047195c --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/User.java @@ -0,0 +1,28 @@ +package com.baeldung.hibernate.entitynotfoundexception; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class User { + + @Id + private long id; + private String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Account.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Account.java new file mode 100644 index 0000000000..b051809ee5 --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Account.java @@ -0,0 +1,41 @@ +package com.baeldung.hibernate.serializable; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class Account { + + @Id + private long id; + private String type; + @ManyToOne + @JoinColumn(referencedColumnName = "email") + private User user; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Email.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Email.java new file mode 100644 index 0000000000..11e7c6f159 --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Email.java @@ -0,0 +1,38 @@ +package com.baeldung.hibernate.serializable; + +import javax.persistence.Entity; +import javax.persistence.Id; +import java.io.Serializable; + +@Entity +public class Email implements Serializable { + + @Id + private long id; + private String name; + private String domain; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } +} diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/User.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/User.java new file mode 100644 index 0000000000..e7820fe52f --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/User.java @@ -0,0 +1,36 @@ +package com.baeldung.hibernate.serializable; + +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +@Entity +public class User { + + @EmbeddedId private UserId userId; + private Email email; + + + public User() { + } + + public User(UserId userId, Email email) { + this.userId = userId; + this.email = email; + } + + public UserId getUserId() { + return userId; + } + + public void setUserId(UserId userId) { + this.userId = userId; + } + + public Email getEmail() { + return email; + } + + public void setEmail(Email email) { + this.email = email; + } +} diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/UserId.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/UserId.java new file mode 100644 index 0000000000..7d3d382f67 --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/UserId.java @@ -0,0 +1,27 @@ +package com.baeldung.hibernate.serializable; + +import javax.persistence.Embeddable; +import java.io.Serializable; + +@Embeddable +public class UserId implements Serializable { + + private String name; + private String lastName; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/persistence-modules/hibernate-jpa/src/main/resources/META-INF/persistence.xml b/persistence-modules/hibernate-jpa/src/main/resources/META-INF/persistence.xml index c2d5bf59ab..e895ac6ba9 100644 --- a/persistence-modules/hibernate-jpa/src/main/resources/META-INF/persistence.xml +++ b/persistence-modules/hibernate-jpa/src/main/resources/META-INF/persistence.xml @@ -100,4 +100,41 @@ + + + EntityManager serializable persistence unit + com.baeldung.hibernate.serializable.Email + com.baeldung.hibernate.serializable.Account + com.baeldung.hibernate.serializable.User + com.baeldung.hibernate.serializable.UserId + true + + + + + + + + + + + + + + EntityManager EntityNotFoundException persistence unit + com.baeldung.hibernate.entitynotfoundexception.Category + com.baeldung.hibernate.entitynotfoundexception.Item + com.baeldung.hibernate.entitynotfoundexception.User + true + + + + + + + + + + + \ No newline at end of file diff --git a/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/entitynotfoundexception/EntityNotFoundExceptionIntegrationTest.java b/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/entitynotfoundexception/EntityNotFoundExceptionIntegrationTest.java new file mode 100644 index 0000000000..bcb4e3eb95 --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/entitynotfoundexception/EntityNotFoundExceptionIntegrationTest.java @@ -0,0 +1,45 @@ +package com.baeldung.hibernate.entitynotfoundexception; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.EntityNotFoundException; +import javax.persistence.Persistence; +import java.io.IOException; + +public class EntityNotFoundExceptionIntegrationTest { + + private static EntityManager entityManager; + + @Before + public void setUp() throws IOException { + EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("com.baeldung.hibernate.entitynotfoundexception.h2_persistence_unit"); + entityManager = entityManagerFactory.createEntityManager(); + entityManager.getTransaction().begin(); + + } + + + @Test(expected = EntityNotFoundException.class) + public void givenNonExistingUserId_whenGetReferenceIsUsed_thenExceptionIsThrown() { + User user = entityManager.getReference(User.class, 1L); + user.getName(); + } + + @Test(expected = EntityNotFoundException.class) + public void givenItem_whenManyToOneEntityIsMissing_thenExceptionIsThrown() { + entityManager.createNativeQuery("Insert into Item (category_id, name, id) values (1, 'test', 1)").executeUpdate(); + entityManager.flush(); + Item item = entityManager.find(Item.class, 1L); + item.getCategory().getName(); + } + + @After + public void tearDown() { + entityManager.close(); + } + +} diff --git a/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/serializable/JPASerializableIntegrationTest.java b/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/serializable/JPASerializableIntegrationTest.java new file mode 100644 index 0000000000..696bc23ab0 --- /dev/null +++ b/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/serializable/JPASerializableIntegrationTest.java @@ -0,0 +1,77 @@ +package com.baeldung.hibernate.serializable; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import java.io.IOException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class JPASerializableIntegrationTest { + + private static EntityManager entityManager; + + @Before + public void setUp() throws IOException { + EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("com.baeldung.hibernate.serializable.h2_persistence_unit"); + entityManager = entityManagerFactory.createEntityManager(); + entityManager.getTransaction().begin(); + } + + @Test + public void givenUser_whenPersisted_thenUserWillBeFoundById() { + UserId userId = new UserId(); + userId.setName("John"); + userId.setLastName("Doe"); + Email email = new Email(); + email.setId(1); + email.setName("johndoe"); + email.setDomain("gmail.com"); + User user = new User(userId, email); + + entityManager.persist(user); + + User userDb = entityManager.find(User.class, userId); + assertEquals("johndoe", userDb.getEmail().getName()); + } + + @Test + public void givenAssociation_whenPersisted_thenMultipleAccountsWillBeFoundByEmail() { + UserId userId = new UserId(); + userId.setName("John"); + userId.setLastName("Doe"); + Email email = new Email(); + email.setId(1); + email.setName("johndoe"); + email.setDomain("gmail.com"); + User user = new User(userId, email); + Account account = new Account(); + account.setType("test"); + account.setId(10); + account.setUser(user); + Account account2 = new Account(); + account2.setType("main"); + account2.setId(11); + account2.setUser(user); + + entityManager.persist(user); + entityManager.persist(account); + entityManager.persist(account2); + + List userAccounts = entityManager.createQuery("select a from Account a join fetch a.user where a.user.email = :email") + .setParameter("email", email) + .getResultList(); + assertEquals(2, userAccounts.size()); + } + + @After + public void tearDown() { + entityManager.close(); + } + +} diff --git a/persistence-modules/hibernate-mapping/pom.xml b/persistence-modules/hibernate-mapping/pom.xml index 0248f0cb04..805402951e 100644 --- a/persistence-modules/hibernate-mapping/pom.xml +++ b/persistence-modules/hibernate-mapping/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 hibernate-mapping hibernate-mapping @@ -75,7 +75,6 @@ - 42.2.20 5.4.12.Final 2.10.4 3.8.0 diff --git a/persistence-modules/hibernate-ogm/pom.xml b/persistence-modules/hibernate-ogm/pom.xml index 58098ebb65..9274186cd1 100644 --- a/persistence-modules/hibernate-ogm/pom.xml +++ b/persistence-modules/hibernate-ogm/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 hibernate-ogm 0.0.1-SNAPSHOT diff --git a/persistence-modules/hibernate-queries/pom.xml b/persistence-modules/hibernate-queries/pom.xml index 4a8c578aba..83b2ea4d00 100644 --- a/persistence-modules/hibernate-queries/pom.xml +++ b/persistence-modules/hibernate-queries/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 hibernate-queries 0.0.1-SNAPSHOT diff --git a/persistence-modules/hibernate5/pom.xml b/persistence-modules/hibernate5/pom.xml index 9b4ffff739..d46d2c16d4 100644 --- a/persistence-modules/hibernate5/pom.xml +++ b/persistence-modules/hibernate5/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 hibernate5 0.0.1-SNAPSHOT diff --git a/persistence-modules/java-cassandra/README.md b/persistence-modules/java-cassandra/README.md index f3710c9d69..81ad5aca59 100644 --- a/persistence-modules/java-cassandra/README.md +++ b/persistence-modules/java-cassandra/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: - [A Guide to Cassandra with Java](http://www.baeldung.com/cassandra-with-java) - [Intro to DataStax Java Driver for Apache Cassandra](https://www.baeldung.com/cassandra-datastax-java-driver) +- [CQL Data Types](https://www.baeldung.com/cassandra-data-types) diff --git a/persistence-modules/java-cassandra/src/main/resources/data_types.cql b/persistence-modules/java-cassandra/src/main/resources/data_types.cql new file mode 100644 index 0000000000..67e6fb4634 --- /dev/null +++ b/persistence-modules/java-cassandra/src/main/resources/data_types.cql @@ -0,0 +1,75 @@ +CREATE + KEYSPACE baeldung + WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1}; +USE baeldung; + +CREATE TABLE numeric_types +( + type1 int PRIMARY KEY, + type2 bigint, + type3 smallint, + type4 tinyint, + type5 varint, + type6 float, + type7 double, + type8 decimal +); + +CREATE TABLE text_types +( + primaryKey int PRIMARY KEY, + type2 text, + type3 varchar, + type4 ascii +); + +CREATE TABLE date_types +( + primaryKey int PRIMARY KEY, + type1 timestamp, + type2 time, + type3 date, + type4 timeuuid, + type5 duration +); + +CREATE TABLE other_types +( + primaryKey int PRIMARY KEY, + type1 boolean, + type2 uuid, + type3 blob, + type4 inet +); + +CREATE TABLE counter_type +( + primaryKey uuid PRIMARY KEY, + type1 counter +); + +CREATE TABLE collection_types +( + primaryKey int PRIMARY KEY, + email set +); + +ALTER TABLE collection_types + ADD scores list; + +ALTER TABLE collection_types + ADD address map; + +CREATE TABLE tuple_type +( + primaryKey int PRIMARY KEY, + type1 tuple +); + +CREATE TYPE user_defined_type ( type1 timestamp, type2 text, type3 text, type4 text); + +CREATE TABLE user_type +( + primaryKey int PRIMARY KEY, + our_type user_defined_type +); diff --git a/persistence-modules/java-cockroachdb/pom.xml b/persistence-modules/java-cockroachdb/pom.xml index 588ad29cb1..61fb193975 100644 --- a/persistence-modules/java-cockroachdb/pom.xml +++ b/persistence-modules/java-cockroachdb/pom.xml @@ -21,8 +21,4 @@ - - 42.1.4 - - \ No newline at end of file diff --git a/persistence-modules/java-jpa-2/pom.xml b/persistence-modules/java-jpa-2/pom.xml index daa775f39d..26895f3a87 100644 --- a/persistence-modules/java-jpa-2/pom.xml +++ b/persistence-modules/java-jpa-2/pom.xml @@ -44,7 +44,7 @@ org.postgresql postgresql - ${postgres.version} + ${postgresql.version} runtime @@ -138,7 +138,6 @@ 5.4.14.Final 2.7.4 - 42.2.5 2.2 3.11.1 3.5.1 diff --git a/persistence-modules/java-jpa-3/pom.xml b/persistence-modules/java-jpa-3/pom.xml index d81bfca5bd..cecabc10cc 100644 --- a/persistence-modules/java-jpa-3/pom.xml +++ b/persistence-modules/java-jpa-3/pom.xml @@ -59,7 +59,7 @@ org.postgresql postgresql - ${postgres.version} + ${postgresql.version} runtime @@ -92,7 +92,6 @@ 5.4.14.Final 2.7.4 - 42.2.5 8.0.21 2.2 3.11.1 diff --git a/persistence-modules/java-jpa/pom.xml b/persistence-modules/java-jpa/pom.xml index bb87c10edb..9a55633773 100644 --- a/persistence-modules/java-jpa/pom.xml +++ b/persistence-modules/java-jpa/pom.xml @@ -44,7 +44,7 @@ org.postgresql postgresql - ${postgres.version} + ${postgresql.version} runtime @@ -104,7 +104,6 @@ 5.4.0.Final 2.7.4 - 42.2.5 2.2 3.3.3 3.0.0 diff --git a/persistence-modules/jooq/pom.xml b/persistence-modules/jooq/pom.xml index f4751f412a..c66be9db77 100644 --- a/persistence-modules/jooq/pom.xml +++ b/persistence-modules/jooq/pom.xml @@ -45,7 +45,6 @@ 3.13.4 - 42.2.16 1.4.200 diff --git a/persistence-modules/pom.xml b/persistence-modules/pom.xml index 5e0cd59d18..a8f1d5c103 100644 --- a/persistence-modules/pom.xml +++ b/persistence-modules/pom.xml @@ -57,7 +57,9 @@ spring-boot-persistence spring-boot-persistence-h2 spring-boot-persistence-mongodb + spring-data-arangodb spring-data-cassandra + spring-data-cassandra-test spring-data-cassandra-reactive spring-data-cosmosdb spring-data-couchbase-2 @@ -77,6 +79,7 @@ spring-data-jdbc spring-data-keyvalue spring-data-mongodb + spring-data-mongodb-reactive spring-data-neo4j spring-data-redis spring-data-solr @@ -86,7 +89,7 @@ spring-jpa-2 spring-jdbc spring-jooq - + spring-mybatis spring-persistence-simple @@ -94,6 +97,8 @@ 5.2.17.Final + 42.2.20 + 2.22.2 5.6.2 diff --git a/persistence-modules/redis/pom.xml b/persistence-modules/redis/pom.xml index c407482788..8079d56cbd 100644 --- a/persistence-modules/redis/pom.xml +++ b/persistence-modules/redis/pom.xml @@ -59,7 +59,6 @@ 3.13.1 3.3.0 4.1.50.Final - \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties index 0466eaac79..134cda6628 100644 --- a/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties +++ b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties @@ -2,10 +2,11 @@ spring.datasource.url=jdbc:h2:mem:mydb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= +spring.jpa.defer-datasource-initialization=true spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.validator.apply_to_ddl=false #spring.jpa.properties.hibernate.check_nullability=true spring.h2.console.enabled=true -spring.h2.console.path=/h2-console \ No newline at end of file +spring.h2.console.path=/h2-console diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java index 21cf56172e..354aa6fe56 100644 --- a/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java +++ b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java @@ -5,8 +5,8 @@ import com.mongodb.DBObject; import com.mongodb.client.MongoClients; import de.flapdoodle.embed.mongo.MongodExecutable; import de.flapdoodle.embed.mongo.MongodStarter; -import de.flapdoodle.embed.mongo.config.IMongodConfig; -import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; +import de.flapdoodle.embed.mongo.config.ImmutableMongodConfig; +import de.flapdoodle.embed.mongo.config.MongodConfig; import de.flapdoodle.embed.mongo.config.Net; import de.flapdoodle.embed.mongo.distribution.Version; import de.flapdoodle.embed.process.runtime.Network; @@ -36,7 +36,9 @@ class ManualEmbeddedMongoDbIntegrationTest { String ip = "localhost"; int randomPort = SocketUtils.findAvailableTcpPort(); - IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION) + ImmutableMongodConfig mongodConfig = MongodConfig + .builder() + .version(Version.Main.PRODUCTION) .net(new Net(ip, randomPort, Network.localhostIsIPv6())) .build(); diff --git a/persistence-modules/spring-data-arangodb/README.md b/persistence-modules/spring-data-arangodb/README.md new file mode 100644 index 0000000000..632d9a256e --- /dev/null +++ b/persistence-modules/spring-data-arangodb/README.md @@ -0,0 +1,6 @@ +========= + +## Spring Data ArangoDB + + +### Relevant Articles: diff --git a/persistence-modules/spring-data-arangodb/pom.xml b/persistence-modules/spring-data-arangodb/pom.xml new file mode 100644 index 0000000000..562f06ae40 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + spring-data-arangodb + spring-data-arangodb + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter + + + + com.arangodb + arangodb-spring-data + 3.5.0 + + + + \ No newline at end of file diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/Dockerfile b/persistence-modules/spring-data-arangodb/src/live-test/resources/Dockerfile new file mode 100644 index 0000000000..8edb2bbbf6 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/Dockerfile @@ -0,0 +1,7 @@ +FROM arangodb:3.8.0 + +COPY init-session.js /docker-entrypoint-initdb.d/ + +EXPOSE 8529 + +ENV ARANGO_ROOT_PASSWORD=password diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/init-session.js b/persistence-modules/spring-data-arangodb/src/live-test/resources/init-session.js new file mode 100644 index 0000000000..2e968884cc --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/init-session.js @@ -0,0 +1 @@ +rs.initiate(); diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-setup.sh b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-setup.sh new file mode 100644 index 0000000000..b1a4cfb9d0 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-setup.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +docker image build -t spring-data-arangodb:live-test . + +docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=password --name spring-data-arangodb-live-test spring-data-arangodb:live-test diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-teardown.sh b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-teardown.sh new file mode 100644 index 0000000000..2199cf7403 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-teardown.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +docker stop spring-data-arangodb-live-test +docker rm spring-data-arangodb-live-test diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test.sh b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test.sh new file mode 100644 index 0000000000..307a68a3bd --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +mvn clean compile test -P live-all -f ../../../pom.xml diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/ArangoDbSpringDataApplication.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/ArangoDbSpringDataApplication.java new file mode 100644 index 0000000000..355001df06 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/ArangoDbSpringDataApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.arangodb; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ArangoDbSpringDataApplication { + + public static void main(String[] args) { + SpringApplication.run(ArangoDbSpringDataApplication.class, args); + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/configuration/ArangoDbConfiguration.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/configuration/ArangoDbConfiguration.java new file mode 100644 index 0000000000..3aae10ce60 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/configuration/ArangoDbConfiguration.java @@ -0,0 +1,24 @@ +package com.baeldung.arangodb.configuration; + +import com.arangodb.ArangoDB; +import com.arangodb.springframework.annotation.EnableArangoRepositories; +import com.arangodb.springframework.config.ArangoConfiguration; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableArangoRepositories(basePackages = {"com.baeldung"}) +public class ArangoDbConfiguration implements ArangoConfiguration { + + @Override + public ArangoDB.Builder arango() { + return new ArangoDB.Builder() + .host("127.0.0.1", 8529) + .user("root") + .password("password"); + } + + @Override + public String database() { + return "baeldung-database"; + } +} diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Article.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Article.java new file mode 100644 index 0000000000..ea7ee1d2a6 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Article.java @@ -0,0 +1,86 @@ +package com.baeldung.arangodb.model; + +import com.arangodb.springframework.annotation.ArangoId; +import com.arangodb.springframework.annotation.Document; +import com.arangodb.springframework.annotation.Relations; +import org.springframework.data.annotation.Id; + +import java.time.ZonedDateTime; +import java.util.Collection; + +@Document("articles") +public class Article { + + @Id + private String id; + + @ArangoId + private String arangoId; + + private String name; + private String author; + private ZonedDateTime publishDate; + private String htmlContent; + + @Relations(edges = ArticleLink.class, lazy = true) + private Collection authors; + + public Article() { + super(); + } + + public Article(String name, String author, ZonedDateTime publishDate, String htmlContent) { + this.name = name; + this.author = author; + this.publishDate = publishDate; + this.htmlContent = htmlContent; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getArangoId() { + return arangoId; + } + + public void setArangoId(String arangoId) { + this.arangoId = arangoId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public ZonedDateTime getPublishDate() { + return publishDate; + } + + public void setPublishDate(ZonedDateTime publishDate) { + this.publishDate = publishDate; + } + + public String getHtmlContent() { + return htmlContent; + } + + public void setHtmlContent(String htmlContent) { + this.htmlContent = htmlContent; + } +} diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/ArticleLink.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/ArticleLink.java new file mode 100644 index 0000000000..18a35815e1 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/ArticleLink.java @@ -0,0 +1,39 @@ +package com.baeldung.arangodb.model; + +import com.arangodb.springframework.annotation.Edge; +import com.arangodb.springframework.annotation.From; +import com.arangodb.springframework.annotation.To; + +@Edge +public class ArticleLink { + + @From + private Article article; + + @To + private Author author; + + public ArticleLink() { + } + + public ArticleLink(Article article, Author author) { + this.article = article; + this.author = author; + } + + public Article getArticle() { + return article; + } + + public void setArticle(Article article) { + this.article = article; + } + + public Author getAuthor() { + return author; + } + + public void setAuthor(Author author) { + this.author = author; + } +} diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Author.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Author.java new file mode 100644 index 0000000000..6e9fed3312 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Author.java @@ -0,0 +1,49 @@ +package com.baeldung.arangodb.model; + +import com.arangodb.springframework.annotation.ArangoId; +import com.arangodb.springframework.annotation.Document; +import org.springframework.data.annotation.Id; + +@Document("articles") +public class Author { + + @Id + private String id; + + @ArangoId + private String arangoId; + + private String name; + + public Author() { + super(); + } + + public Author(String name) { + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getArangoId() { + return arangoId; + } + + public void setArangoId(String arangoId) { + this.arangoId = arangoId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/repository/ArticleRepository.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/repository/ArticleRepository.java new file mode 100644 index 0000000000..fdc434cae4 --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/repository/ArticleRepository.java @@ -0,0 +1,17 @@ +package com.baeldung.arangodb.repository; + +import com.arangodb.springframework.annotation.Query; +import com.arangodb.springframework.repository.ArangoRepository; +import com.baeldung.arangodb.model.Article; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface ArticleRepository extends ArangoRepository { + + Iterable
findByAuthor(String author); + + @Query("FOR a IN articles FILTER a.author == @author SORT a.publishDate ASC RETURN a") + Iterable
getByAuthor(@Param("author") String author); + +} diff --git a/persistence-modules/spring-data-arangodb/src/main/resources/arangodb.properties b/persistence-modules/spring-data-arangodb/src/main/resources/arangodb.properties new file mode 100644 index 0000000000..7f8ded478c --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/main/resources/arangodb.properties @@ -0,0 +1,3 @@ +arangodb.hosts=127.0.0.1:8529 +arangodb.user=root +arangodb.password=password \ No newline at end of file diff --git a/persistence-modules/spring-data-arangodb/src/test/java/com/baeldung/arangodb/ArticleRepositoryIntegrationTest.java b/persistence-modules/spring-data-arangodb/src/test/java/com/baeldung/arangodb/ArticleRepositoryIntegrationTest.java new file mode 100644 index 0000000000..1d4258688a --- /dev/null +++ b/persistence-modules/spring-data-arangodb/src/test/java/com/baeldung/arangodb/ArticleRepositoryIntegrationTest.java @@ -0,0 +1,113 @@ +package com.baeldung.arangodb; + +import com.baeldung.arangodb.model.Article; +import com.baeldung.arangodb.repository.ArticleRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest +public class ArticleRepositoryIntegrationTest { + + @Autowired + ArticleRepository articleRepository; + + @Test + public void givenNewArticle_whenSaveInArangoDb_thenDataIsCorrect() { + Article newArticle = new Article( + "ArangoDb with Spring Data", + "Baeldung Writer", + ZonedDateTime.now(), + "Some HTML content" + ); + + Article savedArticle = articleRepository.save(newArticle); + + assertNotNull(savedArticle.getId()); + assertNotNull(savedArticle.getArangoId()); + + assertEquals(savedArticle.getName(), newArticle.getName()); + assertEquals(savedArticle.getAuthor(), newArticle.getAuthor()); + assertEquals(savedArticle.getPublishDate(), newArticle.getPublishDate()); + assertEquals(savedArticle.getHtmlContent(), newArticle.getHtmlContent()); + } + + @Test + public void givenArticleId_whenReadFromArangoDb_thenDataIsCorrect() { + Article newArticle = new Article( + "ArangoDb with Spring Data", + "Baeldung Writer", + ZonedDateTime.now(), + "Some HTML content" + ); + + Article savedArticle = articleRepository.save(newArticle); + + String articleId = savedArticle.getId(); + + Optional
article = articleRepository.findById(articleId); + assertTrue(article.isPresent()); + + Article foundArticle = article.get(); + + assertEquals(foundArticle.getId(), articleId); + assertEquals(foundArticle.getArangoId(), savedArticle.getArangoId()); + assertEquals(foundArticle.getName(), savedArticle.getName()); + assertEquals(foundArticle.getAuthor(), savedArticle.getAuthor()); + assertEquals(foundArticle.getPublishDate(), savedArticle.getPublishDate()); + assertEquals(foundArticle.getHtmlContent(), savedArticle.getHtmlContent()); + } + + @Test + public void givenArticleId_whenDeleteFromArangoDb_thenDataIsGone() { + Article newArticle = new Article( + "ArangoDb with Spring Data", + "Baeldung Writer", + ZonedDateTime.now(), + "Some HTML content" + ); + + Article savedArticle = articleRepository.save(newArticle); + + String articleId = savedArticle.getId(); + + articleRepository.deleteById(articleId); + + Optional
article = articleRepository.findById(articleId); + assertFalse(article.isPresent()); + } + + @Test + public void givenAuthorName_whenGetByAuthor_thenListOfArticles() { + Article newArticle = new Article( + "ArangoDb with Spring Data", + "Baeldung Writer", + ZonedDateTime.now(), + "Some HTML content" + ); + articleRepository.save(newArticle); + + Iterable
articlesByAuthor = articleRepository.findByAuthor(newArticle.getAuthor()); + List
articlesByAuthorList = new ArrayList<>(); + articlesByAuthor.forEach(articlesByAuthorList::add); + + assertEquals(1, articlesByAuthorList.size()); + + Article foundArticle = articlesByAuthorList.get(0); + assertEquals(foundArticle.getName(), newArticle.getName()); + assertEquals(foundArticle.getAuthor(), newArticle.getAuthor()); + assertEquals(foundArticle.getPublishDate(), newArticle.getPublishDate()); + assertEquals(foundArticle.getHtmlContent(), newArticle.getHtmlContent()); + } + +} diff --git a/persistence-modules/spring-data-cassandra-reactive/pom.xml b/persistence-modules/spring-data-cassandra-reactive/pom.xml index 630e13583b..5dd5ab4b69 100644 --- a/persistence-modules/spring-data-cassandra-reactive/pom.xml +++ b/persistence-modules/spring-data-cassandra-reactive/pom.xml @@ -54,7 +54,6 @@ 2.2.6.RELEASE 3.11.2.0 - \ No newline at end of file diff --git a/persistence-modules/spring-data-cassandra-test/README.md b/persistence-modules/spring-data-cassandra-test/README.md new file mode 100644 index 0000000000..cfad972cb3 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/README.md @@ -0,0 +1,16 @@ +## Spring @DataCassandraTest + +### Build the Project +``` +mvn clean install +``` + +### Prerequisite To Run Test +- Docker Engine must be running on the system +- Docker Compose must be installed + +### Run Tests Directly +``` +mvn test +``` + diff --git a/persistence-modules/spring-data-cassandra-test/pom.xml b/persistence-modules/spring-data-cassandra-test/pom.xml new file mode 100644 index 0000000000..f2cbc834de --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + spring-data-cassandra-test + spring-data-cassandra-test + jar + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + 2.5.3 + 1.18.18 + 4.13.0 + 4.3.1.0 + 1.15.3 + 1.5.0 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-data-cassandra + ${spring-boot-starter-data-cassandra.version} + + + + com.datastax.oss + java-driver-core + ${java-driver-core.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + com.datastax.oss + native-protocol + ${native-protocol.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.testcontainers + testcontainers + ${testcontainers.version} + test + + + + org.testcontainers + cassandra + ${testcontainers.version} + test + + + + \ No newline at end of file diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/CassandraDataTestApplication.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/CassandraDataTestApplication.java new file mode 100644 index 0000000000..f0e6ba3c88 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/CassandraDataTestApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.spring.data.cassandra.test; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CassandraDataTestApplication { + public static void main(String[] args) { + SpringApplication.run(CassandraDataTestApplication.class, args); + } +} diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/api/InventoryController.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/api/InventoryController.java new file mode 100644 index 0000000000..6c4dfe055c --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/api/InventoryController.java @@ -0,0 +1,43 @@ +package com.baeldung.spring.data.cassandra.test.api; + +import com.baeldung.spring.data.cassandra.test.domain.Vehicle; +import com.baeldung.spring.data.cassandra.test.service.InventoryService; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/v1/api/inventory") +public class InventoryController { + private InventoryService inventoryService; + + public InventoryController(InventoryService inventoryService) { + this.inventoryService = inventoryService; + } + + @GetMapping("/vehicles") + public List getVehicles() { + return this.inventoryService.getVehicles(); + } + + @PostMapping("/vehicles") + public void addVehicles(@RequestBody List vehicles) { + this.inventoryService.addVehicles(vehicles); + } + + @PutMapping("/vehicles") + public void updateVehicles(@RequestBody List vehicles) { + this.inventoryService.updateVehicles(vehicles); + } + + @PutMapping("/vehicles/{vin}") + public void updateVehicles(@PathVariable(name = "vin") String vin, + @RequestBody Vehicle vehicles) { + this.inventoryService.updateVehicle(vin, vehicles); + } + + @DeleteMapping("/vehicles/{vin}") + public void removeVehicle(@PathVariable(name = "vin") String vin) { + this.inventoryService.deleteVehicle(vin); + } +} diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/config/CassandraConfig.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/config/CassandraConfig.java new file mode 100644 index 0000000000..d362d67a68 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/config/CassandraConfig.java @@ -0,0 +1,24 @@ +package com.baeldung.spring.data.cassandra.test.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.cassandra.config.AbstractCassandraConfiguration; + +@Configuration +public class CassandraConfig extends AbstractCassandraConfiguration { + @Override + protected String getKeyspaceName() { + return "inventory"; + } + + @Override + public String getContactPoints() { + return "localhost"; + } + + @Override + protected String getLocalDataCenter() { + return "datacenter1"; + } + + +} diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/domain/Vehicle.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/domain/Vehicle.java new file mode 100644 index 0000000000..aa16440c83 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/domain/Vehicle.java @@ -0,0 +1,17 @@ +package com.baeldung.spring.data.cassandra.test.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.cassandra.core.mapping.Table; + +@Data +@Table("vehicles") +@AllArgsConstructor +public class Vehicle { + @Id + private String vin; + private Integer year; + private String make; + private String model; +} diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/repository/InventoryRepository.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/repository/InventoryRepository.java new file mode 100644 index 0000000000..c6fe4a91fe --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/repository/InventoryRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.spring.data.cassandra.test.repository; + +import com.baeldung.spring.data.cassandra.test.domain.Vehicle; +import com.datastax.oss.driver.api.core.DefaultConsistencyLevel; +import org.springframework.data.cassandra.repository.Consistency; +import org.springframework.data.cassandra.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface InventoryRepository extends CrudRepository { + + @Query("select * from vehicles") + @Consistency(DefaultConsistencyLevel.LOCAL_QUORUM) + List findAllVehicles(); + + @Consistency(DefaultConsistencyLevel.LOCAL_QUORUM) + Optional findByVin(@Param("vin") String vin); + + @Consistency(DefaultConsistencyLevel.LOCAL_QUORUM) + void deleteByVin(String vin); +} diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/service/InventoryService.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/service/InventoryService.java new file mode 100644 index 0000000000..75a198738a --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/service/InventoryService.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.data.cassandra.test.service; + +import com.baeldung.spring.data.cassandra.test.domain.Vehicle; +import com.baeldung.spring.data.cassandra.test.repository.InventoryRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class InventoryService { + private final InventoryRepository inventoryRepository; + + public InventoryService(InventoryRepository inventoryRepository) { + this.inventoryRepository = inventoryRepository; + } + + public List getVehicles() { + return this.inventoryRepository.findAllVehicles(); + } + + public Vehicle getVehicle(String vin) { + return this.inventoryRepository.findByVin(vin).orElse(null); + } + + public void addVehicles(List vehicles) { + this.inventoryRepository.saveAll(vehicles); + } + + public void updateVehicles(List vehicles) { + this.inventoryRepository.saveAll(vehicles); + } + + public void updateVehicle(String vin, Vehicle vehicle) { + Vehicle existingVehicle = this.inventoryRepository.findByVin(vin) + .orElseThrow(() -> new RuntimeException("Vehicle not found")); + + existingVehicle.setMake(vehicle.getMake()); + existingVehicle.setYear(vehicle.getYear()); + existingVehicle.setModel(vehicle.getModel()); + + this.inventoryRepository.save(existingVehicle); + } + + public void deleteVehicle(String vin) { + this.inventoryRepository.deleteByVin(vin); + } +} diff --git a/persistence-modules/spring-data-cassandra-test/src/main/resources/application.yml b/persistence-modules/spring-data-cassandra-test/src/main/resources/application.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/persistence-modules/spring-data-cassandra-test/src/main/resources/logback.xml b/persistence-modules/spring-data-cassandra-test/src/main/resources/logback.xml new file mode 100644 index 0000000000..612e9cac40 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/persistence-modules/spring-data-cassandra-test/src/test/java/com/baeldung/spring/data/cassandra/test/service/InventoryServiceLiveTest.java b/persistence-modules/spring-data-cassandra-test/src/test/java/com/baeldung/spring/data/cassandra/test/service/InventoryServiceLiveTest.java new file mode 100644 index 0000000000..80958d01d2 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/test/java/com/baeldung/spring/data/cassandra/test/service/InventoryServiceLiveTest.java @@ -0,0 +1,71 @@ +package com.baeldung.spring.data.cassandra.test.service; + +import com.baeldung.spring.data.cassandra.test.config.CassandraConfig; +import com.baeldung.spring.data.cassandra.test.domain.Vehicle; +import com.baeldung.spring.data.cassandra.test.repository.InventoryRepository; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; +import org.testcontainers.containers.DockerComposeContainer; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@DataCassandraTest +@Import(CassandraConfig.class) +public class InventoryServiceLiveTest { + @Autowired + private InventoryRepository repository; + + private InventoryService inventoryService; + + @ClassRule + public static DockerComposeContainer environment = + new DockerComposeContainer(new File("src/test/resources/compose-test.yml")); + + @Before + public void setUp() { + inventoryService = new InventoryService(this.repository); + } + + @Test + public void givenVehiclesInDBInitially_whenRetrieved_thenReturnAllVehiclesFromDB() { + List vehicles = inventoryService.getVehicles(); + assertThat(vehicles).isNotNull(); + assertThat(vehicles).isNotEmpty(); + } + + @Test + public void whenAddMoreVehiclesToDB_thenRetrievalReturnsAllVehicles() { + String vin1 = "ABC123"; + String vin2 = "XYZ123"; + List vehicles = Arrays.asList( + new Vehicle(vin1, 2020, "Toyota", "Camry"), + new Vehicle(vin2, 2019, "Honda", "Prius") + ); + inventoryService.addVehicles(vehicles); + + vehicles = inventoryService.getVehicles(); + assertThat(vehicles).isNotNull(); + assertThat(vehicles).isNotEmpty(); + assertThat(vehicles.size()).isEqualTo(5); + + Vehicle vehicle = inventoryService.getVehicle(vin1); + assertThat(vehicle).isNotNull(); + assertThat(vehicle.getVin()).isEqualTo(vin1); + + vehicle = inventoryService.getVehicle(vin2); + assertThat(vehicle).isNotNull(); + assertThat(vehicle.getVin()).isEqualTo(vin2); + } +} diff --git a/persistence-modules/spring-data-cassandra-test/src/test/resources/bootstrap-test.cql b/persistence-modules/spring-data-cassandra-test/src/test/resources/bootstrap-test.cql new file mode 100644 index 0000000000..6f3821ed83 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/test/resources/bootstrap-test.cql @@ -0,0 +1,20 @@ +CREATE KEYSPACE inventory +WITH replication = { + 'class' : 'NetworkTopologyStrategy', + 'datacenter1' : 3 +}; + +use inventory; + +CREATE TABLE vehicles ( + vin text PRIMARY KEY, + year int, + make varchar, + model varchar +); + +consistency LOCAL_QUORUM; + +insert into vehicles (vin, year, make, model) values ('387KSJHFK23874GH', 2020, 'Ford', 'F-150'); +insert into vehicles (vin, year, make, model) values ('534HNDHFK23873EF', 2020, 'Honda', 'Accord'); +insert into vehicles (vin, year, make, model) values ('953TOYJEK23853DB', 2020, 'Toyota', 'Camry'); \ No newline at end of file diff --git a/persistence-modules/spring-data-cassandra-test/src/test/resources/compose-test.yml b/persistence-modules/spring-data-cassandra-test/src/test/resources/compose-test.yml new file mode 100644 index 0000000000..50f0f448f6 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-test/src/test/resources/compose-test.yml @@ -0,0 +1,79 @@ +version: '2.1' +services: + cassandra1: + image: cassandra:3.11.10 + hostname: cassandra1 + networks: + - cassandranet + ports: + - "9042:9042" + environment: + CASSANDRA_SEEDS: "cassandra1" + CASSANDRA_DC: datacenter1 + CASSANDRA_RACK: rack1 + CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch + healthcheck: + test: [ "CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces" ] + interval: 15s + timeout: 10s + retries: 10 + + cassandra2: + image: cassandra:3.11.10 + hostname: cassandra2 + networks: + - cassandranet + depends_on: + cassandra1: + condition: service_healthy + ports: + - "9043:9042" + environment: + CASSANDRA_SEEDS: "cassandra1" + CASSANDRA_DC: datacenter1 + CASSANDRA_RACK: rack1 + CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch + healthcheck: + test: [ "CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces" ] + interval: 15s + timeout: 10s + retries: 10 + + cassandra3: + image: cassandra:3.11.10 + hostname: cassandra3 + networks: + - cassandranet + depends_on: + cassandra2: + condition: service_healthy + ports: + - "9044:9042" + environment: + CASSANDRA_SEEDS: "cassandra1" + CASSANDRA_DC: datacenter1 + CASSANDRA_RACK: rack1 + CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch + healthcheck: + test: [ "CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces" ] + interval: 15s + timeout: 10s + retries: 10 + + cassandra-load-keyspace: + image: cassandra:3.11.10 + networks: + - cassandranet + depends_on: + cassandra1: + condition: service_healthy + cassandra2: + condition: service_healthy + cassandra3: + condition: service_healthy + volumes: + - ./bootstrap-test.cql:/schema.cql + command: /bin/bash -c "echo loading cassandra keyspace && cqlsh cassandra1 -f /schema.cql" + +networks: + cassandranet: \ No newline at end of file diff --git a/persistence-modules/spring-data-cassandra/pom.xml b/persistence-modules/spring-data-cassandra/pom.xml index e43e3821ae..d21541abf8 100644 --- a/persistence-modules/spring-data-cassandra/pom.xml +++ b/persistence-modules/spring-data-cassandra/pom.xml @@ -105,7 +105,6 @@ 2.1.9.2 2.1.9.2 2.0-0 - \ No newline at end of file diff --git a/persistence-modules/spring-data-dynamodb/pom.xml b/persistence-modules/spring-data-dynamodb/pom.xml index 28127179c2..148215b68a 100644 --- a/persistence-modules/spring-data-dynamodb/pom.xml +++ b/persistence-modules/spring-data-dynamodb/pom.xml @@ -183,6 +183,7 @@ 1.11.86 https://s3-us-west-2.amazonaws.com/dynamodb-local/release 3.1.1 + 2.4.7 \ No newline at end of file diff --git a/persistence-modules/spring-data-jdbc/pom.xml b/persistence-modules/spring-data-jdbc/pom.xml index 2672405dd6..2b4c6d21aa 100644 --- a/persistence-modules/spring-data-jdbc/pom.xml +++ b/persistence-modules/spring-data-jdbc/pom.xml @@ -2,14 +2,15 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + spring-data-jdbc + spring-data-jdbc + com.baeldung parent-boot-2 0.0.1-SNAPSHOT ../../parent-boot-2 - spring-data-jdbc - spring-data-jdbc diff --git a/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties index 18ef8d4e60..3829f676d3 100644 --- a/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties +++ b/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties @@ -1,5 +1,3 @@ -spring.main.allow-bean-definition-overriding=true - spring.jpa.properties.hibernate.jdbc.batch_size=4 spring.jpa.properties.hibernate.order_inserts=true spring.jpa.properties.hibernate.order_updates=true diff --git a/persistence-modules/spring-data-jpa-enterprise/pom.xml b/persistence-modules/spring-data-jpa-enterprise/pom.xml index 4367a11222..a76bcb8b0a 100644 --- a/persistence-modules/spring-data-jpa-enterprise/pom.xml +++ b/persistence-modules/spring-data-jpa-enterprise/pom.xml @@ -2,7 +2,6 @@ - 4.0.0 spring-data-jpa-enterprise spring-data-jpa-enterprise diff --git a/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties index 29326c6061..32d3e640f9 100644 --- a/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties +++ b/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties @@ -10,7 +10,6 @@ spring.datasource.url=jdbc:h2:mem:baeldung #spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true -spring.main.allow-bean-definition-overriding=true #hibernate.dialect=org.hibernate.dialect.H2Dialect spring.jpa.properties.hibernate.id.new_generator_mappings=false \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java b/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java new file mode 100644 index 0000000000..c976590966 --- /dev/null +++ b/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java @@ -0,0 +1,46 @@ +package com.baeldung.boot.daos; + +import com.baeldung.boot.Application; +import com.baeldung.boot.daos.user.UserRepository; +import com.baeldung.boot.domain.User; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@ActiveProfiles("tc-jdbc") +@SpringBootTest(classes = Application.class) +public class UserRepositoryTCJdbcLiveTest { + + final String USER_EMAIL = "email@example.com"; + final String USER_EMAIL2 = "email2@example.com"; + final String USER_EMAIL3 = "email3@example.com"; + final String USER_EMAIL4 = "email4@example.com"; + final Integer INACTIVE_STATUS = 0; + final Integer ACTIVE_STATUS = 1; + + @Autowired + private UserRepository userRepository; + + @Test + @Transactional + public void givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationNative_ThenModifyMatchingUsers() { + userRepository.save(new User("SAMPLE", LocalDate.now(), USER_EMAIL, ACTIVE_STATUS)); + userRepository.save(new User("SAMPLE1", LocalDate.now(), USER_EMAIL2, ACTIVE_STATUS)); + userRepository.save(new User("SAMPLE", LocalDate.now(), USER_EMAIL3, ACTIVE_STATUS)); + userRepository.save(new User("SAMPLE3", LocalDate.now(), USER_EMAIL4, ACTIVE_STATUS)); + userRepository.flush(); + + int updatedUsersSize = userRepository.updateUserSetStatusForNameNativePostgres(INACTIVE_STATUS, "SAMPLE"); + + assertThat(updatedUsersSize).isEqualTo(2); + } +} diff --git a/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml b/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml new file mode 100644 index 0000000000..ad5906fa6e --- /dev/null +++ b/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml @@ -0,0 +1,6 @@ +spring: + datasource: + url: jdbc:tc:postgresql:11.1:///integration-tests-db + jpa: + hibernate: + ddl-auto: create \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-query-2/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-query-2/src/main/resources/application.properties new file mode 100644 index 0000000000..f37cce0b29 --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-2/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.jpa.defer-datasource-initialization=true \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-query/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-query/src/main/resources/application.properties index 72fc330767..9bb870895d 100644 --- a/persistence-modules/spring-data-jpa-query/src/main/resources/application.properties +++ b/persistence-modules/spring-data-jpa-query/src/main/resources/application.properties @@ -1 +1,2 @@ -spring.jpa.show-sql=true \ No newline at end of file +spring.jpa.show-sql=true +spring.jpa.defer-datasource-initialization=true \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-2/pom.xml b/persistence-modules/spring-data-jpa-repo-2/pom.xml index b382e35e28..12e178dd49 100644 --- a/persistence-modules/spring-data-jpa-repo-2/pom.xml +++ b/persistence-modules/spring-data-jpa-repo-2/pom.xml @@ -42,4 +42,5 @@ 29.0-jre + \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-repo/src/main/resources/application.properties index 65d7b0bf29..ae1afe6e98 100644 --- a/persistence-modules/spring-data-jpa-repo/src/main/resources/application.properties +++ b/persistence-modules/spring-data-jpa-repo/src/main/resources/application.properties @@ -1,4 +1,5 @@ spring.jpa.show-sql=true +spring.jpa.defer-datasource-initialization=true #MySql #spring.datasource.url=jdbc:mysql://localhost:3306/baeldung #spring.datasource.username=baeldung diff --git a/persistence-modules/spring-data-keyvalue/pom.xml b/persistence-modules/spring-data-keyvalue/pom.xml index aa2696dd12..704be15b4a 100644 --- a/persistence-modules/spring-data-keyvalue/pom.xml +++ b/persistence-modules/spring-data-keyvalue/pom.xml @@ -26,4 +26,5 @@ spring-boot-starter-test + \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/README.md b/persistence-modules/spring-data-mongodb-reactive/README.md new file mode 100644 index 0000000000..0931161700 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/README.md @@ -0,0 +1,12 @@ +## Spring Data Reactive Project + +This module contains articles about reactive Spring 5 Data + +### The Course +The "REST With Spring" Classes: http://bit.ly/restwithspring + +### Relevant Articles +- [Spring Data Reactive Repositories with MongoDB](https://www.baeldung.com/spring-data-mongodb-reactive) +- [Spring Data MongoDB Tailable Cursors](https://www.baeldung.com/spring-data-mongodb-tailable-cursors) +- [A Quick Look at R2DBC with Spring Data](https://www.baeldung.com/spring-data-r2dbc) +- [Spring Data Reactive Repositories with Couchbase](https://www.baeldung.com/spring-data-reactive-couchbase) diff --git a/persistence-modules/spring-data-mongodb-reactive/pom.xml b/persistence-modules/spring-data-mongodb-reactive/pom.xml new file mode 100644 index 0000000000..9fb22b6033 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/pom.xml @@ -0,0 +1,157 @@ + + + 4.0.0 + spring-data-mongodb-reactive + spring-data-mongodb-reactive + jar + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + io.projectreactor + reactor-core + ${reactor-core.version} + + + org.springframework.boot + spring-boot-starter-data-couchbase-reactive + + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + + + io.projectreactor + reactor-test + test + + + io.reactivex.rxjava2 + rxjava + + + org.springframework + spring-test + + + org.springframework.boot + spring-boot-starter-test + test + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + test + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework + spring-tx + ${spring-tx.version} + + + org.springframework.data + spring-data-r2dbc + ${spring-data-r2dbc.version} + + + io.r2dbc + r2dbc-h2 + ${r2dbc-h2.version} + + + com.h2database + h2 + ${h2.version} + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + com.couchbase.mock + CouchbaseMock + ${couchbaseMock.version} + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + + + + + + 5.2.2.RELEASE + 1.0.0.RELEASE + 0.8.1.RELEASE + 4.5.2 + 1.4.200 + 1.5.23 + 3.3.1.RELEASE + + 2.2.6.RELEASE + + + \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/ReactiveCouchbaseApplication.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/ReactiveCouchbaseApplication.java new file mode 100644 index 0000000000..4e5bf9d5dc --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/ReactiveCouchbaseApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.couchbase; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; + +@SpringBootApplication(exclude = MongoAutoConfiguration.class) +public class ReactiveCouchbaseApplication { + + public static void main(String[] args) { + SpringApplication.run(ReactiveCouchbaseApplication.class, args); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/CouchbaseProperties.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/CouchbaseProperties.java new file mode 100644 index 0000000000..81f19eebd6 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/CouchbaseProperties.java @@ -0,0 +1,43 @@ +package com.baeldung.couchbase.configuration; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import java.util.Collections; +import java.util.List; + +@Configuration +@PropertySource("classpath:couchbase.properties") +public class CouchbaseProperties { + + private final List bootstrapHosts; + private final String bucketName; + private final String bucketPassword; + private final int port; + + public CouchbaseProperties(@Value("${spring.couchbase.bootstrap-hosts}") final List bootstrapHosts, @Value("${spring.couchbase.bucket.name}") final String bucketName, @Value("${spring.couchbase.bucket.password}") final String bucketPassword, + @Value("${spring.couchbase.port}") final int port) { + this.bootstrapHosts = Collections.unmodifiableList(bootstrapHosts); + this.bucketName = bucketName; + this.bucketPassword = bucketPassword; + this.port = port; + } + + public List getBootstrapHosts() { + return bootstrapHosts; + } + + public String getBucketName() { + return bucketName; + } + + public String getBucketPassword() { + return bucketPassword; + } + + public int getPort() { + return port; + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/N1QLReactiveCouchbaseConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/N1QLReactiveCouchbaseConfiguration.java new file mode 100644 index 0000000000..059bd36cae --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/N1QLReactiveCouchbaseConfiguration.java @@ -0,0 +1,15 @@ +package com.baeldung.couchbase.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories; + +@Configuration +@EnableReactiveCouchbaseRepositories("com.baeldung.couchbase.domain.repository.n1ql") +@Primary +public class N1QLReactiveCouchbaseConfiguration extends ReactiveCouchbaseConfiguration { + + public N1QLReactiveCouchbaseConfiguration(CouchbaseProperties couchbaseProperties) { + super(couchbaseProperties); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ReactiveCouchbaseConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ReactiveCouchbaseConfiguration.java new file mode 100644 index 0000000000..a51b19ee22 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ReactiveCouchbaseConfiguration.java @@ -0,0 +1,48 @@ +package com.baeldung.couchbase.configuration; + +import com.couchbase.client.java.env.CouchbaseEnvironment; +import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; + +import org.springframework.context.annotation.Bean; +import org.springframework.data.couchbase.config.AbstractReactiveCouchbaseConfiguration; +import org.springframework.data.couchbase.config.BeanNames; +import org.springframework.data.couchbase.repository.support.IndexManager; + +import java.util.List; + +public abstract class ReactiveCouchbaseConfiguration extends AbstractReactiveCouchbaseConfiguration { + + private CouchbaseProperties couchbaseProperties; + + public ReactiveCouchbaseConfiguration(final CouchbaseProperties couchbaseProperties) { + this.couchbaseProperties = couchbaseProperties; + } + + @Override + protected List getBootstrapHosts() { + return couchbaseProperties.getBootstrapHosts(); + } + + @Override + protected String getBucketName() { + return couchbaseProperties.getBucketName(); + } + + @Override + protected String getBucketPassword() { + return couchbaseProperties.getBucketPassword(); + } + + @Override + public CouchbaseEnvironment couchbaseEnvironment() { + return DefaultCouchbaseEnvironment + .builder() + .bootstrapHttpDirectPort(couchbaseProperties.getPort()) + .build(); + } + + @Bean(name = BeanNames.COUCHBASE_INDEX_MANAGER) + public IndexManager couchbaseIndexManager() { + return new IndexManager(true, true, false); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ViewReactiveCouchbaseConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ViewReactiveCouchbaseConfiguration.java new file mode 100644 index 0000000000..9b4d9b0319 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ViewReactiveCouchbaseConfiguration.java @@ -0,0 +1,13 @@ +package com.baeldung.couchbase.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories; + +@Configuration +@EnableReactiveCouchbaseRepositories("com.baeldung.couchbase.domain.repository.view") +public class ViewReactiveCouchbaseConfiguration extends ReactiveCouchbaseConfiguration { + + public ViewReactiveCouchbaseConfiguration(CouchbaseProperties couchbaseProperties) { + super(couchbaseProperties); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/Person.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/Person.java new file mode 100644 index 0000000000..285de34df8 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/Person.java @@ -0,0 +1,43 @@ +package com.baeldung.couchbase.domain; + +import org.springframework.data.annotation.Id; +import org.springframework.data.couchbase.core.mapping.Document; + +import java.util.Objects; +import java.util.UUID; + +@Document +public class Person { + + @Id private UUID id; + private String firstName; + + public Person(final UUID id, final String firstName) { + this.id = id; + this.firstName = firstName; + } + + private Person() { + } + + public UUID getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Person person = (Person) o; + return Objects.equals(id, person.id) && Objects.equals(firstName, person.firstName); + } + + @Override + public int hashCode() { + return Objects.hash(id, firstName); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepository.java new file mode 100644 index 0000000000..6f73a77ceb --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepository.java @@ -0,0 +1,16 @@ +package com.baeldung.couchbase.domain.repository.n1ql; + +import com.baeldung.couchbase.domain.Person; +import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; + +import java.util.UUID; + +@Repository +@N1qlPrimaryIndexed +public interface N1QLPersonRepository extends ReactiveCrudRepository { + + Flux findAllByFirstName(final String firstName); +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepository.java new file mode 100644 index 0000000000..57dd149425 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepository.java @@ -0,0 +1,13 @@ +package com.baeldung.couchbase.domain.repository.n1ql; + +import com.baeldung.couchbase.domain.Person; +import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed; +import org.springframework.data.repository.reactive.ReactiveSortingRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +@N1qlPrimaryIndexed +public interface N1QLSortingPersonRepository extends ReactiveSortingRepository { +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepository.java new file mode 100644 index 0000000000..06c47c2393 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepository.java @@ -0,0 +1,20 @@ +package com.baeldung.couchbase.domain.repository.view; + +import com.baeldung.couchbase.domain.Person; +import org.springframework.data.couchbase.core.query.View; +import org.springframework.data.couchbase.core.query.ViewIndexed; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; + +import java.util.UUID; + +@Repository +@ViewIndexed(designDoc = ViewPersonRepository.DESIGN_DOCUMENT) +public interface ViewPersonRepository extends ReactiveCrudRepository { + + String DESIGN_DOCUMENT = "person"; + + @View(designDocument = ViewPersonRepository.DESIGN_DOCUMENT) + Flux findByFirstName(String firstName); +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/R2dbcApplication.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/R2dbcApplication.java new file mode 100644 index 0000000000..557b6ff42a --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/R2dbcApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.r2dbc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = "com.baeldung.r2dbc") +public class R2dbcApplication { + + public static void main(String[] args) { + SpringApplication.run(R2dbcApplication.class, args); + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/configuration/R2DBCConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/configuration/R2DBCConfiguration.java new file mode 100644 index 0000000000..54f06d9c6c --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/configuration/R2DBCConfiguration.java @@ -0,0 +1,21 @@ +package com.baeldung.r2dbc.configuration; + +import io.r2dbc.h2.H2ConnectionConfiguration; +import io.r2dbc.h2.H2ConnectionFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; + +@Configuration +@EnableR2dbcRepositories(basePackages = "com.baeldung.r2dbc.repository") +public class R2DBCConfiguration extends AbstractR2dbcConfiguration { + @Bean + public H2ConnectionFactory connectionFactory() { + return new H2ConnectionFactory( + H2ConnectionConfiguration.builder() + .url("mem:testdb;DB_CLOSE_DELAY=-1;TRACE_LEVEL_FILE=4") + .username("sa") + .build()); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/model/Player.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/model/Player.java new file mode 100644 index 0000000000..1e28cb3d07 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/model/Player.java @@ -0,0 +1,18 @@ +package com.baeldung.r2dbc.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Table +public class Player { + @Id + Integer id; + String name; + Integer age; +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/repository/PlayerRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/repository/PlayerRepository.java new file mode 100644 index 0000000000..20f7642a7c --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/repository/PlayerRepository.java @@ -0,0 +1,17 @@ +package com.baeldung.r2dbc.repository; + +import org.springframework.data.r2dbc.repository.Query; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; + +import com.baeldung.r2dbc.model.Player; + +import reactor.core.publisher.Flux; + +public interface PlayerRepository extends ReactiveCrudRepository { + + @Query("select id, name, age from player where name = $1") + Flux findAllByName(String name); + + @Query("select * from player where age = $1") + Flux findByAge(int age); +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java new file mode 100644 index 0000000000..e96767145e --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java @@ -0,0 +1,25 @@ +package com.baeldung.reactive; + +import com.mongodb.reactivestreams.client.MongoClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; + +@SpringBootApplication +public class Spring5ReactiveApplication{ + + public static void main(String[] args) { + SpringApplication.run(Spring5ReactiveApplication.class, args); + } + + @Autowired + MongoClient mongoClient; + + @Bean + public ReactiveMongoTemplate reactiveMongoTemplate() { + return new ReactiveMongoTemplate(mongoClient, "test"); + } + +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/model/Account.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/model/Account.java new file mode 100644 index 0000000000..57abd80009 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/model/Account.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document +@Data +@ToString +@AllArgsConstructor +@NoArgsConstructor +public class Account { + + @Id + private String id; + private String owner; + private Double value; +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java new file mode 100644 index 0000000000..8798c13772 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java @@ -0,0 +1,15 @@ +package com.baeldung.reactive.repository; + +import com.baeldung.reactive.model.Account; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Repository +public interface AccountCrudRepository extends ReactiveCrudRepository { + + public Flux findAllByValue(Double value); + + public Mono findFirstByOwner(Mono owner); +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java new file mode 100644 index 0000000000..5c09e4a264 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.reactive.repository; + +import com.baeldung.reactive.model.Account; +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; + +public interface AccountMongoRepository extends ReactiveMongoRepository { +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java new file mode 100644 index 0000000000..6afe92a21b --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java @@ -0,0 +1,15 @@ +package com.baeldung.reactive.repository; + +import com.baeldung.reactive.model.Account; +import io.reactivex.Observable; +import io.reactivex.Single; +import org.springframework.data.repository.reactive.RxJava2CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AccountRxJavaRepository extends RxJava2CrudRepository{ + + public Observable findAllByValue(Double value); + + public Single findFirstByOwner(Single owner); +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java new file mode 100644 index 0000000000..9d32f34e3b --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java @@ -0,0 +1,33 @@ +package com.baeldung.reactive.template; + +import com.baeldung.reactive.model.Account; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.data.mongodb.core.ReactiveRemoveOperation; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Service +public class AccountTemplateOperations { + + @Autowired + ReactiveMongoTemplate template; + + public Mono findById(String id) { + return template.findById(id, Account.class); + } + + public Flux findAll() { + return template.findAll(Account.class); + } + + public Mono save(Mono account) { + return template.save(account); + } + + public ReactiveRemoveOperation.ReactiveRemove deleteAll() { + return template.remove(Account.class); + } + +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java new file mode 100644 index 0000000000..8b2511a8f3 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.tailablecursor; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LogsCounterApplication { + public static void main(String[] args) { + SpringApplication.run(LogsCounterApplication.class, args); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java new file mode 100644 index 0000000000..717a367751 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java @@ -0,0 +1,21 @@ +package com.baeldung.tailablecursor.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Data +@Document +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Log { + @Id + private String id; + private String service; + private LogLevel level; + private String message; +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java new file mode 100644 index 0000000000..6826fbffd3 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java @@ -0,0 +1,5 @@ +package com.baeldung.tailablecursor.domain; + +public enum LogLevel { + ERROR, WARN, INFO +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java new file mode 100644 index 0000000000..dce11c548c --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java @@ -0,0 +1,12 @@ +package com.baeldung.tailablecursor.repository; + +import com.baeldung.tailablecursor.domain.Log; +import com.baeldung.tailablecursor.domain.LogLevel; +import org.springframework.data.mongodb.repository.Tailable; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import reactor.core.publisher.Flux; + +public interface LogsRepository extends ReactiveCrudRepository { + @Tailable + Flux findByLevel(LogLevel level); +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java new file mode 100644 index 0000000000..c243e64f97 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java @@ -0,0 +1,62 @@ +package com.baeldung.tailablecursor.service; + +import com.baeldung.tailablecursor.domain.Log; +import com.baeldung.tailablecursor.domain.LogLevel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.messaging.DefaultMessageListenerContainer; +import org.springframework.data.mongodb.core.messaging.MessageListener; +import org.springframework.data.mongodb.core.messaging.MessageListenerContainer; +import org.springframework.data.mongodb.core.messaging.TailableCursorRequest; + +import javax.annotation.PreDestroy; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.springframework.data.mongodb.core.query.Criteria.where; +import static org.springframework.data.mongodb.core.query.Query.query; + +@Slf4j +public class ErrorLogsCounter implements LogsCounter { + + private static final String LEVEL_FIELD_NAME = "level"; + + private final String collectionName; + private final MessageListenerContainer container; + + private final AtomicInteger counter = new AtomicInteger(); + + public ErrorLogsCounter(MongoTemplate mongoTemplate, + String collectionName) { + this.collectionName = collectionName; + this.container = new DefaultMessageListenerContainer(mongoTemplate); + + container.start(); + TailableCursorRequest request = getTailableCursorRequest(); + container.register(request, Log.class); + } + + @SuppressWarnings("unchecked") + private TailableCursorRequest getTailableCursorRequest() { + MessageListener listener = message -> { + log.info("ERROR log received: {}", message.getBody()); + counter.incrementAndGet(); + }; + + return TailableCursorRequest.builder() + .collection(collectionName) + .filter(query(where(LEVEL_FIELD_NAME).is(LogLevel.ERROR))) + .publishTo(listener) + .build(); + } + + @Override + public int count() { + return counter.get(); + } + + @PreDestroy + public void close() { + container.stop(); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java new file mode 100644 index 0000000000..29301bffec --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java @@ -0,0 +1,36 @@ +package com.baeldung.tailablecursor.service; + +import com.baeldung.tailablecursor.domain.Log; +import com.baeldung.tailablecursor.domain.LogLevel; +import com.baeldung.tailablecursor.repository.LogsRepository; +import lombok.extern.slf4j.Slf4j; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; + +import javax.annotation.PreDestroy; +import java.util.concurrent.atomic.AtomicInteger; + +@Slf4j +public class InfoLogsCounter implements LogsCounter { + + private final AtomicInteger counter = new AtomicInteger(); + private final Disposable subscription; + + public InfoLogsCounter(LogsRepository repository) { + Flux stream = repository.findByLevel(LogLevel.INFO); + this.subscription = stream.subscribe(logEntity -> { + log.info("INFO log received: " + logEntity); + counter.incrementAndGet(); + }); + } + + @Override + public int count() { + return this.counter.get(); + } + + @PreDestroy + public void close() { + this.subscription.dispose(); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java new file mode 100644 index 0000000000..e14a3eadd7 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java @@ -0,0 +1,5 @@ +package com.baeldung.tailablecursor.service; + +public interface LogsCounter { + int count(); +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java new file mode 100644 index 0000000000..2dff8e8e40 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java @@ -0,0 +1,41 @@ +package com.baeldung.tailablecursor.service; + +import com.baeldung.tailablecursor.domain.Log; +import com.baeldung.tailablecursor.domain.LogLevel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.mongodb.core.ReactiveMongoOperations; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; + +import javax.annotation.PreDestroy; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.springframework.data.mongodb.core.query.Criteria.where; +import static org.springframework.data.mongodb.core.query.Query.query; + +@Slf4j +public class WarnLogsCounter implements LogsCounter { + + private static final String LEVEL_FIELD_NAME = "level"; + + private final AtomicInteger counter = new AtomicInteger(); + private final Disposable subscription; + + public WarnLogsCounter(ReactiveMongoOperations template) { + Flux stream = template.tail(query(where(LEVEL_FIELD_NAME).is(LogLevel.WARN)), Log.class); + subscription = stream.subscribe(logEntity -> { + log.warn("WARN log received: " + logEntity); + counter.incrementAndGet(); + }); + } + + @Override + public int count() { + return counter.get(); + } + + @PreDestroy + public void close() { + subscription.dispose(); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/resources/couchbase.properties b/persistence-modules/spring-data-mongodb-reactive/src/main/resources/couchbase.properties new file mode 100644 index 0000000000..53fad807fe --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/resources/couchbase.properties @@ -0,0 +1,4 @@ +spring.couchbase.bucket.name=default +spring.couchbase.bootstrap-hosts=localhost +spring.couchbase.port=8091 +spring.couchbase.bucket.password=123456 \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/resources/logback.xml b/persistence-modules/spring-data-mongodb-reactive/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/SpringContextTest.java similarity index 60% rename from spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java rename to persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/SpringContextTest.java index 9cba7f8fc1..bedb30fcaa 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/SpringContextTest.java @@ -5,12 +5,13 @@ import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; +import com.baeldung.reactive.Spring5ReactiveApplication; + @RunWith(SpringRunner.class) -@SpringBootTest(classes = {SpringBootRestApplication.class}) +@SpringBootTest(classes = Spring5ReactiveApplication.class) public class SpringContextTest { @Test - public void contextLoads() { + public void whenSpringContextIsBootstrapped_thenNoExceptions() { } - } diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/CouchbaseMockConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/CouchbaseMockConfiguration.java new file mode 100644 index 0000000000..2a09fce4b0 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/CouchbaseMockConfiguration.java @@ -0,0 +1,54 @@ +package com.baeldung.couchbase.domain.repository; + +import com.baeldung.couchbase.configuration.CouchbaseProperties; +import com.couchbase.mock.Bucket; +import com.couchbase.mock.BucketConfiguration; +import com.couchbase.mock.CouchbaseMock; +import org.springframework.boot.test.context.TestConfiguration; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Collections; + +@TestConfiguration +public class CouchbaseMockConfiguration { + + private CouchbaseMock couchbaseMock; + + public CouchbaseMockConfiguration(final CouchbaseProperties couchbaseProperties) { + final BucketConfiguration bucketConfiguration = new BucketConfiguration(); + bucketConfiguration.numNodes = 1; + bucketConfiguration.numReplicas = 1; + bucketConfiguration.numVBuckets = 1024; + bucketConfiguration.name = couchbaseProperties.getBucketName(); + bucketConfiguration.type = Bucket.BucketType.COUCHBASE; + bucketConfiguration.password = couchbaseProperties.getBucketPassword(); + + try { + couchbaseMock = new CouchbaseMock(couchbaseProperties.getPort(), Collections.singletonList(bucketConfiguration)); + } catch (final IOException ex) { + throw new UncheckedIOException(ex); + } + } + + @PostConstruct + public void postConstruct() { + try { + couchbaseMock.start(); + } catch (final IOException ex) { + throw new UncheckedIOException(ex); + } + try { + couchbaseMock.waitForStartup(); + } catch (final InterruptedException ex) { + throw new RuntimeException(ex); + } + } + + @PreDestroy + public void preDestroy() { + couchbaseMock.stop(); + } +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepositoryLiveTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepositoryLiveTest.java new file mode 100644 index 0000000000..c8dbbf429e --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepositoryLiveTest.java @@ -0,0 +1,55 @@ +package com.baeldung.couchbase.domain.repository.n1ql; + +import com.baeldung.couchbase.domain.Person; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import java.util.UUID; + +@RunWith(SpringRunner.class) +@SpringBootTest(properties = {"spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration"}) +public class N1QLPersonRepositoryLiveTest { + + @Autowired private N1QLPersonRepository personRepository; + + @Test + public void shouldFindAll_byLastName() { + //Given + final String firstName = "John"; + final Person matchingPerson = new Person(UUID.randomUUID(), firstName); + final Person nonMatchingPerson = new Person(UUID.randomUUID(), "NotJohn"); + wrap(() -> { + personRepository + .save(matchingPerson) + .subscribe(); + personRepository + .save(nonMatchingPerson) + .subscribe(); + //When + final Flux allByFirstName = personRepository.findAllByFirstName(firstName); + //Then + StepVerifier + .create(allByFirstName) + .expectNext(matchingPerson) + .verifyComplete(); + + }, matchingPerson, nonMatchingPerson); + } + + private void wrap(final Runnable runnable, final Person... people) { + try { + runnable.run(); + } finally { + for (final Person person : people) { + personRepository + .delete(person) + .subscribe(); + } + } + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepositoryLiveTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepositoryLiveTest.java new file mode 100644 index 0000000000..8c6ce137f1 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepositoryLiveTest.java @@ -0,0 +1,60 @@ +package com.baeldung.couchbase.domain.repository.n1ql; + +import com.baeldung.couchbase.domain.Person; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Sort; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import java.util.UUID; + +@RunWith(SpringRunner.class) +@SpringBootTest(properties = {"spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration"}) +public class N1QLSortingPersonRepositoryLiveTest { + + @Autowired private N1QLSortingPersonRepository personRepository; + + @Test + public void shouldFindAll_sortedByFirstName() { + //Given + final Person firstPerson = new Person(UUID.randomUUID(), "John"); + final Person secondPerson = new Person(UUID.randomUUID(), "Mikki"); + wrap(() -> { + personRepository + .save(firstPerson) + .subscribe(); + personRepository + .save(secondPerson) + .subscribe(); + //When + final Flux allByFirstName = personRepository.findAll(Sort.by(Sort.Direction.DESC, "firstName")); + //Then + StepVerifier + .create(allByFirstName) + .expectNextMatches(person -> person + .getFirstName() + .equals(secondPerson.getFirstName())) + .expectNextMatches(person -> person + .getFirstName() + .equals(firstPerson.getFirstName())) + .verifyComplete(); + }, firstPerson, secondPerson); + } + + //workaround for deleteAll() + private void wrap(final Runnable runnable, final Person... people) { + try { + runnable.run(); + } finally { + for (final Person person : people) { + personRepository + .delete(person) + .subscribe(); + } + } + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepositoryIntegrationTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepositoryIntegrationTest.java new file mode 100644 index 0000000000..15688e1b80 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepositoryIntegrationTest.java @@ -0,0 +1,81 @@ +package com.baeldung.couchbase.domain.repository.view; + +import com.baeldung.couchbase.configuration.CouchbaseProperties; +import com.baeldung.couchbase.configuration.ViewReactiveCouchbaseConfiguration; +import com.baeldung.couchbase.domain.Person; +import com.baeldung.couchbase.domain.repository.CouchbaseMockConfiguration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.util.UUID; + +@RunWith(SpringRunner.class) +@SpringBootTest(properties = { "spring.couchbase.port=10010", "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration" }, + classes = { CouchbaseMockConfiguration.class, ViewReactiveCouchbaseConfiguration.class, CouchbaseProperties.class }) +public class ViewPersonRepositoryIntegrationTest { + + @Autowired private ViewPersonRepository personRepository; + + @Test + public void shouldSavePerson_findById_thenDeleteIt() { + //Given + final UUID id = UUID.randomUUID(); + final Person person = new Person(id, "John"); + wrap(() -> { + personRepository + .save(person) + .subscribe(); + //When + final Mono byId = personRepository.findById(id); + //Then + StepVerifier + .create(byId) + .expectNextMatches(result -> result + .getId() + .equals(id)) + .expectComplete() + .verify(); + }, person); + } + + @Test + public void shouldFindAll_thenDeleteIt() { + //Given + final Person person = new Person(UUID.randomUUID(), "John"); + final Person secondPerson = new Person(UUID.randomUUID(), "Mikki"); + wrap(() -> { + personRepository + .save(person) + .subscribe(); + personRepository + .save(secondPerson) + .subscribe(); + //When + final Flux all = personRepository.findAll(); + //Then + StepVerifier + .create(all) + .expectNextCount(2) + .verifyComplete(); + }, person, secondPerson); + } + + private void wrap(final Runnable runnable, final Person... people) { + try { + runnable.run(); + } finally { + for (final Person person : people) { + personRepository + .delete(person) + .subscribe(); + } + } + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/r2dbc/R2dbcApplicationIntegrationTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/r2dbc/R2dbcApplicationIntegrationTest.java new file mode 100644 index 0000000000..1af570587e --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/r2dbc/R2dbcApplicationIntegrationTest.java @@ -0,0 +1,124 @@ +package com.baeldung.r2dbc; + + +import com.baeldung.r2dbc.model.Player; +import com.baeldung.r2dbc.repository.PlayerRepository; +import io.r2dbc.h2.H2ConnectionFactory; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.r2dbc.core.DatabaseClient; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.util.Arrays; +import java.util.List; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class R2dbcApplicationIntegrationTest { + + + @Autowired + PlayerRepository playerRepository; + + @Autowired + DatabaseClient client; + + @Autowired + H2ConnectionFactory factory; + + + @Before + public void setup() { + + Hooks.onOperatorDebug(); + + List statements = Arrays.asList(// + "DROP TABLE IF EXISTS player;", + "CREATE table player (id INT AUTO_INCREMENT NOT NULL, name VARCHAR2, age INT NOT NULL);"); + + statements.forEach(it -> client.execute(it) // + .fetch() // + .rowsUpdated() // + .as(StepVerifier::create) // + .expectNextCount(1) // + .verifyComplete()); + + } + + @Test + public void whenDeleteAll_then0IsExpected() { + + + playerRepository.deleteAll() + .as(StepVerifier::create) + .expectNextCount(0) + .verifyComplete(); + } + + @Test + public void whenInsert6_then6AreExpected() { + + insertPlayers(); + + playerRepository.findAll() + .as(StepVerifier::create) + .expectNextCount(6) + .verifyComplete(); + } + + @Test + public void whenSearchForCR7_then1IsExpected() { + + insertPlayers(); + + playerRepository.findAllByName("CR7") + .as(StepVerifier::create) + .expectNextCount(1) + .verifyComplete(); + } + + @Test + public void whenSearchFor32YearsOld_then2AreExpected() { + insertPlayers(); + + playerRepository.findByAge(32) + .as(StepVerifier::create) + .expectNextCount(2) + .verifyComplete(); + } + + @Test + public void whenBatchHas2Operations_then2AreExpected() { + Mono.from(factory.create()) + .flatMapMany(connection -> Flux.from(connection + .createBatch() + .add("select * from player") + .add("select * from player") + .execute())) + .as(StepVerifier::create) + .expectNextCount(2) + .verifyComplete(); + + } + + private void insertPlayers() { + List players = Arrays.asList( + new Player(null, "Kaka", 37), + new Player(null, "Messi", 32), + new Player(null, "Mbappé", 20), + new Player(null, "CR7", 34), + new Player(null, "Lewandowski", 30), + new Player(null, "Cavani", 32) + ); + + playerRepository.saveAll(players).subscribe(); + } +} + diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryManualTest.java new file mode 100644 index 0000000000..d4b1d0eeda --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryManualTest.java @@ -0,0 +1,70 @@ +package com.baeldung.reactive.repository; + + +import com.baeldung.reactive.Spring5ReactiveApplication; +import com.baeldung.reactive.model.Account; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class) +public class AccountCrudRepositoryManualTest { + + @Autowired + AccountCrudRepository repository; + + @Test + public void givenValue_whenFindAllByValue_thenFindAccount() { + repository.save(new Account(null, "Bill", 12.3)).block(); + Flux accountFlux = repository.findAllByValue(12.3); + + StepVerifier.create(accountFlux) + .assertNext(account -> { + assertEquals("Bill", account.getOwner()); + assertEquals(Double.valueOf(12.3) , account.getValue()); + assertNotNull(account.getId()); + }) + .expectComplete() + .verify(); + } + + @Test + public void givenOwner_whenFindFirstByOwner_thenFindAccount() { + repository.save(new Account(null, "Bill", 12.3)).block(); + Mono accountMono = repository.findFirstByOwner(Mono.just("Bill")); + + StepVerifier.create(accountMono) + .assertNext(account -> { + assertEquals("Bill", account.getOwner()); + assertEquals(Double.valueOf(12.3) , account.getValue()); + assertNotNull(account.getId()); + }) + .expectComplete() + .verify(); + + + + } + + @Test + public void givenAccount_whenSave_thenSaveAccount() { + Mono accountMono = repository.save(new Account(null, "Bill", 12.3)); + + StepVerifier + .create(accountMono) + .assertNext(account -> assertNotNull(account.getId())) + .expectComplete() + .verify(); + } + + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryManualTest.java new file mode 100644 index 0000000000..2ca075aa5e --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryManualTest.java @@ -0,0 +1,67 @@ +package com.baeldung.reactive.repository; + +import com.baeldung.reactive.Spring5ReactiveApplication; +import com.baeldung.reactive.model.Account; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.startsWith; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class) +public class AccountMongoRepositoryManualTest { + + @Autowired + AccountMongoRepository repository; + + @Test + public void givenExample_whenFindAllWithExample_thenFindAllMacthings() { + repository.save(new Account(null, "john", 12.3)).block(); + ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("owner", startsWith()); + Example example = Example.of(new Account(null, "jo", null), matcher); + Flux accountFlux = repository.findAll(example); + + StepVerifier + .create(accountFlux) + .assertNext(account -> assertEquals("john", account.getOwner())) + .expectComplete() + .verify(); + } + + @Test + public void givenAccount_whenSave_thenSave() { + Mono accountMono = repository.save(new Account(null, "john", 12.3)); + + StepVerifier + .create(accountMono) + .assertNext(account -> assertNotNull(account.getId())) + .expectComplete() + .verify(); + } + + @Test + public void givenId_whenFindById_thenFindAccount() { + Account inserted = repository.save(new Account(null, "john", 12.3)).block(); + Mono accountMono = repository.findById(inserted.getId()); + + StepVerifier + .create(accountMono) + .assertNext(account -> { + assertEquals("john", account.getOwner()); + assertEquals(Double.valueOf(12.3), account.getValue()); + assertNotNull(account.getId()); + }) + .expectComplete() + .verify(); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryManualTest.java new file mode 100644 index 0000000000..d91acd24e2 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryManualTest.java @@ -0,0 +1,58 @@ +package com.baeldung.reactive.repository; + +import com.baeldung.reactive.Spring5ReactiveApplication; +import com.baeldung.reactive.model.Account; +import io.reactivex.Observable; +import io.reactivex.Single; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class) +public class AccountRxJavaRepositoryManualTest { + + @Autowired + AccountRxJavaRepository repository; + + @Test + public void givenValue_whenFindAllByValue_thenFindAccounts() throws InterruptedException { + repository.save(new Account(null, "bruno", 12.3)).blockingGet(); + Observable accountObservable = repository.findAllByValue(12.3); + + accountObservable + .test() + .await() + .assertComplete() + .assertValueAt(0, account -> { + assertEquals("bruno", account.getOwner()); + assertEquals(Double.valueOf(12.3), account.getValue()); + return true; + }); + + } + + @Test + public void givenOwner_whenFindFirstByOwner_thenFindAccount() throws InterruptedException { + repository.save(new Account(null, "bruno", 12.3)).blockingGet(); + Single accountSingle = repository.findFirstByOwner(Single.just("bruno")); + + accountSingle + .test() + .await() + .assertComplete() + .assertValueAt(0, account -> { + assertEquals("bruno", account.getOwner()); + assertEquals(Double.valueOf(12.3), account.getValue()); + assertNotNull(account.getId()); + return true; + }); + + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsManualTest.java new file mode 100644 index 0000000000..5fa0e39317 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsManualTest.java @@ -0,0 +1,48 @@ +package com.baeldung.reactive.template; + +import com.baeldung.reactive.Spring5ReactiveApplication; +import com.baeldung.reactive.model.Account; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class) +public class AccountTemplateOperationsManualTest { + + @Autowired + AccountTemplateOperations accountTemplate; + + @Test + public void givenAccount_whenSave_thenSave() { + Account account = accountTemplate.save(Mono.just(new Account(null, "Raul", 12.3))).block(); + assertNotNull( account.getId() ); + } + + @Test + public void givenId_whenFindById_thenFindAccount() { + Mono accountMono = accountTemplate.save(Mono.just(new Account(null, "Raul", 12.3))); + Mono accountMonoResult = accountTemplate.findById(accountMono.block().getId()); + assertNotNull(accountMonoResult.block().getId()); + assertEquals(accountMonoResult.block().getOwner(), "Raul"); + } + + @Test + public void whenFindAll_thenFindAllAccounts() { + Account account1 = accountTemplate.save(Mono.just(new Account(null, "Raul", 12.3))).block(); + Account account2 = accountTemplate.save(Mono.just(new Account(null, "Raul Torres", 13.3))).block(); + Flux accountFlux = accountTemplate.findAll(); + List accounts = accountFlux.collectList().block(); + assertTrue(accounts.stream().anyMatch(x -> account1.getId().equals(x.getId()) )); + assertTrue(accounts.stream().anyMatch(x -> account2.getId().equals(x.getId()) )); + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java new file mode 100644 index 0000000000..5e20d3ec79 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java @@ -0,0 +1,112 @@ +package com.baeldung.tailablecursor.service; + +import com.baeldung.tailablecursor.domain.Log; +import com.baeldung.tailablecursor.domain.LogLevel; +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.CreateCollectionOptions; +import de.flapdoodle.embed.mongo.MongodExecutable; +import de.flapdoodle.embed.mongo.MongodProcess; +import de.flapdoodle.embed.mongo.MongodStarter; +import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; +import de.flapdoodle.embed.mongo.config.Net; +import de.flapdoodle.embed.mongo.distribution.Version; +import de.flapdoodle.embed.process.runtime.Network; +import org.bson.Document; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.util.SocketUtils; + +import java.io.IOException; +import java.util.stream.IntStream; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class ErrorLogsCounterManualTest { + + private static final String SERVER = "localhost"; + private static final int PORT = SocketUtils.findAvailableTcpPort(10000); + private static final String DB_NAME = "test"; + private static final String COLLECTION_NAME = Log.class.getName().toLowerCase(); + + private static final MongodStarter starter = MongodStarter.getDefaultInstance(); + private static final int MAX_DOCUMENTS_IN_COLLECTION = 3; + + private ErrorLogsCounter errorLogsCounter; + private MongodExecutable mongodExecutable; + private MongodProcess mongoDaemon; + private MongoDatabase db; + + @Before + public void setup() throws Exception { + MongoTemplate mongoTemplate = initMongoTemplate(); + + MongoCollection collection = createCappedCollection(); + + persistDocument(collection, -1, LogLevel.ERROR, "my-service", "Initial log"); + + errorLogsCounter = new ErrorLogsCounter(mongoTemplate, COLLECTION_NAME); + Thread.sleep(1000L); // wait for initialization + } + + private MongoTemplate initMongoTemplate() throws IOException { + mongodExecutable = starter.prepare(new MongodConfigBuilder() + .version(Version.Main.PRODUCTION) + .net(new Net(SERVER, PORT, Network.localhostIsIPv6())) + .build()); + mongoDaemon = mongodExecutable.start(); + + MongoClient mongoClient = new MongoClient(SERVER, PORT); + db = mongoClient.getDatabase(DB_NAME); + + return new MongoTemplate(mongoClient, DB_NAME); + } + + private MongoCollection createCappedCollection() { + db.createCollection(COLLECTION_NAME, new CreateCollectionOptions() + .capped(true) + .sizeInBytes(100000) + .maxDocuments(MAX_DOCUMENTS_IN_COLLECTION)); + return db.getCollection(COLLECTION_NAME); + } + + private void persistDocument(MongoCollection collection, + int i, LogLevel level, String service, String message) { + Document logMessage = new Document(); + logMessage.append("_id", i); + logMessage.append("level", level.toString()); + logMessage.append("service", service); + logMessage.append("message", message); + collection.insertOne(logMessage); + } + + @After + public void tearDown() { + errorLogsCounter.close(); + mongoDaemon.stop(); + mongodExecutable.stop(); + } + + @Test + public void whenErrorLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception { + MongoCollection collection = db.getCollection(COLLECTION_NAME); + + IntStream.range(1, 10) + .forEach(i -> persistDocument(collection, + i, + i > 5 ? LogLevel.ERROR : LogLevel.INFO, + "service" + i, + "Message from service " + i) + ); + + Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver + + assertThat(collection.countDocuments(), is((long) MAX_DOCUMENTS_IN_COLLECTION)); + assertThat(errorLogsCounter.count(), is(5)); + } + +} diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java new file mode 100644 index 0000000000..cd8bd68257 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java @@ -0,0 +1,75 @@ +package com.baeldung.tailablecursor.service; + +import com.baeldung.tailablecursor.LogsCounterApplication; +import com.baeldung.tailablecursor.domain.Log; +import com.baeldung.tailablecursor.domain.LogLevel; +import com.baeldung.tailablecursor.repository.LogsRepository; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.mongodb.core.CollectionOptions; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = LogsCounterApplication.class) +@Slf4j +public class InfoLogsCounterManualTest { + @Autowired + private LogsRepository repository; + + @Autowired + private ReactiveMongoTemplate template; + + @Before + public void setUp() { + createCappedCollectionUsingReactiveMongoTemplate(template); + + persistDocument(Log.builder() + .level(LogLevel.INFO) + .service("Service 2") + .message("Initial INFO message") + .build()); + } + + private void createCappedCollectionUsingReactiveMongoTemplate(ReactiveMongoTemplate reactiveMongoTemplate) { + reactiveMongoTemplate.dropCollection(Log.class).block(); + reactiveMongoTemplate.createCollection(Log.class, CollectionOptions.empty() + .maxDocuments(5) + .size(1024 * 1024L) + .capped()).block(); + } + + private void persistDocument(Log log) { + repository.save(log).block(); + } + + @Test + public void wheInfoLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception { + InfoLogsCounter infoLogsCounter = new InfoLogsCounter(repository); + + Thread.sleep(1000L); // wait for initialization + + Flux.range(0,10) + .map(i -> Log.builder() + .level(i > 5 ? LogLevel.WARN : LogLevel.INFO) + .service("some-service") + .message("some log message") + .build()) + .map(entity -> repository.save(entity).subscribe()) + .blockLast(); + + Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver + + assertThat(infoLogsCounter.count(), is(7)); + infoLogsCounter.close(); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java new file mode 100644 index 0000000000..79d94b6784 --- /dev/null +++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java @@ -0,0 +1,75 @@ +package com.baeldung.tailablecursor.service; + +import com.baeldung.tailablecursor.LogsCounterApplication; +import com.baeldung.tailablecursor.domain.Log; +import com.baeldung.tailablecursor.domain.LogLevel; +import com.baeldung.tailablecursor.repository.LogsRepository; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.mongodb.core.CollectionOptions; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Flux; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = LogsCounterApplication.class) +@Slf4j +public class WarnLogsCounterManualTest { + @Autowired + private LogsRepository repository; + + @Autowired + private ReactiveMongoTemplate template; + + @Before + public void setUp() { + createCappedCollectionUsingReactiveMongoTemplate(template); + + persistDocument(Log.builder() + .level(LogLevel.WARN) + .service("Service 1") + .message("Initial Warn message") + .build()); + } + + private void createCappedCollectionUsingReactiveMongoTemplate(ReactiveMongoTemplate reactiveMongoTemplate) { + reactiveMongoTemplate.dropCollection(Log.class).block(); + reactiveMongoTemplate.createCollection(Log.class, CollectionOptions.empty() + .maxDocuments(5) + .size(1024 * 1024L) + .capped()).block(); + } + + private void persistDocument(Log log) { + repository.save(log).block(); + } + + @Test + public void whenWarnLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception { + WarnLogsCounter warnLogsCounter = new WarnLogsCounter(template); + + Thread.sleep(1000L); // wait for initialization + + Flux.range(0,10) + .map(i -> Log.builder() + .level(i > 5 ? LogLevel.WARN : LogLevel.INFO) + .service("some-service") + .message("some log message") + .build()) + .map(entity -> repository.save(entity).subscribe()) + .blockLast(); + + Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver + + assertThat(warnLogsCounter.count(), is(5)); + warnLogsCounter.close(); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsManualTest.java similarity index 98% rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsIntegrationTest.java rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsManualTest.java index af790c99b7..2aabe099ea 100644 --- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsIntegrationTest.java +++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsManualTest.java @@ -26,7 +26,7 @@ import java.nio.ByteBuffer; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class) @DirtiesContext(classMode = ClassMode.BEFORE_CLASS) -public class RedisKeyCommandsIntegrationTest { +public class RedisKeyCommandsManualTest { private static redis.embedded.RedisServer redisServer; diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsManualTest.java similarity index 97% rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsIntegrationTest.java rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsManualTest.java index cd5994c854..7353b9630c 100644 --- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsIntegrationTest.java +++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsManualTest.java @@ -23,7 +23,7 @@ import java.io.IOException; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class) @DirtiesContext(classMode = ClassMode.BEFORE_CLASS) -public class RedisTemplateListOpsIntegrationTest { +public class RedisTemplateListOpsManualTest { private static final String LIST_NAME = "demo_list"; private static redis.embedded.RedisServer redisServer; diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsManualTest.java similarity index 98% rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsIntegrationTest.java rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsManualTest.java index 5d12f90f34..af8e41f9e8 100644 --- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsIntegrationTest.java +++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsManualTest.java @@ -27,7 +27,7 @@ import redis.embedded.RedisServerBuilder; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class) @DirtiesContext(classMode = ClassMode.BEFORE_CLASS) -public class RedisTemplateValueOpsIntegrationTest { +public class RedisTemplateValueOpsManualTest { private static redis.embedded.RedisServer redisServer; diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerManualTest.java similarity index 96% rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerIntegrationTest.java rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerManualTest.java index 6501f3f9f0..9166377959 100644 --- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerIntegrationTest.java +++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerManualTest.java @@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisApplication.class) @DirtiesContext(classMode = ClassMode.BEFORE_CLASS) -public class RedisMessageListenerIntegrationTest { +public class RedisMessageListenerManualTest { private static redis.embedded.RedisServer redisServer; diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryManualTest.java similarity index 98% rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryManualTest.java index 24191a42b9..a2b62167a1 100644 --- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java +++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryManualTest.java @@ -25,7 +25,7 @@ import redis.embedded.RedisServerBuilder; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = RedisConfig.class) @DirtiesContext(classMode = ClassMode.BEFORE_CLASS) -public class StudentRepositoryIntegrationTest { +public class StudentRepositoryManualTest { @Autowired private StudentRepository studentRepository; diff --git a/persistence-modules/spring-mybatis/pom.xml b/persistence-modules/spring-mybatis/pom.xml index 9247d59fe4..1a55e76900 100644 --- a/persistence-modules/spring-mybatis/pom.xml +++ b/persistence-modules/spring-mybatis/pom.xml @@ -84,7 +84,7 @@ - 5.1.8.RELEASE + 5.3.8 2.0.2 3.5.2 diff --git a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootIntegrationTest.java b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootUnitTest.java similarity index 82% rename from persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootIntegrationTest.java rename to persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootUnitTest.java index 531356ffa2..d9f757aa30 100644 --- a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootIntegrationTest.java +++ b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootUnitTest.java @@ -8,6 +8,6 @@ import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest @ContextConfiguration(classes = PersistenceAutoConfig.class) -public class ArticleMapperBootIntegrationTest extends ArticleMapperCommonTest { +public class ArticleMapperBootUnitTest extends ArticleMapperCommonUnitTest { } diff --git a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonTest.java b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonUnitTest.java similarity index 94% rename from persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonTest.java rename to persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonUnitTest.java index 33071dc0c1..2a93ca3d61 100644 --- a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonTest.java +++ b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonUnitTest.java @@ -5,7 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired; import static org.assertj.core.api.Assertions.assertThat; -class ArticleMapperCommonTest { +class ArticleMapperCommonUnitTest { @Autowired ArticleMapper articleMapper; diff --git a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperIntegrationTest.java b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperUnitTest.java similarity index 80% rename from persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperIntegrationTest.java rename to persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperUnitTest.java index 9298714cb2..9c677ecee5 100644 --- a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperIntegrationTest.java +++ b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperUnitTest.java @@ -6,6 +6,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = PersistenceConfig.class) -public class ArticleMapperIntegrationTest extends ArticleMapperCommonTest { +public class ArticleMapperUnitTest extends ArticleMapperCommonUnitTest { } diff --git a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLIntegrationTest.java b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLUnitTest.java similarity index 79% rename from persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLIntegrationTest.java rename to persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLUnitTest.java index de8974d4ad..96c7c162c5 100644 --- a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLIntegrationTest.java +++ b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLUnitTest.java @@ -6,6 +6,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:/beans.xml") -public class ArticleMapperXMLIntegrationTest extends ArticleMapperCommonTest { +public class ArticleMapperXMLUnitTest extends ArticleMapperCommonUnitTest { } diff --git a/pom.xml b/pom.xml index 686db65e8f..f56006713a 100644 --- a/pom.xml +++ b/pom.xml @@ -402,7 +402,7 @@ dropwizard dubbo - ethereum + feign flyway-cdi-extension @@ -416,7 +416,7 @@ graphql/graphql-java grpc - gson + gson guava-modules guice @@ -428,7 +428,6 @@ httpclient-simple hystrix - image-processing immutables jackson-modules @@ -473,6 +472,7 @@ jsoup jta kubernetes + ksqldb language-interop libraries-2 @@ -616,7 +616,7 @@ spring-apache-camel spring-batch - spring-batch-2 + spring-batch-2 spring-bom spring-boot-modules spring-boot-rest @@ -646,7 +646,7 @@ spring-ejb spring-exceptions - spring-freemarker + spring-freemarker spring-integration @@ -660,6 +660,7 @@ spring-mobile spring-mockito + spring-native spring-protobuf spring-quartz @@ -671,7 +672,7 @@ spring-security-modules spring-shell spring-sleuth - spring-soap + spring-soap spring-spel spring-state-machine spring-static-resources @@ -750,6 +751,8 @@ parent-spring-5 parent-java + image-processing + jenkins/plugins jhipster jws @@ -868,7 +871,7 @@ dropwizard dubbo - ethereum + feign flyway-cdi-extension @@ -882,7 +885,7 @@ graphql/graphql-java grpc - gson + gson guava-modules guice @@ -894,7 +897,6 @@ httpclient-simple hystrix - image-processing immutables jackson-modules @@ -939,6 +941,8 @@ jsoup jta + ksqldb + libraries-2 libraries-3 @@ -1103,7 +1107,7 @@ spring-ejb spring-exceptions - spring-freemarker + spring-freemarker spring-integration @@ -1117,7 +1121,7 @@ spring-mobile spring-mockito - + spring-native spring-protobuf spring-quartz @@ -1199,6 +1203,8 @@ parent-spring-5 parent-java + image-processing + jenkins/plugins jhipster jws @@ -1283,16 +1289,20 @@ core-java-modules/core-java-9-streams core-java-modules/core-java-10 + core-java-modules/core-java-11 core-java-modules/core-java-11-2 + core-java-modules/core-java-collections-set core-java-modules/core-java-date-operations-1 core-java-modules/core-java-datetime-conversion core-java-modules/core-java-datetime-string + core-java-modules/core-java-io-conversions-2 core-java-modules/core-java-jpms core-java-modules/core-java-os + core-java-modules/core-java-string-operations-3 core-java-modules/core-java-time-measurements core-java-modules/multimodulemavenproject @@ -1332,12 +1342,15 @@ + core-java-modules/core-java-collections-set core-java-modules/core-java-date-operations-1 core-java-modules/core-java-datetime-conversion core-java-modules/core-java-datetime-string + core-java-modules/core-java-io-conversions-2 core-java-modules/core-java-jpms core-java-modules/core-java-os + core-java-modules/core-java-string-operations-3 core-java-modules/core-java-time-measurements core-java-modules/multimodulemavenproject core-java-modules/core-java-strings @@ -1362,7 +1375,7 @@ true false false - false + false true 4.12 diff --git a/quarkus-extension/quarkus-app/pom.xml b/quarkus-extension/quarkus-app/pom.xml index f609261c5a..041b6f1762 100644 --- a/quarkus-extension/quarkus-app/pom.xml +++ b/quarkus-extension/quarkus-app/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 com.baeldung.quarkus.app quarkus-app diff --git a/quarkus/pom.xml b/quarkus/pom.xml index 6e250a2858..e88fd4dc5a 100644 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 com.baeldung.quarkus quarkus diff --git a/rabbitmq/README.md b/rabbitmq/README.md index 218b1a4b14..7fea2e85a0 100644 --- a/rabbitmq/README.md +++ b/rabbitmq/README.md @@ -4,4 +4,6 @@ This module contains articles about RabbitMQ. ### Relevant articles - [Introduction to RabbitMQ](https://www.baeldung.com/rabbitmq) +- [Exchanges, Queues, and Bindings in RabbitMQ](https://www.baeldung.com/java-rabbitmq-exchanges-queues-bindings) +- [Pub-Sub vs. Message Queues](https://www.baeldung.com/pub-sub-vs-message-queues) diff --git a/rabbitmq/pom.xml b/rabbitmq/pom.xml index 8a707a15dd..8befd36ab6 100644 --- a/rabbitmq/pom.xml +++ b/rabbitmq/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 rabbitmq 0.1-SNAPSHOT @@ -9,20 +9,42 @@ com.baeldung - parent-modules - 1.0.0-SNAPSHOT + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + pom + import + + + + com.rabbitmq amqp-client ${amqp-client.version} + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-starter-amqp + - 3.6.6 + 5.12.0 + 2020.0.3 \ No newline at end of file diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java new file mode 100644 index 0000000000..e8acb90f00 --- /dev/null +++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java @@ -0,0 +1,43 @@ +package com.baeldung.pubsubmq.client; + +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + + +@SpringBootApplication +public class ClientApplication { + private static final String MESSAGE_QUEUE = "pizza-message-queue"; + + @Bean + public Queue queue() { + return new Queue(MESSAGE_QUEUE); + } + + @Bean + public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + container.setQueueNames(MESSAGE_QUEUE); + container.setMessageListener(listenerAdapter); + return container; + } + + @Bean + public Consumer consumer() { + return new Consumer(); + } + + @Bean + public MessageListenerAdapter listenerAdapter(Consumer consumer) { + return new MessageListenerAdapter(consumer, "receiveOrder"); + } + + public static void main(String[] args) { + SpringApplication.run(ClientApplication.class, args); + } +} diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java new file mode 100644 index 0000000000..fadc60572d --- /dev/null +++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java @@ -0,0 +1,7 @@ +package com.baeldung.pubsubmq.client; + +public class Consumer { + public void receiveOrder(String message) { + System.out.printf("Order received: %s%n", message); + } +} diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java new file mode 100644 index 0000000000..24c4be0a70 --- /dev/null +++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java @@ -0,0 +1,28 @@ +package com.baeldung.pubsubmq.server; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; + +import javax.annotation.PostConstruct; + +public class Publisher { + + private RabbitTemplate rabbitTemplate; + private String queue; + private String topic; + + public Publisher(RabbitTemplate rabbitTemplate, String queue, String topic) { + this.rabbitTemplate = rabbitTemplate; + this.queue = queue; + this.topic = topic; + } + + @PostConstruct + public void postMessages() { + rabbitTemplate.convertAndSend(queue, "1 Pepperoni"); + rabbitTemplate.convertAndSend(queue, "3 Margarita"); + rabbitTemplate.convertAndSend(queue, "1 Ham and Pineapple (yuck)"); + + rabbitTemplate.convertAndSend(topic, "notification", "New Deal on T-Shirts: 95% off!"); + rabbitTemplate.convertAndSend(topic, "notification", "2 for 1 on all Jeans!"); + } +} diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java new file mode 100644 index 0000000000..f2387f6ae5 --- /dev/null +++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java @@ -0,0 +1,57 @@ +package com.baeldung.pubsubmq.server; + +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.TopicExchange; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class ServerApplication { + private static final String MESSAGE_QUEUE = "pizza-message-queue"; + private static final String PUB_SUB_TOPIC = "notification-topic"; + private static final String PUB_SUB_EMAIL_QUEUE = "email-queue"; + private static final String PUB_SUB_TEXT_QUEUE = "text-queue"; + + @Bean + public Queue queue() { + return new Queue(MESSAGE_QUEUE); + } + + @Bean + public Queue emailQueue() { + return new Queue(PUB_SUB_EMAIL_QUEUE); + } + + @Bean + public Queue textQueue() { + return new Queue(PUB_SUB_TEXT_QUEUE); + } + + @Bean + public TopicExchange exchange() { + return new TopicExchange(PUB_SUB_TOPIC); + } + + @Bean + public Binding emailBinding(Queue emailQueue, TopicExchange exchange) { + return BindingBuilder.bind(emailQueue).to(exchange).with("notification"); + } + + @Bean + public Binding textBinding(Queue textQueue, TopicExchange exchange) { + return BindingBuilder.bind(textQueue).to(exchange).with("notification"); + } + + @Bean + public Publisher publisher(RabbitTemplate rabbitTemplate) { + return new Publisher(rabbitTemplate, MESSAGE_QUEUE, PUB_SUB_TOPIC); + } + + public static void main(String[] args) { + SpringApplication.run(ServerApplication.class, args); + } +} diff --git a/rabbitmq/src/main/java/com/baeldung/setup/Setup.java b/rabbitmq/src/main/java/com/baeldung/setup/Setup.java new file mode 100644 index 0000000000..5ec7f3b5ee --- /dev/null +++ b/rabbitmq/src/main/java/com/baeldung/setup/Setup.java @@ -0,0 +1,38 @@ +package com.baeldung.setup; + +import com.rabbitmq.client.BuiltinExchangeType; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +public class Setup { + + private static final String ORDERS_QUEUE_NAME = "orders-queue"; + private static final String ORDERS_EXCHANGE_NAME = "orders-direct-exchange"; + private static final String ORDERS_ALTERNATE_EXCHANGE_NAME = "orders-alternate-exchange"; + private static final String ORDERS_ROUTING_KEY = "orders-routing-key"; + + public static void main(String[] args) throws IOException, TimeoutException { + ConnectionFactory factory = new ConnectionFactory(); + factory.setHost("localhost"); + Connection connection = factory.newConnection(); + Channel channel = connection.createChannel(); + + Map exchangeArguments = new HashMap<>(); + exchangeArguments.put("alternate-exchange", ORDERS_ALTERNATE_EXCHANGE_NAME); + channel.exchangeDeclare(ORDERS_EXCHANGE_NAME, BuiltinExchangeType.DIRECT, true, false, exchangeArguments); + + Map queueArguments = new HashMap<>(); + queueArguments.put("x-message-ttl", 60000); + queueArguments.put("x-max-priority", 10); + channel.queueDeclare(ORDERS_QUEUE_NAME, true, false, false, queueArguments); + + channel.queueBind(ORDERS_QUEUE_NAME, ORDERS_EXCHANGE_NAME, ORDERS_ROUTING_KEY); + } + +} diff --git a/reactor-core/README.md b/reactor-core/README.md index 08dac354ab..f7c36f5897 100644 --- a/reactor-core/README.md +++ b/reactor-core/README.md @@ -9,3 +9,5 @@ This module contains articles about Reactor Core. - [Programmatically Creating Sequences with Project Reactor](https://www.baeldung.com/flux-sequences-reactor) - [How to Extract a Mono’s Content in Java](https://www.baeldung.com/java-string-from-mono) - [How to Convert Mono> Into Flux](https://www.baeldung.com/java-mono-list-to-flux) +- [Project Reactor: map() vs flatMap()](https://www.baeldung.com/java-reactor-map-flatmap) +- [What Does Mono.defer() Do?](https://www.baeldung.com/java-mono-defer) diff --git a/reactor-core/pom.xml b/reactor-core/pom.xml index 6eead97bc2..6bd725b3b5 100644 --- a/reactor-core/pom.xml +++ b/reactor-core/pom.xml @@ -32,6 +32,12 @@ ${assertj.version} test + + org.projectlombok + lombok + RELEASE + test + diff --git a/reactor-core/src/test/java/com/baeldung/mono/MonoUnitTest.java b/reactor-core/src/test/java/com/baeldung/mono/MonoUnitTest.java index 0c6e0c07ef..fc42620e8e 100644 --- a/reactor-core/src/test/java/com/baeldung/mono/MonoUnitTest.java +++ b/reactor-core/src/test/java/com/baeldung/mono/MonoUnitTest.java @@ -1,5 +1,6 @@ package com.baeldung.mono; +import lombok.extern.slf4j.Slf4j; import org.junit.Test; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -13,6 +14,7 @@ import java.util.Optional; import static org.junit.Assert.assertEquals; +@Slf4j public class MonoUnitTest { @Test public void whenMonoProducesString_thenBlockAndConsume() { @@ -80,4 +82,84 @@ public class MonoUnitTest { return Mono.just(list); } + + @Test + public void whenEmptyList_thenMonoDeferExecuted() { + + Mono> emptyList = Mono.defer(() -> monoOfEmptyList()); + + //Empty list, hence Mono publisher in switchIfEmpty executed after condition evaluation + Flux emptyListElements = emptyList.flatMapIterable(l -> l) + .switchIfEmpty(Mono.defer(() -> sampleMsg("EmptyList"))) + .log(); + + StepVerifier.create(emptyListElements) + .expectNext("EmptyList") + .verifyComplete(); + } + + @Test + public void whenNonEmptyList_thenMonoDeferNotExecuted() { + + Mono> nonEmptyist = Mono.defer(() -> monoOfList()); + + //Non empty list, hence Mono publisher in switchIfEmpty won't evaluated. + Flux listElements = nonEmptyist.flatMapIterable(l -> l) + .switchIfEmpty(Mono.defer(() -> sampleMsg("NonEmptyList"))) + .log(); + + StepVerifier.create(listElements) + .expectNext("one", "two", "three", "four") + .verifyComplete(); + } + + private Mono> monoOfEmptyList() { + List list = new ArrayList<>(); + return Mono.just(list); + } + + @Test + public void whenUsingMonoJust_thenEagerEvaluation() throws InterruptedException { + + Mono msg = sampleMsg("Eager Publisher"); + + log.debug("Intermediate Test Message...."); + + StepVerifier.create(msg) + .expectNext("Eager Publisher") + .verifyComplete(); + + Thread.sleep(5000); + + StepVerifier.create(msg) + .expectNext("Eager Publisher") + .verifyComplete(); + } + + @Test + public void whenUsingMonoDefer_thenLazyEvaluation() throws InterruptedException { + + Mono deferMsg = Mono.defer(() -> sampleMsg("Lazy Publisher")); + + log.debug("Intermediate Test Message...."); + + StepVerifier.create(deferMsg) + .expectNext("Lazy Publisher") + .verifyComplete(); + + Thread.sleep(5000); + + StepVerifier.create(deferMsg) + .expectNext("Lazy Publisher") + .verifyComplete(); + + } + + private Mono sampleMsg(String str) { + log.debug("Call to Retrieve Sample Message!! --> {} at: {}", str, System.currentTimeMillis()); + return Mono.just(str); + } } + + + diff --git a/spark-java/pom.xml b/spark-java/pom.xml index b5538b4ec3..0ecdd02f6a 100644 --- a/spark-java/pom.xml +++ b/spark-java/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 spark-java 0.1.0-SNAPSHOT diff --git a/spf4j/spf4j-aspects-app/pom.xml b/spf4j/spf4j-aspects-app/pom.xml index 4a4c7cb804..a44ee805fb 100644 --- a/spf4j/spf4j-aspects-app/pom.xml +++ b/spf4j/spf4j-aspects-app/pom.xml @@ -20,17 +20,34 @@ org.spf4j spf4j-aspects ${spf4j.version} + + + org.apache.avro + avro + + org.spf4j spf4j-ui ${spf4j.version} + + + org.apache.avro + avro + + org.slf4j slf4j-api ${org.slf4j.version} + + org.apache.avro + avro + 1.10.2 + @@ -81,7 +98,7 @@ - 8.6.10 + 8.9.0 1.7.21 3.8.0 3.1.1 diff --git a/spf4j/spf4j-core-app/pom.xml b/spf4j/spf4j-core-app/pom.xml index 2961174a35..1f9be97854 100644 --- a/spf4j/spf4j-core-app/pom.xml +++ b/spf4j/spf4j-core-app/pom.xml @@ -20,16 +20,39 @@ org.spf4j spf4j-core ${spf4j.version} + + + org.apache.avro + avro + + org.spf4j spf4j-ui ${spf4j.version} + + + org.apache.avro + avro + + org.slf4j slf4j-api ${org.slf4j.version} + + + org.apache.avro + avro + + + + + org.apache.avro + avro + 1.10.2 @@ -81,7 +104,7 @@ - 8.6.10 + 8.9.0 1.7.21 3.8.0 3.1.1 diff --git a/spring-4/pom.xml b/spring-4/pom.xml index 21de925d99..41f55ea13b 100644 --- a/spring-4/pom.xml +++ b/spring-4/pom.xml @@ -39,6 +39,7 @@ com.zaxxer HikariCP + ${hikaricp.version} org.springframework.boot @@ -113,6 +114,7 @@ 1.0.1 3.6 2.4.0 + 4.0.3 \ No newline at end of file diff --git a/spring-5-data-reactive/pom.xml b/spring-5-data-reactive/pom.xml index 5f12636280..94a3c47809 100644 --- a/spring-5-data-reactive/pom.xml +++ b/spring-5-data-reactive/pom.xml @@ -83,11 +83,6 @@ h2 ${h2.version} - - org.springframework.boot - spring-boot-starter-test - test - org.apache.httpcomponents httpclient @@ -152,6 +147,10 @@ 1.4.200 1.5.23 3.3.1.RELEASE + 2.2.6.RELEASE diff --git a/spring-5/pom.xml b/spring-5/pom.xml index 5ebeb4dadf..5799f3bc8f 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -96,6 +96,11 @@ spring-restdocs-restassured test + + com.zaxxer + HikariCP + ${hikaricp.version} + @@ -139,6 +144,7 @@ 1.5.6 4.1 ${project.build.directory}/generated-snippets + 4.0.3 \ No newline at end of file diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml index 464a830383..da981987fe 100644 --- a/spring-aop/pom.xml +++ b/spring-aop/pom.xml @@ -71,4 +71,5 @@ 1.11 + \ No newline at end of file diff --git a/spring-batch/pom.xml b/spring-batch/pom.xml index b195ff8d13..9879a4777f 100644 --- a/spring-batch/pom.xml +++ b/spring-batch/pom.xml @@ -17,6 +17,11 @@ + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-datatype.version} + @@ -91,6 +96,7 @@ 3.15.1 4.1 2.3.1 + 2.12.3 3.1.1 diff --git a/spring-batch/src/main/java/com/baeldung/taskletsvschunks/model/Line.java b/spring-batch/src/main/java/com/baeldung/taskletsvschunks/model/Line.java index 2d7d3ff63f..62cc74abbd 100644 --- a/spring-batch/src/main/java/com/baeldung/taskletsvschunks/model/Line.java +++ b/spring-batch/src/main/java/com/baeldung/taskletsvschunks/model/Line.java @@ -1,5 +1,10 @@ package com.baeldung.taskletsvschunks.model; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; + import java.io.Serializable; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -7,6 +12,8 @@ import java.time.format.DateTimeFormatter; public class Line implements Serializable { private String name; + @JsonDeserialize(using = LocalDateDeserializer.class) + @JsonSerialize(using = LocalDateSerializer.class) private LocalDate dob; private Long age; diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index 53af0de315..7a93cabb82 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -73,6 +73,7 @@ spring-boot-actuator spring-boot-data-2 spring-boot-react + spring-boot-validation diff --git a/spring-boot-modules/spring-boot-actuator/pom.xml b/spring-boot-modules/spring-boot-actuator/pom.xml index 1865974ab0..eaeb3b4ac7 100644 --- a/spring-boot-modules/spring-boot-actuator/pom.xml +++ b/spring-boot-modules/spring-boot-actuator/pom.xml @@ -9,10 +9,9 @@ 0.0.1-SNAPSHOT - org.springframework.boot - spring-boot-starter-parent - 2.3.2.RELEASE - + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT diff --git a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml index 63bc286b45..a1daa3fa19 100644 --- a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml +++ b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml @@ -81,5 +81,5 @@ 1.5.7 2.0.4.RELEASE - + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties b/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties deleted file mode 100644 index 709574239b..0000000000 --- a/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.main.allow-bean-definition-overriding=true \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/errorhandling/controllers/MyErrorController.java b/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/errorhandling/controllers/MyErrorController.java index 8a7a3d6e45..be27f099db 100644 --- a/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/errorhandling/controllers/MyErrorController.java +++ b/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/errorhandling/controllers/MyErrorController.java @@ -11,8 +11,6 @@ import javax.servlet.http.HttpServletRequest; @Controller public class MyErrorController implements ErrorController { - public MyErrorController() {} - @GetMapping(value = "/error") public String handleError(HttpServletRequest request) { @@ -31,10 +29,4 @@ public class MyErrorController implements ErrorController { } return "error"; } - - @Override - public String getErrorPath() { - return null; - } - } diff --git a/spring-boot-modules/spring-boot-cassandre/pom.xml b/spring-boot-modules/spring-boot-cassandre/pom.xml index 75163d2c2b..14849e50d7 100644 --- a/spring-boot-modules/spring-boot-cassandre/pom.xml +++ b/spring-boot-modules/spring-boot-cassandre/pom.xml @@ -1,68 +1,50 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.4.5 - - - com.example - demo - 0.0.1-SNAPSHOT - Cassandre trading bot tutorial - Cassandre trading bot tutorial - - 11 - - - - org.springframework.boot - spring-boot-starter - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + spring-boot-cassandre + jar + spring-boot-cassandre + Cassandre trading bot tutorial - - - tech.cassandre.trading.bot - cassandre-trading-bot-spring-boot-starter - 4.2.1 - - - org.knowm.xchange - xchange-kucoin - 5.0.7 - - - org.hsqldb - hsqldb - 2.5.1 - + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + - - org.springframework.boot - spring-boot-starter-test - test - - - - tech.cassandre.trading.bot - cassandre-trading-bot-spring-boot-starter-test - 4.2.1 - test - + + + + tech.cassandre.trading.bot + cassandre-trading-bot-spring-boot-starter + ${cassandre.trading.bot.version} + + + org.knowm.xchange + xchange-kucoin + ${xchange-kucoin.version} + + + org.hsqldb + hsqldb + ${hsqldb.version} + + + tech.cassandre.trading.bot + cassandre-trading-bot-spring-boot-starter-test + ${cassandre.trading.bot.version} + test + + - + + 11 + 4.2.1 + 5.0.7 + 2.5.1 + - - - - org.springframework.boot - spring-boot-maven-plugin - 2.4.5 - - - - - + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java similarity index 98% rename from spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java rename to spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java index ea8ae74aa6..92ef379a66 100644 --- a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java +++ b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java @@ -1,4 +1,4 @@ -package com.example.demo; +package com.baeldung.trading; import static tech.cassandre.trading.bot.dto.position.PositionStatusDTO.CLOSED; import static tech.cassandre.trading.bot.dto.position.PositionStatusDTO.OPENED; diff --git a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java new file mode 100644 index 0000000000..021c6c1d3b --- /dev/null +++ b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.trading; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TradingBotApplication { + + public static void main(String[] args) { + SpringApplication.run(TradingBotApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java similarity index 94% rename from spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java rename to spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java index bf7c353821..74986df971 100644 --- a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java +++ b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java @@ -1,4 +1,4 @@ -package com.example.demo; +package com.baeldung.trading; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -22,9 +22,9 @@ import tech.cassandre.trading.bot.test.mock.TickerFluxMock; @SpringBootTest @Import(TickerFluxMock.class) @DisplayName("Simple strategy test") -public class MyFirstStrategyUnitTest { +public class MyFirstStrategyLiveTest { - private final Logger logger = LoggerFactory.getLogger(MyFirstStrategyUnitTest.class); + private final Logger logger = LoggerFactory.getLogger(MyFirstStrategyLiveTest.class); @Autowired private MyFirstStrategy strategy; diff --git a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java deleted file mode 100644 index eaa99696e2..0000000000 --- a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.demo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class DemoApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/spring-boot-modules/spring-boot-data/src/main/resources/application.properties b/spring-boot-modules/spring-boot-data/src/main/resources/application.properties index 6378a48506..969464a41c 100644 --- a/spring-boot-modules/spring-boot-data/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-data/src/main/resources/application.properties @@ -6,7 +6,6 @@ spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password= -spring.main.allow-bean-definition-overriding=true javers.mappingStyle=FIELD javers.algorithm=SIMPLE javers.commitIdGenerator=synchronized_sequence diff --git a/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties b/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties index 27b7915cff..2bf15543f0 100644 --- a/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties @@ -3,5 +3,4 @@ management.metrics.enable.root=true management.metrics.enable.jvm=true management.endpoint.restart.enabled=true spring.datasource.jmx-enabled=false -spring.main.allow-bean-definition-overriding=true management.endpoint.shutdown.enabled=true \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties b/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties index 3d6f37230c..4ffb414e92 100644 --- a/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties @@ -3,6 +3,5 @@ management.metrics.enable.root=true management.metrics.enable.jvm=true management.endpoint.restart.enabled=true spring.datasource.tomcat.jmx-enabled=false -spring.main.allow-bean-definition-overriding=true management.endpoint.shutdown.enabled=true spring.config.import=file:./additional.properties,optional:file:/Users/home/config/jdbc.properties \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-exceptions/README.md b/spring-boot-modules/spring-boot-exceptions/README.md index 33ae193fb8..97a51203c7 100644 --- a/spring-boot-modules/spring-boot-exceptions/README.md +++ b/spring-boot-modules/spring-boot-exceptions/README.md @@ -5,3 +5,4 @@ This module contains articles about Spring Boot Exceptions ### Relevant Articles: - [The BeanDefinitionOverrideException in Spring Boot](https://www.baeldung.com/spring-boot-bean-definition-override-exception) +- [Spring Boot Error ApplicationContextException](https://www.baeldung.com/spring-boot-application-context-exception) diff --git a/spring-boot-modules/spring-boot-keycloak/pom.xml b/spring-boot-modules/spring-boot-keycloak/pom.xml index 37d57d6556..505d486509 100644 --- a/spring-boot-modules/spring-boot-keycloak/pom.xml +++ b/spring-boot-modules/spring-boot-keycloak/pom.xml @@ -76,7 +76,9 @@ - 11.0.2 + 13.0.1 + + 2.5.3 \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-libraries-2/pom.xml b/spring-boot-modules/spring-boot-libraries-2/pom.xml index 669c07c23a..04c09754b4 100644 --- a/spring-boot-modules/spring-boot-libraries-2/pom.xml +++ b/spring-boot-modules/spring-boot-libraries-2/pom.xml @@ -91,7 +91,7 @@ - 1.1.0 + 3.1.1 4.0.3 5.1.0 2.4.5 diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/JobRunrSpringBootApp.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/JobRunrSpringBootApp.java index 77297feb92..061dba15bf 100644 --- a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/JobRunrSpringBootApp.java +++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/JobRunrSpringBootApp.java @@ -32,6 +32,6 @@ public class JobRunrSpringBootApp { @PostConstruct public void scheduleRecurrently() { - jobScheduler.scheduleRecurrently(x -> x.executeSampleJob("a recurring job"), Cron.every5minutes()); + jobScheduler.scheduleRecurrently(Cron.every5minutes(), x -> x.executeSampleJob("a recurring job")); } } diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/jobrunr/controller/JobRunrController.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/jobrunr/controller/JobRunrController.java index af5f0b1196..610314222c 100644 --- a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/jobrunr/controller/JobRunrController.java +++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/jobrunr/controller/JobRunrController.java @@ -33,7 +33,7 @@ public class JobRunrController { public ResponseEntity schedule( @PathVariable("input") @DefaultValue("default-input") String input, @RequestParam("scheduleAt") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime scheduleAt) { - jobScheduler.schedule(() -> sampleJobService.executeSampleJob(input), scheduleAt); + jobScheduler.schedule(scheduleAt, () -> sampleJobService.executeSampleJob(input)); return okResponse("job scheduled successfully"); } diff --git a/spring-boot-modules/spring-boot-logging-log4j2/README.md b/spring-boot-modules/spring-boot-logging-log4j2/README.md index aa6bb9b6e1..9688f8f83c 100644 --- a/spring-boot-modules/spring-boot-logging-log4j2/README.md +++ b/spring-boot-modules/spring-boot-logging-log4j2/README.md @@ -6,3 +6,4 @@ This module contains articles about logging in Spring Boot projects with Log4j 2 - [Logging in Spring Boot](https://www.baeldung.com/spring-boot-logging) - [Logging to Graylog with Spring Boot](https://www.baeldung.com/graylog-with-spring-boot) - [Log Groups in Spring Boot 2.1](https://www.baeldung.com/spring-boot-log-groups) +- [Writing Log Data to Syslog Using Log4j2](https://www.baeldung.com/log4j-to-syslog) diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java new file mode 100644 index 0000000000..56c3b62c5d --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java @@ -0,0 +1,23 @@ +package com.baeldung.syslog; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootSyslogApplication { + + private static final Logger logger = LogManager.getLogger(SpringBootSyslogApplication.class); + + public static void main(String[] args) { + SpringApplication.run(SpringBootSyslogApplication.class, args); + + logger.debug("Debug log message"); + logger.info("Info log message"); + logger.error("Error log message"); + logger.warn("Warn log message"); + logger.fatal("Fatal log message"); + logger.trace("Trace log message"); + } +} diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml index b08cd2d22d..77a2074b30 100644 --- a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml +++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml @@ -22,6 +22,10 @@ + + + @@ -29,6 +33,7 @@ + diff --git a/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties index 7070d4c2f0..3ee7660e8b 100644 --- a/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties @@ -1,4 +1,3 @@ -spring.main.allow-bean-definition-overriding=true spring.mvc.static-path-pattern=/content/** spring.webflux.static-path-pattern=/content/** spring.resources.static-locations=classpath:/files/,classpath:/static-files \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml index e0b261510c..a16c8b0a83 100644 --- a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml @@ -8,10 +8,9 @@ 0.0.1-SNAPSHOT - org.springframework.boot - spring-boot-starter-parent - 2.4.2 - + com.baeldung.spring-boot-modules + spring-boot-mvc-jersey + 0.0.1-SNAPSHOT diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTest.java similarity index 82% rename from spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java rename to spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTest.java index 9bd6bbf029..d9018efc4a 100644 --- a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class JerseyApplicationIntegrationTests { +class JerseyApplicationIntegrationTest { @Test void contextLoads() { diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml index 77c8027974..8521ab25ac 100644 --- a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml @@ -8,10 +8,9 @@ 0.0.1-SNAPSHOT - org.springframework.boot - spring-boot-starter-parent - 2.4.2 - + com.baeldung.spring-boot-modules + spring-boot-mvc-jersey + 0.0.1-SNAPSHOT diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTest.java similarity index 83% rename from spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java rename to spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTest.java index d05cb97e47..14b0915fa7 100644 --- a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java +++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class MvcApplicationIntegrationTests { +class MvcApplicationIntegrationTest { @Test void contextLoads() { diff --git a/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties index 6dab470c84..7f399bb11d 100644 --- a/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties @@ -1,2 +1 @@ -spring.main.allow-bean-definition-overriding=true spring.thymeleaf.view-names=thymeleaf/* \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml b/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml index 184becb657..5633e4d260 100644 --- a/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml +++ b/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml @@ -39,4 +39,5 @@ 2.2.5.RELEASE + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-performance/pom.xml b/spring-boot-modules/spring-boot-performance/pom.xml index 02cef986d0..71dfb3c437 100644 --- a/spring-boot-modules/spring-boot-performance/pom.xml +++ b/spring-boot-modules/spring-boot-performance/pom.xml @@ -51,4 +51,5 @@ com.baeldung.lazyinitialization.Application 2.0.0 + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-properties-3/pom.xml b/spring-boot-modules/spring-boot-properties-3/pom.xml index 400229a662..b5e6ef8701 100644 --- a/spring-boot-modules/spring-boot-properties-3/pom.xml +++ b/spring-boot-modules/spring-boot-properties-3/pom.xml @@ -40,4 +40,5 @@ + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-properties/pom.xml b/spring-boot-modules/spring-boot-properties/pom.xml index adeb41e976..0ef44e0546 100644 --- a/spring-boot-modules/spring-boot-properties/pom.xml +++ b/spring-boot-modules/spring-boot-properties/pom.xml @@ -122,16 +122,8 @@ - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - - 2020.0.0-M5 + 2020.0.3 1.10 20.0 @ diff --git a/spring-boot-modules/spring-boot-react/pom.xml b/spring-boot-modules/spring-boot-react/pom.xml index f1e10ae12d..fdefbbdfc7 100644 --- a/spring-boot-modules/spring-boot-react/pom.xml +++ b/spring-boot-modules/spring-boot-react/pom.xml @@ -15,28 +15,28 @@ org.springframework.boot spring-boot-starter-web - 2.4.4 + ${spring-boot.version} org.springframework.boot spring-boot-starter-data-jpa - 2.4.4 + ${spring-boot.version} com.h2database h2 - 1.4.200 + ${h2.version} runtime com.github.javafaker javafaker - 1.0.2 + ${javafaker.version} org.springframework.boot spring-boot-starter-test - 2.4.4 + ${spring-boot.version} test @@ -127,6 +127,9 @@ 1.6 v10.14.2 v1.12.1 + 2.4.4 + 1.4.200 + 1.0.2 \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-runtime-2/pom.xml b/spring-boot-modules/spring-boot-runtime-2/pom.xml index 2c97f63746..c177529a41 100644 --- a/spring-boot-modules/spring-boot-runtime-2/pom.xml +++ b/spring-boot-modules/spring-boot-runtime-2/pom.xml @@ -60,4 +60,5 @@ + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-runtime/README.md b/spring-boot-modules/spring-boot-runtime/README.md index a36a17fa8e..072bd16a78 100644 --- a/spring-boot-modules/spring-boot-runtime/README.md +++ b/spring-boot-modules/spring-boot-runtime/README.md @@ -11,3 +11,4 @@ This module contains articles about administering a Spring Boot runtime - [CORS with Spring](https://www.baeldung.com/spring-cors) - [Spring – Log Incoming Requests](https://www.baeldung.com/spring-http-logging) - [How to Configure Spring Boot Tomcat](https://www.baeldung.com/spring-boot-configure-tomcat) + - [Max-HTTP-Header-Size in Spring Boot 2](https://www.baeldung.com/spring-boot-max-http-header-size) diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/MaxHttpHeaderSizeApplication.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/MaxHttpHeaderSizeApplication.java new file mode 100644 index 0000000000..ab8c52687b --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/MaxHttpHeaderSizeApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.maxhttpheadersize; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; + +@EnableAutoConfiguration +@ComponentScan("com.baeldung.maxhttpheadersize") +public class MaxHttpHeaderSizeApplication { + + public static void main(final String[] args) { + SpringApplication.run(MaxHttpHeaderSizeApplication.class, args); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/config/MaxHTTPHeaderSizeConfig.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/config/MaxHTTPHeaderSizeConfig.java similarity index 64% rename from spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/config/MaxHTTPHeaderSizeConfig.java rename to spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/config/MaxHTTPHeaderSizeConfig.java index 0a71f914db..3f500e0074 100644 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/config/MaxHTTPHeaderSizeConfig.java +++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/config/MaxHTTPHeaderSizeConfig.java @@ -1,11 +1,13 @@ -package com.baeldung.sampleapp.config; +package com.baeldung.maxhttpheadersize.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration -@ComponentScan({ "com.baeldung.sampleapp.web" }) +@EnableWebMvc +@ComponentScan({ "com.baeldung.maxhttpheadersize.*" }) public class MaxHTTPHeaderSizeConfig implements WebMvcConfigurer { public MaxHTTPHeaderSizeConfig() { diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/web/controller/MaxHttpHeaderSizeController.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeController.java similarity index 90% rename from spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/web/controller/MaxHttpHeaderSizeController.java rename to spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeController.java index 0c55f88fa1..89444c7875 100644 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/web/controller/MaxHttpHeaderSizeController.java +++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeController.java @@ -1,4 +1,4 @@ -package com.baeldung.sampleapp.web.controller; +package com.baeldung.maxhttpheadersize.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; diff --git a/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties b/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties index 27b7915cff..2bf15543f0 100644 --- a/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties @@ -3,5 +3,4 @@ management.metrics.enable.root=true management.metrics.enable.jvm=true management.endpoint.restart.enabled=true spring.datasource.jmx-enabled=false -spring.main.allow-bean-definition-overriding=true management.endpoint.shutdown.enabled=true \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerIntegrationTest.java b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerIntegrationTest.java similarity index 73% rename from spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerIntegrationTest.java rename to spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerIntegrationTest.java index 9b839f34a0..dd824491a0 100644 --- a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerIntegrationTest.java +++ b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerIntegrationTest.java @@ -1,4 +1,4 @@ -package com.baeldung.web.controller; +package com.baeldung.maxhttpheadersize.controller; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -16,10 +16,10 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import com.baeldung.sampleapp.config.WebConfig; +import com.baeldung.maxhttpheadersize.config.MaxHTTPHeaderSizeConfig; @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = WebConfig.class) +@ContextConfiguration(classes = MaxHTTPHeaderSizeConfig.class) @WebAppConfiguration public class MaxHttpHeaderSizeControllerIntegrationTest { @@ -30,19 +30,23 @@ public class MaxHttpHeaderSizeControllerIntegrationTest { @Before public void setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) + .build(); } @Test public void givenTokenWithLessThan8KBLegth_whenSendGetRequest_thenReturnsOK() throws Exception { mockMvc.perform(get("/request-header-test").contentType(MediaType.APPLICATION_JSON_VALUE) - .with(httpBasic("user", "password")).header("token", "token")).andExpect(status().isOk()); + .with(httpBasic("user", "password")) + .header("token", "token")) + .andExpect(status().isOk()); } @Test - public void givenTokenIsMissingInHeade_whenSendGetRequest_thenThrowsBadRequest() throws Exception { + public void givenTokenIsMissingInHeader_whenSendGetRequest_thenThrowsBadRequest() throws Exception { mockMvc.perform(get("/request-header-test").contentType(MediaType.APPLICATION_JSON_VALUE) - .with(httpBasic("user", "password"))).andExpect(status().isBadRequest()); + .with(httpBasic("user", "password"))) + .andExpect(status().isBadRequest()); } } diff --git a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerLiveTest.java b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerLiveTest.java similarity index 81% rename from spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerLiveTest.java rename to spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerLiveTest.java index 9c7e7c9029..2178be288d 100644 --- a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerLiveTest.java +++ b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerLiveTest.java @@ -1,4 +1,4 @@ -package com.baeldung.web.controller; +package com.baeldung.maxhttpheadersize.controller; import java.io.BufferedReader; import java.io.FileReader; @@ -11,18 +11,13 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; -import com.baeldung.sampleapp.config.MaxHTTPHeaderSizeConfig; - @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { MaxHTTPHeaderSizeConfig.class }, loader = AnnotationConfigContextLoader.class) @ActiveProfiles("test") -// Start MaxHttpHeaderSizeController Spring Boot App(MainApplication) first +// Start MaxHttpHeaderSizeController Spring Boot App(MaxHttpHeaderSizeApplication) first public class MaxHttpHeaderSizeControllerLiveTest { @Test(expected = HttpClientErrorException.class) diff --git a/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties b/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties index cf0f0ab74c..7bf9450088 100644 --- a/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties +++ b/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties @@ -8,5 +8,4 @@ endpoints.shutdown.enabled=true management.endpoint.restart.enabled=true -spring.main.allow-bean-definition-overriding=true spring.jmx.unique-names=true \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredApplication.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredApplication.java new file mode 100644 index 0000000000..0495e0f716 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.annotations.globalmethod; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +@SpringBootApplication +@EnableWebSecurity +public class AnnotationSecuredApplication { + + public static void main(String[] args) { + SpringApplication.run(AnnotationSecuredApplication.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredController.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredController.java new file mode 100644 index 0000000000..4687299ae5 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredController.java @@ -0,0 +1,50 @@ +package com.baeldung.annotations.globalmethod; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.security.RolesAllowed; + +@RestController +@EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true) +public class AnnotationSecuredController { + + @Autowired + DifferentClass differentClass; + + @GetMapping("/public") + public String publicHello() { + return "Hello Public"; + } + + @RolesAllowed("ADMIN") + @GetMapping("/admin") + public String adminHello() { + return "Hello Admin"; + } + + @RolesAllowed("USER") + @GetMapping("/protected") + public String jsr250Hello() { + return "Hello Jsr250"; + } + + @GetMapping("/indirect") + public String indirectHello() { + return jsr250Hello(); + } + + @GetMapping("/differentclass") + public String differentClassHello() { + return differentClass.differentJsr250Hello(); + } + + @PreAuthorize("hasRole('USER')") + public String preAuthorizeHello() { + return "Hello PreAuthorize"; + } + +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredStaticResourceConfig.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredStaticResourceConfig.java new file mode 100644 index 0000000000..467285adfa --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredStaticResourceConfig.java @@ -0,0 +1,22 @@ +package com.baeldung.annotations.globalmethod; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; + + +@Configuration +@EnableWebSecurity +public class AnnotationSecuredStaticResourceConfig extends WebSecurityConfigurerAdapter { + + @Bean + public WebSecurityCustomizer ignoreResources() { + return (webSecurity) -> webSecurity + .ignoring() + .antMatchers("/hello/*"); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/DifferentClass.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/DifferentClass.java new file mode 100644 index 0000000000..9bcc352d9a --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/DifferentClass.java @@ -0,0 +1,13 @@ +package com.baeldung.annotations.globalmethod; + +import org.springframework.stereotype.Component; + +import javax.annotation.security.RolesAllowed; + +@Component +public class DifferentClass { + @RolesAllowed("USER") + public String differentJsr250Hello() { + return "Hello Jsr250"; + } +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredApplication.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredApplication.java new file mode 100644 index 0000000000..13e405ee22 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredApplication.java @@ -0,0 +1,14 @@ +package com.baeldung.annotations.websecurity; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +@SpringBootApplication +@EnableWebSecurity +public class ConfigSecuredApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigSecuredApplication.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredController.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredController.java new file mode 100644 index 0000000000..198efb8353 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredController.java @@ -0,0 +1,30 @@ +package com.baeldung.annotations.websecurity; + +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@RestController +@EnableWebSecurity +public class ConfigSecuredController { + + @GetMapping("/public") + public String publicHello() { + return "Hello Public"; + } + + @GetMapping("/protected") + public String protectedHello() { + return "Hello from protected"; + } + + @GetMapping("/admin") + public String adminHello() { + return "Hello from admin"; + } + +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/CustomWebSecurityConfig.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/CustomWebSecurityConfig.java new file mode 100644 index 0000000000..ce874e313e --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/CustomWebSecurityConfig.java @@ -0,0 +1,29 @@ +package com.baeldung.annotations.websecurity; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/admin/**") + .hasRole("ADMIN") + .antMatchers("/protected/**") + .hasRole("USER"); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web + .ignoring() + .antMatchers("/public/*"); + } +} diff --git a/spring-boot-modules/spring-boot-security/src/main/resources/public/hello/baeldung.txt b/spring-boot-modules/spring-boot-security/src/main/resources/public/hello/baeldung.txt new file mode 100644 index 0000000000..b58fe6d244 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/resources/public/hello/baeldung.txt @@ -0,0 +1 @@ +Hello From Baeldung \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/globalmethod/GlobalMethodSpringBootIntegrationTest.java b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/globalmethod/GlobalMethodSpringBootIntegrationTest.java new file mode 100644 index 0000000000..be9dff714b --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/globalmethod/GlobalMethodSpringBootIntegrationTest.java @@ -0,0 +1,104 @@ +package com.baeldung.annotations.globalmethod; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +public class GlobalMethodSpringBootIntegrationTest { + public static final String HELLO_JSR_250 = "Hello Jsr250"; + public static final String HELLO_PUBLIC = "Hello Public"; + public static final String HELLO_PRE_AUTHORIZE = "Hello PreAuthorize"; + public static final String PUBLIC_RESOURCE = "/hello/baeldung.txt"; + public static final String HELLO_FROM_PUBLIC_RESOURCE = "Hello From Baeldung"; + private static final String PROTECTED_METHOD = "/protected"; + + @Autowired + private TestRestTemplate template; + + @Autowired + private AnnotationSecuredController api; + + @WithMockUser(username="baeldung", roles = "USER") + @Test + public void givenUserWithRole_whenJsr250_thenOk() { + assertThat(api.jsr250Hello()).isEqualTo(HELLO_JSR_250); + } + + @WithMockUser(username="baeldung", roles = "NOT-USER") + @Test(expected = AccessDeniedException.class) + public void givenWrongRole_whenJsr250_thenAccessDenied() { + api.jsr250Hello(); + } + + @Test + @WithAnonymousUser + public void givenAnonymousUser_whenPublic_thenOk() { + assertThat(api.publicHello()).isEqualTo(HELLO_PUBLIC); + } + + @Test(expected = AccessDeniedException.class) + @WithAnonymousUser + public void givenAnonymousUser_whenJsr250_thenAccessDenied() { + api.jsr250Hello(); + } + + // Tests for indirect calling of method + @Test + @WithAnonymousUser + public void givenAnonymousUser_whenIndirectCall_thenNoSecurity() { + assertThat(api.indirectHello()).isEqualTo(HELLO_JSR_250); + } + + @Test(expected = AccessDeniedException.class) + @WithAnonymousUser + public void givenAnonymousUser_whenIndirectToDifferentClass_thenAccessDenied() { + api.differentClassHello(); + } + + // Tests for static resource + @Test + public void givenPublicResource_whenGetViaWeb_thenOk() { + ResponseEntity result = template.getForEntity(PUBLIC_RESOURCE, String.class); + assertEquals(HELLO_FROM_PUBLIC_RESOURCE, result.getBody()); + } + + @Test + public void givenProtectedMethod_whenGetViaWeb_thenRedirectToLogin() { + ResponseEntity result = template.getForEntity(PROTECTED_METHOD, String.class); + assertEquals(HttpStatus.FOUND, result.getStatusCode()); + } + + // Tests for preAuthorize annotations + @WithMockUser(username="baeldung", roles = "USER") + @Test + public void givenUserWithRole_whenCallPreAuthorize_thenOk() { + assertThat(api.preAuthorizeHello()).isEqualTo(HELLO_PRE_AUTHORIZE); + } + + @WithMockUser(username="baeldung", roles = "NOT-USER") + @Test(expected = AccessDeniedException.class) + public void givenWrongRole_whenCallPreAuthorize_thenAccessDenied() { + api.preAuthorizeHello(); + } + + @Test(expected = AccessDeniedException.class) + @WithAnonymousUser + public void givenAnonymousUser_whenCallPreAuthorize_thenAccessDenied() { + api.preAuthorizeHello(); + } + +} diff --git a/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/websecurity/WebSecuritySpringBootIntegrationTest.java b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/websecurity/WebSecuritySpringBootIntegrationTest.java new file mode 100644 index 0000000000..1360ae939d --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/websecurity/WebSecuritySpringBootIntegrationTest.java @@ -0,0 +1,62 @@ +package com.baeldung.annotations.websecurity; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +public class WebSecuritySpringBootIntegrationTest { + private static final String PUBLIC_RESOURCE = "/hello/baeldung.txt"; + private static final String HELLO_FROM_PUBLIC_RESOURCE = "Hello From Baeldung"; + + @Autowired + private ConfigSecuredController api; + + @Autowired + private TestRestTemplate template; + + @Test + public void whenCallPublicDirectly_thenOk() { + assertThat(api.publicHello()).isEqualTo("Hello Public"); + } + + @Test + public void whenCallProtectedDirectly_thenNoSecurity() { + assertThat(api.protectedHello()).isEqualTo("Hello from protected"); + } + + @Test + public void whenGetProtectedViaWeb_thenForbidden() { + ResponseEntity result = template.getForEntity("/protected", String.class); + assertEquals(HttpStatus.FORBIDDEN, result.getStatusCode()); + } + + @Test + public void whenGetAdminViaWeb_thenForbidden() { + ResponseEntity result = template.getForEntity("/admin", String.class); + assertEquals(HttpStatus.FORBIDDEN, result.getStatusCode()); + } + + @Test + public void whenGetPublicViaWeb_thenSuccess() { + ResponseEntity result = template.getForEntity("/public", String.class); + assertEquals(HttpStatus.OK, result.getStatusCode()); + } + + @Test + public void givenPublicResource_whenGetViaWeb_thenOk() { + ResponseEntity result = template.getForEntity(PUBLIC_RESOURCE, String.class); + assertEquals(HELLO_FROM_PUBLIC_RESOURCE, result.getBody()); + } + +} diff --git a/spring-boot-modules/spring-boot-springdoc/README.md b/spring-boot-modules/spring-boot-springdoc/README.md index 608e4afa2e..4ac4147da6 100644 --- a/spring-boot-modules/spring-boot-springdoc/README.md +++ b/spring-boot-modules/spring-boot-springdoc/README.md @@ -3,3 +3,4 @@ - [Documenting a Spring REST API Using OpenAPI 3.0](https://www.baeldung.com/spring-rest-openapi-documentation) - [Spring REST Docs vs OpenAPI](https://www.baeldung.com/spring-rest-docs-vs-openapi) - [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints) +- [Swagger @Api Description Is Deprecated](https://www.baeldung.com/java-swagger-api-description-deprecated) diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java new file mode 100644 index 0000000000..3141428514 --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java @@ -0,0 +1,12 @@ +package com.baeldung.tagopenapi; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication() +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java new file mode 100644 index 0000000000..780b202d3c --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java @@ -0,0 +1,20 @@ +package com.baeldung.tagopenapi.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; + +public class OpenApi { + + @Bean + public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { + return new OpenAPI().info(new Info().title("Controller API") + .version(appVersion) + .description("This is a sample server created using springdocs - a library for OpenAPI 3 with spring boot.") + .termsOfService("http://swagger.io/terms/") + .license(new License().name("Apache 2.0") + .url("http://springdoc.org"))); + } +} diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java new file mode 100644 index 0000000000..3c81c1cee7 --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java @@ -0,0 +1,20 @@ +package com.baeldung.tagopenapi.controller; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/api/book") +@Tag(name = "book service", description = "the book API with description tag annotation") +public class BookController { + + @GetMapping("/") + public List getBooks() { + return Arrays.asList("book1", "book2"); + } +} diff --git a/spring-boot-modules/spring-boot-swagger/README.md b/spring-boot-modules/spring-boot-swagger/README.md index 1038031210..f94ae75c41 100644 --- a/spring-boot-modules/spring-boot-swagger/README.md +++ b/spring-boot-modules/spring-boot-swagger/README.md @@ -1,3 +1,4 @@ ## Relevant Articles: - [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints) +- [Swagger @Api Description Is Deprecated](https://www.baeldung.com/java-swagger-api-description-deprecated) diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java new file mode 100644 index 0000000000..9232152b5b --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java @@ -0,0 +1,12 @@ +package com.baeldung.apiswagger; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication() +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java new file mode 100644 index 0000000000..d9f51698c3 --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java @@ -0,0 +1,26 @@ +package com.baeldung.apiswagger.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.Tag; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +@Configuration +public class SwaggerConfiguration { + + public static final String BOOK_TAG = "book service"; + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + .tags(new Tag(BOOK_TAG, "the book API with description api tag")); + } + +} diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java new file mode 100644 index 0000000000..394c640751 --- /dev/null +++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java @@ -0,0 +1,21 @@ +package com.baeldung.apiswagger.controller; + +import com.baeldung.apiswagger.config.SwaggerConfiguration; +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/api/book") +@Api(tags = {SwaggerConfiguration.BOOK_TAG}) +public class BookController { + + @GetMapping("/") + public List getBooks() { + return Arrays.asList("book1", "book2"); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-testing/pom.xml b/spring-boot-modules/spring-boot-testing/pom.xml index 1860818b7b..f70e77b31a 100644 --- a/spring-boot-modules/spring-boot-testing/pom.xml +++ b/spring-boot-modules/spring-boot-testing/pom.xml @@ -80,11 +80,6 @@ ${spock.version} test - - io.rest-assured - rest-assured - test - @@ -147,6 +142,7 @@ 1.2-groovy-2.4 1.6 0.7.2 + 2.5.0 \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties b/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties index daab3e8d2c..70cae370a4 100644 --- a/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties @@ -6,4 +6,3 @@ spring.redis.port= 6379 spring.security.user.name=john spring.security.user.password=123 -spring.main.allow-bean-definition-overriding=true \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-validation/.gitignore b/spring-boot-modules/spring-boot-validation/.gitignore new file mode 100644 index 0000000000..8f5ee06047 --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/.gitignore @@ -0,0 +1,63 @@ +# Created by https://www.gitignore.io/api/eclipse + +### Eclipse ### + +.metadata +target/ +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +### Eclipse Patch ### +# Eclipse Core +.project + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# End of https://www.gitignore.io/api/eclipse \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-validation/README.md b/spring-boot-modules/spring-boot-validation/README.md new file mode 100644 index 0000000000..8c3c8b9305 --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/README.md @@ -0,0 +1,3 @@ +### Relevant Articles + +- [Spring Validation in the Service Layer](https://www.baeldung.com/spring-service-layer-validation) diff --git a/spring-boot-modules/spring-boot-validation/pom.xml b/spring-boot-modules/spring-boot-validation/pom.xml new file mode 100644 index 0000000000..795d506dc0 --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + com.baeldung + spring-boot-validation + 0.0.1-SNAPSHOT + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.hibernate.validator + hibernate-validator + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/SpringServiceLayerValidationApp.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/SpringServiceLayerValidationApp.java new file mode 100644 index 0000000000..b976342dda --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/SpringServiceLayerValidationApp.java @@ -0,0 +1,13 @@ +package com.baeldung.spring.servicevalidation; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringServiceLayerValidationApp { + + public static void main(String[] args) { + SpringApplication.run(SpringServiceLayerValidationApp.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/controller/UserAccountController.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/controller/UserAccountController.java new file mode 100644 index 0000000000..33d9966e42 --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/controller/UserAccountController.java @@ -0,0 +1,22 @@ +package com.baeldung.spring.servicevalidation.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.spring.servicevalidation.domain.UserAccount; +import com.baeldung.spring.servicevalidation.service.UserAccountService; + +@RestController +public class UserAccountController { + + @Autowired + private UserAccountService service; + + @PostMapping("/addUserAccount") + public Object addUserAccount(@RequestBody UserAccount userAccount) { + return service.addUserAccount(userAccount); + } + +} diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/dao/UserAccountDao.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/dao/UserAccountDao.java new file mode 100644 index 0000000000..c49c5220f4 --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/dao/UserAccountDao.java @@ -0,0 +1,31 @@ +package com.baeldung.spring.servicevalidation.dao; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.stereotype.Service; + +import com.baeldung.spring.servicevalidation.domain.UserAccount; + +@Service +public class UserAccountDao { + + private Map DB = new HashMap(); + + public String addUserAccount(UserAccount useraccount) { + DB.put(useraccount.getName(), useraccount); + return "success"; + } + + public Collection getAllUserAccounts() { + + Collection list = DB.values(); + if (list.isEmpty()) { + list.addAll(DB.values()); + } + return list; + + } + +} diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAccount.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAccount.java new file mode 100644 index 0000000000..5b0e795a8a --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAccount.java @@ -0,0 +1,77 @@ +package com.baeldung.spring.servicevalidation.domain; + +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +public class UserAccount { + + @NotNull(message = "Password must be between 4 to 15 characters") + @Size(min = 4, max = 15) + private String password; + + @NotBlank(message = "Name must not be blank") + private String name; + + @Min(value = 18, message = "Age should not be less than 18") + private int age; + + @NotBlank(message = "Phone must not be blank") + private String phone; + + @Valid + @NotNull(message = "UserAddress must not be blank") + private UserAddress useraddress; + + public UserAddress getUseraddress() { + return useraddress; + } + + public void setUseraddress(UserAddress useraddress) { + this.useraddress = useraddress; + } + + public UserAccount() { + + } + + public UserAccount(String email, String password, String name, int age) { + this.password = password; + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + +} diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAddress.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAddress.java new file mode 100644 index 0000000000..cd149dc5f5 --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAddress.java @@ -0,0 +1,18 @@ +package com.baeldung.spring.servicevalidation.domain; + +import javax.validation.constraints.NotBlank; + +public class UserAddress { + + @NotBlank + private String countryCode; + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + +} diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java new file mode 100644 index 0000000000..9d79e4e226 --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java @@ -0,0 +1,41 @@ +package com.baeldung.spring.servicevalidation.service; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.baeldung.spring.servicevalidation.dao.UserAccountDao; +import com.baeldung.spring.servicevalidation.domain.UserAccount; + +@Service +public class UserAccountService { + + @Autowired + private Validator validator; + + @Autowired + private UserAccountDao dao; + + public String addUserAccount(UserAccount useraccount) { + + Set> violations = validator.validate(useraccount); + + if (!violations.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (ConstraintViolation constraintViolation : violations) { + sb.append(constraintViolation.getMessage()); + } + + throw new ConstraintViolationException("Error occurred: " + sb.toString(), violations); + } + + dao.addUserAccount(useraccount); + return "Account for " + useraccount.getName() + " Added!"; + } + +} diff --git a/spring-boot-modules/spring-boot-validation/src/test/java/com/baeldung/SpringContextTest.java b/spring-boot-modules/spring-boot-validation/src/test/java/com/baeldung/SpringContextTest.java new file mode 100644 index 0000000000..f4076a7756 --- /dev/null +++ b/spring-boot-modules/spring-boot-validation/src/test/java/com/baeldung/SpringContextTest.java @@ -0,0 +1,14 @@ +package com.baeldung; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import com.baeldung.spring.servicevalidation.SpringServiceLayerValidationApp; + +@SpringBootTest(classes = SpringServiceLayerValidationApp.class) +public class SpringContextTest { + + @Test + public void whenSpringContextIsBootstrapped_thenNoExceptions() { + } +} diff --git a/spring-boot-modules/spring-boot/src/main/java/com/baeldung/common/error/MyCustomErrorController.java b/spring-boot-modules/spring-boot/src/main/java/com/baeldung/common/error/MyCustomErrorController.java index 373ae8f745..55543b0ba7 100644 --- a/spring-boot-modules/spring-boot/src/main/java/com/baeldung/common/error/MyCustomErrorController.java +++ b/spring-boot-modules/spring-boot/src/main/java/com/baeldung/common/error/MyCustomErrorController.java @@ -7,18 +7,9 @@ public class MyCustomErrorController implements ErrorController { private static final String PATH = "/error"; - public MyCustomErrorController() { - // TODO Auto-generated constructor stub - } - @GetMapping(value = PATH) public String error() { return "Error haven"; } - @Override - public String getErrorPath() { - return PATH; - } - } diff --git a/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java b/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java new file mode 100644 index 0000000000..42bf50ac5f --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java @@ -0,0 +1,29 @@ +package com.baeldung.spring; + +import org.springframework.context.annotation.Bean; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; +import org.springframework.oxm.xstream.XStreamMarshaller; + +/** + * Another possibility is to create a bean which will be automatically added to the Spring Boot Autoconfigurations. + * + * ATTENTION: Multiple converter registration of the same type most likely causes problem (serialize twice, etc.) + * Therefore, be sure to remove manually added XML message converter first then uncomment + * this @{@link org.springframework.context.annotation.Configuration} to use + */ +//@Configuration +public class ConverterExtensionsConfig { + + @Bean + public HttpMessageConverter createXmlHttpMessageConverter() { + final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); + + final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); + xmlConverter.setMarshaller(xstreamMarshaller); + xmlConverter.setUnmarshaller(xstreamMarshaller); + + return xmlConverter; + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java b/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java index 13a9933fa6..69724fda29 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java +++ b/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java @@ -5,7 +5,6 @@ import java.util.List; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportResource; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; @@ -16,35 +15,24 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { - // @Override - // public void configureMessageConverters(final List> messageConverters) { - // messageConverters.add(new MappingJackson2HttpMessageConverter()); - // messageConverters.add(createXmlHttpMessageConverter()); - // } - // - // private HttpMessageConverter createXmlHttpMessageConverter() { - // final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); - // - // final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); - // xstreamMarshaller.setAutodetectAnnotations(true); - // xmlConverter.setMarshaller(xstreamMarshaller); - // xmlConverter.setUnmarshaller(xstreamMarshaller); - // - // return xmlConverter; - // } + @Override + public void configureMessageConverters(final List> messageConverters) { + messageConverters.add(new MappingJackson2HttpMessageConverter()); + messageConverters.add(createXmlHttpMessageConverter()); + } - // Another possibility is to create a bean which will be automatically added to the Spring Boot Autoconfigurations - // @Bean - // public HttpMessageConverter createXmlHttpMessageConverter() { - // final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); - // - // final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); - // xstreamMarshaller.setAutodetectAnnotations(true); - // xmlConverter.setMarshaller(xstreamMarshaller); - // xmlConverter.setUnmarshaller(xstreamMarshaller); - // - // return xmlConverter; - // } + /** + * There is another possibility to add a message converter, see {@link ConverterExtensionsConfig} + */ + private HttpMessageConverter createXmlHttpMessageConverter() { + final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); + + final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); + xmlConverter.setMarshaller(xstreamMarshaller); + xmlConverter.setUnmarshaller(xstreamMarshaller); + + return xmlConverter; + } // Etags @@ -52,16 +40,17 @@ public class WebConfig implements WebMvcConfigurer { // AbstractAnnotationConfigDispatcherServletInitializer#getServletFilters @Bean public FilterRegistrationBean shallowEtagHeaderFilter() { - FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>( new ShallowEtagHeaderFilter()); + FilterRegistrationBean filterRegistrationBean = + new FilterRegistrationBean<>(new ShallowEtagHeaderFilter()); filterRegistrationBean.addUrlPatterns("/foos/*"); filterRegistrationBean.setName("etagFilter"); return filterRegistrationBean; } - + // We can also just declare the filter directly // @Bean // public ShallowEtagHeaderFilter shallowEtagHeaderFilter() { // return new ShallowEtagHeaderFilter(); // } -} \ No newline at end of file +} diff --git a/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java index ecf938be50..6e50f828de 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java @@ -24,6 +24,7 @@ import com.google.common.net.HttpHeaders; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.Response; +import org.springframework.http.MediaType; public abstract class AbstractBasicLiveTest extends AbstractLiveTest { @@ -36,7 +37,7 @@ public abstract class AbstractBasicLiveTest extends Abst @Test public void whenResourcesAreRetrievedPaged_then200IsReceived() { create(); - + final Response response = RestAssured.get(getURL() + "?page=0&size=10"); assertThat(response.getStatusCode(), is(200)); @@ -54,7 +55,8 @@ public abstract class AbstractBasicLiveTest extends Abst public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() { create(); - final Response response = RestAssured.get(getURL() + "?page=0&size=10"); + final Response response = RestAssured.given() + .accept(MediaType.APPLICATION_JSON_VALUE).get(getURL() + "?page=0&size=10"); assertFalse(response.body().as(List.class).isEmpty()); } @@ -64,7 +66,7 @@ public abstract class AbstractBasicLiveTest extends Abst create(); create(); create(); - + final Response response = RestAssured.get(getURL() + "?page=0&size=2"); final String uriToNextPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "next"); @@ -95,7 +97,7 @@ public abstract class AbstractBasicLiveTest extends Abst create(); create(); create(); - + final Response first = RestAssured.get(getURL() + "?page=0&size=2"); final String uriToLastPage = extractURIByRel(first.getHeader(HttpHeaders.LINK), "last"); @@ -104,7 +106,7 @@ public abstract class AbstractBasicLiveTest extends Abst final String uriToNextPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "next"); assertNull(uriToNextPage); } - + // etags @Test diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java index 3300b91fde..b1a84b47a7 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java @@ -1,21 +1,22 @@ package com.baeldung.web; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.baeldung.persistence.dao.IFooDao; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; - /** - * - * We'll start the whole context, but not the server. We'll mock the REST calls instead. - * + * We'll start the whole context, but not the server. We'll mock the REST calls instead. */ @RunWith(SpringRunner.class) @SpringBootTest @@ -25,12 +26,22 @@ public class FooControllerAppIntegrationTest { @Autowired private MockMvc mockMvc; + @Autowired + private IFooDao fooDao; + + @Before + public void setup() { + this.fooDao.deleteAll(); + } + @Test public void whenFindPaginatedRequest_thenEmptyResponse() throws Exception { - this.mockMvc.perform(get("/foos").param("page", "0") - .param("size", "2")) - .andExpect(status().isOk()) - .andExpect(content().json("[]")); + this.mockMvc.perform(get("/foos") + .param("page", "0") + .param("size", "2") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().json("[]")); } } diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java index 9e7b60ed8c..e472d308e8 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java @@ -40,7 +40,7 @@ public class FooControllerCustomEtagIntegrationTest { private static String createFooJson() throws Exception { return serializeFoo(new Foo(randomAlphabetic(6))); } - + private static Foo deserializeFoo(String fooJson) throws Exception { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(fooJson, Foo.class); @@ -97,7 +97,8 @@ public class FooControllerCustomEtagIntegrationTest { .getResponse() .getHeader(HttpHeaders.LOCATION); ResultActions findOneResponse = this.mvc - .perform(get(createdResourceUri + CUSTOM_ETAG_ENDPOINT_SUFFIX).contentType(MediaType.APPLICATION_JSON)); + .perform(get(createdResourceUri + CUSTOM_ETAG_ENDPOINT_SUFFIX) + .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)); String etag = findOneResponse.andReturn().getResponse().getHeader(HttpHeaders.ETAG); Foo createdFoo = deserializeFoo(findOneResponse.andReturn().getResponse().getContentAsString()); createdFoo.setName("updated name"); diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java index 4d4a274b1a..070625b7d4 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java @@ -20,6 +20,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; +import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; @@ -30,7 +31,7 @@ import com.baeldung.web.exception.CustomException1; import com.baeldung.web.hateoas.event.PaginatedResultsRetrievedEvent; /** - * + * * We'll start only the web layer. * */ @@ -54,20 +55,22 @@ public class FooControllerWebLayerIntegrationTest { doNothing().when(publisher) .publishEvent(any(PaginatedResultsRetrievedEvent.class)); - this.mockMvc.perform(get("/foos").param("page", "0") - .param("size", "2")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.hasSize(1))); + this.mockMvc.perform(get("/foos") + .param("page", "0") + .param("size", "2") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.hasSize(1))); } - + @Test public void delete_forException_fromService() throws Exception { Mockito.when(service.findAll()).thenThrow(new CustomException1()); this.mockMvc.perform(get("/foos")).andDo(h -> { final Exception expectedException = h.getResolvedException(); Assert.assertTrue(expectedException instanceof CustomException1); - + }); } - + } diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java new file mode 100644 index 0000000000..9b1a9e9733 --- /dev/null +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java @@ -0,0 +1,149 @@ +package com.baeldung.web; + +import static com.baeldung.Consts.APPLICATION_PORT; +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import com.baeldung.common.web.AbstractLiveTest; +import com.baeldung.persistence.model.Foo; +import com.baeldung.spring.ConfigIntegrationTest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; +import org.springframework.oxm.xstream.XStreamMarshaller; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.web.client.RestTemplate; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { ConfigIntegrationTest.class }, loader = AnnotationConfigContextLoader.class) +@ActiveProfiles("test") +public class FooMessageConvertersLiveTest extends AbstractLiveTest { + + private static final String BASE_URI = "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest/"; + + public FooMessageConvertersLiveTest() { + super(Foo.class); + } + + @Override + public final void create() { + create(new Foo(randomAlphabetic(6))); + } + + @Override + public final String createAsUri() { + return createAsUri(new Foo(randomAlphabetic(6))); + } + + @Before + public void setup(){ + create(); + } + + /** + * Without specifying Accept Header, uses the default response from the + * server (in this case json) + */ + @Test + public void whenRetrievingAFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + final Foo resource = restTemplate.getForObject(URI, Foo.class, "1"); + + assertThat(resource, notNullValue()); + } + + @Test + public void givenConsumingXml_whenReadingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getXmlMessageConverters()); + + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML)); + final HttpEntity entity = new HttpEntity<>(headers); + + final ResponseEntity + response = restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1"); + final Foo resource = response.getBody(); + + assertThat(resource, notNullValue()); + } + + private List> getXmlMessageConverters() { + final XStreamMarshaller marshaller = new XStreamMarshaller(); + marshaller.setAnnotatedClasses(Foo.class); + final MarshallingHttpMessageConverter marshallingConverter = new MarshallingHttpMessageConverter(marshaller); + + final List> converters = new ArrayList<>(); + converters.add(marshallingConverter); + return converters; + } + + @Test + public void givenConsumingJson_whenReadingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getJsonMessageConverters()); + + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + final HttpEntity entity = new HttpEntity(headers); + + final ResponseEntity response = restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1"); + final Foo resource = response.getBody(); + + assertThat(resource, notNullValue()); + } + + private List> getJsonMessageConverters() { + final List> converters = new ArrayList<>(); + converters.add(new MappingJackson2HttpMessageConverter()); + return converters; + } + + @Test + public void givenConsumingXml_whenWritingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos"; + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getJsonAndXmlMessageConverters()); + + final Foo resource = new Foo("jason"); + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType((MediaType.APPLICATION_XML)); + final HttpEntity entity = new HttpEntity<>(resource, headers); + + final ResponseEntity response = restTemplate.exchange(URI, HttpMethod.POST, entity, Foo.class); + final Foo fooResponse = response.getBody(); + + assertThat(fooResponse, notNullValue()); + assertEquals(resource.getName(), fooResponse.getName()); + } + + private List> getJsonAndXmlMessageConverters() { + final List> converters = getJsonMessageConverters(); + converters.addAll(getXmlMessageConverters()); + return converters; + } + +} diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java index 6a365f3bd5..242fbb609e 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java @@ -11,6 +11,7 @@ import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -43,12 +44,12 @@ public class FooPageableLiveTest extends AbstractBasicLiveTest { public final String createAsUri() { return createAsUri(new Foo(randomAlphabetic(6))); } - + @Override @Test public void whenResourcesAreRetrievedPaged_then200IsReceived() { this.create(); - + final Response response = RestAssured.get(getPageableURL() + "?page=0&size=10"); assertThat(response.getStatusCode(), is(200)); @@ -68,13 +69,15 @@ public class FooPageableLiveTest extends AbstractBasicLiveTest { public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() { create(); - final Response response = RestAssured.get(getPageableURL() + "?page=0&size=10"); + final Response response = RestAssured.given() + .accept(MediaType.APPLICATION_JSON_VALUE) + .get(getPageableURL() + "?page=0&size=10"); assertFalse(response.body().as(List.class).isEmpty()); } protected String getPageableURL() { - return "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest/foos/pageable"; + return getURL() + "/pageable"; } - + } diff --git a/spring-cloud-bus/pom.xml b/spring-cloud-bus/pom.xml index 938d6d4bb8..88bb766047 100644 --- a/spring-cloud-bus/pom.xml +++ b/spring-cloud-bus/pom.xml @@ -34,7 +34,7 @@ - 2020.0.0 + 2020.0.3 \ No newline at end of file diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml index 83e7175869..76320fe54a 100644 --- a/spring-cloud/pom.xml +++ b/spring-cloud/pom.xml @@ -79,13 +79,13 @@ - 2020.0.1 + 2020.0.3 2.2.3.RELEASE 2.2.3.RELEASE 1.4.7.RELEASE 1.4.7.RELEASE 1.4.7.RELEASE - 3.0.6.RELEASE + 3.1.3 diff --git a/spring-cloud/spring-cloud-archaius/pom.xml b/spring-cloud/spring-cloud-archaius/pom.xml index 56d7f0bc93..9edd5d24c5 100644 --- a/spring-cloud/spring-cloud-archaius/pom.xml +++ b/spring-cloud/spring-cloud-archaius/pom.xml @@ -60,7 +60,7 @@ - 2.0.1.RELEASE + 2.0.3.RELEASE 1.2.0 diff --git a/spring-cloud/spring-cloud-bootstrap/order-service/pom.xml b/spring-cloud/spring-cloud-bootstrap/order-service/pom.xml index 64be52f5f4..273017c386 100644 --- a/spring-cloud/spring-cloud-bootstrap/order-service/pom.xml +++ b/spring-cloud/spring-cloud-bootstrap/order-service/pom.xml @@ -117,6 +117,6 @@ 1.8 1.8 com.baeldung.orderservice.OrderApplication - + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/client/pom.xml b/spring-cloud/spring-cloud-config/client/pom.xml index 00b5ba2edc..55c6e77a72 100644 --- a/spring-cloud/spring-cloud-config/client/pom.xml +++ b/spring-cloud/spring-cloud-config/client/pom.xml @@ -47,4 +47,5 @@ + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/pom.xml b/spring-cloud/spring-cloud-config/pom.xml index 1e46b9accb..4eda3dda0a 100644 --- a/spring-cloud/spring-cloud-config/pom.xml +++ b/spring-cloud/spring-cloud-config/pom.xml @@ -34,7 +34,7 @@ - 2020.0.0 + 2020.0.3 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/server/pom.xml b/spring-cloud/spring-cloud-config/server/pom.xml index c59b6583f2..36d1b5b3aa 100644 --- a/spring-cloud/spring-cloud-config/server/pom.xml +++ b/spring-cloud/spring-cloud-config/server/pom.xml @@ -51,4 +51,5 @@ + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-contract/pom.xml b/spring-cloud/spring-cloud-contract/pom.xml index 1d12656f98..8546e76586 100644 --- a/spring-cloud/spring-cloud-contract/pom.xml +++ b/spring-cloud/spring-cloud-contract/pom.xml @@ -31,7 +31,6 @@ spring-boot-starter-data-rest ${spring-boot.version} - org.springframework.cloud spring-cloud-contract-wiremock diff --git a/spring-cloud/spring-cloud-dapr/README.md b/spring-cloud/spring-cloud-dapr/README.md new file mode 100644 index 0000000000..8f6ccfd9b6 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [An Intro to Dapr with Spring Cloud Gateway](https://www.baeldung.com/dapr-spring-cloud-gateway) diff --git a/spring-cloud/spring-cloud-dapr/dapr-config/basic-config.yaml b/spring-cloud/spring-cloud-dapr/dapr-config/basic-config.yaml new file mode 100644 index 0000000000..d80655f0a2 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/dapr-config/basic-config.yaml @@ -0,0 +1,5 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: daprConfig +spec: {} diff --git a/spring-cloud/spring-cloud-dapr/dapr-config/consul-config.yaml b/spring-cloud/spring-cloud-dapr/dapr-config/consul-config.yaml new file mode 100644 index 0000000000..638af7cf0d --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/dapr-config/consul-config.yaml @@ -0,0 +1,9 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: daprConfig +spec: + nameResolution: + component: "consul" + configuration: + selfRegister: true diff --git a/spring-cloud/spring-cloud-dapr/dapr-config/consul-zipkin-config.yaml b/spring-cloud/spring-cloud-dapr/dapr-config/consul-zipkin-config.yaml new file mode 100644 index 0000000000..8e50decf03 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/dapr-config/consul-zipkin-config.yaml @@ -0,0 +1,13 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: daprConfig +spec: + nameResolution: + component: "consul" + configuration: + selfRegister: true + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://localhost:9411/api/v2/spans" diff --git a/spring-cloud/spring-cloud-dapr/gateway/pom.xml b/spring-cloud/spring-cloud-dapr/gateway/pom.xml new file mode 100644 index 0000000000..13c1556cfe --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/gateway/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + com.baeldung.spring.cloud.spring-cloud-dapr + gateway + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.5.2 + + + + 11 + 11 + + + + + org.springframework.cloud + spring-cloud-dependencies + 2020.0.3 + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java b/spring-cloud/spring-cloud-dapr/gateway/src/main/java/org/example/gateway/GatewayApp.java similarity index 62% rename from spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java rename to spring-cloud/spring-cloud-dapr/gateway/src/main/java/org/example/gateway/GatewayApp.java index 094d95b93f..7166b5b31f 100644 --- a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java +++ b/spring-cloud/spring-cloud-dapr/gateway/src/main/java/org/example/gateway/GatewayApp.java @@ -1,13 +1,11 @@ -package com.example.demo; +package org.example.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class DemoApplication { - +public class GatewayApp { public static void main(String[] args) { - SpringApplication.run(DemoApplication.class, args); + SpringApplication.run(GatewayApp.class, args); } - } diff --git a/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-no-dapr.yml b/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-no-dapr.yml new file mode 100644 index 0000000000..f25d7f0128 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-no-dapr.yml @@ -0,0 +1,14 @@ +server: + port: 3000 + +spring: + cloud: + gateway: + routes: + - id: greeting-service + uri: http://localhost:3001/ + predicates: + - Path=/** + filters: + - RewritePath=/?(?.*), /$\{segment} + diff --git a/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-with-dapr.yml b/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-with-dapr.yml new file mode 100644 index 0000000000..ee67c76339 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-with-dapr.yml @@ -0,0 +1,13 @@ +server: + port: 3000 + +spring: + cloud: + gateway: + routes: + - id: greeting-service + uri: http://localhost:4000/ + predicates: + - Path=/** + filters: + - RewritePath=//?(?.*), /v1.0/invoke/greeting/method/$\{segment} diff --git a/spring-cloud/spring-cloud-dapr/greeting/pom.xml b/spring-cloud/spring-cloud-dapr/greeting/pom.xml new file mode 100644 index 0000000000..58de7240a2 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/greeting/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + com.baeldung.spring.cloud.spring-cloud-dapr + greeting + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.5.2 + + + + 11 + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingApp.java b/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingApp.java new file mode 100644 index 0000000000..2feca49daa --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingApp.java @@ -0,0 +1,11 @@ +package org.example.hello; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class GreetingApp { + public static void main(String[] args) { + SpringApplication.run(GreetingApp.class, args); + } +} diff --git a/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingController.java b/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingController.java new file mode 100644 index 0000000000..5210add4e0 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingController.java @@ -0,0 +1,22 @@ +package org.example.hello; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class GreetingController { + @GetMapping(value = "/") + public String getGreeting() { + return "Welcome to the greeting service."; + } + + @GetMapping(value = "/hello") + public String getHello() { + return "Hello world!"; + } + + @GetMapping(value = "/goodbye") + public String getGoodbye() { + return "Goodbye, cruel world!"; + } +} diff --git a/spring-cloud/spring-cloud-dapr/greeting/src/main/resources/application.yml b/spring-cloud/spring-cloud-dapr/greeting/src/main/resources/application.yml new file mode 100644 index 0000000000..7147f91d26 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/greeting/src/main/resources/application.yml @@ -0,0 +1,2 @@ +server: + port: 3001 diff --git a/spring-cloud/spring-cloud-dapr/pom.xml b/spring-cloud/spring-cloud-dapr/pom.xml new file mode 100644 index 0000000000..76d3cbdaf8 --- /dev/null +++ b/spring-cloud/spring-cloud-dapr/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + spring-cloud-dapr + pom + + + com.baeldung.spring.cloud + spring-cloud + 1.0.0-SNAPSHOT + + + + gateway + greeting + + diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client/src/main/java/com/baeldung/spring/cloud/feign/client/NoFeignClientController.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client/src/main/java/com/baeldung/spring/cloud/feign/client/NoFeignClientController.java new file mode 100644 index 0000000000..89221943f6 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client/src/main/java/com/baeldung/spring/cloud/feign/client/NoFeignClientController.java @@ -0,0 +1,41 @@ +package com.baeldung.spring.cloud.feign.client; + +import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; + +@Controller +public class NoFeignClientController { + + private static final String SERVICE_NAME = "spring-cloud-eureka-client"; + + @Autowired + private EurekaClient eurekaClient; + + @RequestMapping("/get-greeting-no-feign") + public String greeting(Model model) { + + InstanceInfo service = eurekaClient + .getApplication(SERVICE_NAME) + .getInstances() + .get(0); + + String hostName = service.getHostName(); + int port = service.getPort(); + + URI url = URI.create("http://" + hostName + ":" + port + "/greeting"); + + ResponseEntity response = new RestTemplate().getForEntity(url, String.class); + + model.addAttribute("greeting", response.getBody()); + + return "greeting-view"; + } +} diff --git a/spring-cloud/spring-cloud-kubernetes/pom.xml b/spring-cloud/spring-cloud-kubernetes/pom.xml index 1a3e3826d4..d8894eb36f 100644 --- a/spring-cloud/spring-cloud-kubernetes/pom.xml +++ b/spring-cloud/spring-cloud-kubernetes/pom.xml @@ -38,6 +38,7 @@ - 2020.0.1 + 2020.0.3 + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-netflix-feign/pom.xml b/spring-cloud/spring-cloud-netflix-feign/pom.xml index 6180ee7575..8ff09d3070 100644 --- a/spring-cloud/spring-cloud-netflix-feign/pom.xml +++ b/spring-cloud/spring-cloud-netflix-feign/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung.cloud - spring-cloud-netlix-feign + spring-cloud-netflix-feign 0.0.1-SNAPSHOT spring-cloud-netflix-feign Netflix Feign project for Spring Boot diff --git a/spring-cloud/spring-cloud-openfeign/README.md b/spring-cloud/spring-cloud-openfeign/README.md index bcfd769d0c..5d3dc060c7 100644 --- a/spring-cloud/spring-cloud-openfeign/README.md +++ b/spring-cloud/spring-cloud-openfeign/README.md @@ -3,3 +3,4 @@ - [Introduction to Spring Cloud OpenFeign](https://www.baeldung.com/spring-cloud-openfeign) - [Differences Between Netflix Feign and OpenFeign](https://www.baeldung.com/netflix-feign-vs-openfeign) - [File Upload With Open Feign](https://www.baeldung.com/java-feign-file-upload) +- [Feign Logging Configuration](https://www.baeldung.com/java-feign-logging) diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java new file mode 100644 index 0000000000..9416bd30f0 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java @@ -0,0 +1,13 @@ +package com.baeldung.cloud.openfeign.client; + +import com.baeldung.cloud.openfeign.config.FeignConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@FeignClient(name = "user-client", url="https://jsonplaceholder.typicode.com", configuration = FeignConfig.class) +public interface UserClient { + + @RequestMapping(value = "/users", method = RequestMethod.GET) + String getUsers(); +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java new file mode 100644 index 0000000000..d51e97b9e4 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java @@ -0,0 +1,12 @@ +package com.baeldung.cloud.openfeign.config; + +import feign.Logger; +import org.springframework.context.annotation.Bean; + +public class FeignConfig { + + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-books-api/pom.xml b/spring-cloud/spring-cloud-rest/spring-cloud-rest-books-api/pom.xml index 2fa003a634..c7ff472655 100644 --- a/spring-cloud/spring-cloud-rest/spring-cloud-rest-books-api/pom.xml +++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-books-api/pom.xml @@ -75,4 +75,4 @@ - + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-client/pom.xml b/spring-cloud/spring-cloud-ribbon-client/pom.xml index 77c25b2f6b..2c2bce4bcd 100644 --- a/spring-cloud/spring-cloud-ribbon-client/pom.xml +++ b/spring-cloud/spring-cloud-ribbon-client/pom.xml @@ -47,7 +47,7 @@ - 2020.0.1 + 2020.0.3 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/pom.xml b/spring-cloud/spring-cloud-security/pom.xml index 0227989f99..d9072af91b 100644 --- a/spring-cloud/spring-cloud-security/pom.xml +++ b/spring-cloud/spring-cloud-security/pom.xml @@ -34,7 +34,7 @@ - 2020.0.1 + 2020.0.3 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-sentinel/pom.xml b/spring-cloud/spring-cloud-sentinel/pom.xml index a36dcc51bc..f26a13f6f8 100644 --- a/spring-cloud/spring-cloud-sentinel/pom.xml +++ b/spring-cloud/spring-cloud-sentinel/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 spring-cloud-sentinel spring-cloud-sentinel diff --git a/spring-cloud/spring-cloud-task/pom.xml b/spring-cloud/spring-cloud-task/pom.xml index bf43799d2d..0cfc62bdbb 100644 --- a/spring-cloud/spring-cloud-task/pom.xml +++ b/spring-cloud/spring-cloud-task/pom.xml @@ -48,8 +48,8 @@ - Hoxton.SR4 - 2.2.3.RELEASE + 2020.0.3 + 2.3.3 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml index cfee3ce656..0ba93282b9 100644 --- a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml +++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml @@ -42,6 +42,7 @@ org.springframework.cloud spring-cloud-starter-netflix-zuul + ${spring-cloud-netflix-zuul.version} org.springframework.boot diff --git a/spring-cloud/spring-cloud-zuul-fallback/pom.xml b/spring-cloud/spring-cloud-zuul-fallback/pom.xml index 1c426503e5..1aa29e867d 100644 --- a/spring-cloud/spring-cloud-zuul-fallback/pom.xml +++ b/spring-cloud/spring-cloud-zuul-fallback/pom.xml @@ -21,7 +21,8 @@ - Finchley.SR2 + 2020.0.3 + 2.2.7.RELEASE 3.1.1 diff --git a/spring-cloud/spring-cloud-zuul/pom.xml b/spring-cloud/spring-cloud-zuul/pom.xml index fc3a127e90..0590774bef 100644 --- a/spring-cloud/spring-cloud-zuul/pom.xml +++ b/spring-cloud/spring-cloud-zuul/pom.xml @@ -81,8 +81,8 @@ - 2020.0.0 - 2.2.2.RELEASE + 2020.0.3 + 2.2.7.RELEASE \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zuul/spring-zuul-rate-limiting/pom.xml b/spring-cloud/spring-cloud-zuul/spring-zuul-rate-limiting/pom.xml index e09fcd3711..d2b28643fa 100644 --- a/spring-cloud/spring-cloud-zuul/spring-zuul-rate-limiting/pom.xml +++ b/spring-cloud/spring-cloud-zuul/spring-zuul-rate-limiting/pom.xml @@ -42,6 +42,7 @@ 2.2.0.RELEASE + 2.4.7 \ No newline at end of file diff --git a/spring-data-rest/src/main/java/com/baeldung/books/config/RestConfig.java b/spring-data-rest/src/main/java/com/baeldung/books/config/RestConfig.java index 1c6f04331e..8d9ab5952c 100644 --- a/spring-data-rest/src/main/java/com/baeldung/books/config/RestConfig.java +++ b/spring-data-rest/src/main/java/com/baeldung/books/config/RestConfig.java @@ -2,18 +2,19 @@ package com.baeldung.books.config; import com.baeldung.books.models.WebsiteUser; import com.baeldung.books.projections.CustomBook; - import org.springframework.context.annotation.Configuration; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.mapping.ExposureConfiguration; import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer; import org.springframework.http.HttpMethod; +import org.springframework.web.servlet.config.annotation.CorsRegistry; @Configuration public class RestConfig implements RepositoryRestConfigurer { @Override - public void configureRepositoryRestConfiguration(RepositoryRestConfiguration repositoryRestConfiguration) { + public void configureRepositoryRestConfiguration(RepositoryRestConfiguration repositoryRestConfiguration, + CorsRegistry cors) { repositoryRestConfiguration.getProjectionConfiguration().addProjection(CustomBook.class); ExposureConfiguration config = repositoryRestConfiguration.getExposureConfiguration(); config.forDomainType(WebsiteUser.class).withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PATCH)); diff --git a/spring-ejb/ejb-beans/pom.xml b/spring-ejb/ejb-beans/pom.xml index cdc28ef5d7..d820819a78 100644 --- a/spring-ejb/ejb-beans/pom.xml +++ b/spring-ejb/ejb-beans/pom.xml @@ -94,7 +94,6 @@ test - diff --git a/spring-jenkins-pipeline/pom.xml b/spring-jenkins-pipeline/pom.xml index f2fd4191fd..6f00dd5820 100644 --- a/spring-jenkins-pipeline/pom.xml +++ b/spring-jenkins-pipeline/pom.xml @@ -81,7 +81,7 @@ 2.17 - 2.0.0 + 3.0.0 \ No newline at end of file diff --git a/spring-kafka/README.md b/spring-kafka/README.md index ddb086c3bd..2b71beaac9 100644 --- a/spring-kafka/README.md +++ b/spring-kafka/README.md @@ -6,6 +6,8 @@ This module contains articles about Spring with Kafka - [Intro to Apache Kafka with Spring](https://www.baeldung.com/spring-kafka) - [Testing Kafka and Spring Boot](https://www.baeldung.com/spring-boot-kafka-testing) +- [Monitor the Consumer Lag in Apache Kafka](https://www.baeldung.com/java-kafka-consumer-lag) +- [Send Large Messages With Kafka](https://www.baeldung.com/java-kafka-send-large-message) ### Intro diff --git a/spring-kafka/pom.xml b/spring-kafka/pom.xml index 235dd75966..2db62044b2 100644 --- a/spring-kafka/pom.xml +++ b/spring-kafka/pom.xml @@ -40,11 +40,16 @@ ${testcontainers-kafka.version} test + + commons-collections + commons-collections + 3.2.1 + - 2.5.8.RELEASE - 1.15.0 + 2.7.2 + 1.15.3 \ No newline at end of file diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/LagAnalyzerApplication.java b/spring-kafka/src/main/java/com/baeldung/monitoring/LagAnalyzerApplication.java new file mode 100644 index 0000000000..9275151d50 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/LagAnalyzerApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.monitoring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication +@EnableScheduling +public class LagAnalyzerApplication { + + public static void main(String[] args) { + SpringApplication.run(LagAnalyzerApplication.class, args); + while (true) ; + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/Makefile b/spring-kafka/src/main/java/com/baeldung/monitoring/Makefile new file mode 100644 index 0000000000..f85334057d --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/Makefile @@ -0,0 +1,10 @@ +build: + mvn clean install -B -U +start-kafka: + docker-compose up -d +check-kafka: + nc -z localhost 2181 + nc -z localhost 9092 + docker-compose logs kafka | grep -i 'started' +stop-kafka: + docker-compose down --remove-orphans diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/README.md b/spring-kafka/src/main/java/com/baeldung/monitoring/README.md new file mode 100644 index 0000000000..1b3f638ae2 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/README.md @@ -0,0 +1,16 @@ +## Monitoring Consumer Lag + +## Spin Up Local Kafka Container +``` +$ make start-kafka +``` + +## Verify that Kafka is Up +``` +$ make check-kafka +``` + +## Stop Local Kafka Container +``` +$ make stop-kafka +``` diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/docker-compose.yml b/spring-kafka/src/main/java/com/baeldung/monitoring/docker-compose.yml new file mode 100644 index 0000000000..507b8e2f3c --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/docker-compose.yml @@ -0,0 +1,23 @@ +version: '2' +services: + zookeeper: + image: confluentinc/cp-zookeeper:latest + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + ports: + - 22181:2181 + + kafka: + image: confluentinc/cp-kafka:latest + depends_on: + - zookeeper + ports: + - 9092:9092 + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 \ No newline at end of file diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/service/LagAnalyzerService.java b/spring-kafka/src/main/java/com/baeldung/monitoring/service/LagAnalyzerService.java new file mode 100644 index 0000000000..b046f0b2c4 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/service/LagAnalyzerService.java @@ -0,0 +1,105 @@ +package com.baeldung.monitoring.service; + +import com.baeldung.monitoring.util.MonitoringUtil; +import org.apache.kafka.clients.admin.AdminClient; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.ListConsumerGroupOffsetsResult; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.consumer.OffsetAndMetadata; +import org.apache.kafka.common.TopicPartition; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.concurrent.ExecutionException; + +@Service +public class LagAnalyzerService { + + private static final Logger LOGGER = LoggerFactory.getLogger(LagAnalyzerService.class); + + private final AdminClient adminClient; + private final KafkaConsumer consumer; + + @Autowired + public LagAnalyzerService( + @Value("${monitor.kafka.bootstrap.config}") String bootstrapServerConfig) { + adminClient = getAdminClient(bootstrapServerConfig); + consumer = getKafkaConsumer(bootstrapServerConfig); + } + + public Map analyzeLag( + String groupId) + throws ExecutionException, InterruptedException { + Map consumerGrpOffsets = getConsumerGrpOffsets(groupId); + Map producerOffsets = getProducerOffsets(consumerGrpOffsets); + Map lags = computeLags(consumerGrpOffsets, producerOffsets); + for (Map.Entry lagEntry : lags.entrySet()) { + String topic = lagEntry.getKey().topic(); + int partition = lagEntry.getKey().partition(); + Long lag = lagEntry.getValue(); + LOGGER.info("Time={} | Lag for topic = {}, partition = {} is {}", + MonitoringUtil.time(), + topic, + partition, + lag); + } + return lags; + } + + public Map getConsumerGrpOffsets(String groupId) + throws ExecutionException, InterruptedException { + ListConsumerGroupOffsetsResult info = adminClient.listConsumerGroupOffsets(groupId); + Map metadataMap + = info.partitionsToOffsetAndMetadata().get(); + Map groupOffset = new HashMap<>(); + for (Map.Entry entry : metadataMap.entrySet()) { + TopicPartition key = entry.getKey(); + OffsetAndMetadata metadata = entry.getValue(); + groupOffset.putIfAbsent(new TopicPartition(key.topic(), key.partition()), metadata.offset()); + } + return groupOffset; + } + + private Map getProducerOffsets( + Map consumerGrpOffset) { + List topicPartitions = new LinkedList<>(); + for (Map.Entry entry : consumerGrpOffset.entrySet()) { + TopicPartition key = entry.getKey(); + topicPartitions.add(new TopicPartition(key.topic(), key.partition())); + } + return consumer.endOffsets(topicPartitions); + } + + public Map computeLags( + Map consumerGrpOffsets, + Map producerOffsets) { + Map lags = new HashMap<>(); + for (Map.Entry entry : consumerGrpOffsets.entrySet()) { + Long producerOffset = producerOffsets.get(entry.getKey()); + Long consumerOffset = consumerGrpOffsets.get(entry.getKey()); + long lag = Math.abs(Math.max(0, producerOffset) - Math.max(0, consumerOffset)); + lags.putIfAbsent(entry.getKey(), lag); + } + return lags; + } + + private AdminClient getAdminClient(String bootstrapServerConfig) { + Properties config = new Properties(); + config.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServerConfig); + return AdminClient.create(config); + } + + private KafkaConsumer getKafkaConsumer(String bootstrapServerConfig) { + Properties properties = new Properties(); + properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServerConfig); + properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + return new KafkaConsumer<>(properties); + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/service/LiveLagAnalyzerService.java b/spring-kafka/src/main/java/com/baeldung/monitoring/service/LiveLagAnalyzerService.java new file mode 100644 index 0000000000..a20b9e9a0c --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/service/LiveLagAnalyzerService.java @@ -0,0 +1,28 @@ +package com.baeldung.monitoring.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.util.concurrent.ExecutionException; + +@Service +public class LiveLagAnalyzerService { + + private final LagAnalyzerService lagAnalyzerService; + private final String groupId; + + @Autowired + public LiveLagAnalyzerService( + LagAnalyzerService lagAnalyzerService, + @Value(value = "${monitor.kafka.consumer.groupid}") String groupId) { + this.lagAnalyzerService = lagAnalyzerService; + this.groupId = groupId; + } + + @Scheduled(fixedDelay = 5000L) + public void liveLagAnalysis() throws ExecutionException, InterruptedException { + lagAnalyzerService.analyzeLag(groupId); + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ConsumerSimulator.java b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ConsumerSimulator.java new file mode 100644 index 0000000000..2d376432e5 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ConsumerSimulator.java @@ -0,0 +1,17 @@ +package com.baeldung.monitoring.simulation; + +import org.springframework.context.annotation.PropertySource; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; + +@Service +public class ConsumerSimulator { + + @KafkaListener( + topics = "${monitor.topic.name}", + containerFactory = "kafkaListenerContainerFactory", + autoStartup = "${monitor.consumer.simulate}") + public void listenGroup(String message) throws InterruptedException { + Thread.sleep(10L); + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaConsumerConfig.java b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaConsumerConfig.java new file mode 100644 index 0000000000..a4a8847bcf --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaConsumerConfig.java @@ -0,0 +1,54 @@ +package com.baeldung.monitoring.simulation; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; + +import java.util.HashMap; +import java.util.Map; + +@EnableKafka +@Configuration +public class KafkaConsumerConfig { + + @Value(value = "${monitor.kafka.bootstrap.config}") + private String bootstrapAddress; + @Value(value = "${monitor.kafka.consumer.groupid}") + private String groupId; + @Value(value = "${monitor.kafka.consumer.groupid.simulate}") + private String simulateGroupId; + @Value(value = "${monitor.producer.simulate}") + private boolean enabled; + + public ConsumerFactory consumerFactory(String groupId) { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); + if (enabled) { + props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + } else { + props.put(ConsumerConfig.GROUP_ID_CONFIG, simulateGroupId); + } + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 0); + return new DefaultKafkaConsumerFactory<>(props); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + if (enabled) { + factory.setConsumerFactory(consumerFactory(groupId)); + } else { + factory.setConsumerFactory(consumerFactory(simulateGroupId)); + } + return factory; + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaProducerConfig.java b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaProducerConfig.java new file mode 100644 index 0000000000..80048a47ee --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaProducerConfig.java @@ -0,0 +1,29 @@ +package com.baeldung.monitoring.simulation; + +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaProducerConfig { + + @Value("${monitor.kafka.bootstrap.config}") + private String bootstrapAddress; + + @Bean + public KafkaTemplate kafkaTemplate() { + Map configProps = new HashMap<>(); + configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); + configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + DefaultKafkaProducerFactory producerFactory = new DefaultKafkaProducerFactory<>(configProps); + return new KafkaTemplate<>(producerFactory); + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ProducerSimulator.java b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ProducerSimulator.java new file mode 100644 index 0000000000..30476ff7ec --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ProducerSimulator.java @@ -0,0 +1,44 @@ +package com.baeldung.monitoring.simulation; + +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.support.SendResult; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.concurrent.ExecutionException; + +import static com.baeldung.monitoring.util.MonitoringUtil.endTime; +import static com.baeldung.monitoring.util.MonitoringUtil.time; + +@Service +public class ProducerSimulator { + + private final KafkaTemplate kafkaTemplate; + private final String topicName; + private final boolean enabled; + + @Autowired + public ProducerSimulator( + KafkaTemplate kafkaTemplate, + @Value(value = "${monitor.topic.name}") String topicName, + @Value(value = "${monitor.producer.simulate}") String enabled) { + this.kafkaTemplate = kafkaTemplate; + this.topicName = topicName; + this.enabled = BooleanUtils.toBoolean(enabled); + } + + @Scheduled(fixedDelay = 1L, initialDelay = 5L) + public void sendMessage() throws ExecutionException, InterruptedException { + if (enabled) { + if (endTime.after(new Date())) { + String message = "msg-" + time(); + SendResult result = kafkaTemplate.send(topicName, message).get(); + } + } + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/util/MonitoringUtil.java b/spring-kafka/src/main/java/com/baeldung/monitoring/util/MonitoringUtil.java new file mode 100644 index 0000000000..a7ba78b1e3 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/monitoring/util/MonitoringUtil.java @@ -0,0 +1,17 @@ +package com.baeldung.monitoring.util; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +public class MonitoringUtil { + public static final Date startTime = new Date(); + public static final Date endTime = new Date(startTime.getTime() + 30 * 1000); + + public static String time() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); + LocalDateTime now = LocalDateTime.now(); + String date = dtf.format(now); + return date; + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java new file mode 100644 index 0000000000..0af0a4b091 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java @@ -0,0 +1,75 @@ +package com.baeldung.spring.kafka; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@SpringBootApplication +public class KafkaApplicationLongMessage { + + public static void main(String[] args) throws Exception { + + ConfigurableApplicationContext context = SpringApplication.run(KafkaApplicationLongMessage.class, args); + + LongMessageProducer producer = context.getBean(LongMessageProducer.class); + + String fileData = readLongMessage(); + producer.sendMessage(fileData); + + //Deliberate delay to let listener consume produced message before main thread stops + Thread.sleep(5000); + context.close(); + } + + private static String readLongMessage() throws IOException { + String data = ""; + + //update complete location of large message here + data = new String(Files.readAllBytes(Paths.get("RandomTextFile.txt"))); + return data; + } + + @Bean + public LongMessageProducer longMessageProducer() { + return new LongMessageProducer(); + } + + @Bean + public LongMessageListener longMessageListener() { + return new LongMessageListener(); + } + + public static class LongMessageProducer { + + @Autowired + private KafkaTemplate kafkaTemplate; + + @Value(value = "${long.message.topic.name}") + private String topicName; + + public void sendMessage(String message) { + kafkaTemplate.send(topicName, message); + System.out.println("Long message Sent"); + } + + } + + public static class LongMessageListener { + + @KafkaListener(topics = "${long.message.topic.name}", groupId = "longMessage", containerFactory = "longMessageKafkaListenerContainerFactory") + public void listenGroupLongMessage(String message) { + System.out.println("Received Message in group 'longMessage'"); + } + + } + +} diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java index abaa431eec..9495fcf508 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java @@ -27,6 +27,8 @@ public class KafkaConsumerConfig { props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "20971520"); + props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, "20971520"); return new DefaultKafkaConsumerFactory<>(props); } @@ -56,6 +58,11 @@ public class KafkaConsumerConfig { return kafkaListenerContainerFactory("partitions"); } + @Bean + public ConcurrentKafkaListenerContainerFactory longMessageKafkaListenerContainerFactory() { + return kafkaListenerContainerFactory("longMessage"); + } + @Bean public ConcurrentKafkaListenerContainerFactory filterKafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory factory = kafkaListenerContainerFactory("filter"); diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java index 0223bab0fe..9dff81a09d 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java @@ -25,6 +25,8 @@ public class KafkaProducerConfig { configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, "20971520"); + return new DefaultKafkaProducerFactory<>(configProps); } diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java index 00e4147cd0..8a006a72bc 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java @@ -19,6 +19,9 @@ public class KafkaTopicConfig { @Value(value = "${message.topic.name}") private String topicName; + @Value(value = "${long.message.topic.name}") + private String longMsgTopicName; + @Value(value = "${partitioned.topic.name}") private String partitionedTopicName; @@ -54,4 +57,13 @@ public class KafkaTopicConfig { public NewTopic topic4() { return new NewTopic(greetingTopicName, 1, (short) 1); } + + @Bean + public NewTopic topic5() { + NewTopic newTopic = new NewTopic(longMsgTopicName, 1, (short) 1); + Map configs = new HashMap<>(); + configs.put("max.message.bytes", "20971520"); + newTopic.configs(configs); + return newTopic; + } } diff --git a/spring-kafka/src/main/resources/application.properties b/spring-kafka/src/main/resources/application.properties index eaf113191e..e1a983339b 100644 --- a/spring-kafka/src/main/resources/application.properties +++ b/spring-kafka/src/main/resources/application.properties @@ -1,5 +1,16 @@ kafka.bootstrapAddress=localhost:9092 message.topic.name=baeldung +long.message.topic.name=longMessage greeting.topic.name=greeting filtered.topic.name=filtered -partitioned.topic.name=partitioned \ No newline at end of file +partitioned.topic.name=partitioned + +# monitoring - lag analysis +monitor.kafka.bootstrap.config=localhost:9092 +monitor.kafka.consumer.groupid=baeldungGrp +monitor.topic.name=baeldung + +# monitoring - simulation +monitor.producer.simulate=true +monitor.consumer.simulate=true +monitor.kafka.consumer.groupid.simulate=baeldungGrpSimulate \ No newline at end of file diff --git a/spring-kafka/src/test/java/com/baeldung/monitoring/LiveLagAnalyzerServiceLiveTest.java b/spring-kafka/src/test/java/com/baeldung/monitoring/LiveLagAnalyzerServiceLiveTest.java new file mode 100644 index 0000000000..9dcbbc4837 --- /dev/null +++ b/spring-kafka/src/test/java/com/baeldung/monitoring/LiveLagAnalyzerServiceLiveTest.java @@ -0,0 +1,174 @@ +package com.baeldung.monitoring; + +import com.baeldung.monitoring.service.LagAnalyzerService; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.TopicPartition; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.test.context.EmbeddedKafka; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ExecutionException; + +@RunWith(SpringRunner.class) +@DirtiesContext +@EmbeddedKafka(partitions = 1, brokerProperties = {"listeners=PLAINTEXT://localhost:9085", "port=9085"}) +@EnableKafka +public class LiveLagAnalyzerServiceLiveTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(LiveLagAnalyzerServiceLiveTest.class); + + private static KafkaConsumer consumer; + private static KafkaProducer producer; + private static LagAnalyzerService lagAnalyzerService; + private static final String GROUP_ID = "baeldungGrp"; + private static final String TOPIC = "baeldung"; + private static final int PARTITION = 0; + private static final TopicPartition TOPIC_PARTITION = new TopicPartition(TOPIC, PARTITION); + private static final String BOOTSTRAP_SERVER_CONFIG = "localhost:9085"; + private static final int BATCH_SIZE = 100; + private static final long POLL_DURATION = 1000L; + + @Before + public void setup() throws Exception { + initProducer(); + initConsumer(); + lagAnalyzerService = new LagAnalyzerService(BOOTSTRAP_SERVER_CONFIG); + consume(); + } + + @Test + public void givenEmbeddedKafkaBroker_whenAllProducedMessagesAreConsumed_thenLagBecomesZero() + throws ExecutionException, InterruptedException { + produce(); + long consumeLag = 0L; + consume(); + Map lag = lagAnalyzerService.analyzeLag(GROUP_ID); + Assert.assertNotNull(lag); + Assert.assertEquals(1, lag.size()); + consumeLag = lag.get(TOPIC_PARTITION); + Assert.assertEquals(0L, consumeLag); + produce(); + produce(); + lag = lagAnalyzerService.analyzeLag(GROUP_ID); + Assert.assertNotNull(lag); + Assert.assertEquals(1, lag.size()); + consumeLag = lag.get(TOPIC_PARTITION); + Assert.assertEquals(200L, consumeLag); + + produce(); + produce(); + + lag = lagAnalyzerService.analyzeLag(GROUP_ID); + Assert.assertNotNull(lag); + Assert.assertEquals(1, lag.size()); + consumeLag = lag.get(TOPIC_PARTITION); + Assert.assertEquals(400L, consumeLag); + + produce(); + lag = lagAnalyzerService.analyzeLag(GROUP_ID); + Assert.assertNotNull(lag); + Assert.assertEquals(1, lag.size()); + consumeLag = lag.get(TOPIC_PARTITION); + Assert.assertEquals(500L, consumeLag); + + consume(); + lag = lagAnalyzerService.analyzeLag(GROUP_ID); + Assert.assertNotNull(lag); + Assert.assertEquals(1, lag.size()); + consumeLag = lag.get(TOPIC_PARTITION); + Assert.assertEquals(consumeLag, 0L); + } + + @Test + public void givenEmbeddedKafkaBroker_whenMessageNotConsumed_thenLagIsEqualToProducedMessage() + throws ExecutionException, InterruptedException { + produce(); + Map lagByTopicPartition = lagAnalyzerService.analyzeLag(GROUP_ID); + Assert.assertNotNull(lagByTopicPartition); + Assert.assertEquals(1, lagByTopicPartition.size()); + long LAG = lagByTopicPartition.get(TOPIC_PARTITION); + Assert.assertEquals(BATCH_SIZE, LAG); + } + + @Test + public void givenEmbeddedKafkaBroker_whenMessageConsumedLessThanProduced_thenLagIsNonZero() + throws ExecutionException, InterruptedException { + produce(); + consume(); + produce(); + produce(); + Map lagByTopicPartition = lagAnalyzerService.analyzeLag(GROUP_ID); + Assert.assertNotNull(lagByTopicPartition); + Assert.assertEquals(1, lagByTopicPartition.size()); + long LAG = lagByTopicPartition.get(TOPIC_PARTITION); + Assert.assertEquals(2 * BATCH_SIZE, LAG); + } + + private static void consume() { + try { + ConsumerRecords record = consumer.poll(Duration.ofMillis(POLL_DURATION)); + consumer.commitSync(); + Thread.sleep(POLL_DURATION); + } catch (Exception ex) { + LOGGER.error("Exception caught in consume", ex); + } + } + + private static void produce() { + try { + int count = BATCH_SIZE; + while (count > 0) { + String messageKey = UUID.randomUUID().toString(); + String messageValue = UUID.randomUUID().toString() + "_" + count; + ProducerRecord producerRecord = new ProducerRecord<>(TOPIC, messageKey, messageValue); + RecordMetadata recordMetadata = producer.send(producerRecord).get(); + LOGGER.info("Message with key = {}, value = {} sent to partition = {}, offset = {}, topic = {}", + messageKey, + messageValue, + recordMetadata.partition(), + recordMetadata.offset(), + recordMetadata.topic()); + count--; + } + } catch (Exception ex) { + LOGGER.error("Exception caught in produce", ex); + } + } + + private static void initConsumer() { + Properties props = new Properties(); + props.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVER_CONFIG); + props.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + props.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + props.setProperty(ConsumerConfig.GROUP_ID_CONFIG, GROUP_ID); + consumer = new KafkaConsumer<>(props); + consumer.assign(Arrays.asList(TOPIC_PARTITION)); + consumer.poll(Duration.ofMillis(1L)); + consumer.commitSync(); + } + + private static void initProducer() { + Map configProps = new HashMap<>(); + configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVER_CONFIG); + configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + producer = new KafkaProducer<>(configProps); + } +} diff --git a/spring-kafka/src/test/resources/application.yml b/spring-kafka/src/test/resources/application.yml index 7d7997c6fd..8b245f08b1 100644 --- a/spring-kafka/src/test/resources/application.yml +++ b/spring-kafka/src/test/resources/application.yml @@ -4,4 +4,11 @@ spring: auto-offset-reset: earliest group-id: baeldung test: - topic: embedded-test-topic \ No newline at end of file + topic: embedded-test-topic + +monitor: + kafka: + bootstrap: + config: "PLAINTEXT://localhost:9085" + consumer: + groupid: "baeldungGrp" diff --git a/spring-mobile/pom.xml b/spring-mobile/pom.xml index 1593de27a8..7f715c8735 100644 --- a/spring-mobile/pom.xml +++ b/spring-mobile/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 spring-mobile 1.0-SNAPSHOT diff --git a/spring-native/README.md b/spring-native/README.md new file mode 100644 index 0000000000..0f193252d0 --- /dev/null +++ b/spring-native/README.md @@ -0,0 +1,3 @@ +## Relevant Articles: + +- [Introduction to Spring Native](https://www.baeldung.com/spring-native-intro) diff --git a/spring-native/pom-nativeimage.xml b/spring-native/pom-nativeimage.xml new file mode 100644 index 0000000000..1b2cc3944a --- /dev/null +++ b/spring-native/pom-nativeimage.xml @@ -0,0 +1,122 @@ + + + 4.0.0 + baeldung-spring-native + baeldung-spring-native + jar + Intro to Spring Native + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + spring-release + Spring release + https://repo.spring.io/release + + + + + + spring-release + Spring release + https://repo.spring.io/release + + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring-boot.version} + pom + + + org.springframework.experimental + spring-native + ${spring-native.version} + + + org.springframework.experimental + spring-aot + ${spring-native.version} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.springframework.experimental + spring-aot-maven-plugin + ${spring-native.version} + + + test-generate + + test-generate + + + + generate + + generate + + + + + + + + + + native + + + + org.graalvm.buildtools + native-maven-plugin + ${native-maven-plugin.version} + + + build-native + + build + + package + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + + + + 2.5.1 + 0.10.0 + 0.9.0 + 1.8 + 1.8 + 1.8 + + + \ No newline at end of file diff --git a/spring-native/pom.xml b/spring-native/pom.xml new file mode 100644 index 0000000000..4455e050f2 --- /dev/null +++ b/spring-native/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + spring-native + spring-native + jar + Intro to Spring Native + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + spring-release + Spring release + https://repo.spring.io/release + + + + + + spring-release + Spring release + https://repo.spring.io/release + + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring-boot.version} + pom + + + org.springframework.experimental + spring-native + ${spring-native.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + ${builder} + + true + + IF_NOT_PRESENT + + + + + org.springframework.experimental + spring-aot-maven-plugin + ${spring-native.version} + + + + + + paketobuildpacks/builder:tiny + 2.5.1 + 0.10.0 + 1.8 + 1.8 + 1.8 + + + \ No newline at end of file diff --git a/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java b/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java new file mode 100644 index 0000000000..fa54d34f9f --- /dev/null +++ b/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java @@ -0,0 +1,9 @@ +package com.baeldung.springnativeintro; + +public class SpringNativeApp { + + public static void main(String[] args) { + System.out.println("Hello, World! This is a Baledung Spring Native Application"); + } + +} diff --git a/spring-roo/pom.xml b/spring-roo/pom.xml index d1cf3e6412..8730ef7a33 100644 --- a/spring-roo/pom.xml +++ b/spring-roo/pom.xml @@ -2,7 +2,7 @@ - 4.0.0 com.baeldung @@ -230,6 +230,11 @@ org.webjars.bower ie10-viewport-bug-workaround + + org.olap4j + olap4j + ${olap4j.version} + @@ -619,6 +624,7 @@ 3.7.3 1.0.3 2.0.0.RELEASE + 1.2.0 - \ No newline at end of file + diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml index 0f4ba872ba..917360dc3e 100644 --- a/spring-security-modules/pom.xml +++ b/spring-security-modules/pom.xml @@ -37,7 +37,7 @@ spring-security-web-login spring-security-web-mvc-custom spring-security-web-mvc - spring-security-web-persisted-remember-me + spring-security-web-persistent-login spring-security-web-react spring-security-web-rest-basic-auth spring-security-web-rest-custom @@ -49,4 +49,4 @@ spring-social-login - \ No newline at end of file + diff --git a/spring-security-modules/spring-5-security/pom.xml b/spring-security-modules/spring-5-security/pom.xml index 73b956d1e7..b487e8b282 100644 --- a/spring-security-modules/spring-5-security/pom.xml +++ b/spring-security-modules/spring-5-security/pom.xml @@ -55,12 +55,12 @@ org.jsoup jsoup - 1.13.1 + ${jsoup.version} commons-io commons-io - 2.8.0 + ${commons-io.version} @@ -77,4 +77,8 @@ + + 1.13.1 + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-config/cors/pom.xml b/spring-security-modules/spring-security-config/cors/pom.xml index 2b8efb9add..b45f1b43e6 100644 --- a/spring-security-modules/spring-security-config/cors/pom.xml +++ b/spring-security-modules/spring-security-config/cors/pom.xml @@ -20,7 +20,7 @@ org.springframework.boot spring-boot-dependencies - 2.1.2.RELEASE + ${spring-boot.version} pom import @@ -57,4 +57,8 @@ + + 2.1.2.RELEASE + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-config/pom.xml b/spring-security-modules/spring-security-config/pom.xml index 860a602aeb..8f82dcd40e 100644 --- a/spring-security-modules/spring-security-config/pom.xml +++ b/spring-security-modules/spring-security-config/pom.xml @@ -5,7 +5,7 @@ 4.0.0 spring-security-config 0.0.1-SNAPSHOT - spring-security-conf + spring-security-config pom diff --git a/spring-security-modules/spring-security-core/src/main/resources/application.properties b/spring-security-modules/spring-security-core/src/main/resources/application.properties deleted file mode 100644 index 709574239b..0000000000 --- a/spring-security-modules/spring-security-core/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.main.allow-bean-definition-overriding=true \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/pom.xml b/spring-security-modules/spring-security-legacy-oidc/pom.xml index 148b836137..ca54a6765d 100644 --- a/spring-security-modules/spring-security-legacy-oidc/pom.xml +++ b/spring-security-modules/spring-security-legacy-oidc/pom.xml @@ -49,6 +49,7 @@ 2.2.1.RELEASE 1.0.9.RELEASE 0.3.0 + 2.4.7 \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-angular/server/pom.xml b/spring-security-modules/spring-security-web-angular/server/pom.xml index 2e9ff9969d..9f4f0a5caa 100644 --- a/spring-security-modules/spring-security-web-angular/server/pom.xml +++ b/spring-security-modules/spring-security-web-angular/server/pom.xml @@ -21,7 +21,7 @@ org.springframework.boot spring-boot-dependencies - 1.5.9.RELEASE + ${spring-boot.version} pom import @@ -62,4 +62,8 @@ + + 1.5.9.RELEASE + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/.gitignore b/spring-security-modules/spring-security-web-persistent-login/.gitignore similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/.gitignore rename to spring-security-modules/spring-security-web-persistent-login/.gitignore diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/README.md b/spring-security-modules/spring-security-web-persistent-login/README.md similarity index 71% rename from spring-security-modules/spring-security-web-persisted-remember-me/README.md rename to spring-security-modules/spring-security-web-persistent-login/README.md index e1208330a3..2ffffec267 100644 --- a/spring-security-modules/spring-security-web-persisted-remember-me/README.md +++ b/spring-security-modules/spring-security-web-persistent-login/README.md @@ -1,6 +1,6 @@ -## Spring Security Persisted Remember Me +## Spring Security Persistent Login -This module contains articles about 'remember me' with Spring Security +This module contains articles about persistent login with Spring Security ### The Course diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/pom.xml b/spring-security-modules/spring-security-web-persistent-login/pom.xml similarity index 95% rename from spring-security-modules/spring-security-web-persisted-remember-me/pom.xml rename to spring-security-modules/spring-security-web-persistent-login/pom.xml index 66a2b3866b..2d931c416c 100644 --- a/spring-security-modules/spring-security-web-persisted-remember-me/pom.xml +++ b/spring-security-modules/spring-security-web-persistent-login/pom.xml @@ -3,16 +3,16 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - spring-security-web-persisted-remember-me + spring-security-web-persistent-login 0.1-SNAPSHOT - spring-security-web-persisted-remember-me + spring-security-web-persistent-login war com.baeldung - parent-spring-4 + parent-spring-5 0.0.1-SNAPSHOT - ../../parent-spring-4 + ../../parent-spring-5 @@ -136,7 +136,7 @@ - spring-security-web-persisted-remember-me + spring-security-web-persistent-login src/main/resources diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/controller/MyController.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/controller/MyController.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/controller/MyController.java rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/controller/MyController.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/SecurityRole.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/SecurityRole.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/SecurityRole.java rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/SecurityRole.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/SecurityWebApplicationInitializer.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/SecurityWebApplicationInitializer.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/SecurityWebApplicationInitializer.java rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/SecurityWebApplicationInitializer.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/service/MyUserDetailsService.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/service/MyUserDetailsService.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/service/MyUserDetailsService.java rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/service/MyUserDetailsService.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/MvcConfig.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/MvcConfig.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/MvcConfig.java rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/MvcConfig.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/PersistenceConfig.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/PersistenceConfig.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/PersistenceConfig.java rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/PersistenceConfig.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/SecurityConfig.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/SecurityConfig.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/SecurityConfig.java rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/SecurityConfig.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/logback.xml b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/logback.xml similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/logback.xml rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/logback.xml diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persisted_logins_create_table.sql b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/persisted_logins_create_table.sql similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persisted_logins_create_table.sql rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/persisted_logins_create_table.sql diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persistence-h2.properties b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/persistence-h2.properties similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persistence-h2.properties rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/persistence-h2.properties diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persistence-postgres.properties b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/persistence-postgres.properties similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persistence-postgres.properties rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/persistence-postgres.properties diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/webSecurityConfig.xml b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/webSecurityConfig.xml similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/webSecurityConfig.xml rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/webSecurityConfig.xml diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/mvc-servlet.xml b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/mvc-servlet.xml similarity index 73% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/mvc-servlet.xml rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/mvc-servlet.xml index 4c74be8912..1dbff70b83 100644 --- a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/mvc-servlet.xml +++ b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/mvc-servlet.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/anonymous.jsp b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/anonymous.jsp similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/anonymous.jsp rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/anonymous.jsp diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/console.jsp b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/console.jsp similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/console.jsp rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/console.jsp diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/homepage.jsp b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/homepage.jsp similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/homepage.jsp rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/homepage.jsp diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/login.jsp b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/login.jsp similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/login.jsp rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/login.jsp diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/web.xml b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/web.xml similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/web.xml rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/web.xml diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/test/java/com/baeldung/SpringContextTest.java b/spring-security-modules/spring-security-web-persistent-login/src/test/java/com/baeldung/SpringContextTest.java similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/test/java/com/baeldung/SpringContextTest.java rename to spring-security-modules/spring-security-web-persistent-login/src/test/java/com/baeldung/SpringContextTest.java diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/test/resources/.gitignore b/spring-security-modules/spring-security-web-persistent-login/src/test/resources/.gitignore similarity index 100% rename from spring-security-modules/spring-security-web-persisted-remember-me/src/test/resources/.gitignore rename to spring-security-modules/spring-security-web-persistent-login/src/test/resources/.gitignore diff --git a/spring-security-modules/spring-security-web-react/pom.xml b/spring-security-modules/spring-security-web-react/pom.xml index e8f74413ff..e24d657409 100644 --- a/spring-security-modules/spring-security-web-react/pom.xml +++ b/spring-security-modules/spring-security-web-react/pom.xml @@ -80,7 +80,7 @@ org.springframework.boot spring-boot-starter-test - 1.5.10.RELEASE + ${spring-boot-starter-test.version} test diff --git a/spring-security-modules/spring-security-web-react/src/main/webapp/WEB-INF/view/react/package-lock.json b/spring-security-modules/spring-security-web-react/src/main/webapp/WEB-INF/view/react/package-lock.json index 6b183d2e5c..75dc571cad 100644 --- a/spring-security-modules/spring-security-web-react/src/main/webapp/WEB-INF/view/react/package-lock.json +++ b/spring-security-modules/spring-security-web-react/src/main/webapp/WEB-INF/view/react/package-lock.json @@ -34,9 +34,9 @@ } }, "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==" + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==" }, "acorn-dynamic-import": { "version": "2.0.2", @@ -129,11 +129,6 @@ "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, "ansi-align": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", @@ -1351,15 +1346,24 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "body-parser": { "version": "1.19.0", @@ -1769,51 +1773,77 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" }, "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "optional": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "dependencies": { "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "optional": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "optional": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "optional": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - } + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "optional": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" } }, "is-extglob": { @@ -1822,12 +1852,41 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "requires": { "is-extglob": "^2.1.1" } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "optional": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "optional": true, + "requires": { + "is-number": "^7.0.0" + } } } }, @@ -2154,11 +2213,6 @@ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -2176,13 +2230,6 @@ "os-homedir": "^1.0.1", "parse-json": "^2.2.0", "require-from-string": "^1.1.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } } }, "create-ecdh": { @@ -2710,9 +2757,9 @@ "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" }, "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", "requires": { "ip": "^1.1.0", "safe-buffer": "^5.0.1" @@ -2801,9 +2848,9 @@ } }, "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", "requires": { "is-obj": "^1.0.0" } @@ -2848,17 +2895,24 @@ "integrity": "sha1-dDi3b5K0G5GfP73TUPvQdX2s3fc=" }, "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } } }, "emoji-regex": { @@ -2876,14 +2930,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, "enhanced-resolve": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", @@ -3121,9 +3167,9 @@ "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -3570,9 +3616,9 @@ } }, "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -3714,20 +3760,6 @@ "bser": "^2.0.0" } }, - "fbjs": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", - "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", - "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" - } - }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -3754,6 +3786,12 @@ "schema-utils": "^0.3.0" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -3932,465 +3970,13 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.5.1", - "bundled": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.21", - "bundled": true, - "optional": true, - "requires": { - "safer-buffer": "^2.1.0" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "minipass": { - "version": "2.2.4", - "bundled": true, - "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "needle": { - "version": "2.2.0", - "bundled": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.0", - "bundled": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.7", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "optional": true, - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "optional": true - }, - "semver": { - "version": "5.5.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "4.4.1", - "bundled": true, - "optional": true, - "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "optional": true, - "requires": { - "string-width": "^1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - }, - "yallist": { - "version": "3.0.2", - "bundled": true - } + "bindings": "^1.5.0", + "nan": "^2.12.1" } }, "function-bind": { @@ -4551,60 +4137,15 @@ "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=" }, "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": ">=0.0.4" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "optional": true, - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "optional": true - } - } - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "optional": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" } }, "har-schema": { @@ -4727,9 +4268,9 @@ } }, "hosted-git-info": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.1.tgz", - "integrity": "sha512-Ba4+0M4YvIDUUsprMjhVTU1yN9F2/LJSAl69ZpzaLT4l4j5mwTS6jqqW9Ojvj6lKz/veqPzpJBqGbXspOb533A==" + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "hpack.js": { "version": "2.1.6", @@ -4872,9 +4413,9 @@ "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=" }, "http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "requires": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -5094,9 +4635,9 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "inquirer": { "version": "3.3.0", @@ -5503,15 +5044,6 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -5657,6 +5189,48 @@ "integrity": "sha1-PdJgwpidba1nix6cxNkZRPbWAqw=", "requires": { "jest-cli": "^20.0.4" + } + }, + "jest-changed-files": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-20.0.3.tgz", + "integrity": "sha1-k5TVzGXEOEBhSb7xv01Sto4D4/g=" + }, + "jest-cli": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz", + "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=", + "requires": { + "ansi-escapes": "^1.4.0", + "callsites": "^2.0.0", + "chalk": "^1.1.3", + "graceful-fs": "^4.1.11", + "is-ci": "^1.0.10", + "istanbul-api": "^1.1.1", + "istanbul-lib-coverage": "^1.0.1", + "istanbul-lib-instrument": "^1.4.2", + "istanbul-lib-source-maps": "^1.1.0", + "jest-changed-files": "^20.0.3", + "jest-config": "^20.0.4", + "jest-docblock": "^20.0.3", + "jest-environment-jsdom": "^20.0.3", + "jest-haste-map": "^20.0.4", + "jest-jasmine2": "^20.0.4", + "jest-message-util": "^20.0.3", + "jest-regex-util": "^20.0.3", + "jest-resolve-dependencies": "^20.0.3", + "jest-runtime": "^20.0.4", + "jest-snapshot": "^20.0.3", + "jest-util": "^20.0.3", + "micromatch": "^2.3.11", + "node-notifier": "^5.0.2", + "pify": "^2.3.0", + "slash": "^1.0.0", + "string-length": "^1.0.1", + "throat": "^3.0.0", + "which": "^1.2.12", + "worker-farm": "^1.3.1", + "yargs": "^7.0.2" }, "dependencies": { "ansi-escapes": { @@ -5708,43 +5282,6 @@ "is-extglob": "^1.0.0" } }, - "jest-cli": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz", - "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=", - "requires": { - "ansi-escapes": "^1.4.0", - "callsites": "^2.0.0", - "chalk": "^1.1.3", - "graceful-fs": "^4.1.11", - "is-ci": "^1.0.10", - "istanbul-api": "^1.1.1", - "istanbul-lib-coverage": "^1.0.1", - "istanbul-lib-instrument": "^1.4.2", - "istanbul-lib-source-maps": "^1.1.0", - "jest-changed-files": "^20.0.3", - "jest-config": "^20.0.4", - "jest-docblock": "^20.0.3", - "jest-environment-jsdom": "^20.0.3", - "jest-haste-map": "^20.0.4", - "jest-jasmine2": "^20.0.4", - "jest-message-util": "^20.0.3", - "jest-regex-util": "^20.0.3", - "jest-resolve-dependencies": "^20.0.3", - "jest-runtime": "^20.0.4", - "jest-snapshot": "^20.0.3", - "jest-util": "^20.0.3", - "micromatch": "^2.3.11", - "node-notifier": "^5.0.2", - "pify": "^2.3.0", - "slash": "^1.0.0", - "string-length": "^1.0.1", - "throat": "^3.0.0", - "which": "^1.2.12", - "worker-farm": "^1.3.1", - "yargs": "^7.0.2" - } - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -5775,11 +5312,6 @@ } } }, - "jest-changed-files": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-20.0.3.tgz", - "integrity": "sha1-k5TVzGXEOEBhSb7xv01Sto4D4/g=" - }, "jest-config": { "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-20.0.4.tgz", @@ -6327,9 +5859,9 @@ "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "klaw": { "version": "1.3.1", @@ -6387,12 +5919,12 @@ } }, "loader-fs-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", - "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", + "integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==", "requires": { "find-cache-dir": "^0.1.1", - "mkdirp": "0.5.1" + "mkdirp": "^0.5.1" }, "dependencies": { "find-cache-dir": { @@ -6457,9 +5989,9 @@ } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash._reinterpolate": { "version": "3.0.0", @@ -6476,11 +6008,6 @@ "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=" }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -6656,19 +6183,12 @@ "read-pkg-up": "^1.0.1", "redent": "^1.0.0", "trim-newlines": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } } }, "merge": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", - "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==" }, "merge-descriptors": { "version": "1.0.1", @@ -6751,14 +6271,14 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -6775,11 +6295,11 @@ } }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "ms": { @@ -6807,9 +6327,9 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", "optional": true }, "nanomatch": { @@ -6841,9 +6361,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "neo-async": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz", - "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "next-tick": { "version": "1.0.0", @@ -6858,19 +6378,10 @@ "lower-case": "^1.1.1" } }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, "node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==" + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" }, "node-int64": { "version": "0.4.0", @@ -7127,22 +6638,6 @@ "is-wsl": "^1.1.0" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" - } - } - }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -7301,7 +6796,8 @@ "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "optional": true }, "path-exists": { "version": "3.0.0", @@ -7370,6 +6866,11 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -7427,11 +6928,6 @@ "ms": "^2.1.1" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -8646,14 +8142,6 @@ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=" }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, "prop-types": { "version": "15.6.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", @@ -8820,30 +8308,22 @@ "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } } }, "react": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.4.1.tgz", - "integrity": "sha512-3GEs0giKp6E0Oh/Y9ZC60CmYgUPnp7voH9fbjWsvXtYFb4EWtgQub0ADSq0sJR0BbHc4FThLLtzlcFaFXIorwg==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", "requires": { - "fbjs": "^0.8.16", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.0" + "prop-types": "^15.6.2" } }, "react-dev-utils": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.1.tgz", - "integrity": "sha512-+y92rG6pmXt3cpcg/NGmG4w/W309tWNSmyyPL8hCMxuCSg2UP/hUg3npACj2UZc8UKVSXexyLrCnxowizGoAsw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.3.tgz", + "integrity": "sha512-Mvs6ofsc2xTjeZIrMaIfbXfsPVrbdVy/cVqq6SAacnqfMlcBpDuivhWZ1ODGeJ8HgmyWTLH971PYjj/EPCDVAw==", "requires": { "address": "1.0.3", "babel-code-frame": "6.26.0", @@ -8857,29 +8337,44 @@ "inquirer": "3.3.0", "is-root": "1.0.0", "opn": "5.2.0", - "react-error-overlay": "^4.0.0", + "react-error-overlay": "^4.0.1", "recursive-readdir": "2.2.1", "shell-quote": "1.6.1", - "sockjs-client": "1.1.4", + "sockjs-client": "1.1.5", "strip-ansi": "3.0.1", "text-table": "0.2.0" + }, + "dependencies": { + "sockjs-client": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz", + "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=", + "requires": { + "debug": "^2.6.6", + "eventsource": "0.1.6", + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.8" + } + } } }, "react-dom": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.1.tgz", - "integrity": "sha512-1Gin+wghF/7gl4Cqcvr1DxFX2Osz7ugxSwl6gBqCMpdrxHjIFUS7GYxrFftZ9Ln44FHw0JxCFD9YtZsrbR5/4A==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "requires": { - "fbjs": "^0.8.16", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.0" + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" } }, "react-error-overlay": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.0.tgz", - "integrity": "sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.1.tgz", + "integrity": "sha512-xXUbDAZkU08aAkjtUvldqbvI04ogv+a1XdHxvYuHPYKIVk/42BIOD0zSKTHAWV4+gDy3yGm283z2072rA2gdtw==" }, "react-scripts": { "version": "1.1.4", @@ -9014,14 +8509,13 @@ } }, "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" } }, "recursive-readdir": { @@ -9509,11 +9003,6 @@ "requires": { "bser": "1.0.2" } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, @@ -9522,6 +9011,15 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "schema-utils": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", @@ -9536,11 +9034,11 @@ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" }, "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", + "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", "requires": { - "node-forge": "0.9.0" + "node-forge": "^0.10.0" } }, "semver": { @@ -9636,15 +9134,10 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" - }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -10502,11 +9995,6 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, - "ua-parser-js": { - "version": "0.7.18", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", - "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA==" - }, "uglify-js": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.2.tgz", @@ -10568,35 +10056,14 @@ } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "uniq": { @@ -10669,9 +10136,10 @@ "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" }, "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "optional": true }, "update-notifier": { "version": "2.5.0", @@ -10716,9 +10184,9 @@ } }, "urijs": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.1.tgz", - "integrity": "sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==" + "version": "1.19.7", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.7.tgz", + "integrity": "sha512-Id+IKjdU0Hx+7Zx717jwLPsPeUqz7rAtuVBRLLs+qn+J2nf9NGITWVCxcijgYxBqe83C7sqsQPs6H1pyz3x9gA==" }, "urix": { "version": "0.1.0", @@ -10752,9 +10220,9 @@ } }, "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz", + "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==", "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -10855,13 +10323,98 @@ "integrity": "sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=" }, "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "requires": { - "chokidar": "^2.0.2", + "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "optional": true + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "optional": true, + "requires": { + "is-extglob": "^2.1.1" + } + } } }, "wbuf": { @@ -11291,9 +10844,9 @@ } }, "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" }, "whatwg-encoding": { "version": "1.0.3", @@ -11310,11 +10863,6 @@ } } }, - "whatwg-fetch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" - }, "whatwg-url": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-4.8.0.tgz", @@ -11443,9 +10991,9 @@ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" }, "yallist": { "version": "2.1.2", @@ -11453,9 +11001,9 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", "requires": { "camelcase": "^3.0.0", "cliui": "^3.2.0", @@ -11469,7 +11017,7 @@ "string-width": "^1.0.2", "which-module": "^1.0.0", "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" + "yargs-parser": "^5.0.1" }, "dependencies": { "camelcase": { @@ -11508,11 +11056,12 @@ } }, "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", "requires": { - "camelcase": "^3.0.0" + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" }, "dependencies": { "camelcase": { diff --git a/spring-security-modules/spring-security-web-sockets/pom.xml b/spring-security-modules/spring-security-web-sockets/pom.xml index e822b6beda..b1536e88ea 100644 --- a/spring-security-modules/spring-security-web-sockets/pom.xml +++ b/spring-security-modules/spring-security-web-sockets/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 com.baeldung.springsecuredsockets spring-security-web-sockets diff --git a/spring-security-modules/spring-social-login/src/main/resources/application.properties b/spring-security-modules/spring-social-login/src/main/resources/application.properties index 22e6acf9e2..57348051ac 100644 --- a/spring-security-modules/spring-social-login/src/main/resources/application.properties +++ b/spring-security-modules/spring-social-login/src/main/resources/application.properties @@ -1,4 +1,6 @@ spring.social.facebook.appId=1715784745414888 spring.social.facebook.appSecret=abefd6497e9cc01ad03be28509617bf0 spring.thymeleaf.cache=false -server.servlet.register-default-servlet=true \ No newline at end of file +server.servlet.register-default-servlet=true +spring.sql.init.mode=always +spring.jpa.defer-datasource-initialization=true \ No newline at end of file diff --git a/spring-vault/pom.xml b/spring-vault/pom.xml index 759de80a6b..68856de2fc 100644 --- a/spring-vault/pom.xml +++ b/spring-vault/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 spring-vault 0.0.1-SNAPSHOT diff --git a/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties b/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties index aca20f4e3c..fcdaabe007 100644 --- a/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties +++ b/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties @@ -1,5 +1,3 @@ -spring.main.allow-bean-definition-overriding=true - spring.mail.host=localhost spring.mail.port=8025 diff --git a/spring-web-modules/spring-mvc-basics-4/README.md b/spring-web-modules/spring-mvc-basics-4/README.md index 211564a363..f003f7df01 100644 --- a/spring-web-modules/spring-mvc-basics-4/README.md +++ b/spring-web-modules/spring-mvc-basics-4/README.md @@ -9,4 +9,5 @@ The "REST With Spring" Classes: https://bit.ly/restwithspring - [Spring Web Contexts](https://www.baeldung.com/spring-web-contexts) - [Spring Optional Path variables](https://www.baeldung.com/spring-optional-path-variables) - [JSON Parameters with Spring MVC](https://www.baeldung.com/spring-mvc-send-json-parameters) +- [How to Set JSON Content Type In Spring MVC](https://www.baeldung.com/spring-mvc-set-json-content-type) - More articles: [[<-- prev]](/spring-mvc-basics-3) diff --git a/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java b/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java new file mode 100644 index 0000000000..fbf78b8a0e --- /dev/null +++ b/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java @@ -0,0 +1,49 @@ +package com.baeldung.controller.controller; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class GreetingsController { + + @RequestMapping( + value = "/greetings-with-response-body", + method = RequestMethod.GET, + produces="application/json" + ) + @ResponseBody + public String getGreetingWhileReturnTypeIsString() { + return "{\"test\": \"Hello using @ResponseBody\"}"; + } + + @RequestMapping( + value = "/greetings-with-response-entity", + method = RequestMethod.GET, + produces = "application/json" + ) + public ResponseEntity getGreetingWithResponseEntity() { + final HttpHeaders httpHeaders= new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + return new ResponseEntity("{\"test\": \"Hello with ResponseEntity\"}", httpHeaders, HttpStatus.OK); + } + @RequestMapping( + value = "/greetings-with-map-return-type", + method = RequestMethod.GET, + produces = "application/json" + ) + @ResponseBody + public Map getGreetingWhileReturnTypeIsMap() { + HashMap map = new HashMap(); + map.put("test", "Hello from map"); + return map; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties b/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties deleted file mode 100644 index 709574239b..0000000000 --- a/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.main.allow-bean-definition-overriding=true \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java b/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java new file mode 100644 index 0000000000..ee9a8da8d4 --- /dev/null +++ b/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java @@ -0,0 +1,58 @@ +package com.baeldung.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.baeldung.controller.controller.GreetingsController; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.AnnotationConfigWebContextLoader; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@ContextConfiguration(classes = { GreetingsController.class }, loader = AnnotationConfigWebContextLoader.class) +public class GreetingsControllerUnitTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext wac; + + @Before + public void setUp() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @Test + public void givenReturnTypeIsString_whenJacksonOnClasspath_thenDefaultContentTypeIsJSON() throws Exception { + + // Given + String expectedMimeType = "application/json"; + + // Then + String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-body", 1)).andReturn().getResponse().getContentType(); + + Assert.assertEquals(expectedMimeType, actualMimeType); + + } + + @Test + public void givenReturnTypeIsResponseEntity_thenDefaultContentTypeIsJSON() throws Exception { + + // Given + String expectedMimeType = "application/json"; + + // Then + String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-entity", 1)).andReturn().getResponse().getContentType(); + + Assert.assertEquals(expectedMimeType, actualMimeType); + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-forms-jsp/pom.xml b/spring-web-modules/spring-mvc-forms-jsp/pom.xml index 94eb51a32d..707c18fa19 100644 --- a/spring-web-modules/spring-mvc-forms-jsp/pom.xml +++ b/spring-web-modules/spring-mvc-forms-jsp/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 0.1-SNAPSHOT spring-mvc-forms-jsp diff --git a/spring-web-modules/spring-mvc-forms-thymeleaf/pom.xml b/spring-web-modules/spring-mvc-forms-thymeleaf/pom.xml index 37bcee0b8d..eb31723798 100644 --- a/spring-web-modules/spring-mvc-forms-thymeleaf/pom.xml +++ b/spring-web-modules/spring-mvc-forms-thymeleaf/pom.xml @@ -28,7 +28,6 @@ org.projectlombok lombok - org.springframework.boot diff --git a/spring-web-modules/spring-mvc-java/pom.xml b/spring-web-modules/spring-mvc-java/pom.xml index b8f5ec7910..68b4c74dce 100644 --- a/spring-web-modules/spring-mvc-java/pom.xml +++ b/spring-web-modules/spring-mvc-java/pom.xml @@ -226,7 +226,6 @@ 4.4.5 4.5.2 - 3.0.7 2.23 3.2.2 diff --git a/spring-web-modules/spring-rest-angular/pom.xml b/spring-web-modules/spring-rest-angular/pom.xml index ef14e78198..e7cb5c8664 100644 --- a/spring-web-modules/spring-rest-angular/pom.xml +++ b/spring-web-modules/spring-rest-angular/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 spring-rest-angular 1.0 diff --git a/spring-web-modules/spring-rest-angular/src/main/resources/application.properties b/spring-web-modules/spring-rest-angular/src/main/resources/application.properties index cfa27938c9..d6355dd416 100644 --- a/spring-web-modules/spring-rest-angular/src/main/resources/application.properties +++ b/spring-web-modules/spring-rest-angular/src/main/resources/application.properties @@ -2,4 +2,6 @@ server.servlet.contextPath=/ spring.h2.console.enabled=true logging.level.org.hibernate.SQL=info spring.jpa.generate-ddl=true -spring.jpa.hibernate.ddl-auto=create \ No newline at end of file +spring.jpa.hibernate.ddl-auto=create +spring.sql.init.mode=always +spring.jpa.defer-datasource-initialization=true \ No newline at end of file diff --git a/spring-web-modules/spring-rest-query-language/src/main/resources/application.properties b/spring-web-modules/spring-rest-query-language/src/main/resources/application.properties index 4bbf3ed4fc..c432c45970 100644 --- a/spring-web-modules/spring-rest-query-language/src/main/resources/application.properties +++ b/spring-web-modules/spring-rest-query-language/src/main/resources/application.properties @@ -1,2 +1,4 @@ server.port=8082 -server.servlet.context-path=/spring-rest-query-language \ No newline at end of file +server.servlet.context-path=/spring-rest-query-language +spring.sql.init.mode=always +spring.jpa.defer-datasource-initialization=true \ No newline at end of file diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java index 9b01328e45..efab434e31 100644 --- a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java +++ b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java @@ -1,4 +1,4 @@ -package main.java.com.baeldung.thymeleaf.articles; +package com.baeldung.thymeleaf.articles; public class Article { diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java index cfbf0fcaa6..e3972ffb51 100644 --- a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java +++ b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java @@ -1,4 +1,4 @@ -package main.java.com.baeldung.thymeleaf.articles; +package com.baeldung.thymeleaf.articles; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties b/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties index 75770808da..730dded1b7 100644 --- a/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties +++ b/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties @@ -2,5 +2,4 @@ spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect -spring.main.allow-bean-definition-overriding=true \ No newline at end of file +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect \ No newline at end of file diff --git a/testing-modules/junit-5/README.md b/testing-modules/junit-5/README.md index 984b79c29d..5101f92928 100644 --- a/testing-modules/junit-5/README.md +++ b/testing-modules/junit-5/README.md @@ -8,3 +8,4 @@ - [Guide to Dynamic Tests in JUnit 5](https://www.baeldung.com/junit5-dynamic-tests) - [Determine the Execution Time of JUnit Tests](https://www.baeldung.com/junit-test-execution-time) - [@BeforeAll and @AfterAll in Non-Static Methods](https://www.baeldung.com/java-beforeall-afterall-non-static) +- [The java.lang.NoClassDefFoundError in JUnit](https://www.baeldung.com/junit-noclassdeffounderror) diff --git a/testing-modules/rest-assured/pom.xml b/testing-modules/rest-assured/pom.xml index bd4c1456c1..3bb351f123 100644 --- a/testing-modules/rest-assured/pom.xml +++ b/testing-modules/rest-assured/pom.xml @@ -126,11 +126,6 @@ org.apache.httpcomponents httpclient - - org.codehaus.groovy - groovy-all - ${groovy.version} - com.github.tomakehurst wiremock @@ -180,7 +175,6 @@ 2.9.6 1.1 1.2 - 2.4.7 2.4.1 2.5.3 diff --git a/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/authentication/BasicPreemtiveAuthenticationLiveTest.java b/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/authentication/BasicPreemtiveAuthenticationLiveTest.java index 02138f22e3..3d19d08ba3 100644 --- a/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/authentication/BasicPreemtiveAuthenticationLiveTest.java +++ b/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/authentication/BasicPreemtiveAuthenticationLiveTest.java @@ -25,7 +25,7 @@ public class BasicPreemtiveAuthenticationLiveTest { get(SVC_URL).then() .assertThat() .statusCode(HttpStatus.OK.value()) - .content(containsString(" + + + + + false + true + false + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + SYSTEM + + + 8080 + true + + 8080 + + 8080 + + 8080 + localhost + true + false + false + C:\Users\Dell\Downloads\chromedriver.exe + false + false + false + false + false + + + + WDS.sampleResult.sampleStart() +WDS.browser.get('http://baeldung.com') +WDS.sampleResult.sampleEnd() +WDS.log.info("successfully navigated to http://baeldung.com/"); + + + javascript + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + diff --git a/testing-modules/testing-libraries-2/README.md b/testing-modules/testing-libraries-2/README.md index 868d8f307d..7cc08a8140 100644 --- a/testing-modules/testing-libraries-2/README.md +++ b/testing-modules/testing-libraries-2/README.md @@ -3,3 +3,4 @@ - [Guide to the System Rules Library](https://www.baeldung.com/java-system-rules-junit) - [Guide to the System Stubs Library](https://www.baeldung.com/java-system-stubs) - [Code Coverage with SonarQube and JaCoCo](https://www.baeldung.com/sonarqube-jacoco-code-coverage) +- [Exclusions from Jacoco Report](https://www.baeldung.com/jacoco-report-exclude) diff --git a/testing-modules/testing-libraries-2/pom.xml b/testing-modules/testing-libraries-2/pom.xml index d914fcfb86..dcbddd60b4 100644 --- a/testing-modules/testing-libraries-2/pom.xml +++ b/testing-modules/testing-libraries-2/pom.xml @@ -131,5 +131,5 @@ 5.6.2 3.16.1 - - + + \ No newline at end of file diff --git a/vaadin/src/main/resources/application.properties b/vaadin/src/main/resources/application.properties deleted file mode 100644 index 1cb7086b82..0000000000 --- a/vaadin/src/main/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Vaadin supports spring-boot 2.1 properly from V8 onwards (according to this comment https://github.com/vaadin/spring/issues/331#issuecomment-435128475) -spring.main.allow-bean-definition-overriding=true \ No newline at end of file diff --git a/vertx/pom.xml b/vertx/pom.xml index 9a22e2dd72..7608b3e355 100644 --- a/vertx/pom.xml +++ b/vertx/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 vertx 1.0-SNAPSHOT diff --git a/wildfly/pom.xml b/wildfly/pom.xml index c5d9bce37a..eaec4d176c 100644 --- a/wildfly/pom.xml +++ b/wildfly/pom.xml @@ -60,7 +60,6 @@ - org.apache.maven.plugins maven-war-plugin