diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/graphcycledetection/domain/Graph.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/graphcycledetection/domain/Graph.java new file mode 100644 index 0000000000..c77173b288 --- /dev/null +++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/graphcycledetection/domain/Graph.java @@ -0,0 +1,51 @@ +package com.baeldung.algorithms.graphcycledetection.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Graph { + + private List vertices; + + public Graph() { + this.vertices = new ArrayList<>(); + } + + public Graph(List vertices) { + this.vertices = vertices; + } + + public void addVertex(Vertex vertex) { + this.vertices.add(vertex); + } + + public void addEdge(Vertex from, Vertex to) { + from.addNeighbour(to); + } + + public boolean hasCycle() { + for (Vertex vertex : vertices) { + if (!vertex.isVisited() && hasCycle(vertex)) { + return true; + } + } + return false; + } + + public boolean hasCycle(Vertex sourceVertex) { + sourceVertex.setBeingVisited(true); + + for (Vertex neighbour : sourceVertex.getAdjacencyList()) { + if (neighbour.isBeingVisited()) { + // backward edge exists + return true; + } else if (!neighbour.isVisited() && hasCycle(neighbour)) { + return true; + } + } + + sourceVertex.setBeingVisited(false); + sourceVertex.setVisited(true); + return false; + } +} diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/graphcycledetection/domain/Vertex.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/graphcycledetection/domain/Vertex.java new file mode 100644 index 0000000000..398cdf0d9c --- /dev/null +++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/graphcycledetection/domain/Vertex.java @@ -0,0 +1,56 @@ +package com.baeldung.algorithms.graphcycledetection.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Vertex { + + private String label; + + private boolean visited; + + private boolean beingVisited; + + private List adjacencyList; + + public Vertex(String label) { + this.label = label; + this.adjacencyList = new ArrayList<>(); + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public boolean isVisited() { + return visited; + } + + public void setVisited(boolean visited) { + this.visited = visited; + } + + public boolean isBeingVisited() { + return beingVisited; + } + + public void setBeingVisited(boolean beingVisited) { + this.beingVisited = beingVisited; + } + + public List getAdjacencyList() { + return adjacencyList; + } + + public void setAdjacencyList(List adjacencyList) { + this.adjacencyList = adjacencyList; + } + + public void addNeighbour(Vertex adjacent) { + this.adjacencyList.add(adjacent); + } +} diff --git a/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/graphcycledetection/GraphCycleDetectionUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/graphcycledetection/GraphCycleDetectionUnitTest.java new file mode 100644 index 0000000000..8d464d7b97 --- /dev/null +++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/graphcycledetection/GraphCycleDetectionUnitTest.java @@ -0,0 +1,56 @@ +package com.baeldung.algorithms.graphcycledetection; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.baeldung.algorithms.graphcycledetection.domain.Graph; +import com.baeldung.algorithms.graphcycledetection.domain.Vertex; + +public class GraphCycleDetectionUnitTest { + + @Test + public void givenGraph_whenCycleExists_thenReturnTrue() { + + Vertex vertexA = new Vertex("A"); + Vertex vertexB = new Vertex("B"); + Vertex vertexC = new Vertex("C"); + Vertex vertexD = new Vertex("D"); + + Graph graph = new Graph(); + graph.addVertex(vertexA); + graph.addVertex(vertexB); + graph.addVertex(vertexC); + graph.addVertex(vertexD); + + graph.addEdge(vertexA, vertexB); + graph.addEdge(vertexB, vertexC); + graph.addEdge(vertexC, vertexA); + graph.addEdge(vertexD, vertexC); + + assertTrue(graph.hasCycle()); + } + + @Test + public void givenGraph_whenNoCycleExists_thenReturnFalse() { + + Vertex vertexA = new Vertex("A"); + Vertex vertexB = new Vertex("B"); + Vertex vertexC = new Vertex("C"); + Vertex vertexD = new Vertex("D"); + + Graph graph = new Graph(); + graph.addVertex(vertexA); + graph.addVertex(vertexB); + graph.addVertex(vertexC); + graph.addVertex(vertexD); + + graph.addEdge(vertexA, vertexB); + graph.addEdge(vertexB, vertexC); + graph.addEdge(vertexA, vertexC); + graph.addEdge(vertexD, vertexC); + + assertFalse(graph.hasCycle()); + } +} diff --git a/core-groovy-2/README.md b/core-groovy-2/README.md index c8df242e1a..1d35669cd7 100644 --- a/core-groovy-2/README.md +++ b/core-groovy-2/README.md @@ -3,5 +3,6 @@ ## Relevant articles: - [String Matching in Groovy](http://www.baeldung.com/) +- [Template Engines in Groovy](https://www.baeldung.com/groovy-template-engines) - [Groovy def Keyword](https://www.baeldung.com/groovy-def-keyword) -- [Pattern Matching in Strings in Groovy](https://www.baeldung.com/groovy-pattern-matching) +- [Pattern Matching in Strings in Groovy](https://www.baeldung.com/groovy-pattern-matching) \ No newline at end of file diff --git a/core-groovy-2/gmavenplus-pom.xml b/core-groovy-2/gmavenplus-pom.xml new file mode 100644 index 0000000000..54c89b9834 --- /dev/null +++ b/core-groovy-2/gmavenplus-pom.xml @@ -0,0 +1,178 @@ + + + 4.0.0 + core-groovy-2 + 1.0-SNAPSHOT + core-groovy-2 + jar + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + org.codehaus.groovy + groovy-all + ${groovy.version} + pom + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + org.hsqldb + hsqldb + ${hsqldb.version} + test + + + org.spockframework + spock-core + ${spock-core.version} + test + + + + + src/main/groovy + src/main/java + + + org.codehaus.gmavenplus + gmavenplus-plugin + 1.7.0 + + + + execute + addSources + addTestSources + generateStubs + compile + generateTestStubs + compileTests + removeStubs + removeTestStubs + + + + + + org.codehaus.groovy + groovy-all + + ${groovy.version} + runtime + pom + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + junit5 + + integration-test + verify + + + + **/*Test5.java + + + + + + + maven-surefire-plugin + 2.20.1 + + false + + **/*Test.java + **/*Spec.java + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + + jar-with-dependencies + + + + + com.baeldung.MyJointCompilationApp + + + + + + + make-assembly + + package + + single + + + + + + + + + + central + http://jcenter.bintray.com + + + + + UTF-8 + 1.0.0 + 2.4.0 + 1.1-groovy-2.4 + 3.9 + 1.8 + 1.2.3 + 2.5.7 + 1.6 + + + diff --git a/core-groovy-2/pom.xml b/core-groovy-2/pom.xml index 77de9c8fc8..b945546c8a 100644 --- a/core-groovy-2/pom.xml +++ b/core-groovy-2/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 core-groovy-2 1.0-SNAPSHOT @@ -15,25 +15,20 @@ - org.codehaus.groovy - groovy - ${groovy.version} + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + ch.qos.logback + logback-classic + ${logback.version} org.codehaus.groovy groovy-all - ${groovy-all.version} - pom - - - org.codehaus.groovy - groovy-dateutil ${groovy.version} - - - org.codehaus.groovy - groovy-sql - ${groovy-sql.version} + pom org.junit.platform @@ -56,21 +51,35 @@ + src/main/groovy + src/main/java - org.codehaus.gmavenplus - gmavenplus-plugin - ${gmavenplus-plugin.version} - - - - addSources - addTestSources - compile - compileTests - - - + org.codehaus.groovy + groovy-eclipse-compiler + 3.3.0-01 + true + + + maven-compiler-plugin + 3.8.0 + + groovy-eclipse-compiler + ${java.version} + ${java.version} + + + + org.codehaus.groovy + groovy-eclipse-compiler + 3.3.0-01 + + + org.codehaus.groovy + groovy-eclipse-batch + ${groovy.version}-01 + + maven-failsafe-plugin @@ -101,13 +110,42 @@ maven-surefire-plugin 2.20.1 - false - - **/*Test.java - **/*Spec.java - + false + + **/*Test.java + **/*Spec.java + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + + jar-with-dependencies + + + + + com.baeldung.MyJointCompilationApp + + + + + + + make-assembly + + package + + single + + + + @@ -118,14 +156,32 @@ + + + bintray + Groovy Bintray + https://dl.bintray.com/groovy/maven + + + never + + + false + + + + + 1.0.0 - 2.5.6 - 2.5.6 - 2.5.6 2.4.0 1.1-groovy-2.4 - 1.6 + 3.9 + 1.8 + 3.8.1 + 1.2.3 + 2.5.7 + UTF-8 diff --git a/core-groovy-2/src/main/groovy/com/baeldung/CalcMath.groovy b/core-groovy-2/src/main/groovy/com/baeldung/CalcMath.groovy new file mode 100644 index 0000000000..0e233793b2 --- /dev/null +++ b/core-groovy-2/src/main/groovy/com/baeldung/CalcMath.groovy @@ -0,0 +1,25 @@ +package com.baeldung + +import org.slf4j.LoggerFactory + +class CalcMath { + def log = LoggerFactory.getLogger(this.getClass()) + + def calcSum(x, y) { + log.info "Executing $x + $y" + x + y + } + + /** + * example of method that in java would throw error at compile time + * @param x + * @param y + * @return + */ + def calcSum2(x, y) { + log.info "Executing $x + $y" + // DANGER! This won't throw a compilation issue and fail only at runtime!!! + calcSum3() + log.info("Logging an undefined variable: $z") + } +} \ No newline at end of file diff --git a/core-groovy-2/src/main/groovy/com/baeldung/CalcScript.groovy b/core-groovy-2/src/main/groovy/com/baeldung/CalcScript.groovy new file mode 100644 index 0000000000..84615b2217 --- /dev/null +++ b/core-groovy-2/src/main/groovy/com/baeldung/CalcScript.groovy @@ -0,0 +1,16 @@ +package com.baeldung + +def calcSum(x, y) { + x + y +} + +def calcSum2(x, y) { + // DANGER! The variable "log" may be undefined + log.info "Executing $x + $y" + // DANGER! This method doesn't exist! + calcSum3() + // DANGER! The logged variable "z" is undefined! + log.info("Logging an undefined variable: $z") +} + +calcSum(1,5) diff --git a/core-groovy-2/src/main/java/com/baeldung/MyJointCompilationApp.java b/core-groovy-2/src/main/java/com/baeldung/MyJointCompilationApp.java new file mode 100644 index 0000000000..c49f6edc30 --- /dev/null +++ b/core-groovy-2/src/main/java/com/baeldung/MyJointCompilationApp.java @@ -0,0 +1,120 @@ +package com.baeldung; + +import groovy.lang.*; +import groovy.util.GroovyScriptEngine; +import groovy.util.ResourceException; +import groovy.util.ScriptException; +import org.codehaus.groovy.jsr223.GroovyScriptEngineFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.script.ScriptEngine; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Hello world! + * + */ +public class MyJointCompilationApp { + private final static Logger LOG = LoggerFactory.getLogger(MyJointCompilationApp.class); + private final GroovyClassLoader loader; + private final GroovyShell shell; + private final GroovyScriptEngine engine; + private final ScriptEngine engineFromFactory; + + public MyJointCompilationApp() { + loader = new GroovyClassLoader(this.getClass().getClassLoader()); + shell = new GroovyShell(loader, new Binding()); + + URL url = null; + try { + url = new File("src/main/groovy/com/baeldung/").toURI().toURL(); + } catch (MalformedURLException e) { + LOG.error("Exception while creating url", e); + } + engine = new GroovyScriptEngine(new URL[] {url}, this.getClass().getClassLoader()); + engineFromFactory = new GroovyScriptEngineFactory().getScriptEngine(); + } + + private void addWithCompiledClasses(int x, int y) { + LOG.info("Executing {} + {}", x, y); + Object result1 = new CalcScript().calcSum(x, y); + LOG.info("Result of CalcScript.calcSum() method is {}", result1); + + Object result2 = new CalcMath().calcSum(x, y); + LOG.info("Result of CalcMath.calcSum() method is {}", result2); + } + + private void addWithGroovyShell(int x, int y) throws IOException { + Script script = shell.parse(new File("src/main/groovy/com/baeldung/", "CalcScript.groovy")); + LOG.info("Executing {} + {}", x, y); + Object result = script.invokeMethod("calcSum", new Object[] { x, y }); + LOG.info("Result of CalcScript.calcSum() method is {}", result); + } + + private void addWithGroovyShellRun() throws IOException { + Script script = shell.parse(new File("src/main/groovy/com/baeldung/", "CalcScript.groovy")); + LOG.info("Executing script run method"); + Object result = script.run(); + LOG.info("Result of CalcScript.run() method is {}", result); + } + + private void addWithGroovyClassLoader(int x, int y) throws IllegalAccessException, InstantiationException, IOException { + Class calcClass = loader.parseClass( + new File("src/main/groovy/com/baeldung/", "CalcMath.groovy")); + GroovyObject calc = (GroovyObject) calcClass.newInstance(); + Object result = calc.invokeMethod("calcSum", new Object[] { x + 14, y + 14 }); + LOG.info("Result of CalcMath.calcSum() method is {}", result); + } + + private void addWithGroovyScriptEngine(int x, int y) throws IllegalAccessException, + InstantiationException, ResourceException, ScriptException { + Class calcClass = engine.loadScriptByName("CalcMath.groovy"); + GroovyObject calc = calcClass.newInstance(); + //WARNING the following will throw a ClassCastException + //((CalcMath)calc).calcSum(1,2); + Object result = calc.invokeMethod("calcSum", new Object[] { x, y }); + LOG.info("Result of CalcMath.calcSum() method is {}", result); + } + + private void addWithEngineFactory(int x, int y) throws IllegalAccessException, + InstantiationException, javax.script.ScriptException, FileNotFoundException { + Class calcClass = (Class) engineFromFactory.eval( + new FileReader(new File("src/main/groovy/com/baeldung/", "CalcMath.groovy"))); + GroovyObject calc = (GroovyObject) calcClass.newInstance(); + Object result = calc.invokeMethod("calcSum", new Object[] { x, y }); + LOG.info("Result of CalcMath.calcSum() method is {}", result); + } + + private void addWithStaticCompiledClasses() { + LOG.info("Running the Groovy classes compiled statically..."); + addWithCompiledClasses(5, 10); + + } + + private void addWithDynamicCompiledClasses() throws IOException, IllegalAccessException, InstantiationException, + ResourceException, ScriptException, javax.script.ScriptException { + LOG.info("Invocation of a dynamic groovy script..."); + addWithGroovyShell(5, 10); + LOG.info("Invocation of the run method of a dynamic groovy script..."); + addWithGroovyShellRun(); + LOG.info("Invocation of a dynamic groovy class loaded with GroovyClassLoader..."); + addWithGroovyClassLoader(10, 30); + LOG.info("Invocation of a dynamic groovy class loaded with GroovyScriptEngine..."); + addWithGroovyScriptEngine(15, 0); + LOG.info("Invocation of a dynamic groovy class loaded with GroovyScriptEngine JSR223..."); + addWithEngineFactory(5, 6); + } + + public static void main(String[] args) throws InstantiationException, IllegalAccessException, + ResourceException, ScriptException, IOException, javax.script.ScriptException { + MyJointCompilationApp myJointCompilationApp = new MyJointCompilationApp(); + LOG.info("Example of addition operation via Groovy scripts integration with Java."); + myJointCompilationApp.addWithStaticCompiledClasses(); + myJointCompilationApp.addWithDynamicCompiledClasses(); + } +} diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy new file mode 100644 index 0000000000..c0c8c98392 --- /dev/null +++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy @@ -0,0 +1,40 @@ +package com.baeldung.xml + +import groovy.xml.MarkupBuilder +import groovy.xml.XmlUtil +import spock.lang.Specification + +class MarkupBuilderUnitTest extends Specification { + + def xmlFile = getClass().getResource("articles_short_formatted.xml") + +def "Should create XML properly"() { + given: "Node structures" + + when: "Using MarkupBuilderUnitTest to create com.baeldung.xml structure" + def writer = new StringWriter() + new MarkupBuilder(writer).articles { + article { + title('First steps in Java') + author(id: '1') { + firstname('Siena') + lastname('Kerr') + } + 'release-date'('2018-12-01') + } + article { + title('Dockerize your SpringBoot application') + author(id: '2') { + firstname('Jonas') + lastname('Lugo') + } + 'release-date'('2018-12-01') + } + } + + then: "Xml is created properly" + XmlUtil.serialize(writer.toString()) == XmlUtil.serialize(xmlFile.text) +} + + +} diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy new file mode 100644 index 0000000000..ada47406a1 --- /dev/null +++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy @@ -0,0 +1,94 @@ +package com.baeldung.xml + + +import spock.lang.Shared +import spock.lang.Specification + +class XmlParserUnitTest extends Specification { + + def xmlFile = getClass().getResourceAsStream("articles.xml") + + @Shared + def parser = new XmlParser() + + def "Should read XML file properly"() { + given: "XML file" + + when: "Using XmlParser to read file" + def articles = parser.parse(xmlFile) + + then: "Xml is loaded properly" + articles.'*'.size() == 4 + articles.article[0].author.firstname.text() == "Siena" + articles.article[2].'release-date'.text() == "2018-06-12" + articles.article[3].title.text() == "Java 12 insights" + articles.article.find { it.author.'@id'.text() == "3" }.author.firstname.text() == "Daniele" + } + + + def "Should add node to existing com.baeldung.xml using NodeBuilder"() { + given: "XML object" + def articles = parser.parse(xmlFile) + + when: "Adding node to com.baeldung.xml" + def articleNode = new NodeBuilder().article(id: '5') { + title('Traversing XML in the nutshell') + author { + firstname('Martin') + lastname('Schmidt') + } + 'release-date'('2019-05-18') + } + articles.append(articleNode) + + then: "Node is added to com.baeldung.xml properly" + articles.'*'.size() == 5 + articles.article[4].title.text() == "Traversing XML in the nutshell" + } + + def "Should replace node"() { + given: "XML object" + def articles = parser.parse(xmlFile) + + when: "Adding node to com.baeldung.xml" + def articleNode = new NodeBuilder().article(id: '5') { + title('Traversing XML in the nutshell') + author { + firstname('Martin') + lastname('Schmidt') + } + 'release-date'('2019-05-18') + } + articles.article[0].replaceNode(articleNode) + + then: "Node is added to com.baeldung.xml properly" + articles.'*'.size() == 4 + articles.article[0].title.text() == "Traversing XML in the nutshell" + } + + def "Should modify node"() { + given: "XML object" + def articles = parser.parse(xmlFile) + + when: "Changing value of one of the nodes" + articles.article.each { it.'release-date'[0].value = "2019-05-18" } + + then: "XML is updated" + articles.article.findAll { it.'release-date'.text() != "2019-05-18" }.isEmpty() + } + + def "Should remove article from com.baeldung.xml"() { + given: "XML object" + def articles = parser.parse(xmlFile) + + when: "Removing all articles but with id==3" + articles.article + .findAll { it.author.'@id'.text() != "3" } + .each { articles.remove(it) } + + then: "There is only one article left" + articles.children().size() == 1 + articles.article[0].author.'@id'.text() == "3" + } + +} diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy new file mode 100644 index 0000000000..ffeaa46fce --- /dev/null +++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy @@ -0,0 +1,102 @@ +package com.baeldung.xml + + +import groovy.xml.XmlUtil +import spock.lang.Shared +import spock.lang.Specification + +class XmlSlurperUnitTest extends Specification { + + def xmlFile = getClass().getResourceAsStream("articles.xml") + + @Shared + def parser = new XmlSlurper() + + def "Should read XML file properly"() { + given: "XML file" + + when: "Using XmlSlurper to read file" + def articles = parser.parse(xmlFile) + + then: "Xml is loaded properly" + articles.'*'.size() == 4 + articles.article[0].author.firstname == "Siena" + articles.article[2].'release-date' == "2018-06-12" + articles.article[3].title == "Java 12 insights" + articles.article.find { it.author.'@id' == "3" }.author.firstname == "Daniele" + } + + def "Should add node to existing com.baeldung.xml"() { + given: "XML object" + def articles = parser.parse(xmlFile) + + when: "Adding node to com.baeldung.xml" + articles.appendNode { + article(id: '5') { + title('Traversing XML in the nutshell') + author { + firstname('Martin') + lastname('Schmidt') + } + 'release-date'('2019-05-18') + } + } + + articles = parser.parseText(XmlUtil.serialize(articles)) + + then: "Node is added to com.baeldung.xml properly" + articles.'*'.size() == 5 + articles.article[4].title == "Traversing XML in the nutshell" + } + + def "Should modify node"() { + given: "XML object" + def articles = parser.parse(xmlFile) + + when: "Changing value of one of the nodes" + articles.article.each { it.'release-date' = "2019-05-18" } + + then: "XML is updated" + articles.article.findAll { it.'release-date' != "2019-05-18" }.isEmpty() + } + + def "Should replace node"() { + given: "XML object" + def articles = parser.parse(xmlFile) + + when: "Replacing node" + articles.article[0].replaceNode { + article(id: '5') { + title('Traversing XML in the nutshell') + author { + firstname('Martin') + lastname('Schmidt') + } + 'release-date'('2019-05-18') + } + } + + articles = parser.parseText(XmlUtil.serialize(articles)) + + then: "Node is replaced properly" + articles.'*'.size() == 4 + articles.article[0].title == "Traversing XML in the nutshell" + } + + def "Should remove article from com.baeldung.xml"() { + given: "XML object" + def articles = parser.parse(xmlFile) + + when: "Removing all articles but with id==3" + articles.article + .findAll { it.author.'@id' != "3" } + .replaceNode {} + + articles = parser.parseText(XmlUtil.serialize(articles)) + + then: "There is only one article left" + articles.children().size() == 1 + articles.article[0].author.'@id' == "3" + } + +} diff --git a/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml b/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml new file mode 100644 index 0000000000..ef057405f5 --- /dev/null +++ b/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml @@ -0,0 +1,34 @@ + +
+ First steps in Java + + Siena + Kerr + + 2018-12-01 +
+
+ Dockerize your SpringBoot application + + Jonas + Lugo + + 2018-12-01 +
+
+ SpringBoot tutorial + + Daniele + Ferguson + + 2018-06-12 +
+
+ Java 12 insights + + Siena + Kerr + + 2018-07-22 +
+
diff --git a/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml b/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml new file mode 100644 index 0000000000..6492020e03 --- /dev/null +++ b/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml @@ -0,0 +1,18 @@ + +
+ First steps in Java + + Siena + Kerr + + 2018-12-01 +
+
+ Dockerize your SpringBoot application + + Jonas + Lugo + + 2018-12-01 +
+
diff --git a/core-java-modules/README.md b/core-java-modules/README.md index a90535a44f..7a7d0a7a1b 100644 --- a/core-java-modules/README.md +++ b/core-java-modules/README.md @@ -1,3 +1,5 @@ ## Relevant articles: - [Multi-Module Maven Application with Java Modules](https://www.baeldung.com/maven-multi-module-project-java-jpms) +- [Guide to Java FileChannel](https://www.baeldung.com/java-filechannel) +- [Understanding the NumberFormatException in Java](https://www.baeldung.com/java-number-format-exception) diff --git a/core-java-modules/core-java-10/src/main/java/com/baeldung/set/CopySets.java b/core-java-modules/core-java-10/src/main/java/com/baeldung/set/CopySets.java new file mode 100644 index 0000000000..d49724f81d --- /dev/null +++ b/core-java-modules/core-java-10/src/main/java/com/baeldung/set/CopySets.java @@ -0,0 +1,13 @@ +package com.baeldung.set; + +import java.util.Set; + +public class CopySets { + + // Using Java 10 + public static Set copyBySetCopyOf(Set original) { + Set copy = Set.copyOf(original); + return copy; + } + +} diff --git a/core-java-modules/core-java-11/README.md b/core-java-modules/core-java-11/README.md index d68a1c87eb..11c7d9d388 100644 --- a/core-java-modules/core-java-11/README.md +++ b/core-java-modules/core-java-11/README.md @@ -7,4 +7,5 @@ - [Exploring the New HTTP Client in Java 9 and 11](https://www.baeldung.com/java-9-http-client) - [An Introduction to Epsilon GC: A No-Op Experimental Garbage Collector](https://www.baeldung.com/jvm-epsilon-gc-garbage-collector) - [Guide to jlink](https://www.baeldung.com/jlink) +- [Negate a Predicate Method Reference with Java 11](https://www.baeldung.com/java-negate-predicate-method-reference) - [Transforming an Empty String into an Empty Optional](https://www.baeldung.com/java-empty-string-to-empty-optional) diff --git a/core-java-modules/core-java-8-2/README.md b/core-java-modules/core-java-8-2/README.md index 383fe5da34..d53b731878 100644 --- a/core-java-modules/core-java-8-2/README.md +++ b/core-java-modules/core-java-8-2/README.md @@ -3,5 +3,6 @@ ## Core Java 8 Cookbooks and Examples (part 2) ### Relevant Articles: -- [Anonymous Classes in Java](https://www.baeldung.com/java-anonymous-classes) +- [Anonymous Classes in Java](http://www.baeldung.com/) +- [How to Delay Code Execution in Java](https://www.baeldung.com/java-delay-code-execution) - [Run JAR Application With Command Line Arguments](https://www.baeldung.com/java-run-jar-with-arguments) diff --git a/core-java-modules/core-java-8-2/pom.xml b/core-java-modules/core-java-8-2/pom.xml index ff2e290086..cc184de529 100644 --- a/core-java-modules/core-java-8-2/pom.xml +++ b/core-java-modules/core-java-8-2/pom.xml @@ -22,9 +22,6 @@ 1.8 1.8 64.2 - 5.4.0.Final - 1.4.197 - 2.9.8 @@ -33,21 +30,6 @@ icu4j ${icu.version} - - org.hibernate - hibernate-core - ${hibernate.core.version} - - - com.h2database - h2 - ${h2database.version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.databind.version} - diff --git a/core-java-modules/core-java-arrays/README.md b/core-java-modules/core-java-arrays/README.md index ed8221ebe4..b5f71cc253 100644 --- a/core-java-modules/core-java-arrays/README.md +++ b/core-java-modules/core-java-arrays/README.md @@ -15,3 +15,4 @@ - [Intersection Between two Integer Arrays](https://www.baeldung.com/java-array-intersection) - [Sorting Arrays in Java](https://www.baeldung.com/java-sorting-arrays) - [Convert a Float to a Byte Array in Java](https://www.baeldung.com/java-convert-float-to-byte-array) +- [Converting Between Stream and Array in Java](https://www.baeldung.com/java-stream-to-array) diff --git a/core-java-modules/core-java-collections-set/pom.xml b/core-java-modules/core-java-collections-set/pom.xml index 2a930efde8..4435f8b151 100644 --- a/core-java-modules/core-java-collections-set/pom.xml +++ b/core-java-modules/core-java-collections-set/pom.xml @@ -24,10 +24,20 @@ commons-collections4 ${commons-collections4.version} + + com.google.code.gson + gson + 2.8.5 + + + commons-lang + commons-lang + 2.6 + 4.3 27.1-jre - + \ No newline at end of file diff --git a/core-java-modules/core-java-collections-set/src/main/java/com/baeldung/set/CopySets.java b/core-java-modules/core-java-collections-set/src/main/java/com/baeldung/set/CopySets.java new file mode 100644 index 0000000000..53933e4439 --- /dev/null +++ b/core-java-modules/core-java-collections-set/src/main/java/com/baeldung/set/CopySets.java @@ -0,0 +1,59 @@ +package com.baeldung.set; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.lang.SerializationUtils; + +import com.google.gson.Gson; + +public class CopySets { + + // Copy Constructor + public static Set copyByConstructor(Set original) { + Set copy = new HashSet<>(original); + return copy; + } + + // Set.addAll + public static Set copyBySetAddAll(Set original) { + Set copy = new HashSet<>(); + copy.addAll(original); + return copy; + } + + // Set.clone + public static Set copyBySetClone(HashSet original) { + Set copy = (Set) original.clone(); + return copy; + } + + // JSON + public static Set copyByJson(Set original) { + Gson gson = new Gson(); + String jsonStr = gson.toJson(original); + Set copy = gson.fromJson(jsonStr, Set.class); + + return copy; + } + + // Apache Commons Lang + public static Set copyByApacheCommonsLang(Set original) { + Set copy = new HashSet<>(); + for (T item : original) { + copy.add((T) SerializationUtils.clone(item)); + } + return copy; + } + + // Collectors.toSet + public static Set copyByCollectorsToSet(Set original) { + Set copy = original.stream() + .collect(Collectors.toSet()); + + return copy; + } + +} diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/delay/Delay.java b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/delay/Delay.java similarity index 95% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/delay/Delay.java rename to core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/delay/Delay.java index 0cc0c9487f..1689c09f51 100644 --- a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/delay/Delay.java +++ b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/delay/Delay.java @@ -1,4 +1,4 @@ -package com.baeldung.delay; +package com.baeldung.concurrent.delay; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -61,6 +61,7 @@ public class Delay { executorService.schedule(Delay::someTask1, delayInSeconds, TimeUnit.SECONDS); + executorService.shutdown(); } private static void fixedRateServiceTask(Integer delayInSeconds) { @@ -78,6 +79,7 @@ public class Delay { sf.cancel(true); + executorService.shutdown(); } private static void someTask1() { diff --git a/core-java-modules/core-java-nio/README.md b/core-java-modules/core-java-nio/README.md new file mode 100644 index 0000000000..e73a9850ad --- /dev/null +++ b/core-java-modules/core-java-nio/README.md @@ -0,0 +1,3 @@ +## Relevant articles: + +- [Determine File Creating Date in Java](https://www.baeldung.com/file-creation-date-java) diff --git a/core-java-modules/core-java-nio/src/main/java/com/baeldung/creationdate/CreationDateResolver.java b/core-java-modules/core-java-nio/src/main/java/com/baeldung/creationdate/CreationDateResolver.java new file mode 100644 index 0000000000..6347a6e681 --- /dev/null +++ b/core-java-modules/core-java-nio/src/main/java/com/baeldung/creationdate/CreationDateResolver.java @@ -0,0 +1,35 @@ +package com.baeldung.creationdate; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.time.Instant; +import java.util.Optional; + +public class CreationDateResolver { + + public Instant resolveCreationTimeWithBasicAttributes(Path path) { + try { + final BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class); + final FileTime fileTime = attr.creationTime(); + + return fileTime.toInstant(); + } catch (IOException ex) { + throw new RuntimeException("An issue occured went wrong when resolving creation time", ex); + } + } + + public Optional resolveCreationTimeWithAttribute(Path path) { + try { + final FileTime creationTime = (FileTime) Files.getAttribute(path, "creationTime"); + + return Optional + .ofNullable(creationTime) + .map((FileTime::toInstant)); + } catch (IOException ex) { + throw new RuntimeException("An issue occured went wrong when resolving creation time", ex); + } + } +} diff --git a/core-java-modules/core-java-nio/src/test/java/com/baeldung/creationdate/CreationDateResolverUnitTest.java b/core-java-modules/core-java-nio/src/test/java/com/baeldung/creationdate/CreationDateResolverUnitTest.java new file mode 100644 index 0000000000..5402852c74 --- /dev/null +++ b/core-java-modules/core-java-nio/src/test/java/com/baeldung/creationdate/CreationDateResolverUnitTest.java @@ -0,0 +1,45 @@ +package com.baeldung.creationdate; + +import org.junit.Test; + +import java.io.File; +import java.nio.file.Path; +import java.time.Instant; +import java.util.Optional; + +import static org.junit.Assert.assertTrue; + +public class CreationDateResolverUnitTest { + + private final CreationDateResolver creationDateResolver = new CreationDateResolver(); + + @Test + public void givenFile_whenGettingCreationDateTimeFromBasicAttributes_thenReturnDate() throws Exception { + + final File file = File.createTempFile("createdFile", ".txt"); + final Path path = file.toPath(); + + final Instant response = creationDateResolver.resolveCreationTimeWithBasicAttributes(path); + + assertTrue(Instant + .now() + .isAfter(response)); + + } + + @Test + public void givenFile_whenGettingCreationDateTimeFromAttribute_thenReturnDate() throws Exception { + + final File file = File.createTempFile("createdFile", ".txt"); + final Path path = file.toPath(); + + final Optional response = creationDateResolver.resolveCreationTimeWithAttribute(path); + + response.ifPresent((value) -> { + assertTrue(Instant + .now() + .isAfter(value)); + }); + + } +} diff --git a/core-java-modules/core-java-optional/README.md b/core-java-modules/core-java-optional/README.md new file mode 100644 index 0000000000..12a6fd1a56 --- /dev/null +++ b/core-java-modules/core-java-optional/README.md @@ -0,0 +1,5 @@ +========= + +## Core Java Optional + +### Relevant Articles: \ No newline at end of file diff --git a/core-java-modules/core-java-optional/pom.xml b/core-java-modules/core-java-optional/pom.xml new file mode 100644 index 0000000000..f2478c2c87 --- /dev/null +++ b/core-java-modules/core-java-optional/pom.xml @@ -0,0 +1,53 @@ + + 4.0.0 + + com.baeldung.core-java-modules + core-java-modules + 1.0.0-SNAPSHOT + + core-java-optional + 0.1.0-SNAPSHOT + jar + + + UTF-8 + 1.8 + 1.8 + 5.4.0.Final + 1.4.197 + 2.9.8 + + + + + org.hibernate + hibernate-core + ${hibernate.core.version} + + + com.h2database + h2 + ${h2database.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.databind.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + \ No newline at end of file diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/HandleOptionalTypeExample.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/HandleOptionalTypeExample.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/HandleOptionalTypeExample.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/HandleOptionalTypeExample.java diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/OptionalToJsonExample.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/OptionalToJsonExample.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/OptionalToJsonExample.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/OptionalToJsonExample.java diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample.java diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample2.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample2.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample2.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/PersistOptionalTypeExample2.java diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistUserExample.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/PersistUserExample.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/PersistUserExample.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/PersistUserExample.java diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/SerializeOptionalTypeExample.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/SerializeOptionalTypeExample.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/SerializeOptionalTypeExample.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/SerializeOptionalTypeExample.java diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/User.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/User.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/User.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/User.java diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptional.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/UserOptional.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptional.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/UserOptional.java diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptionalField.java b/core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/UserOptionalField.java similarity index 100% rename from core-java-modules/core-java-8-2/src/main/java/com/baeldung/optionalReturnType/UserOptionalField.java rename to core-java-modules/core-java-optional/src/main/java/com/baeldung/optionalReturnType/UserOptionalField.java diff --git a/core-java-modules/core-java/README.md b/core-java-modules/core-java/README.md index 65fb5ddb9d..b32b5d5cd9 100644 --- a/core-java-modules/core-java/README.md +++ b/core-java-modules/core-java/README.md @@ -51,4 +51,5 @@ - [Java Bitwise Operators](https://www.baeldung.com/java-bitwise-operators) - [Guide to Creating and Running a Jar File in Java](https://www.baeldung.com/java-create-jar) - [Making a JSON POST Request With HttpURLConnection](https://www.baeldung.com/httpurlconnection-post) +- [How to Find an Exception’s Root Cause in Java](https://www.baeldung.com/java-exception-root-cause) - [Convert Hex to ASCII in Java](https://www.baeldung.com/java-convert-hex-to-ascii) diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index 2b563a7be4..11a1003460 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -16,6 +16,7 @@ pre-jpms core-java-exceptions + core-java-optional diff --git a/ddd/README.md b/ddd/README.md index 4275bc26b3..6b68fe2205 100644 --- a/ddd/README.md +++ b/ddd/README.md @@ -1,4 +1,5 @@ ### Relevant articles - [Persisting DDD Aggregates](https://www.baeldung.com/spring-persisting-ddd-aggregates) +- [Double Dispatch in DDD](https://www.baeldung.com/ddd-double-dispatch) - [DDD Aggregates and @DomainEvents](https://www.baeldung.com/spring-data-ddd) diff --git a/jackson-2/pom.xml b/jackson-2/pom.xml index 6b973dd6f5..6a975f1de7 100644 --- a/jackson-2/pom.xml +++ b/jackson-2/pom.xml @@ -28,6 +28,13 @@ jackson-dataformat-yaml 2.9.8 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-csv + 2.9.8 + diff --git a/jackson-2/src/main/java/com/baeldung/jackson/csv/JsonCsvConverter.java b/jackson-2/src/main/java/com/baeldung/jackson/csv/JsonCsvConverter.java new file mode 100644 index 0000000000..71c6de4d7e --- /dev/null +++ b/jackson-2/src/main/java/com/baeldung/jackson/csv/JsonCsvConverter.java @@ -0,0 +1,59 @@ +package com.baeldung.jackson.csv; + +import java.io.File; +import java.io.IOException; + +import com.baeldung.jackson.entities.OrderLine; +import com.baeldung.jackson.mixin.OrderLineForCsv; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.MappingIterator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.csv.CsvMapper; +import com.fasterxml.jackson.dataformat.csv.CsvSchema; +import com.fasterxml.jackson.dataformat.csv.CsvSchema.Builder; + +public class JsonCsvConverter { + + public static void JsonToCsv(File jsonFile, File csvFile) throws IOException { + JsonNode jsonTree = new ObjectMapper().readTree(jsonFile); + + Builder csvSchemaBuilder = CsvSchema.builder(); + JsonNode firstObject = jsonTree.elements().next(); + firstObject.fieldNames().forEachRemaining(fieldName -> {csvSchemaBuilder.addColumn(fieldName);} ); + CsvSchema csvSchema = csvSchemaBuilder + .build() + .withHeader(); + + CsvMapper csvMapper = new CsvMapper(); + csvMapper.writerFor(JsonNode.class) + .with(csvSchema) + .writeValue(csvFile, jsonTree); + } + + public static void csvToJson(File csvFile, File jsonFile) throws IOException { + CsvSchema orderLineSchema = CsvSchema.emptySchema().withHeader(); + CsvMapper csvMapper = new CsvMapper(); + MappingIterator orderLines = csvMapper.readerFor(OrderLine.class) + .with(orderLineSchema) + .readValues(csvFile); + + new ObjectMapper() + .configure(SerializationFeature.INDENT_OUTPUT, true) + .writeValue(jsonFile, orderLines.readAll()); + } + + public static void JsonToFormattedCsv(File jsonFile, File csvFile) throws IOException { + CsvMapper csvMapper = new CsvMapper(); + CsvSchema csvSchema = csvMapper + .schemaFor(OrderLineForCsv.class) + .withHeader(); + csvMapper.addMixIn(OrderLine.class, OrderLineForCsv.class); + + OrderLine[] orderLines = new ObjectMapper() + .readValue(jsonFile, OrderLine[].class); + csvMapper.writerFor(OrderLine[].class) + .with(csvSchema) + .writeValue(csvFile, orderLines); + } +} diff --git a/jackson-2/src/main/java/com/baeldung/jackson/mixin/OrderLineForCsv.java b/jackson-2/src/main/java/com/baeldung/jackson/mixin/OrderLineForCsv.java new file mode 100644 index 0000000000..05d70a8053 --- /dev/null +++ b/jackson-2/src/main/java/com/baeldung/jackson/mixin/OrderLineForCsv.java @@ -0,0 +1,25 @@ +package com.baeldung.jackson.mixin; + +import java.math.BigDecimal; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonPropertyOrder({ + "count", + "name" +}) +public abstract class OrderLineForCsv { + + @JsonProperty("name") + private String item; + + @JsonProperty("count") + private int quantity; + + @JsonIgnore + private BigDecimal unitPrice; + + +} diff --git a/jackson-2/src/main/resources/orderLines.csv b/jackson-2/src/main/resources/orderLines.csv new file mode 100644 index 0000000000..e15e12f2bf --- /dev/null +++ b/jackson-2/src/main/resources/orderLines.csv @@ -0,0 +1,3 @@ +item,quantity,unitPrice +"No. 9 Sprockets",12,1.23 +"Widget (10mm)",4,3.45 diff --git a/jackson-2/src/main/resources/orderLines.json b/jackson-2/src/main/resources/orderLines.json new file mode 100644 index 0000000000..64f18e1673 --- /dev/null +++ b/jackson-2/src/main/resources/orderLines.json @@ -0,0 +1,9 @@ +[ { + "item" : "No. 9 Sprockets", + "quantity" : 12, + "unitPrice" : 1.23 +}, { + "item" : "Widget (10mm)", + "quantity" : 4, + "unitPrice" : 3.45 +} ] \ No newline at end of file diff --git a/jackson-2/src/test/java/com/baeldung/jackson/csv/CsvUnitTest.java b/jackson-2/src/test/java/com/baeldung/jackson/csv/CsvUnitTest.java new file mode 100644 index 0000000000..60c8ce79f3 --- /dev/null +++ b/jackson-2/src/test/java/com/baeldung/jackson/csv/CsvUnitTest.java @@ -0,0 +1,54 @@ +package com.baeldung.jackson.csv; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; + +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.google.common.io.Files; + + +public class CsvUnitTest { + + @Test + public void givenJsonInput_thenWriteCsv() throws JsonParseException, JsonMappingException, IOException { + JsonCsvConverter.JsonToCsv(new File("src/main/resources/orderLines.json"), + new File("src/main/resources/csvFromJson.csv")); + + assertEquals(readFile("src/main/resources/csvFromJson.csv"), + readFile("src/test/resources/expectedCsvFromJson.csv")); + } + + @Test + public void givenCsvInput_thenWritesJson() throws JsonParseException, JsonMappingException, IOException { + JsonCsvConverter.csvToJson(new File("src/main/resources/orderLines.csv"), + new File("src/main/resources/jsonFromCsv.json")); + + assertEquals(readFile("src/main/resources/jsonFromCsv.json"), + readFile("src/test/resources/expectedJsonFromCsv.json")); + + } + + @Test + public void givenJsonInput_thenWriteFormattedCsvOutput() throws JsonParseException, JsonMappingException, IOException { + JsonCsvConverter.JsonToFormattedCsv(new File("src/main/resources/orderLines.json"), + new File("src/main/resources/formattedCsvFromJson.csv")); + + assertEquals(readFile("src/main/resources/formattedCsvFromJson.csv"), + readFile("src/test/resources/expectedFormattedCsvFromJson.csv")); + + } + + private List readFile(String filename) throws IOException { + return Files.readLines(new File(filename), Charset.forName("utf-8")); + } + + +} +; \ No newline at end of file diff --git a/jackson-2/src/test/resources/expectedCsvFromJson.csv b/jackson-2/src/test/resources/expectedCsvFromJson.csv new file mode 100644 index 0000000000..e15e12f2bf --- /dev/null +++ b/jackson-2/src/test/resources/expectedCsvFromJson.csv @@ -0,0 +1,3 @@ +item,quantity,unitPrice +"No. 9 Sprockets",12,1.23 +"Widget (10mm)",4,3.45 diff --git a/jackson-2/src/test/resources/expectedFormattedCsvFromJson.csv b/jackson-2/src/test/resources/expectedFormattedCsvFromJson.csv new file mode 100644 index 0000000000..5a60ba602a --- /dev/null +++ b/jackson-2/src/test/resources/expectedFormattedCsvFromJson.csv @@ -0,0 +1,3 @@ +count,name +12,"No. 9 Sprockets" +4,"Widget (10mm)" diff --git a/jackson-2/src/test/resources/expectedJsonFromCsv.json b/jackson-2/src/test/resources/expectedJsonFromCsv.json new file mode 100644 index 0000000000..64f18e1673 --- /dev/null +++ b/jackson-2/src/test/resources/expectedJsonFromCsv.json @@ -0,0 +1,9 @@ +[ { + "item" : "No. 9 Sprockets", + "quantity" : 12, + "unitPrice" : 1.23 +}, { + "item" : "Widget (10mm)", + "quantity" : 4, + "unitPrice" : 3.45 +} ] \ No newline at end of file diff --git a/jee-7/README.md b/jee-7/README.md index a783e7860e..c57863651d 100644 --- a/jee-7/README.md +++ b/jee-7/README.md @@ -6,3 +6,4 @@ - [A Guide to Java EE Web-Related Annotations](http://www.baeldung.com/javaee-web-annotations) - [Introduction to Testing with Arquillian](http://www.baeldung.com/arquillian) - [Java EE 7 Batch Processing](https://www.baeldung.com/java-ee-7-batch-processing) +- [The Difference Between CDI and EJB Singleton](https://www.baeldung.com/jee-cdi-vs-ejb-singleton) diff --git a/jersey/README.md b/jersey/README.md index 1dd871b3e8..126dc542ba 100644 --- a/jersey/README.md +++ b/jersey/README.md @@ -3,3 +3,4 @@ - [Bean Validation in Jersey](https://www.baeldung.com/jersey-bean-validation) - [Set a Response Body in JAX-RS](https://www.baeldung.com/jax-rs-response) - [Exploring the Jersey Test Framework](https://www.baeldung.com/jersey-test) +- [Explore Jersey Request Parameters](https://www.baeldung.com/jersey-request-parameters) diff --git a/jersey/src/test/java/com/baeldung/jersey/server/rest/FruitResourceIntegrationTest.java b/jersey/src/test/java/com/baeldung/jersey/server/rest/FruitResourceIntegrationTest.java index 376c8c1e75..f7bb0df1ed 100644 --- a/jersey/src/test/java/com/baeldung/jersey/server/rest/FruitResourceIntegrationTest.java +++ b/jersey/src/test/java/com/baeldung/jersey/server/rest/FruitResourceIntegrationTest.java @@ -26,6 +26,7 @@ public class FruitResourceIntegrationTest extends JerseyTest { protected Application configure() { enable(TestProperties.LOG_TRAFFIC); enable(TestProperties.DUMP_ENTITY); + forceSet(TestProperties.CONTAINER_PORT, "0"); ViewApplicationConfig config = new ViewApplicationConfig(); config.register(FruitExceptionMapper.class); diff --git a/kotlin-libraries-2/README.md b/kotlin-libraries-2/README.md index fdb7c2830d..0b9f238d51 100644 --- a/kotlin-libraries-2/README.md +++ b/kotlin-libraries-2/README.md @@ -1,3 +1,4 @@ ## Relevant articles: - [Jackson Support for Kotlin](https://www.baeldung.com/jackson-kotlin) +- [Introduction to RxKotlin](https://www.baeldung.com/rxkotlin) diff --git a/libraries-2/pom.xml b/libraries-2/pom.xml index 6303c0cab5..8e493e2d05 100644 --- a/libraries-2/pom.xml +++ b/libraries-2/pom.xml @@ -55,6 +55,39 @@ spring-boot-starter ${spring-boot-starter.version} + + + + com.squareup.okhttp3 + okhttp + 3.14.2 + + + + com.fasterxml.jackson.core + jackson-databind + 2.9.9 + + + + com.google.code.gson + gson + 2.8.5 + + + + com.squareup.okhttp3 + mockwebserver + 3.14.2 + test + + + + edu.uci.ics + crawler4j + ${crawler4j.version} + + @@ -62,6 +95,7 @@ 4.8.28 6.0.0.Final 3.9.6 + 4.4.0 2.1.4.RELEASE diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/CrawlerStatistics.java b/libraries-2/src/main/java/com/baeldung/crawler4j/CrawlerStatistics.java new file mode 100644 index 0000000000..e3237a2755 --- /dev/null +++ b/libraries-2/src/main/java/com/baeldung/crawler4j/CrawlerStatistics.java @@ -0,0 +1,22 @@ +package com.baeldung.crawler4j; + +public class CrawlerStatistics { + private int processedPageCount = 0; + private int totalLinksCount = 0; + + public void incrementProcessedPageCount() { + processedPageCount++; + } + + public void incrementTotalLinksCount(int linksCount) { + totalLinksCount += linksCount; + } + + public int getProcessedPageCount() { + return processedPageCount; + } + + public int getTotalLinksCount() { + return totalLinksCount; + } +} diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawler.java b/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawler.java new file mode 100644 index 0000000000..b77e1e075f --- /dev/null +++ b/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawler.java @@ -0,0 +1,48 @@ +package com.baeldung.crawler4j; + +import java.util.Set; +import java.util.regex.Pattern; + +import edu.uci.ics.crawler4j.crawler.Page; +import edu.uci.ics.crawler4j.crawler.WebCrawler; +import edu.uci.ics.crawler4j.parser.HtmlParseData; +import edu.uci.ics.crawler4j.url.WebURL; + +public class HtmlCrawler extends WebCrawler { + + private final static Pattern EXCLUSIONS = Pattern.compile(".*(\\.(css|js|xml|gif|jpg|png|mp3|mp4|zip|gz|pdf))$"); + + private CrawlerStatistics stats; + + public HtmlCrawler(CrawlerStatistics stats) { + this.stats = stats; + } + + @Override + public boolean shouldVisit(Page referringPage, WebURL url) { + String urlString = url.getURL().toLowerCase(); + return !EXCLUSIONS.matcher(urlString).matches() + && urlString.startsWith("https://www.baeldung.com/"); + } + + @Override + public void visit(Page page) { + String url = page.getWebURL().getURL(); + stats.incrementProcessedPageCount(); + + if (page.getParseData() instanceof HtmlParseData) { + HtmlParseData htmlParseData = (HtmlParseData) page.getParseData(); + String title = htmlParseData.getTitle(); + String text = htmlParseData.getText(); + String html = htmlParseData.getHtml(); + Set links = htmlParseData.getOutgoingUrls(); + stats.incrementTotalLinksCount(links.size()); + + System.out.printf("Page with title '%s' %n", title); + System.out.printf(" Text length: %d %n", text.length()); + System.out.printf(" HTML length: %d %n", html.length()); + System.out.printf(" %d outbound links %n", links.size()); + } + } + +} diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawlerController.java b/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawlerController.java new file mode 100644 index 0000000000..82c1c6bdd7 --- /dev/null +++ b/libraries-2/src/main/java/com/baeldung/crawler4j/HtmlCrawlerController.java @@ -0,0 +1,36 @@ +package com.baeldung.crawler4j; + +import java.io.File; + +import edu.uci.ics.crawler4j.crawler.CrawlConfig; +import edu.uci.ics.crawler4j.crawler.CrawlController; +import edu.uci.ics.crawler4j.fetcher.PageFetcher; +import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig; +import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer; + +public class HtmlCrawlerController { + + public static void main(String[] args) throws Exception { + File crawlStorage = new File("src/test/resources/crawler4j"); + CrawlConfig config = new CrawlConfig(); + config.setCrawlStorageFolder(crawlStorage.getAbsolutePath()); + config.setMaxDepthOfCrawling(2); + + int numCrawlers = 12; + + PageFetcher pageFetcher = new PageFetcher(config); + RobotstxtConfig robotstxtConfig = new RobotstxtConfig(); + RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher); + CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer); + + controller.addSeed("https://www.baeldung.com/"); + + CrawlerStatistics stats = new CrawlerStatistics(); + CrawlController.WebCrawlerFactory factory = () -> new HtmlCrawler(stats); + + controller.start(factory, numCrawlers); + System.out.printf("Crawled %d pages %n", stats.getProcessedPageCount()); + System.out.printf("Total Number of outbound links = %d %n", stats.getTotalLinksCount()); + } + +} diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawler.java b/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawler.java new file mode 100644 index 0000000000..ebb5d96f36 --- /dev/null +++ b/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawler.java @@ -0,0 +1,49 @@ +package com.baeldung.crawler4j; + +import java.io.File; +import java.util.regex.Pattern; + +import edu.uci.ics.crawler4j.crawler.Page; +import edu.uci.ics.crawler4j.crawler.WebCrawler; +import edu.uci.ics.crawler4j.parser.BinaryParseData; +import edu.uci.ics.crawler4j.url.WebURL; + +public class ImageCrawler extends WebCrawler { + private final static Pattern EXCLUSIONS = Pattern.compile(".*(\\.(css|js|xml|gif|png|mp3|mp4|zip|gz|pdf))$"); + + private static final Pattern IMG_PATTERNS = Pattern.compile(".*(\\.(jpg|jpeg))$"); + + private File saveDir; + + public ImageCrawler(File saveDir) { + this.saveDir = saveDir; + } + + @Override + public boolean shouldVisit(Page referringPage, WebURL url) { + String urlString = url.getURL().toLowerCase(); + if (EXCLUSIONS.matcher(urlString).matches()) { + return false; + } + + if (IMG_PATTERNS.matcher(urlString).matches() + || urlString.startsWith("https://www.baeldung.com/")) { + return true; + } + + return false; + } + + @Override + public void visit(Page page) { + String url = page.getWebURL().getURL(); + if (IMG_PATTERNS.matcher(url).matches() + && page.getParseData() instanceof BinaryParseData) { + String extension = url.substring(url.lastIndexOf(".")); + int contentLength = page.getContentData().length; + + System.out.printf("Extension is '%s' with content length %d %n", extension, contentLength); + } + } + +} diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawlerController.java b/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawlerController.java new file mode 100644 index 0000000000..9db32f1bfb --- /dev/null +++ b/libraries-2/src/main/java/com/baeldung/crawler4j/ImageCrawlerController.java @@ -0,0 +1,36 @@ +package com.baeldung.crawler4j; + +import java.io.File; + +import edu.uci.ics.crawler4j.crawler.CrawlConfig; +import edu.uci.ics.crawler4j.crawler.CrawlController; +import edu.uci.ics.crawler4j.fetcher.PageFetcher; +import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig; +import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer; + +public class ImageCrawlerController { + + public static void main(String[] args) throws Exception { + File crawlStorage = new File("src/test/resources/crawler4j"); + CrawlConfig config = new CrawlConfig(); + config.setCrawlStorageFolder(crawlStorage.getAbsolutePath()); + config.setIncludeBinaryContentInCrawling(true); + config.setMaxPagesToFetch(500); + + File saveDir = new File("src/test/resources/crawler4j"); + + int numCrawlers = 12; + + PageFetcher pageFetcher = new PageFetcher(config); + RobotstxtConfig robotstxtConfig = new RobotstxtConfig(); + RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher); + CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer); + + controller.addSeed("https://www.baeldung.com/"); + + CrawlController.WebCrawlerFactory factory = () -> new ImageCrawler(saveDir); + + controller.start(factory, numCrawlers); + } + +} diff --git a/libraries-2/src/main/java/com/baeldung/crawler4j/MultipleCrawlerController.java b/libraries-2/src/main/java/com/baeldung/crawler4j/MultipleCrawlerController.java new file mode 100644 index 0000000000..7662607f80 --- /dev/null +++ b/libraries-2/src/main/java/com/baeldung/crawler4j/MultipleCrawlerController.java @@ -0,0 +1,54 @@ +package com.baeldung.crawler4j; + +import java.io.File; + +import edu.uci.ics.crawler4j.crawler.CrawlConfig; +import edu.uci.ics.crawler4j.crawler.CrawlController; +import edu.uci.ics.crawler4j.fetcher.PageFetcher; +import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig; +import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer; + +public class MultipleCrawlerController { + public static void main(String[] args) throws Exception { + File crawlStorageBase = new File("src/test/resources/crawler4j"); + CrawlConfig htmlConfig = new CrawlConfig(); + CrawlConfig imageConfig = new CrawlConfig(); + + htmlConfig.setCrawlStorageFolder(new File(crawlStorageBase, "html").getAbsolutePath()); + imageConfig.setCrawlStorageFolder(new File(crawlStorageBase, "image").getAbsolutePath()); + imageConfig.setIncludeBinaryContentInCrawling(true); + + htmlConfig.setMaxPagesToFetch(500); + imageConfig.setMaxPagesToFetch(1000); + + PageFetcher pageFetcherHtml = new PageFetcher(htmlConfig); + PageFetcher pageFetcherImage = new PageFetcher(imageConfig); + + RobotstxtConfig robotstxtConfig = new RobotstxtConfig(); + RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcherHtml); + + CrawlController htmlController = new CrawlController(htmlConfig, pageFetcherHtml, robotstxtServer); + CrawlController imageController = new CrawlController(imageConfig, pageFetcherImage, robotstxtServer); + + htmlController.addSeed("https://www.baeldung.com/"); + imageController.addSeed("https://www.baeldung.com/"); + + CrawlerStatistics stats = new CrawlerStatistics(); + CrawlController.WebCrawlerFactory htmlFactory = () -> new HtmlCrawler(stats); + + File saveDir = new File("src/test/resources/crawler4j"); + CrawlController.WebCrawlerFactory imageFactory = () -> new ImageCrawler(saveDir); + + imageController.startNonBlocking(imageFactory, 7); + htmlController.startNonBlocking(htmlFactory, 10); + + + htmlController.waitUntilFinish(); + System.out.printf("Crawled %d pages %n", stats.getProcessedPageCount()); + System.out.printf("Total Number of outbound links = %d %n", stats.getTotalLinksCount()); + + imageController.waitUntilFinish(); + System.out.printf("Image Crawler is finished."); + + } +} diff --git a/libraries-2/src/test/java/com/baeldung/okhttp/ResponseDecoderUnitTest.java b/libraries-2/src/test/java/com/baeldung/okhttp/ResponseDecoderUnitTest.java new file mode 100644 index 0000000000..11a295031a --- /dev/null +++ b/libraries-2/src/test/java/com/baeldung/okhttp/ResponseDecoderUnitTest.java @@ -0,0 +1,102 @@ +package com.baeldung.okhttp; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.ResponseBody; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.InputStreamReader; + +public class ResponseDecoderUnitTest { + + @Rule public ExpectedException exceptionRule = ExpectedException.none(); + + @Rule public MockWebServer server = new MockWebServer(); + + SimpleEntity sampleResponse; + + MockResponse mockResponse; + + OkHttpClient client; + + @Before + public void setUp() { + sampleResponse = new SimpleEntity("Baeldung"); + client = new OkHttpClient.Builder().build(); + mockResponse = new MockResponse() + .setResponseCode(200) + .setHeader("Content-Type", "application/json") + .setBody(new Gson().toJson(sampleResponse)); + } + + @Test + public void givenJacksonDecoder_whenGetStringOfResponse_thenExpectSimpleEntity() throws Exception { + server.enqueue(mockResponse); + Request request = new Request.Builder() + .url(server.url("")) + .build(); + ResponseBody responseBody = client + .newCall(request) + .execute() + .body(); + + Assert.assertNotNull(responseBody); + Assert.assertNotEquals(0, responseBody.contentLength()); + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleEntity entity = objectMapper.readValue(responseBody.string(), SimpleEntity.class); + + Assert.assertNotNull(entity); + Assert.assertEquals(sampleResponse.getName(), entity.getName()); + } + + @Test + public void givenGsonDecoder_whenGetByteStreamOfResponse_thenExpectSimpleEntity() throws Exception { + server.enqueue(mockResponse); + Request request = new Request.Builder() + .url(server.url("")) + .build(); + ResponseBody responseBody = client + .newCall(request) + .execute() + .body(); + + Assert.assertNotNull(responseBody); + Assert.assertNotEquals(0, responseBody.contentLength()); + + Gson gson = new Gson(); + SimpleEntity entity = gson.fromJson(new InputStreamReader(responseBody.byteStream()), SimpleEntity.class); + + Assert.assertNotNull(entity); + Assert.assertEquals(sampleResponse.getName(), entity.getName()); + } + + @Test + public void givenGsonDecoder_whenGetStringOfResponse_thenExpectSimpleEntity() throws Exception { + server.enqueue(mockResponse); + Request request = new Request.Builder() + .url(server.url("")) + .build(); + ResponseBody responseBody = client + .newCall(request) + .execute() + .body(); + + Assert.assertNotNull(responseBody); + + Gson gson = new Gson(); + SimpleEntity entity = gson.fromJson(responseBody.string(), SimpleEntity.class); + + Assert.assertNotNull(entity); + Assert.assertEquals(sampleResponse.getName(), entity.getName()); + } + +} diff --git a/libraries-2/src/test/java/com/baeldung/okhttp/SimpleEntity.java b/libraries-2/src/test/java/com/baeldung/okhttp/SimpleEntity.java new file mode 100644 index 0000000000..211e43e556 --- /dev/null +++ b/libraries-2/src/test/java/com/baeldung/okhttp/SimpleEntity.java @@ -0,0 +1,22 @@ +package com.baeldung.okhttp; + +public class SimpleEntity { + protected String name; + + public SimpleEntity(String name) { + this.name = name; + } + + //no-arg constructor, getters and setters here + + public SimpleEntity() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/libraries-data/pom.xml b/libraries-data/pom.xml index 3276ebcdbb..31aaaea951 100644 --- a/libraries-data/pom.xml +++ b/libraries-data/pom.xml @@ -263,6 +263,16 @@ org.apache.storm storm-core ${storm.version} + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + log4j-over-slf4j + + diff --git a/libraries-data/src/test/java/com/baeldung/flink/BackupCreatorIntegrationTest.java b/libraries-data/src/test/java/com/baeldung/flink/BackupCreatorIntegrationTest.java index ab7d119c16..f46fffbb59 100644 --- a/libraries-data/src/test/java/com/baeldung/flink/BackupCreatorIntegrationTest.java +++ b/libraries-data/src/test/java/com/baeldung/flink/BackupCreatorIntegrationTest.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; public class BackupCreatorIntegrationTest { @@ -88,7 +89,7 @@ public class BackupCreatorIntegrationTest { SerializationSchema serializationSchema = new BackupSerializationSchema(); byte[] backupProcessed = serializationSchema.serialize(backup); - assertEquals(backupSerialized, backupProcessed); + assertArrayEquals(backupSerialized, backupProcessed); } private static class CollectingSink implements SinkFunction { diff --git a/libraries/src/main/java/com/baeldung/ftp/FtpClient.java b/libraries/src/main/java/com/baeldung/ftp/FtpClient.java index 209bed35f0..f885ff13b3 100644 --- a/libraries/src/main/java/com/baeldung/ftp/FtpClient.java +++ b/libraries/src/main/java/com/baeldung/ftp/FtpClient.java @@ -59,5 +59,6 @@ class FtpClient { void downloadFile(String source, String destination) throws IOException { FileOutputStream out = new FileOutputStream(destination); ftp.retrieveFile(source, out); + out.close(); } } diff --git a/libraries/src/test/java/com/baeldung/serenity/spring/AdderMethodDirtiesContextIntegrationTest.java b/libraries/src/test/java/com/baeldung/serenity/spring/AdderMethodDirtiesContextIntegrationTest.java index fc7067520d..3201908bf7 100644 --- a/libraries/src/test/java/com/baeldung/serenity/spring/AdderMethodDirtiesContextIntegrationTest.java +++ b/libraries/src/test/java/com/baeldung/serenity/spring/AdderMethodDirtiesContextIntegrationTest.java @@ -28,7 +28,7 @@ public class AdderMethodDirtiesContextIntegrationTest { @Test public void _1_givenNumber_whenAdd_thenSumWrong() { adderServiceSteps.whenAdd(); - adderServiceSteps.sumWrong(); + adderServiceSteps.summedUp(); } @Rule diff --git a/logging-modules/log4j2/pom.xml b/logging-modules/log4j2/pom.xml index 14420356de..db0c6eb659 100644 --- a/logging-modules/log4j2/pom.xml +++ b/logging-modules/log4j2/pom.xml @@ -78,7 +78,7 @@ - integration + integration-lite-first diff --git a/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/appender/MapAppenderIntegrationTest.java b/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/appender/MapAppenderIntegrationTest.java index 020aaafc74..e340ebb784 100644 --- a/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/appender/MapAppenderIntegrationTest.java +++ b/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/appender/MapAppenderIntegrationTest.java @@ -23,7 +23,7 @@ public class MapAppenderIntegrationTest { @Test public void whenLoggerEmitsLoggingEvent_thenAppenderReceivesEvent() throws Exception { - logger.info("Test from {}", this.getClass() + logger.error("Error log message from {}", this.getClass() .getSimpleName()); LoggerContext context = LoggerContext.getContext(false); Configuration config = context.getConfiguration(); diff --git a/persistence-modules/core-java-persistence/src/test/java/com/baeldung/jdbc/joins/ArticleWithAuthorDAOIntegrationTest.java b/persistence-modules/core-java-persistence/src/test/java/com/baeldung/jdbc/joins/ArticleWithAuthorDAOLiveTest.java similarity index 98% rename from persistence-modules/core-java-persistence/src/test/java/com/baeldung/jdbc/joins/ArticleWithAuthorDAOIntegrationTest.java rename to persistence-modules/core-java-persistence/src/test/java/com/baeldung/jdbc/joins/ArticleWithAuthorDAOLiveTest.java index 5c20b6bf1e..3f69a0e333 100644 --- a/persistence-modules/core-java-persistence/src/test/java/com/baeldung/jdbc/joins/ArticleWithAuthorDAOIntegrationTest.java +++ b/persistence-modules/core-java-persistence/src/test/java/com/baeldung/jdbc/joins/ArticleWithAuthorDAOLiveTest.java @@ -12,7 +12,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -public class ArticleWithAuthorDAOIntegrationTest { +public class ArticleWithAuthorDAOLiveTest { private Connection connection; private ArticleWithAuthorDAO articleWithAuthorDAO; diff --git a/persistence-modules/hibernate-mapping/pom.xml b/persistence-modules/hibernate-mapping/pom.xml index 6bab3c5b1f..bb8ebdab65 100644 --- a/persistence-modules/hibernate-mapping/pom.xml +++ b/persistence-modules/hibernate-mapping/pom.xml @@ -5,9 +5,9 @@ com.baeldung - parent-modules + persistence-modules 1.0.0-SNAPSHOT - ../../ + .. hibernate-mapping @@ -31,22 +31,28 @@ h2 ${h2.version} - + org.hibernate hibernate-validator ${hibernate-validator.version} - - javax.el - javax.el-api - ${javax.el-api.version} - org.glassfish javax.el ${org.glassfish.javax.el.version} + + javax.money + money-api + 1.0.3 + + + org.javamoney + moneta + 1.3 + pom + @@ -60,11 +66,10 @@ - 5.3.7.Final + 5.3.10.Final 3.8.0 - 5.3.3.Final - 2.2.5 - 3.0.1-b08 + 6.0.16.Final + 3.0.1-b11 - \ No newline at end of file + diff --git a/persistence-modules/hibernate-mapping/src/main/java/com/baeldung/hibernate/persistmaps/mapkey/User.java b/persistence-modules/hibernate-mapping/src/main/java/com/baeldung/hibernate/persistmaps/mapkey/User.java index f6e8f1cdd6..b2ee7e85fe 100644 --- a/persistence-modules/hibernate-mapping/src/main/java/com/baeldung/hibernate/persistmaps/mapkey/User.java +++ b/persistence-modules/hibernate-mapping/src/main/java/com/baeldung/hibernate/persistmaps/mapkey/User.java @@ -4,8 +4,11 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.validation.constraints.Size; +import javax.money.MonetaryAmount; import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.CreditCardNumber; +import org.hibernate.validator.constraints.Currency; @Entity public class User { @@ -62,5 +65,4 @@ public class User { public void setCity(String city) { this.city = city; } - } diff --git a/persistence-modules/hibernate-mapping/src/test/java/com/baeldung/hibernate/validation/UserAdditionalValidationUnitTest.java b/persistence-modules/hibernate-mapping/src/test/java/com/baeldung/hibernate/validation/UserAdditionalValidationUnitTest.java new file mode 100644 index 0000000000..0f2a0403e9 --- /dev/null +++ b/persistence-modules/hibernate-mapping/src/test/java/com/baeldung/hibernate/validation/UserAdditionalValidationUnitTest.java @@ -0,0 +1,290 @@ +package com.baeldung.hibernate.validation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.math.BigDecimal; +import java.time.Duration; +import java.util.Set; + +import javax.money.CurrencyContextBuilder; +import javax.money.Monetary; +import javax.money.MonetaryAmount; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.hibernate.validator.constraints.CodePointLength; +import org.hibernate.validator.constraints.CreditCardNumber; +import org.hibernate.validator.constraints.Currency; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.LuhnCheck; +import org.hibernate.validator.constraints.Range; +import org.hibernate.validator.constraints.SafeHtml; +import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.constraints.URL; +import org.hibernate.validator.constraints.time.DurationMax; +import org.hibernate.validator.constraints.time.DurationMin; +import org.javamoney.moneta.CurrencyUnitBuilder; +import org.javamoney.moneta.Money; +import org.junit.BeforeClass; +import org.junit.Test; + +public class UserAdditionalValidationUnitTest { + + private static Validator validator; + private Set> constraintViolations; + + @BeforeClass + public static void before() { + ValidatorFactory config = Validation.buildDefaultValidatorFactory(); + validator = config.getValidator(); + } + + @Test + public void whenValidationWithCCNAndNullCCN_thenNoConstraintViolation() { + AdditionalValidations validations = new AdditionalValidations(); + constraintViolations = validator.validateProperty(validations, "creditCardNumber"); + assertTrue(constraintViolations.isEmpty()); + } + + @Test + public void whenValidationWithCCNAndValidCCN_thenNoConstraintViolation() { + AdditionalValidations validations = new AdditionalValidations(); + validations.setCreditCardNumber("79927398713"); + constraintViolations = validator.validateProperty(validations, "creditCardNumber"); + assertTrue(constraintViolations.isEmpty()); + } + + @Test + public void whenValidationWithCCNAndInvalidCCN_thenConstraintViolation() { + AdditionalValidations validations = new AdditionalValidations(); + validations.setCreditCardNumber("79927398714"); + constraintViolations = validator.validateProperty(validations, "creditCardNumber"); + assertEquals(constraintViolations.size(), 2); + } + + @Test + public void whenValidationWithCCNAndValidCCNWithDashes_thenConstraintViolation() { + AdditionalValidations validations = new AdditionalValidations(); + validations.setCreditCardNumber("7992-7398-713"); + constraintViolations = validator.validateProperty(validations, "creditCardNumber"); + assertEquals(1, constraintViolations.size()); + } + + @Test + public void whenValidationWithLenientCCNAndValidCCNWithDashes_thenNoConstraintViolation() { + AdditionalValidations validations = new AdditionalValidations(); + validations.setLenientCreditCardNumber("7992-7398-713"); + constraintViolations = validator.validateProperty(validations, "lenientCreditCardNumber"); + assertTrue(constraintViolations.isEmpty()); + } + + @Test + public void whenMonetaryAmountWithRightCurrency_thenNoConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setBalance(Money.of(new BigDecimal(100.0), Monetary.getCurrency("EUR"))); + constraintViolations = validator.validateProperty(bean, "balance"); + assertEquals(0, constraintViolations.size()); + } + + @Test + public void whenMonetaryAmountWithWrongCurrency_thenConstraintViolation() { + AdditionalValidations validations = new AdditionalValidations(); + validations.setBalance(Money.of(new BigDecimal(100.0), Monetary.getCurrency("USD"))); + constraintViolations = validator.validateProperty(validations, "balance"); + assertEquals(1, constraintViolations.size()); + } + + @Test + public void whenDurationShorterThanMin_thenConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setDuration(Duration.ofDays(1).plusHours(1)); + constraintViolations = validator.validateProperty(bean, "duration"); + assertEquals(1, constraintViolations.size()); + } + + @Test + public void whenDurationLongerThanMax_thenConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setDuration(Duration.ofDays(2).plusHours(3)); + constraintViolations = validator.validateProperty(bean, "duration"); + assertEquals(1, constraintViolations.size()); + } + + @Test + public void whenDurationBetweenMinAndMax_thenNoConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setDuration(Duration.ofDays(2)); + constraintViolations = validator.validateProperty(bean, "duration"); + assertEquals(0, constraintViolations.size()); + } + + @Test + public void whenValueBelowRangeMin_thenConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setPercent(new BigDecimal("-1.4")); + constraintViolations = validator.validateProperty(bean, "percent"); + assertEquals(1, constraintViolations.size()); + } + + @Test + public void whenValueAboveRangeMax_thenConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setPercent(new BigDecimal("100.03")); + constraintViolations = validator.validateProperty(bean, "percent"); + assertEquals(1, constraintViolations.size()); + } + + @Test + public void whenValueInRange_thenNoConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setPercent(new BigDecimal("53.23")); + constraintViolations = validator.validateProperty(bean, "percent"); + assertEquals(0, constraintViolations.size()); + } + + @Test + public void whenLengthInRange_thenNoConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setSomeString("aaa"); + constraintViolations = validator.validateProperty(bean, "someString"); + assertEquals(0, constraintViolations.size()); + } + + @Test + public void whenCodePointLengthNotInRange_thenConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + bean.setSomeString("aa\uD835\uDD0A"); + constraintViolations = validator.validateProperty(bean, "someString"); + assertEquals(1, constraintViolations.size()); + } + + @Test + public void whenValidUrlWithWrongProtocol_thenConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + + bean.setUrl("https://www.google.com/"); + constraintViolations = validator.validateProperty(bean, "url"); + assertEquals(0, constraintViolations.size()); + + bean.setUrl("http://www.google.com/"); + constraintViolations = validator.validateProperty(bean, "url"); + assertEquals(1, constraintViolations.size()); + + bean.setUrl("https://foo:bar"); + constraintViolations = validator.validateProperty(bean, "url"); + assertEquals(1, constraintViolations.size()); + } + + @Test + public void whenScriptAssertFails_thenConstraintViolation() { + AdditionalValidations bean = new AdditionalValidations(); + + constraintViolations = validator.validate(bean); + assertEquals(0, constraintViolations.size()); + + bean.setValid(false); + + constraintViolations = validator.validate(bean); + assertEquals(1, constraintViolations.size()); + + constraintViolations = validator.validateProperty(bean, "valid"); + assertEquals(0, constraintViolations.size()); + } + + @ScriptAssert(lang = "nashorn", script = "_this.valid") + public class AdditionalValidations { + private boolean valid = true; + + @CreditCardNumber + @LuhnCheck(startIndex = 0, endIndex = Integer.MAX_VALUE, checkDigitIndex = -1) + private String creditCardNumber; + + @CreditCardNumber(ignoreNonDigitCharacters = true) + private String lenientCreditCardNumber; + + @Currency("EUR") + private MonetaryAmount balance; + + @DurationMin(days = 1, hours = 2) + @DurationMax(days = 2, hours = 1) + private Duration duration; + + @Range(min = 0, max = 100) + private BigDecimal percent; + + @Length(min = 1, max = 3) + @CodePointLength(min = 1, max = 3) + private String someString; + + @URL(protocol = "https") + private String url; + + public String getCreditCardNumber() { + return creditCardNumber; + } + + public void setCreditCardNumber(String creditCardNumber) { + this.creditCardNumber = creditCardNumber; + } + + public String getLenientCreditCardNumber() { + return lenientCreditCardNumber; + } + + public void setLenientCreditCardNumber(String lenientCreditCardNumber) { + this.lenientCreditCardNumber = lenientCreditCardNumber; + } + + public MonetaryAmount getBalance() { + return balance; + } + + public void setBalance(MonetaryAmount balance) { + this.balance = balance; + } + + public Duration getDuration() { + return duration; + } + + public void setDuration(Duration duration) { + this.duration = duration; + } + + public BigDecimal getPercent() { + return percent; + } + + public void setPercent(BigDecimal percent) { + this.percent = percent; + } + + public String getSomeString() { + return someString; + } + + public void setSomeString(String someString) { + this.someString = someString; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + } +} diff --git a/persistence-modules/java-cassandra/pom.xml b/persistence-modules/java-cassandra/pom.xml index aac5d49547..708d2b3c76 100644 --- a/persistence-modules/java-cassandra/pom.xml +++ b/persistence-modules/java-cassandra/pom.xml @@ -39,6 +39,7 @@ 3.1.2 3.1.1.0 + 18.0 diff --git a/persistence-modules/java-jpa/README.md b/persistence-modules/java-jpa/README.md index ca9ec0d74d..7edcd168e9 100644 --- a/persistence-modules/java-jpa/README.md +++ b/persistence-modules/java-jpa/README.md @@ -8,6 +8,8 @@ - [Converting Between LocalDate and SQL Date](https://www.baeldung.com/java-convert-localdate-sql-date) - [Combining JPA And/Or Criteria Predicates](https://www.baeldung.com/jpa-and-or-criteria-predicates) - [Types of JPA Queries](https://www.baeldung.com/jpa-queries) +- [JPA/Hibernate Projections](https://www.baeldung.com/jpa-hibernate-projections) +- [Composite Primary Keys in JPA](https://www.baeldung.com/jpa-composite-primary-keys) - [Defining JPA Entities](https://www.baeldung.com/jpa-entities) - [JPA @Basic Annotation](https://www.baeldung.com/jpa-basic-annotation) - [Default Column Values in JPA](https://www.baeldung.com/jpa-default-column-values) diff --git a/persistence-modules/spring-data-jpa-2/README.md b/persistence-modules/spring-data-jpa-2/README.md index 6b73729f9a..3bd62cbd45 100644 --- a/persistence-modules/spring-data-jpa-2/README.md +++ b/persistence-modules/spring-data-jpa-2/README.md @@ -1,7 +1,3 @@ -========= - -## Spring Data JPA Example Project - ### Relevant Articles: - [Spring Data JPA – Derived Delete Methods](https://www.baeldung.com/spring-data-jpa-deleteby) - [JPA Join Types](https://www.baeldung.com/jpa-join-types) @@ -15,3 +11,4 @@ - [Spring Data JPA and Named Entity Graphs](https://www.baeldung.com/spring-data-jpa-named-entity-graphs) - [Batch Insert/Update with Hibernate/JPA](https://www.baeldung.com/jpa-hibernate-batch-insert-update) - [Difference Between save() and saveAndFlush() in Spring Data JPA](https://www.baeldung.com/spring-data-jpa-save-saveandflush) +- [Derived Query Methods in Spring Data JPA Repositories](https://www.baeldung.com/spring-data-derived-queries) diff --git a/persistence-modules/spring-data-redis/pom.xml b/persistence-modules/spring-data-redis/pom.xml index fb80b0413f..4ae8ac0a87 100644 --- a/persistence-modules/spring-data-redis/pom.xml +++ b/persistence-modules/spring-data-redis/pom.xml @@ -79,6 +79,21 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + true + true + -Xmx1024m + + + + + 3.2.4 2.9.0 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/RedisKeyCommandsIntegrationTest.java index e48aa1e06a..1333f94653 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/RedisKeyCommandsIntegrationTest.java @@ -2,6 +2,9 @@ package com.baeldung.spring.data.reactive.redis.template; import com.baeldung.spring.data.reactive.redis.SpringRedisReactiveApplication; + +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -9,22 +12,40 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.connection.ReactiveKeyCommands; import org.springframework.data.redis.connection.ReactiveStringCommands; import org.springframework.data.redis.connection.ReactiveStringCommands.SetCommand; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.junit4.SpringRunner; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import redis.embedded.RedisServerBuilder; +import java.io.IOException; import java.nio.ByteBuffer; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class) +@DirtiesContext(classMode = ClassMode.BEFORE_CLASS) public class RedisKeyCommandsIntegrationTest { + + private static redis.embedded.RedisServer redisServer; @Autowired private ReactiveKeyCommands keyCommands; @Autowired private ReactiveStringCommands stringCommands; + + @BeforeClass + public static void startRedisServer() throws IOException { + redisServer = new RedisServerBuilder().port(6379).setting("maxheap 256M").build(); + redisServer.start(); + } + + @AfterClass + public static void stopRedisServer() throws IOException { + redisServer.stop(); + } @Test public void givenFluxOfKeys_whenPerformOperations_thenPerformOperations() { 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/RedisTemplateListOpsIntegrationTest.java index 3ebeff87b1..88c4fa6eed 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/RedisTemplateListOpsIntegrationTest.java @@ -2,27 +2,48 @@ package com.baeldung.spring.data.reactive.redis.template; import com.baeldung.spring.data.reactive.redis.SpringRedisReactiveApplication; + +import java.io.IOException; + +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; 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.redis.core.ReactiveListOperations; import org.springframework.data.redis.core.ReactiveRedisTemplate; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.junit4.SpringRunner; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import redis.embedded.RedisServerBuilder; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class) +@DirtiesContext(classMode = ClassMode.BEFORE_CLASS) public class RedisTemplateListOpsIntegrationTest { private static final String LIST_NAME = "demo_list"; + private static redis.embedded.RedisServer redisServer; @Autowired private ReactiveRedisTemplate redisTemplate; private ReactiveListOperations reactiveListOps; + + @BeforeClass + public static void startRedisServer() throws IOException { + redisServer = new RedisServerBuilder().port(6379).setting("maxheap 128M").build(); + redisServer.start(); + } + + @AfterClass + public static void stopRedisServer() throws IOException { + redisServer.stop(); + } @Before public void setup() { 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/RedisTemplateValueOpsIntegrationTest.java index 9490568089..afa5267582 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/RedisTemplateValueOpsIntegrationTest.java @@ -1,29 +1,51 @@ package com.baeldung.spring.data.reactive.redis.template; -import com.baeldung.spring.data.reactive.redis.SpringRedisReactiveApplication; -import com.baeldung.spring.data.reactive.redis.model.Employee; +import java.io.IOException; +import java.time.Duration; + +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; 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.redis.core.ReactiveRedisTemplate; import org.springframework.data.redis.core.ReactiveValueOperations; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.junit4.SpringRunner; + +import com.baeldung.spring.data.reactive.redis.SpringRedisReactiveApplication; +import com.baeldung.spring.data.reactive.redis.model.Employee; + import reactor.core.publisher.Mono; import reactor.test.StepVerifier; - -import java.time.Duration; +import redis.embedded.RedisServerBuilder; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class) +@DirtiesContext(classMode = ClassMode.BEFORE_CLASS) public class RedisTemplateValueOpsIntegrationTest { + + private static redis.embedded.RedisServer redisServer; @Autowired private ReactiveRedisTemplate redisTemplate; private ReactiveValueOperations reactiveValueOps; + + @BeforeClass + public static void startRedisServer() throws IOException { + redisServer = new RedisServerBuilder().port(6379).setting("maxheap 256M").build(); + redisServer.start(); + } + + @AfterClass + public static void stopRedisServer() throws IOException { + redisServer.stop(); + } @Before public void setup() { 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/RedisMessageListenerIntegrationTest.java index 99febb6430..1c69b63c09 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/RedisMessageListenerIntegrationTest.java @@ -19,9 +19,11 @@ import com.baeldung.spring.data.redis.config.RedisConfig; import com.baeldung.spring.data.redis.queue.RedisMessagePublisher; import com.baeldung.spring.data.redis.queue.RedisMessageSubscriber; +import redis.embedded.RedisServerBuilder; + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = RedisConfig.class) -@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +@DirtiesContext(classMode = ClassMode.BEFORE_CLASS) public class RedisMessageListenerIntegrationTest { private static redis.embedded.RedisServer redisServer; @@ -31,7 +33,7 @@ public class RedisMessageListenerIntegrationTest { @BeforeClass public static void startRedisServer() throws IOException { - redisServer = new redis.embedded.RedisServer(6380); + redisServer = new RedisServerBuilder().port(6379).setting("maxheap 256M").build(); redisServer.start(); } 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/StudentRepositoryIntegrationTest.java index 43aadefc01..b1a36475c3 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/StudentRepositoryIntegrationTest.java @@ -20,9 +20,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.baeldung.spring.data.redis.config.RedisConfig; import com.baeldung.spring.data.redis.model.Student; +import redis.embedded.RedisServerBuilder; + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = RedisConfig.class) -@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +@DirtiesContext(classMode = ClassMode.BEFORE_CLASS) public class StudentRepositoryIntegrationTest { @Autowired @@ -32,7 +34,7 @@ public class StudentRepositoryIntegrationTest { @BeforeClass public static void startRedisServer() throws IOException { - redisServer = new redis.embedded.RedisServer(6380); + redisServer = new RedisServerBuilder().port(6379).setting("maxheap 128M").build(); redisServer.start(); } diff --git a/pom.xml b/pom.xml index 20113d90a2..11d5f8df95 100644 --- a/pom.xml +++ b/pom.xml @@ -646,6 +646,7 @@ spring-boot-ops-2 spring-boot-rest spring-boot-data + spring-boot-parent spring-boot-property-exp spring-boot-security spring-boot-testing @@ -926,6 +927,7 @@ spring-vault spring-vertx spring-zuul/spring-zuul-foos-resource + persistence-modules/hibernate-mapping persistence-modules/spring-data-dynamodb persistence-modules/spring-data-eclipselink persistence-modules/spring-data-solr @@ -1310,6 +1312,7 @@ spring-boot-ops-2 spring-boot-rest spring-boot-data + spring-boot-parent spring-boot-property-exp spring-boot-security spring-boot-vue diff --git a/spf4j/pom.xml b/spf4j/pom.xml new file mode 100644 index 0000000000..43a8028dc4 --- /dev/null +++ b/spf4j/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + com.baeldung.spf4j + spf4j + spf4j + pom + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + + + spf4j-core-app + spf4j-aspects-app + + + diff --git a/spf4j/spf4j-aspects-app/pom.xml b/spf4j/spf4j-aspects-app/pom.xml new file mode 100644 index 0000000000..9fccec673a --- /dev/null +++ b/spf4j/spf4j-aspects-app/pom.xml @@ -0,0 +1,83 @@ + + 4.0.0 + spf4j-aspects-app + 0.0.1-SNAPSHOT + jar + spf4j-aspects-app + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + ../../ + + + + org.spf4j + spf4j-aspects + ${spf4j.version} + + + org.spf4j + spf4j-ui + ${spf4j.version} + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + + spf4j-aspects-app + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + copy-dependencies + package + + copy-dependencies + + + + ${project.build.directory}/dependency-jars/ + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.baeldung.spf4j.aspects.App + true + dependency-jars/ + + + + + + + + UTF-8 + 8.6.10 + 1.7.21 + + diff --git a/spf4j/spf4j-aspects-app/src/main/java/com/baeldung/spf4j/aspects/App.java b/spf4j/spf4j-aspects-app/src/main/java/com/baeldung/spf4j/aspects/App.java new file mode 100644 index 0000000000..6d74292c75 --- /dev/null +++ b/spf4j/spf4j-aspects-app/src/main/java/com/baeldung/spf4j/aspects/App.java @@ -0,0 +1,28 @@ +package com.baeldung.spf4j.aspects; + +import java.util.Random; + +import org.spf4j.annotations.PerformanceMonitor; + +public class App { + + public static void main(String[] args) throws InterruptedException { + Spf4jConfig.initialize(); + Random random = new Random(); + for (int i = 0; i < 100; i++) { + long numberToCheck = random.nextInt(999_999_999 - 100_000_000 + 1) + 100_000_000; + isPrimeNumber(numberToCheck); + } + System.exit(0); + } + + @PerformanceMonitor(warnThresholdMillis = 1, errorThresholdMillis = 100, recorderSource = Spf4jConfig.RecorderSourceForIsPrimeNumber.class) + public static boolean isPrimeNumber(long number) { + for (long i = 2; i <= number / 2; i++) { + if (number % i == 0) + return false; + } + return true; + } + +} diff --git a/spf4j/spf4j-aspects-app/src/main/java/com/baeldung/spf4j/aspects/Spf4jConfig.java b/spf4j/spf4j-aspects-app/src/main/java/com/baeldung/spf4j/aspects/Spf4jConfig.java new file mode 100644 index 0000000000..a12213f0cd --- /dev/null +++ b/spf4j/spf4j-aspects-app/src/main/java/com/baeldung/spf4j/aspects/Spf4jConfig.java @@ -0,0 +1,37 @@ +package com.baeldung.spf4j.aspects; + +import java.io.File; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spf4j.annotations.RecorderSourceInstance; +import org.spf4j.perf.MeasurementRecorderSource; +import org.spf4j.perf.impl.RecorderFactory; + +public class Spf4jConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public static void initialize() { + String tsDbFile = System.getProperty("user.dir") + File.separator + "spf4j-performance-monitoring.tsdb2"; + String tsTextFile = System.getProperty("user.dir") + File.separator + "spf4j-performance-monitoring.txt"; + + LOGGER.info("\nTime Series DB (TSDB) : {}\nTime Series text file : {}", tsDbFile, tsTextFile); + System.setProperty("spf4j.perf.ms.config", "TSDB@" + tsDbFile + "," + "TSDB_TXT@" + tsTextFile); + } + + public static final class RecorderSourceForIsPrimeNumber extends RecorderSourceInstance { + public static final MeasurementRecorderSource INSTANCE; + + static { + Object forWhat = App.class + " isPrimeNumber"; + String unitOfMeasurement = "ms"; + int sampleTimeMillis = 1_000; + int factor = 10; + int lowerMagnitude = 0; + int higherMagnitude = 4; + int quantasPerMagnitude = 10; + INSTANCE = RecorderFactory.createScalableQuantizedRecorderSource(forWhat, unitOfMeasurement, + sampleTimeMillis, factor, lowerMagnitude, higherMagnitude, quantasPerMagnitude); + } + } +} diff --git a/spf4j/spf4j-aspects-app/src/main/resources/META-INF/aop.xml b/spf4j/spf4j-aspects-app/src/main/resources/META-INF/aop.xml new file mode 100644 index 0000000000..1643f5fba9 --- /dev/null +++ b/spf4j/spf4j-aspects-app/src/main/resources/META-INF/aop.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/spf4j/spf4j-aspects-app/src/main/resources/logback.xml b/spf4j/spf4j-aspects-app/src/main/resources/logback.xml new file mode 100644 index 0000000000..4677fac9bf --- /dev/null +++ b/spf4j/spf4j-aspects-app/src/main/resources/logback.xml @@ -0,0 +1,11 @@ + + + + [%level] %msg%n + + + + + + \ No newline at end of file diff --git a/spf4j/spf4j-core-app/pom.xml b/spf4j/spf4j-core-app/pom.xml new file mode 100644 index 0000000000..ae346065ef --- /dev/null +++ b/spf4j/spf4j-core-app/pom.xml @@ -0,0 +1,83 @@ + + 4.0.0 + spf4j-core-app + 0.0.1-SNAPSHOT + jar + spf4j-core-app + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + ../../ + + + + org.spf4j + spf4j-core + ${spf4j.version} + + + org.spf4j + spf4j-ui + ${spf4j.version} + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + + spf4j-core-app + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + copy-dependencies + package + + copy-dependencies + + + + ${project.build.directory}/dependency-jars/ + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.baeldung.spf4j.core.App + true + dependency-jars/ + + + + + + + + UTF-8 + 8.6.10 + 1.7.21 + + diff --git a/spf4j/spf4j-core-app/src/main/java/com/baeldung/spf4j/core/App.java b/spf4j/spf4j-core-app/src/main/java/com/baeldung/spf4j/core/App.java new file mode 100644 index 0000000000..fa107d8e4f --- /dev/null +++ b/spf4j/spf4j-core-app/src/main/java/com/baeldung/spf4j/core/App.java @@ -0,0 +1,34 @@ +package com.baeldung.spf4j.core; + +import java.util.Random; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spf4j.perf.MeasurementRecorder; + +public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public static void main(String[] args) throws InterruptedException { + Spf4jConfig.initialize(); + MeasurementRecorder measurementRecorder = Spf4jConfig.getMeasurementRecorder(App.class + " isPrimeNumber"); + Random random = new Random(); + for (int i = 0; i < 100; i++) { + long numberToCheck = random.nextInt(999_999_999 - 100_000_000 + 1) + 100_000_000; + long startTime = System.currentTimeMillis(); + boolean isPrime = isPrimeNumber(numberToCheck); + measurementRecorder.record(System.currentTimeMillis() - startTime); + LOGGER.info("{}. {} is prime? {}", i + 1, numberToCheck, isPrime); + } + System.exit(0); + } + + private static boolean isPrimeNumber(long number) { + for (long i = 2; i <= number / 2; i++) { + if (number % i == 0) + return false; + } + return true; + } + +} diff --git a/spf4j/spf4j-core-app/src/main/java/com/baeldung/spf4j/core/Spf4jConfig.java b/spf4j/spf4j-core-app/src/main/java/com/baeldung/spf4j/core/Spf4jConfig.java new file mode 100644 index 0000000000..0f806e1576 --- /dev/null +++ b/spf4j/spf4j-core-app/src/main/java/com/baeldung/spf4j/core/Spf4jConfig.java @@ -0,0 +1,31 @@ +package com.baeldung.spf4j.core; + +import java.io.File; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spf4j.perf.MeasurementRecorder; +import org.spf4j.perf.impl.RecorderFactory; + +public class Spf4jConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public static void initialize() { + String tsDbFile = System.getProperty("user.dir") + File.separator + "spf4j-performance-monitoring.tsdb2"; + String tsTextFile = System.getProperty("user.dir") + File.separator + "spf4j-performance-monitoring.txt"; + + LOGGER.info("\nTime Series DB (TSDB) : {}\nTime Series text file : {}", tsDbFile, tsTextFile); + System.setProperty("spf4j.perf.ms.config", "TSDB@" + tsDbFile + "," + "TSDB_TXT@" + tsTextFile); + } + + public static MeasurementRecorder getMeasurementRecorder(Object forWhat) { + String unitOfMeasurement = "ms"; + int sampleTimeMillis = 1_000; + int factor = 10; + int lowerMagnitude = 0; + int higherMagnitude = 4; + int quantasPerMagnitude = 10; + + return RecorderFactory.createScalableQuantizedRecorder(forWhat, unitOfMeasurement, sampleTimeMillis, factor, lowerMagnitude, higherMagnitude, quantasPerMagnitude); + } +} diff --git a/spf4j/spf4j-core-app/src/main/resources/logback.xml b/spf4j/spf4j-core-app/src/main/resources/logback.xml new file mode 100644 index 0000000000..4677fac9bf --- /dev/null +++ b/spf4j/spf4j-core-app/src/main/resources/logback.xml @@ -0,0 +1,11 @@ + + + + [%level] %msg%n + + + + + + \ No newline at end of file diff --git a/spring-5-webflux/README.md b/spring-5-webflux/README.md index e84ee863bf..87d9ae0dd3 100644 --- a/spring-5-webflux/README.md +++ b/spring-5-webflux/README.md @@ -3,3 +3,4 @@ - [Spring Boot Reactor Netty Configuration](https://www.baeldung.com/spring-boot-reactor-netty) - [How to Return 404 with Spring WebFlux](https://www.baeldung.com/spring-webflux-404) - [Spring WebClient Requests with Parameters](https://www.baeldung.com/webflux-webclient-parameters) +- [RSocket Using Spring Boot](https://www.baeldung.com/spring-boot-rsocket) diff --git a/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/CustomService.java b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/CustomService.java new file mode 100644 index 0000000000..634e49fed3 --- /dev/null +++ b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/CustomService.java @@ -0,0 +1,10 @@ +package com.baeldung.autoconfiguration.service; + +public class CustomService implements SimpleService { + + @Override + public String serve() { + return "Custom Service"; + } + +} diff --git a/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/DefaultService.java b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/DefaultService.java new file mode 100644 index 0000000000..ee91bcb051 --- /dev/null +++ b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/DefaultService.java @@ -0,0 +1,10 @@ +package com.baeldung.autoconfiguration.service; + +public class DefaultService implements SimpleService { + + @Override + public String serve() { + return "Default Service"; + } + +} diff --git a/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/SimpleService.java b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/SimpleService.java new file mode 100644 index 0000000000..b6c72d7159 --- /dev/null +++ b/spring-boot-autoconfiguration/src/main/java/com/baeldung/autoconfiguration/service/SimpleService.java @@ -0,0 +1,7 @@ +package com.baeldung.autoconfiguration.service; + +public interface SimpleService { + + public String serve(); + +} diff --git a/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnBeanIntegrationTest.java b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnBeanIntegrationTest.java new file mode 100644 index 0000000000..32f63edde4 --- /dev/null +++ b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnBeanIntegrationTest.java @@ -0,0 +1,77 @@ +package com.baeldung.autoconfiguration; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +public class ConditionalOnBeanIntegrationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); + + @Test + public void whenDependentBeanIsPresent_thenConditionalBeanCreated() { + this.contextRunner.withUserConfiguration(BasicConfiguration.class, ConditionalOnBeanConfiguration.class) + .run((context) -> { + assertThat(context).hasBean("created"); + assertThat(context).getBean("created") + .isEqualTo("This is always created"); + assertThat(context).hasBean("createOnBean"); + assertThat(context).getBean("createOnBean") + .isEqualTo("This is created when bean (name=created) is present"); + }); + } + + @Test + public void whenDependentBeanIsPresent_thenConditionalMissingBeanIgnored() { + this.contextRunner.withUserConfiguration(BasicConfiguration.class, ConditionalOnMissingBeanConfiguration.class) + .run((context) -> { + assertThat(context).hasBean("created"); + assertThat(context).getBean("created") + .isEqualTo("This is always created"); + assertThat(context).doesNotHaveBean("createOnMissingBean"); + }); + } + + @Test + public void whenDependentBeanIsNotPresent_thenConditionalMissingBeanCreated() { + this.contextRunner.withUserConfiguration(ConditionalOnMissingBeanConfiguration.class) + .run((context) -> { + assertThat(context).hasBean("createOnMissingBean"); + assertThat(context).getBean("createOnMissingBean") + .isEqualTo("This is created when bean (name=created) is missing"); + assertThat(context).doesNotHaveBean("created"); + }); + } + + @Configuration + protected static class BasicConfiguration { + @Bean + public String created() { + return "This is always created"; + } + } + + @Configuration + @ConditionalOnBean(name = "created") + protected static class ConditionalOnBeanConfiguration { + @Bean + public String createOnBean() { + return "This is created when bean (name=created) is present"; + } + } + + @Configuration + @ConditionalOnMissingBean(name = "created") + protected static class ConditionalOnMissingBeanConfiguration { + @Bean + public String createOnMissingBean() { + return "This is created when bean (name=created) is missing"; + } + } + +} diff --git a/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnClassIntegrationTest.java b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnClassIntegrationTest.java new file mode 100644 index 0000000000..f2866867f2 --- /dev/null +++ b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnClassIntegrationTest.java @@ -0,0 +1,76 @@ +package com.baeldung.autoconfiguration; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +public class ConditionalOnClassIntegrationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); + + @Test + public void whenDependentClassIsPresent_thenBeanCreated() { + this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class) + .run(context -> { + assertThat(context).hasBean("created"); + assertThat(context.getBean("created")).isEqualTo("This is created when ConditionalOnClassIntegrationTest is present on the classpath"); + }); + } + + @Test + public void whenDependentClassIsPresent_thenBeanMissing() { + this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class) + .run(context -> { + assertThat(context).doesNotHaveBean("missed"); + }); + } + + @Test + public void whenDependentClassIsNotPresent_thenBeanMissing() { + this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class) + .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class)) + .run((context) -> { + assertThat(context).doesNotHaveBean("created"); + assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class); + + }); + } + + @Test + public void whenDependentClassIsNotPresent_thenBeanCreated() { + this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class) + .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class)) + .run((context) -> { + assertThat(context).hasBean("missed"); + assertThat(context).getBean("missed") + .isEqualTo("This is missed when ConditionalOnClassIntegrationTest is present on the classpath"); + assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class); + + }); + } + + @Configuration + @ConditionalOnClass(ConditionalOnClassIntegrationTest.class) + protected static class ConditionalOnClassConfiguration { + @Bean + public String created() { + return "This is created when ConditionalOnClassIntegrationTest is present on the classpath"; + } + } + + @Configuration + @ConditionalOnMissingClass("com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest") + protected static class ConditionalOnMissingClassConfiguration { + @Bean + public String missed() { + return "This is missed when ConditionalOnClassIntegrationTest is present on the classpath"; + } + } + +} diff --git a/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnPropertyIntegrationTest.java b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnPropertyIntegrationTest.java new file mode 100644 index 0000000000..c0733722dc --- /dev/null +++ b/spring-boot-autoconfiguration/src/test/java/com/baeldung/autoconfiguration/ConditionalOnPropertyIntegrationTest.java @@ -0,0 +1,64 @@ +package com.baeldung.autoconfiguration; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.TestPropertySource; + +import com.baeldung.autoconfiguration.service.CustomService; +import com.baeldung.autoconfiguration.service.DefaultService; +import com.baeldung.autoconfiguration.service.SimpleService; + +public class ConditionalOnPropertyIntegrationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); + + @Test + public void whenGivenCustomPropertyValue_thenCustomServiceCreated() { + this.contextRunner.withPropertyValues("com.baeldung.service=custom") + .withUserConfiguration(SimpleServiceConfiguration.class) + .run(context -> { + assertThat(context).hasBean("customService"); + SimpleService simpleService = context.getBean(CustomService.class); + assertThat(simpleService.serve()).isEqualTo("Custom Service"); + assertThat(context).doesNotHaveBean("defaultService"); + }); + } + + @Test + public void whenGivenDefaultPropertyValue_thenDefaultServiceCreated() { + this.contextRunner.withPropertyValues("com.baeldung.service=default") + .withUserConfiguration(SimpleServiceConfiguration.class) + .run(context -> { + assertThat(context).hasBean("defaultService"); + SimpleService simpleService = context.getBean(DefaultService.class); + assertThat(simpleService.serve()).isEqualTo("Default Service"); + assertThat(context).doesNotHaveBean("customService"); + }); + } + + @Configuration + @TestPropertySource("classpath:ConditionalOnPropertyTest.properties") + protected static class SimpleServiceConfiguration { + + @Bean + @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "default") + @ConditionalOnMissingBean + public DefaultService defaultService() { + return new DefaultService(); + } + + @Bean + @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "custom") + @ConditionalOnMissingBean + public CustomService customService() { + return new CustomService(); + } + } + +} diff --git a/spring-boot-autoconfiguration/src/test/resources/ConditionalOnPropertyTest.properties b/spring-boot-autoconfiguration/src/test/resources/ConditionalOnPropertyTest.properties new file mode 100644 index 0000000000..b6334bc1ce --- /dev/null +++ b/spring-boot-autoconfiguration/src/test/resources/ConditionalOnPropertyTest.properties @@ -0,0 +1 @@ +com.baeldung.service=custom \ No newline at end of file diff --git a/spring-boot-ops-2/README.MD b/spring-boot-ops-2/README.MD index 20b30515fb..b4218dc395 100644 --- a/spring-boot-ops-2/README.MD +++ b/spring-boot-ops-2/README.MD @@ -1,3 +1,4 @@ ### Relevant Articles -- [How to Configure Spring Boot Tomcat](https://www.baeldung.com/spring-boot-configure-tomcat) \ No newline at end of file +- [How to Configure Spring Boot Tomcat](https://www.baeldung.com/spring-boot-configure-tomcat) +- [Spring Boot Embedded Tomcat Logs](https://www.baeldung.com/spring-boot-embedded-tomcat-logs) diff --git a/spring-boot-parent/pom.xml b/spring-boot-parent/pom.xml new file mode 100644 index 0000000000..0924917505 --- /dev/null +++ b/spring-boot-parent/pom.xml @@ -0,0 +1,26 @@ + + + + 4.0.0 + com.baeldung + spring-boot-parent + 1.0.0-SNAPSHOT + spring-boot-parent + spring-boot-parent + pom + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + .. + + + + spring-boot-with-starter-parent + spring-boot-with-custom-parent + + + diff --git a/spring-boot-parent/spring-boot-with-custom-parent/pom.xml b/spring-boot-parent/spring-boot-with-custom-parent/pom.xml new file mode 100644 index 0000000000..de2946fbb2 --- /dev/null +++ b/spring-boot-parent/spring-boot-with-custom-parent/pom.xml @@ -0,0 +1,41 @@ + + + + 4.0.0 + spring-boot-with-custom-parent + 1.0.0-SNAPSHOT + spring-boot-with-custom-parent + + + com.baeldung + spring-boot-parent + 1.0.0-SNAPSHOT + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + 1.8 + 2.1.5.RELEASE + + + diff --git a/spring-boot-parent/spring-boot-with-custom-parent/src/main/java/com/baeldung/customparent/SpringBootStarterCustomParentApplication.java b/spring-boot-parent/spring-boot-with-custom-parent/src/main/java/com/baeldung/customparent/SpringBootStarterCustomParentApplication.java new file mode 100644 index 0000000000..169717d7bb --- /dev/null +++ b/spring-boot-parent/spring-boot-with-custom-parent/src/main/java/com/baeldung/customparent/SpringBootStarterCustomParentApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.customparent; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootStarterCustomParentApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootStarterCustomParentApplication.class, args); + System.out.println("Spring boot application running without starter parent"); + } +} diff --git a/spring-boot-parent/spring-boot-with-starter-parent/pom.xml b/spring-boot-parent/spring-boot-with-starter-parent/pom.xml new file mode 100644 index 0000000000..1c6479ca60 --- /dev/null +++ b/spring-boot-parent/spring-boot-with-starter-parent/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + com.baeldung + spring-boot-with-starter-parent + 1.0.0-SNAPSHOT + spring-boot-with-starter-parent + + + org.springframework.boot + spring-boot-starter-parent + 2.1.5.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + 2.1.1.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + + + + + 1.8 + 4.11 + + + diff --git a/spring-boot-parent/spring-boot-with-starter-parent/src/main/java/com/baeldung/starterparent/SpringBootStarterParentApplication.java b/spring-boot-parent/spring-boot-with-starter-parent/src/main/java/com/baeldung/starterparent/SpringBootStarterParentApplication.java new file mode 100644 index 0000000000..f987165ce0 --- /dev/null +++ b/spring-boot-parent/spring-boot-with-starter-parent/src/main/java/com/baeldung/starterparent/SpringBootStarterParentApplication.java @@ -0,0 +1,14 @@ +package com.baeldung.starterparent; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootStarterParentApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootStarterParentApplication.class, args); + System.out.println("Spring boot application running with starter parent"); + } + +} diff --git a/spring-cloud/spring-cloud-security/auth-client/pom.xml b/spring-cloud/spring-cloud-security/auth-client/pom.xml index 4f64f470f0..4152552640 100644 --- a/spring-cloud/spring-cloud-security/auth-client/pom.xml +++ b/spring-cloud/spring-cloud-security/auth-client/pom.xml @@ -24,7 +24,7 @@ org.springframework.cloud - spring-cloud-starter-zuul + spring-cloud-starter-netflix-zuul org.springframework.boot @@ -34,14 +34,16 @@ org.webjars jquery + ${jquery.version} org.webjars bootstrap + ${bootstrap.version} org.webjars - webjars-locator + webjars-locator-core org.springframework.boot @@ -62,8 +64,8 @@ spring-boot-starter-thymeleaf - org.springframework.security.oauth - spring-security-oauth2 + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure @@ -89,8 +91,10 @@ - 2.1.0 - Dalston.SR4 + 2.2.0 + Greenwich.SR1 + 3.4.1 + 4.3.1 diff --git a/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.properties b/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.properties deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.yml index 2a758faeae..69617555d9 100644 --- a/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.yml +++ b/spring-cloud/spring-cloud-security/auth-client/src/main/resources/application.yml @@ -2,7 +2,8 @@ # These are default settings, but we add them for clarity. server: port: 8080 - contextPath: / + servlet: + context-path: / # Configure the Authorization Server and User Info Resource Server details security: @@ -21,6 +22,7 @@ person: # Proxies the calls to http://localhost:8080/api/* to our REST service at http://localhost:8081/* # and automatically includes our OAuth2 token in the request headers zuul: + sensitiveHeaders: Cookie,Set-Cookie routes: resource: path: /api/** diff --git a/spring-cloud/spring-cloud-security/auth-resource/pom.xml b/spring-cloud/spring-cloud-security/auth-resource/pom.xml index 22ee0528c3..a60eca740c 100644 --- a/spring-cloud/spring-cloud-security/auth-resource/pom.xml +++ b/spring-cloud/spring-cloud-security/auth-resource/pom.xml @@ -19,8 +19,8 @@ spring-boot-starter-web - org.springframework.security.oauth - spring-security-oauth2 + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure org.springframework.boot @@ -30,6 +30,7 @@ org.springframework.security spring-security-jwt + ${spring-jwt.version} @@ -55,7 +56,8 @@ - Edgware.RELEASE + Greenwich.SR1 + 1.0.10.RELEASE diff --git a/spring-cloud/spring-cloud-security/auth-resource/src/main/java/com/baeldung/config/ResourceConfigurer.java b/spring-cloud/spring-cloud-security/auth-resource/src/main/java/com/baeldung/config/ResourceConfigurer.java index 977d74093a..abe942325f 100644 --- a/spring-cloud/spring-cloud-security/auth-resource/src/main/java/com/baeldung/config/ResourceConfigurer.java +++ b/spring-cloud/spring-cloud-security/auth-resource/src/main/java/com/baeldung/config/ResourceConfigurer.java @@ -3,7 +3,7 @@ package com.baeldung.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; @@ -11,15 +11,18 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R * REST API Resource Server. */ @Configuration -@EnableWebSecurity @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) // Allow method annotations like @PreAuthorize public class ResourceConfigurer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { - http.httpBasic().disable(); - http.authorizeRequests().anyRequest().authenticated(); + http.sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.NEVER) + .and() + .authorizeRequests() + .anyRequest().authenticated(); + } } diff --git a/spring-cloud/spring-cloud-security/auth-resource/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/auth-resource/src/main/resources/application.yml index 52e02ba41b..35063a7879 100644 --- a/spring-cloud/spring-cloud-security/auth-resource/src/main/resources/application.yml +++ b/spring-cloud/spring-cloud-security/auth-resource/src/main/resources/application.yml @@ -5,7 +5,6 @@ server: # Configure the public key to use for verifying the incoming JWT tokens security: - sessions: NEVER oauth2: resource: jwt: diff --git a/spring-cloud/spring-cloud-security/auth-server/pom.xml b/spring-cloud/spring-cloud-security/auth-server/pom.xml index 4b3f94b825..afd8dbef44 100644 --- a/spring-cloud/spring-cloud-security/auth-server/pom.xml +++ b/spring-cloud/spring-cloud-security/auth-server/pom.xml @@ -38,7 +38,7 @@ - 1.1.2.RELEASE + 2.1.2.RELEASE \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/AuthServerConfigurer.java b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/AuthServerConfigurer.java index 32e445f998..7c9ee9ae18 100644 --- a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/AuthServerConfigurer.java +++ b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/AuthServerConfigurer.java @@ -9,6 +9,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.core.io.Resource; import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; @@ -19,9 +20,7 @@ import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFacto @Configuration @EnableAuthorizationServer @Order(6) -public class AuthServerConfigurer - extends - AuthorizationServerConfigurerAdapter { +public class AuthServerConfigurer extends AuthorizationServerConfigurerAdapter { @Value("${jwt.certificate.store.file}") private Resource keystore; @@ -37,6 +36,9 @@ public class AuthServerConfigurer @Autowired private UserDetailsService userDetailsService; + + @Autowired + private BCryptPasswordEncoder passwordEncoder; @Override public void configure( @@ -45,8 +47,8 @@ public class AuthServerConfigurer clients .inMemory() .withClient("authserver") - .secret("passwordforauthserver") - .redirectUris("http://localhost:8080/") + .secret(passwordEncoder.encode("passwordforauthserver")) + .redirectUris("http://localhost:8080/login") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("myscope") diff --git a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfigurer.java b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfig.java similarity index 83% rename from spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfigurer.java rename to spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfig.java index 23b56151e7..3cefd323b3 100644 --- a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfigurer.java +++ b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebMvcConfig.java @@ -2,10 +2,10 @@ package com.baeldung.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration -public class WebMvcConfigurer extends WebMvcConfigurerAdapter { +public class WebMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { diff --git a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebSecurityConfigurer.java b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebSecurityConfigurer.java index 44406b8fa0..6a48c62097 100644 --- a/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebSecurityConfigurer.java +++ b/spring-cloud/spring-cloud-security/auth-server/src/main/java/com/baeldung/config/WebSecurityConfigurer.java @@ -6,8 +6,8 @@ import org.springframework.security.config.annotation.authentication.builders.Au import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; @Configuration @@ -34,7 +34,7 @@ public class WebSecurityConfigurer AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() - .withUser("user").password("user") + .withUser("user").password(passwordEncoder().encode("user")) .roles("USER") .and() .withUser("admin").password("admin") @@ -48,5 +48,9 @@ public class WebSecurityConfigurer return super.userDetailsServiceBean(); } + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } } diff --git a/spring-cloud/spring-cloud-security/auth-server/src/main/resources/application.yml b/spring-cloud/spring-cloud-security/auth-server/src/main/resources/application.yml index 1dc63d3f0e..b6e385e5c7 100644 --- a/spring-cloud/spring-cloud-security/auth-server/src/main/resources/application.yml +++ b/spring-cloud/spring-cloud-security/auth-server/src/main/resources/application.yml @@ -1,7 +1,8 @@ # Make the application available at http://localhost:7070/authserver server: port: 7070 - contextPath: /authserver + servlet: + context-path: /authserver # Our certificate settings for enabling JWT tokens jwt: @@ -11,11 +12,4 @@ jwt: password: abirkhan04 key: alias: myauthkey - password: abirkhan04 - - -security: - oauth2: - resource: - filter-order: 3 - \ No newline at end of file + password: abirkhan04 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-security/pom.xml b/spring-cloud/spring-cloud-security/pom.xml index 2eecf579a5..d65fd6520b 100644 --- a/spring-cloud/spring-cloud-security/pom.xml +++ b/spring-cloud/spring-cloud-security/pom.xml @@ -8,10 +8,10 @@ pom - parent-boot-1 + parent-boot-2 com.baeldung 0.0.1-SNAPSHOT - ../../parent-boot-1 + ../../parent-boot-2 diff --git a/spring-resttemplate/src/test/java/org/baeldung/resttemplate/LargeFileDownloadIntegrationTest.java b/spring-resttemplate/src/test/java/org/baeldung/resttemplate/LargeFileDownloadIntegrationTest.java new file mode 100644 index 0000000000..21639818db --- /dev/null +++ b/spring-resttemplate/src/test/java/org/baeldung/resttemplate/LargeFileDownloadIntegrationTest.java @@ -0,0 +1,109 @@ +package org.baeldung.resttemplate; + +import org.assertj.core.api.Assertions; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.util.StreamUtils; +import org.springframework.web.client.RestTemplate; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; + +public class LargeFileDownloadIntegrationTest { + + static String FILE_URL = "http://ovh.net/files/1Mio.dat"; + + RestTemplate restTemplate; + + @Before + public void setUp() { + restTemplate = new RestTemplate(); + } + + @Test + public void givenResumableUrl_whenUrlCalledByHeadOption_thenExpectHeadersAvailable() { + HttpHeaders headers = restTemplate.headForHeaders(FILE_URL); + Assertions + .assertThat(headers.get("Accept-Ranges")) + .contains("bytes"); + Assertions + .assertThat(headers.getContentLength()) + .isGreaterThan(0); + } + + @Test + public void givenResumableUrl_whenDownloadCompletely_thenExpectCorrectFileSize() { + HttpHeaders headers = restTemplate.headForHeaders(FILE_URL); + long contentLength = headers.getContentLength(); + File file = restTemplate.execute(FILE_URL, HttpMethod.GET, null, clientHttpResponse -> { + File ret = File.createTempFile("download", "tmp"); + StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret)); + return ret; + }); + + Assert.assertNotNull(file); + Assertions + .assertThat(file.length()) + .isEqualTo(contentLength); + } + + @Test + public void givenResumableUrl_whenDownloadRange_thenExpectFileSizeEqualOrLessThanRange() { + int range = 10; + File file = restTemplate.execute(FILE_URL, HttpMethod.GET, clientHttpRequest -> clientHttpRequest + .getHeaders() + .set("Range", String.format("bytes=0-%d", range - 1)), clientHttpResponse -> { + File ret = File.createTempFile("download", "tmp"); + StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret)); + return ret; + }); + + Assert.assertNotNull(file); + Assertions + .assertThat(file.length()) + .isLessThanOrEqualTo(range); + } + + @Test + public void givenResumableUrl_whenPauseDownloadAndResume_thenExpectCorrectFileSize() { + + int range = 10; + + HttpHeaders headers = restTemplate.headForHeaders(FILE_URL); + long contentLength = headers.getContentLength(); + + File file = restTemplate.execute(FILE_URL, HttpMethod.GET, clientHttpRequest -> clientHttpRequest + .getHeaders() + .set("Range", String.format("bytes=0-%d", range - 1)), clientHttpResponse -> { + File ret = File.createTempFile("download", "tmp"); + StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(ret)); + return ret; + }); + + Assert.assertNotNull(file); + + Assertions + .assertThat(file.length()) + .isLessThanOrEqualTo(range); + + restTemplate.execute(FILE_URL, HttpMethod.GET, clientHttpRequest -> clientHttpRequest + .getHeaders() + .set("Range", String.format("bytes=%d-%d", file.length(), contentLength)), clientHttpResponse -> { + StreamUtils.copy(clientHttpResponse.getBody(), new FileOutputStream(file, true)); + return file; + }); + + Assertions + .assertThat(file.length()) + .isEqualTo(contentLength); + + } + +}