diff --git a/JGit/pom.xml b/JGit/pom.xml
index 176d55d321..deae1e45e3 100644
--- a/JGit/pom.xml
+++ b/JGit/pom.xml
@@ -5,9 +5,9 @@
com.baeldungJGit1.0-SNAPSHOT
+ JGitjarhttp://maven.apache.org
- JGitcom.baeldung
diff --git a/README.md b/README.md
index f3fe5e3bf0..378d77196a 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Java and Spring Tutorials
================
This project is **a collection of small and focused tutorials** - each covering a single and well defined area of development in the Java ecosystem.
-A strong focus of these is, of course, the Spring Framework - Spring, Spring Boot and Spring Securiyt.
+A strong focus of these is, of course, the Spring Framework - Spring, Spring Boot and Spring Security.
In additional to Spring, the following technologies are in focus: `core Java`, `Jackson`, `HttpClient`, `Guava`.
diff --git a/Twitter4J/pom.xml b/Twitter4J/pom.xml
index 982c1adc23..b82b9f6778 100644
--- a/Twitter4J/pom.xml
+++ b/Twitter4J/pom.xml
@@ -2,8 +2,8 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0Twitter4J
- jarTwitter4J
+ jarcom.baeldung
diff --git a/akka-http/README.md b/akka-http/README.md
new file mode 100644
index 0000000000..3831b5079f
--- /dev/null
+++ b/akka-http/README.md
@@ -0,0 +1,3 @@
+## Relevant articles:
+
+- [Introduction to Akka HTTP](https://www.baeldung.com/akka-http)
diff --git a/akka-http/pom.xml b/akka-http/pom.xml
index 51e70fb583..05e50d2229 100644
--- a/akka-http/pom.xml
+++ b/akka-http/pom.xml
@@ -23,7 +23,7 @@
com.typesafe.akkaakka-stream_2.12
- 2.5.11
+ ${akka.stream.version}com.typesafe.akka
diff --git a/algorithms-miscellaneous-1/README.md b/algorithms-miscellaneous-1/README.md
index a725bbd141..7ed805f7c4 100644
--- a/algorithms-miscellaneous-1/README.md
+++ b/algorithms-miscellaneous-1/README.md
@@ -12,4 +12,8 @@
- [Check If a String Contains All The Letters of The Alphabet](https://www.baeldung.com/java-string-contains-all-letters)
- [Find the Middle Element of a Linked List](http://www.baeldung.com/java-linked-list-middle-element)
- [Calculate Factorial in Java](https://www.baeldung.com/java-calculate-factorial)
-- [Find Substrings That Are Palindromes in Java](https://www.baeldung.com/java-palindrome-substrings)
\ No newline at end of file
+- [Find Substrings That Are Palindromes in Java](https://www.baeldung.com/java-palindrome-substrings)
+- [Find the Longest Substring without Repeating Characters](https://www.baeldung.com/java-longest-substring-without-repeated-characters)
+- [Java Two Pointer Technique](https://www.baeldung.com/java-two-pointer-technique)
+- [Permutations of an Array in Java](https://www.baeldung.com/java-array-permutations)
+- [Implementing Simple State Machines with Java Enums](https://www.baeldung.com/java-enum-simple-state-machine)
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/enumstatemachine/LeaveRequestState.java b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/enumstatemachine/LeaveRequestState.java
new file mode 100644
index 0000000000..5153c2e18e
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/enumstatemachine/LeaveRequestState.java
@@ -0,0 +1,46 @@
+package com.baeldung.algorithms.enumstatemachine;
+
+public enum LeaveRequestState {
+
+ Submitted {
+ @Override
+ public LeaveRequestState nextState() {
+ System.out.println("Starting the Leave Request and sending to Team Leader for approval.");
+ return Escalated;
+ }
+
+ @Override
+ public String responsiblePerson() {
+ return "Employee";
+ }
+ },
+ Escalated {
+ @Override
+ public LeaveRequestState nextState() {
+ System.out.println("Reviewing the Leave Request and escalating to Department Manager.");
+ return Approved;
+ }
+
+ @Override
+ public String responsiblePerson() {
+ return "Team Leader";
+ }
+ },
+ Approved {
+ @Override
+ public LeaveRequestState nextState() {
+ System.out.println("Approving the Leave Request.");
+ return this;
+ }
+
+ @Override
+ public String responsiblePerson() {
+ return "Department Manager";
+ }
+ };
+
+ public abstract String responsiblePerson();
+
+ public abstract LeaveRequestState nextState();
+
+}
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/permutation/Permutation.java b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/permutation/Permutation.java
new file mode 100644
index 0000000000..7fedd78ffb
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/permutation/Permutation.java
@@ -0,0 +1,123 @@
+package com.baeldung.algorithms.permutation;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+public class Permutation {
+
+ public static void printAllRecursive(T[] elements, char delimiter) {
+ printAllRecursive(elements.length, elements, delimiter);
+ }
+
+ public static void printAllRecursive(int n, T[] elements, char delimiter) {
+
+ if(n == 1) {
+ printArray(elements, delimiter);
+ } else {
+ for(int i = 0; i < n-1; i++) {
+ printAllRecursive(n - 1, elements, delimiter);
+ if(n % 2 == 0) {
+ swap(elements, i, n-1);
+ } else {
+ swap(elements, 0, n-1);
+ }
+ }
+ printAllRecursive(n - 1, elements, delimiter);
+ }
+ }
+
+ public static void printAllIterative(int n, T[] elements, char delimiter) {
+
+ int[] indexes = new int[n];
+ for (int i = 0; i < n; i++) {
+ indexes[i] = 0;
+ }
+
+ printArray(elements, delimiter);
+
+ int i = 0;
+ while (i < n) {
+ if (indexes[i] < i) {
+ swap(elements, i % 2 == 0 ? 0: indexes[i], i);
+ printArray(elements, delimiter);
+ indexes[i]++;
+ i = 0;
+ }
+ else {
+ indexes[i] = 0;
+ i++;
+ }
+ }
+ }
+
+ public static > void printAllOrdered(T[] elements, char delimiter) {
+
+ Arrays.sort(elements);
+ boolean hasNext = true;
+
+ while(hasNext) {
+ printArray(elements, delimiter);
+ int k = 0, l = 0;
+ hasNext = false;
+ for (int i = elements.length - 1; i > 0; i--) {
+ if (elements[i].compareTo(elements[i - 1]) > 0) {
+ k = i - 1;
+ hasNext = true;
+ break;
+ }
+ }
+
+ for (int i = elements.length - 1; i > k; i--) {
+ if (elements[i].compareTo(elements[k]) > 0) {
+ l = i;
+ break;
+ }
+ }
+
+ swap(elements, k, l);
+ Collections.reverse(Arrays.asList(elements).subList(k + 1, elements.length));
+ }
+ }
+
+ public static void printRandom(T[] elements, char delimiter) {
+
+ Collections.shuffle(Arrays.asList(elements));
+ printArray(elements, delimiter);
+ }
+
+ private static void swap(T[] elements, int a, int b) {
+
+ T tmp = elements[a];
+ elements[a] = elements[b];
+ elements[b] = tmp;
+ }
+
+ private static void printArray(T[] elements, char delimiter) {
+
+ String delimiterSpace = delimiter + " ";
+ for(int i = 0; i < elements.length; i++) {
+ System.out.print(elements[i] + delimiterSpace);
+ }
+ System.out.print('\n');
+ }
+
+ public static void main(String[] argv) {
+
+ Integer[] elements = {1,2,3,4};
+
+ System.out.println("Rec:");
+ printAllRecursive(elements, ';');
+
+ System.out.println("Iter:");
+ printAllIterative(elements.length, elements, ';');
+
+ System.out.println("Orderes:");
+ printAllOrdered(elements, ';');
+
+ System.out.println("Random:");
+ printRandom(elements, ';');
+
+ System.out.println("Random:");
+ printRandom(elements, ';');
+ }
+}
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/LinkedListFindMiddle.java b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/LinkedListFindMiddle.java
new file mode 100644
index 0000000000..a7031f4fba
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/LinkedListFindMiddle.java
@@ -0,0 +1,16 @@
+package com.baeldung.algorithms.twopointertechnique;
+
+public class LinkedListFindMiddle {
+
+ public T findMiddle(MyNode head) {
+ MyNode slowPointer = head;
+ MyNode fastPointer = head;
+
+ while (fastPointer.next != null && fastPointer.next.next != null) {
+ fastPointer = fastPointer.next.next;
+ slowPointer = slowPointer.next;
+ }
+ return slowPointer.data;
+ }
+
+}
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/MyNode.java b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/MyNode.java
new file mode 100644
index 0000000000..7d93f03ef9
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/MyNode.java
@@ -0,0 +1,20 @@
+package com.baeldung.algorithms.twopointertechnique;
+
+public class MyNode {
+ MyNode next;
+ E data;
+
+ public MyNode(E value) {
+ data = value;
+ next = null;
+ }
+
+ public MyNode(E value, MyNode n) {
+ data = value;
+ next = n;
+ }
+
+ public void setNext(MyNode n) {
+ next = n;
+ }
+}
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/RotateArray.java b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/RotateArray.java
new file mode 100644
index 0000000000..b4e3698c01
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/RotateArray.java
@@ -0,0 +1,22 @@
+package com.baeldung.algorithms.twopointertechnique;
+
+public class RotateArray {
+
+ public void rotate(int[] input, int step) {
+ step %= input.length;
+ reverse(input, 0, input.length - 1);
+ reverse(input, 0, step - 1);
+ reverse(input, step, input.length - 1);
+ }
+
+ private void reverse(int[] input, int start, int end) {
+ while (start < end) {
+ int temp = input[start];
+ input[start] = input[end];
+ input[end] = temp;
+ start++;
+ end--;
+ }
+ }
+
+}
diff --git a/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/TwoSum.java b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/TwoSum.java
new file mode 100644
index 0000000000..14eceaa1bd
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/main/java/com/baeldung/algorithms/twopointertechnique/TwoSum.java
@@ -0,0 +1,38 @@
+package com.baeldung.algorithms.twopointertechnique;
+
+public class TwoSum {
+
+ public boolean twoSum(int[] input, int targetValue) {
+
+ int pointerOne = 0;
+ int pointerTwo = input.length - 1;
+
+ while (pointerOne < pointerTwo) {
+ int sum = input[pointerOne] + input[pointerTwo];
+
+ if (sum == targetValue) {
+ return true;
+ } else if (sum < targetValue) {
+ pointerOne++;
+ } else {
+ pointerTwo--;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean twoSumSlow(int[] input, int targetValue) {
+
+ for (int i = 0; i < input.length; i++) {
+ for (int j = 1; j < input.length; j++) {
+ if (input[i] + input[j] == targetValue) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/enumstatemachine/LeaveRequestStateUnitTest.java b/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/enumstatemachine/LeaveRequestStateUnitTest.java
new file mode 100644
index 0000000000..61ed6b3aec
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/enumstatemachine/LeaveRequestStateUnitTest.java
@@ -0,0 +1,37 @@
+package com.baeldung.algorithms.enumstatemachine;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class LeaveRequestStateUnitTest {
+
+ @Test
+ public void givenLeaveRequest_whenStateEscalated_thenResponsibleIsTeamLeader() {
+ LeaveRequestState state = LeaveRequestState.Escalated;
+
+ assertEquals(state.responsiblePerson(), "Team Leader");
+ }
+
+
+ @Test
+ public void givenLeaveRequest_whenStateApproved_thenResponsibleIsDepartmentManager() {
+ LeaveRequestState state = LeaveRequestState.Approved;
+
+ assertEquals(state.responsiblePerson(), "Department Manager");
+ }
+
+ @Test
+ public void givenLeaveRequest_whenNextStateIsCalled_thenStateIsChanged() {
+ LeaveRequestState state = LeaveRequestState.Submitted;
+
+ state = state.nextState();
+ assertEquals(state, LeaveRequestState.Escalated);
+
+ state = state.nextState();
+ assertEquals(state, LeaveRequestState.Approved);
+
+ state = state.nextState();
+ assertEquals(state, LeaveRequestState.Approved);
+ }
+}
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/LinkedListFindMiddleUnitTest.java b/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/LinkedListFindMiddleUnitTest.java
new file mode 100644
index 0000000000..422a53fa3e
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/LinkedListFindMiddleUnitTest.java
@@ -0,0 +1,37 @@
+package com.baeldung.algorithms.twopointertechnique;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+
+public class LinkedListFindMiddleUnitTest {
+
+ LinkedListFindMiddle linkedListFindMiddle = new LinkedListFindMiddle();
+
+ @Test
+ public void givenLinkedListOfMyNodes_whenLinkedListFindMiddle_thenCorrect() {
+
+ MyNode head = createNodesList(8);
+
+ assertThat(linkedListFindMiddle.findMiddle(head)).isEqualTo("4");
+
+ head = createNodesList(9);
+
+ assertThat(linkedListFindMiddle.findMiddle(head)).isEqualTo("5");
+ }
+
+ private static MyNode createNodesList(int n) {
+
+ MyNode head = new MyNode("1");
+ MyNode current = head;
+
+ for (int i = 2; i <= n; i++) {
+ MyNode newNode = new MyNode(String.valueOf(i));
+ current.setNext(newNode);
+ current = newNode;
+ }
+
+ return head;
+ }
+
+}
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/RotateArrayUnitTest.java b/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/RotateArrayUnitTest.java
new file mode 100644
index 0000000000..da227ae751
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/RotateArrayUnitTest.java
@@ -0,0 +1,26 @@
+package com.baeldung.algorithms.twopointertechnique;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+
+public class RotateArrayUnitTest {
+
+ private RotateArray rotateArray = new RotateArray();
+
+ private int[] inputArray;
+
+ private int step;
+
+ @Test
+ public void givenAnArrayOfIntegers_whenRotateKsteps_thenCorrect() {
+
+ inputArray = new int[] { 1, 2, 3, 4, 5, 6, 7 };
+ step = 4;
+
+ rotateArray.rotate(inputArray, step);
+
+ assertThat(inputArray).containsExactly(new int[] { 4, 5, 6, 7, 1, 2, 3 });
+ }
+
+}
diff --git a/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/TwoSumUnitTest.java b/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/TwoSumUnitTest.java
new file mode 100644
index 0000000000..aa76f8e1cf
--- /dev/null
+++ b/algorithms-miscellaneous-1/src/test/java/com/baeldung/algorithms/twopointertechnique/TwoSumUnitTest.java
@@ -0,0 +1,56 @@
+package com.baeldung.algorithms.twopointertechnique;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class TwoSumUnitTest {
+
+ private TwoSum twoSum = new TwoSum();
+
+ private int[] sortedArray;
+
+ private int targetValue;
+
+ @Test
+ public void givenASortedArrayOfIntegers_whenTwoSumSlow_thenPairExists() {
+
+ sortedArray = new int[] { 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9 };
+
+ targetValue = 12;
+
+ assertTrue(twoSum.twoSumSlow(sortedArray, targetValue));
+ }
+
+ @Test
+ public void givenASortedArrayOfIntegers_whenTwoSumSlow_thenPairDoesNotExists() {
+
+ sortedArray = new int[] { 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9 };
+
+ targetValue = 20;
+
+ assertFalse(twoSum.twoSumSlow(sortedArray, targetValue));
+ }
+
+ @Test
+ public void givenASortedArrayOfIntegers_whenTwoSum_thenPairExists() {
+
+ sortedArray = new int[] { 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9 };
+
+ targetValue = 12;
+
+ assertTrue(twoSum.twoSum(sortedArray, targetValue));
+ }
+
+ @Test
+ public void givenASortedArrayOfIntegers_whenTwoSum_thenPairDoesNotExists() {
+
+ sortedArray = new int[] { 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9 };
+
+ targetValue = 20;
+
+ assertFalse(twoSum.twoSum(sortedArray, targetValue));
+ }
+
+}
diff --git a/algorithms-miscellaneous-2/pom.xml b/algorithms-miscellaneous-2/pom.xml
index 5461f4ebe1..d5f3172eaa 100644
--- a/algorithms-miscellaneous-2/pom.xml
+++ b/algorithms-miscellaneous-2/pom.xml
@@ -68,7 +68,7 @@
org.codehaus.mojocobertura-maven-plugin
- 2.7
+ ${cobertura-maven-plugin.version}
@@ -91,6 +91,7 @@
1.0.13.9.01.11
+ 2.7
\ No newline at end of file
diff --git a/algorithms-miscellaneous-2/src/test/resources/graph.png b/algorithms-miscellaneous-2/src/test/resources/graph.png
index 56995b8dd9..7165a51782 100644
Binary files a/algorithms-miscellaneous-2/src/test/resources/graph.png and b/algorithms-miscellaneous-2/src/test/resources/graph.png differ
diff --git a/animal-sniffer-mvn-plugin/pom.xml b/animal-sniffer-mvn-plugin/pom.xml
index cdfb1fb2a3..55e37e2ec4 100644
--- a/animal-sniffer-mvn-plugin/pom.xml
+++ b/animal-sniffer-mvn-plugin/pom.xml
@@ -3,9 +3,9 @@
4.0.0com.baeldunganimal-sniffer-mvn-plugin
- jar1.0-SNAPSHOTanimal-sniffer-mvn-plugin
+ jarhttp://maven.apache.org
diff --git a/annotations/pom.xml b/annotations/pom.xml
index 6d83f5b057..5fe89adf0a 100644
--- a/annotations/pom.xml
+++ b/annotations/pom.xml
@@ -3,8 +3,8 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0annotations
- pomannotations
+ pomparent-modules
diff --git a/apache-avro/pom.xml b/apache-avro/pom.xml
index 18f9c34d64..b98e52be75 100644
--- a/apache-avro/pom.xml
+++ b/apache-avro/pom.xml
@@ -7,14 +7,6 @@
0.0.1-SNAPSHOTapache-avro
-
- UTF-8
- 3.5
- 1.8.2
- 1.8
- 1.7.25
-
-
com.baeldungparent-modules
@@ -85,4 +77,13 @@
+
+
+ UTF-8
+ 3.5
+ 1.8.2
+ 1.8
+ 1.7.25
+
+
diff --git a/apache-curator/pom.xml b/apache-curator/pom.xml
index e6be32277d..3306c0613f 100644
--- a/apache-curator/pom.xml
+++ b/apache-curator/pom.xml
@@ -3,8 +3,8 @@
4.0.0apache-curator0.0.1-SNAPSHOT
- jarapache-curator
+ jarcom.baeldung
diff --git a/apache-geode/pom.xml b/apache-geode/pom.xml
index 738accdcb8..a39e4a811e 100644
--- a/apache-geode/pom.xml
+++ b/apache-geode/pom.xml
@@ -12,22 +12,6 @@
parent-modules1.0.0-SNAPSHOT
-
-
- 1.6.0
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- 1.8
- 1.8
-
-
-
-
@@ -38,8 +22,26 @@
junitjunit
- RELEASE
+ ${junit.version}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+
+
+
+ 1.6.0
+ 1.8
+ 1.8
+
\ No newline at end of file
diff --git a/apache-meecrowave/pom.xml b/apache-meecrowave/pom.xml
index cf13aa1c1b..fb5af69f9b 100644
--- a/apache-meecrowave/pom.xml
+++ b/apache-meecrowave/pom.xml
@@ -7,51 +7,58 @@
apache-meecrowaveA sample REST API application with Meecrowave
-
- 1.8
- 1.8
- org.apache.meecrowavemeecrowave-core
- 1.2.1
+ ${meecrowave-core.version}org.apache.meecrowavemeecrowave-jpa
- 1.2.1
+ ${meecrowave-jpa.version}com.squareup.okhttp3okhttp
- 3.10.0
+ ${okhttp.version}org.apache.meecrowavemeecrowave-junit
- 1.2.0
+ ${meecrowave-junit.version}testjunitjunit
- 4.10
+ ${junit.version}test
-
+
org.apache.meecrowavemeecrowave-maven-plugin
- 1.2.1
+ ${meecrowave-maven-plugin.version}
+
+
+ 1.8
+ 1.8
+ 4.10
+ 1.2.0
+ 3.10.0
+ 1.2.1
+ 1.2.1
+ 1.2.1
+
\ No newline at end of file
diff --git a/apache-pulsar/pom.xml b/apache-pulsar/pom.xml
index a4c09586eb..11df6d0b87 100644
--- a/apache-pulsar/pom.xml
+++ b/apache-pulsar/pom.xml
@@ -11,12 +11,14 @@
org.apache.pulsarpulsar-client
- 2.1.1-incubating
+ ${pulsar-client.version}compile
+
1.81.8
+ 2.1.1-incubating
diff --git a/apache-solrj/pom.xml b/apache-solrj/pom.xml
index 9a807c2f26..1227fdca46 100644
--- a/apache-solrj/pom.xml
+++ b/apache-solrj/pom.xml
@@ -4,8 +4,8 @@
com.baeldungapache-solrj0.0.1-SNAPSHOT
- jarapache-solrj
+ jarcom.baeldung
diff --git a/apache-spark/README.md b/apache-spark/README.md
index fb8059eb27..a4dce212b4 100644
--- a/apache-spark/README.md
+++ b/apache-spark/README.md
@@ -1,3 +1,4 @@
### Relevant articles
- [Introduction to Apache Spark](http://www.baeldung.com/apache-spark)
+- [Building a Data Pipeline with Kafka, Spark Streaming and Cassandra](https://www.baeldung.com/kafka-spark-data-pipeline)
diff --git a/apache-spark/pom.xml b/apache-spark/pom.xml
index 290b63a14d..1aed5b1db9 100644
--- a/apache-spark/pom.xml
+++ b/apache-spark/pom.xml
@@ -4,8 +4,8 @@
com.baeldungapache-spark1.0-SNAPSHOT
- jarapache-spark
+ jarhttp://maven.apache.org
@@ -15,16 +15,79 @@
-
org.apache.spark
- spark-core_2.10
+ spark-core_2.11${org.apache.spark.spark-core.version}
+ provided
+
+ org.apache.spark
+ spark-sql_2.11
+ ${org.apache.spark.spark-sql.version}
+ provided
+
+
+ org.apache.spark
+ spark-streaming_2.11
+ ${org.apache.spark.spark-streaming.version}
+ provided
+
+
+ org.apache.spark
+ spark-streaming-kafka-0-10_2.11
+ ${org.apache.spark.spark-streaming-kafka.version}
+
+
+ com.datastax.spark
+ spark-cassandra-connector_2.11
+ ${com.datastax.spark.spark-cassandra-connector.version}
+
+
+ com.datastax.spark
+ spark-cassandra-connector-java_2.11
+ ${com.datastax.spark.spark-cassandra-connector-java.version}
+
-
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ jar-with-dependencies
+
+
+
+
+
- 2.2.0
+ 2.3.0
+ 2.3.0
+ 2.3.0
+ 2.3.0
+ 2.3.0
+ 1.5.2
+ 1.8
+ 1.8
+ 3.2
diff --git a/apache-spark/src/main/java/com/baeldung/data/pipeline/Word.java b/apache-spark/src/main/java/com/baeldung/data/pipeline/Word.java
new file mode 100644
index 0000000000..b0caa468b1
--- /dev/null
+++ b/apache-spark/src/main/java/com/baeldung/data/pipeline/Word.java
@@ -0,0 +1,25 @@
+package com.baeldung.data.pipeline;
+
+import java.io.Serializable;
+
+public class Word implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private String word;
+ private int count;
+ Word(String word, int count) {
+ this.word = word;
+ this.count = count;
+ }
+ public String getWord() {
+ return word;
+ }
+ public void setWord(String word) {
+ this.word = word;
+ }
+ public int getCount() {
+ return count;
+ }
+ public void setCount(int count) {
+ this.count = count;
+ }
+}
\ No newline at end of file
diff --git a/apache-spark/src/main/java/com/baeldung/data/pipeline/WordCountingApp.java b/apache-spark/src/main/java/com/baeldung/data/pipeline/WordCountingApp.java
new file mode 100644
index 0000000000..db2a73b411
--- /dev/null
+++ b/apache-spark/src/main/java/com/baeldung/data/pipeline/WordCountingApp.java
@@ -0,0 +1,80 @@
+package com.baeldung.data.pipeline;
+
+import static com.datastax.spark.connector.japi.CassandraJavaUtil.javaFunctions;
+import static com.datastax.spark.connector.japi.CassandraJavaUtil.mapToRow;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.spark.SparkConf;
+import org.apache.spark.api.java.JavaRDD;
+import org.apache.spark.streaming.Durations;
+import org.apache.spark.streaming.api.java.JavaDStream;
+import org.apache.spark.streaming.api.java.JavaInputDStream;
+import org.apache.spark.streaming.api.java.JavaPairDStream;
+import org.apache.spark.streaming.api.java.JavaStreamingContext;
+import org.apache.spark.streaming.kafka010.ConsumerStrategies;
+import org.apache.spark.streaming.kafka010.KafkaUtils;
+import org.apache.spark.streaming.kafka010.LocationStrategies;
+
+import scala.Tuple2;
+
+public class WordCountingApp {
+
+ public static void main(String[] args) throws InterruptedException {
+ Logger.getLogger("org")
+ .setLevel(Level.OFF);
+ Logger.getLogger("akka")
+ .setLevel(Level.OFF);
+
+ Map kafkaParams = new HashMap<>();
+ kafkaParams.put("bootstrap.servers", "localhost:9092");
+ kafkaParams.put("key.deserializer", StringDeserializer.class);
+ kafkaParams.put("value.deserializer", StringDeserializer.class);
+ kafkaParams.put("group.id", "use_a_separate_group_id_for_each_stream");
+ kafkaParams.put("auto.offset.reset", "latest");
+ kafkaParams.put("enable.auto.commit", false);
+
+ Collection topics = Arrays.asList("messages");
+
+ SparkConf sparkConf = new SparkConf();
+ sparkConf.setMaster("local[2]");
+ sparkConf.setAppName("WordCountingApp");
+ sparkConf.set("spark.cassandra.connection.host", "127.0.0.1");
+
+ JavaStreamingContext streamingContext = new JavaStreamingContext(sparkConf, Durations.seconds(1));
+
+ JavaInputDStream> messages = KafkaUtils.createDirectStream(streamingContext, LocationStrategies.PreferConsistent(), ConsumerStrategies. Subscribe(topics, kafkaParams));
+
+ JavaPairDStream results = messages.mapToPair(record -> new Tuple2<>(record.key(), record.value()));
+
+ JavaDStream lines = results.map(tuple2 -> tuple2._2());
+
+ JavaDStream words = lines.flatMap(x -> Arrays.asList(x.split("\\s+"))
+ .iterator());
+
+ JavaPairDStream wordCounts = words.mapToPair(s -> new Tuple2<>(s, 1))
+ .reduceByKey((i1, i2) -> i1 + i2);
+
+ wordCounts.foreachRDD(javaRdd -> {
+ Map wordCountMap = javaRdd.collectAsMap();
+ for (String key : wordCountMap.keySet()) {
+ List wordList = Arrays.asList(new Word(key, wordCountMap.get(key)));
+ JavaRDD rdd = streamingContext.sparkContext()
+ .parallelize(wordList);
+ javaFunctions(rdd).writerBuilder("vocabulary", "words", mapToRow(Word.class))
+ .saveToCassandra();
+ }
+ });
+
+ streamingContext.start();
+ streamingContext.awaitTermination();
+ }
+}
\ No newline at end of file
diff --git a/apache-spark/src/main/java/com/baeldung/data/pipeline/WordCountingAppWithCheckpoint.java b/apache-spark/src/main/java/com/baeldung/data/pipeline/WordCountingAppWithCheckpoint.java
new file mode 100644
index 0000000000..efbe5f3851
--- /dev/null
+++ b/apache-spark/src/main/java/com/baeldung/data/pipeline/WordCountingAppWithCheckpoint.java
@@ -0,0 +1,97 @@
+package com.baeldung.data.pipeline;
+
+import static com.datastax.spark.connector.japi.CassandraJavaUtil.javaFunctions;
+import static com.datastax.spark.connector.japi.CassandraJavaUtil.mapToRow;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.spark.SparkConf;
+import org.apache.spark.api.java.JavaRDD;
+import org.apache.spark.api.java.JavaSparkContext;
+import org.apache.spark.api.java.function.Function2;
+import org.apache.spark.streaming.Durations;
+import org.apache.spark.streaming.StateSpec;
+import org.apache.spark.streaming.api.java.JavaDStream;
+import org.apache.spark.streaming.api.java.JavaInputDStream;
+import org.apache.spark.streaming.api.java.JavaMapWithStateDStream;
+import org.apache.spark.streaming.api.java.JavaPairDStream;
+import org.apache.spark.streaming.api.java.JavaStreamingContext;
+import org.apache.spark.streaming.kafka010.ConsumerStrategies;
+import org.apache.spark.streaming.kafka010.KafkaUtils;
+import org.apache.spark.streaming.kafka010.LocationStrategies;
+
+import scala.Tuple2;
+
+public class WordCountingAppWithCheckpoint {
+
+ public static JavaSparkContext sparkContext;
+
+ public static void main(String[] args) throws InterruptedException {
+
+ Logger.getLogger("org")
+ .setLevel(Level.OFF);
+ Logger.getLogger("akka")
+ .setLevel(Level.OFF);
+
+ Map kafkaParams = new HashMap<>();
+ kafkaParams.put("bootstrap.servers", "localhost:9092");
+ kafkaParams.put("key.deserializer", StringDeserializer.class);
+ kafkaParams.put("value.deserializer", StringDeserializer.class);
+ kafkaParams.put("group.id", "use_a_separate_group_id_for_each_stream");
+ kafkaParams.put("auto.offset.reset", "latest");
+ kafkaParams.put("enable.auto.commit", false);
+
+ Collection topics = Arrays.asList("messages");
+
+ SparkConf sparkConf = new SparkConf();
+ sparkConf.setMaster("local[2]");
+ sparkConf.setAppName("WordCountingAppWithCheckpoint");
+ sparkConf.set("spark.cassandra.connection.host", "127.0.0.1");
+
+ JavaStreamingContext streamingContext = new JavaStreamingContext(sparkConf, Durations.seconds(1));
+
+ sparkContext = streamingContext.sparkContext();
+
+ streamingContext.checkpoint("./.checkpoint");
+
+ JavaInputDStream> messages = KafkaUtils.createDirectStream(streamingContext, LocationStrategies.PreferConsistent(), ConsumerStrategies. Subscribe(topics, kafkaParams));
+
+ JavaPairDStream results = messages.mapToPair(record -> new Tuple2<>(record.key(), record.value()));
+
+ JavaDStream lines = results.map(tuple2 -> tuple2._2());
+
+ JavaDStream words = lines.flatMap(x -> Arrays.asList(x.split("\\s+"))
+ .iterator());
+
+ JavaPairDStream wordCounts = words.mapToPair(s -> new Tuple2<>(s, 1))
+ .reduceByKey((Function2) (i1, i2) -> i1 + i2);
+
+ JavaMapWithStateDStream> cumulativeWordCounts = wordCounts.mapWithState(StateSpec.function((word, one, state) -> {
+ int sum = one.orElse(0) + (state.exists() ? state.get() : 0);
+ Tuple2 output = new Tuple2<>(word, sum);
+ state.update(sum);
+ return output;
+ }));
+
+ cumulativeWordCounts.foreachRDD(javaRdd -> {
+ List> wordCountList = javaRdd.collect();
+ for (Tuple2 tuple : wordCountList) {
+ List wordList = Arrays.asList(new Word(tuple._1, tuple._2));
+ JavaRDD rdd = sparkContext.parallelize(wordList);
+ javaFunctions(rdd).writerBuilder("vocabulary", "words", mapToRow(Word.class))
+ .saveToCassandra();
+ }
+ });
+
+ streamingContext.start();
+ streamingContext.awaitTermination();
+ }
+}
\ No newline at end of file
diff --git a/apache-velocity/pom.xml b/apache-velocity/pom.xml
index 19cf77d945..a0a8389f7d 100644
--- a/apache-velocity/pom.xml
+++ b/apache-velocity/pom.xml
@@ -4,8 +4,8 @@
com.baeldung0.1-SNAPSHOTapache-velocity
- warapache-velocity
+ warcom.baeldung
diff --git a/aws-lambda/pom.xml b/aws-lambda/pom.xml
index c47c3cd86f..c799718e61 100644
--- a/aws-lambda/pom.xml
+++ b/aws-lambda/pom.xml
@@ -5,8 +5,8 @@
com.baeldungaws-lambda0.1.0-SNAPSHOT
- jaraws-lambda
+ jarparent-modules
diff --git a/aws/pom.xml b/aws/pom.xml
index ab63f6afa1..560f9145f9 100644
--- a/aws/pom.xml
+++ b/aws/pom.xml
@@ -4,8 +4,8 @@
com.baeldungaws0.1.0-SNAPSHOT
- jaraws
+ jarcom.baeldung
diff --git a/axon/pom.xml b/axon/pom.xml
index c643ea9e57..2b9ac1fcdd 100644
--- a/axon/pom.xml
+++ b/axon/pom.xml
@@ -4,29 +4,61 @@
4.0.0axonaxon
-
+ Basic Axon Framework with Spring Boot configuration tutorial
+
- parent-modulescom.baeldung
- 1.0.0-SNAPSHOT
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+ org.axonframework
+ axon-spring-boot-starter
+ ${axon.version}
+
+
+ org.axonframework
+ axon-server-connector
+
+
+
+
org.axonframeworkaxon-test${axon.version}test
+
- org.axonframework
- axon-core
- ${axon.version}
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring-boot.version}
+ compile
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ com.h2database
+ h2
+ runtime
- 3.0.2
+ 4.0.3
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/MessagesRunner.java b/axon/src/main/java/com/baeldung/axon/MessagesRunner.java
deleted file mode 100644
index 77b50d09bd..0000000000
--- a/axon/src/main/java/com/baeldung/axon/MessagesRunner.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.baeldung.axon;
-
-import com.baeldung.axon.aggregates.MessagesAggregate;
-import com.baeldung.axon.commands.CreateMessageCommand;
-import com.baeldung.axon.commands.MarkReadMessageCommand;
-import com.baeldung.axon.eventhandlers.MessagesEventHandler;
-import org.axonframework.commandhandling.AggregateAnnotationCommandHandler;
-import org.axonframework.commandhandling.CommandBus;
-import org.axonframework.commandhandling.SimpleCommandBus;
-import org.axonframework.commandhandling.gateway.CommandGateway;
-import org.axonframework.commandhandling.gateway.DefaultCommandGateway;
-import org.axonframework.eventhandling.AnnotationEventListenerAdapter;
-import org.axonframework.eventsourcing.EventSourcingRepository;
-import org.axonframework.eventsourcing.eventstore.EmbeddedEventStore;
-import org.axonframework.eventsourcing.eventstore.EventStore;
-import org.axonframework.eventsourcing.eventstore.inmemory.InMemoryEventStorageEngine;
-
-import java.util.UUID;
-
-public class MessagesRunner {
-
- public static void main(String[] args) {
- CommandBus commandBus = new SimpleCommandBus();
-
- CommandGateway commandGateway = new DefaultCommandGateway(commandBus);
-
- EventStore eventStore = new EmbeddedEventStore(new InMemoryEventStorageEngine());
-
- EventSourcingRepository repository =
- new EventSourcingRepository<>(MessagesAggregate.class, eventStore);
-
-
- AggregateAnnotationCommandHandler messagesAggregateAggregateAnnotationCommandHandler =
- new AggregateAnnotationCommandHandler(MessagesAggregate.class, repository);
- messagesAggregateAggregateAnnotationCommandHandler.subscribe(commandBus);
-
- final AnnotationEventListenerAdapter annotationEventListenerAdapter =
- new AnnotationEventListenerAdapter(new MessagesEventHandler());
- eventStore.subscribe(eventMessages -> eventMessages.forEach(e -> {
- try {
- annotationEventListenerAdapter.handle(e);
- } catch (Exception e1) {
- throw new RuntimeException(e1);
-
- }
- }
-
- ));
-
- final String itemId = UUID.randomUUID().toString();
- commandGateway.send(new CreateMessageCommand(itemId, "Hello, how is your day? :-)"));
- commandGateway.send(new MarkReadMessageCommand(itemId));
- }
-}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/OrderApplication.java b/axon/src/main/java/com/baeldung/axon/OrderApplication.java
new file mode 100644
index 0000000000..8f507e141c
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/OrderApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.axon;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class OrderApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(OrderApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/aggregates/MessagesAggregate.java b/axon/src/main/java/com/baeldung/axon/aggregates/MessagesAggregate.java
deleted file mode 100644
index e762604b74..0000000000
--- a/axon/src/main/java/com/baeldung/axon/aggregates/MessagesAggregate.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.baeldung.axon.aggregates;
-
-import com.baeldung.axon.commands.CreateMessageCommand;
-import com.baeldung.axon.commands.MarkReadMessageCommand;
-import com.baeldung.axon.events.MessageCreatedEvent;
-import com.baeldung.axon.events.MessageReadEvent;
-import org.axonframework.commandhandling.CommandHandler;
-import org.axonframework.commandhandling.model.AggregateIdentifier;
-import org.axonframework.eventhandling.EventHandler;
-
-import static org.axonframework.commandhandling.model.AggregateLifecycle.apply;
-
-
-public class MessagesAggregate {
-
- @AggregateIdentifier
- private String id;
-
- public MessagesAggregate() {
- }
-
- @CommandHandler
- public MessagesAggregate(CreateMessageCommand command) {
- apply(new MessageCreatedEvent(command.getId(), command.getText()));
- }
-
- @EventHandler
- public void on(MessageCreatedEvent event) {
- this.id = event.getId();
- }
-
- @CommandHandler
- public void markRead(MarkReadMessageCommand command) {
- apply(new MessageReadEvent(id));
- }
-}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/commandmodel/OrderAggregate.java b/axon/src/main/java/com/baeldung/axon/commandmodel/OrderAggregate.java
new file mode 100644
index 0000000000..b37b2fdd66
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/commandmodel/OrderAggregate.java
@@ -0,0 +1,58 @@
+package com.baeldung.axon.commandmodel;
+
+import static org.axonframework.modelling.command.AggregateLifecycle.apply;
+
+import org.axonframework.commandhandling.CommandHandler;
+import org.axonframework.eventsourcing.EventSourcingHandler;
+import org.axonframework.modelling.command.AggregateIdentifier;
+import org.axonframework.spring.stereotype.Aggregate;
+
+import com.baeldung.axon.coreapi.commands.ConfirmOrderCommand;
+import com.baeldung.axon.coreapi.commands.PlaceOrderCommand;
+import com.baeldung.axon.coreapi.commands.ShipOrderCommand;
+import com.baeldung.axon.coreapi.events.OrderConfirmedEvent;
+import com.baeldung.axon.coreapi.events.OrderPlacedEvent;
+import com.baeldung.axon.coreapi.events.OrderShippedEvent;
+
+@Aggregate
+public class OrderAggregate {
+
+ @AggregateIdentifier
+ private String orderId;
+ private boolean orderConfirmed;
+
+ @CommandHandler
+ public OrderAggregate(PlaceOrderCommand command) {
+ apply(new OrderPlacedEvent(command.getOrderId(), command.getProduct()));
+ }
+
+ @CommandHandler
+ public void handle(ConfirmOrderCommand command) {
+ apply(new OrderConfirmedEvent(orderId));
+ }
+
+ @CommandHandler
+ public void handle(ShipOrderCommand command) {
+ if (!orderConfirmed) {
+ throw new IllegalStateException("Cannot ship an order which has not been confirmed yet.");
+ }
+
+ apply(new OrderShippedEvent(orderId));
+ }
+
+ @EventSourcingHandler
+ public void on(OrderPlacedEvent event) {
+ this.orderId = event.getOrderId();
+ orderConfirmed = false;
+ }
+
+ @EventSourcingHandler
+ public void on(OrderConfirmedEvent event) {
+ orderConfirmed = true;
+ }
+
+ protected OrderAggregate() {
+ // Required by Axon to build a default Aggregate prior to Event Sourcing
+ }
+
+}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/commands/CreateMessageCommand.java b/axon/src/main/java/com/baeldung/axon/commands/CreateMessageCommand.java
deleted file mode 100644
index d0651bf12e..0000000000
--- a/axon/src/main/java/com/baeldung/axon/commands/CreateMessageCommand.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.baeldung.axon.commands;
-
-
-import org.axonframework.commandhandling.TargetAggregateIdentifier;
-
-public class CreateMessageCommand {
-
- @TargetAggregateIdentifier
- private final String id;
- private final String text;
-
- public CreateMessageCommand(String id, String text) {
- this.id = id;
- this.text = text;
- }
-
- public String getId() {
- return id;
- }
-
- public String getText() {
- return text;
- }
-}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/commands/MarkReadMessageCommand.java b/axon/src/main/java/com/baeldung/axon/commands/MarkReadMessageCommand.java
deleted file mode 100644
index e66582d9ec..0000000000
--- a/axon/src/main/java/com/baeldung/axon/commands/MarkReadMessageCommand.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.baeldung.axon.commands;
-
-
-import org.axonframework.commandhandling.TargetAggregateIdentifier;
-
-public class MarkReadMessageCommand {
-
- @TargetAggregateIdentifier
- private final String id;
-
- public MarkReadMessageCommand(String id) {
- this.id = id;
- }
-
- public String getId() {
- return id;
- }
-}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/commands/ConfirmOrderCommand.java b/axon/src/main/java/com/baeldung/axon/coreapi/commands/ConfirmOrderCommand.java
new file mode 100644
index 0000000000..244b69f3b7
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/commands/ConfirmOrderCommand.java
@@ -0,0 +1,43 @@
+package com.baeldung.axon.coreapi.commands;
+
+import org.axonframework.modelling.command.TargetAggregateIdentifier;
+
+import java.util.Objects;
+
+public class ConfirmOrderCommand {
+
+ @TargetAggregateIdentifier
+ private final String orderId;
+
+ public ConfirmOrderCommand(String orderId) {
+ this.orderId = orderId;
+ }
+
+ public String getOrderId() {
+ return orderId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(orderId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final ConfirmOrderCommand other = (ConfirmOrderCommand) obj;
+ return Objects.equals(this.orderId, other.orderId);
+ }
+
+ @Override
+ public String toString() {
+ return "ConfirmOrderCommand{" +
+ "orderId='" + orderId + '\'' +
+ '}';
+ }
+}
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/commands/PlaceOrderCommand.java b/axon/src/main/java/com/baeldung/axon/coreapi/commands/PlaceOrderCommand.java
new file mode 100644
index 0000000000..c70d503050
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/commands/PlaceOrderCommand.java
@@ -0,0 +1,51 @@
+package com.baeldung.axon.coreapi.commands;
+
+import java.util.Objects;
+
+import org.axonframework.modelling.command.TargetAggregateIdentifier;
+
+public class PlaceOrderCommand {
+
+ @TargetAggregateIdentifier
+ private final String orderId;
+ private final String product;
+
+ public PlaceOrderCommand(String orderId, String product) {
+ this.orderId = orderId;
+ this.product = product;
+ }
+
+ public String getOrderId() {
+ return orderId;
+ }
+
+ public String getProduct() {
+ return product;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(orderId, product);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final PlaceOrderCommand other = (PlaceOrderCommand) obj;
+ return Objects.equals(this.orderId, other.orderId)
+ && Objects.equals(this.product, other.product);
+ }
+
+ @Override
+ public String toString() {
+ return "PlaceOrderCommand{" +
+ "orderId='" + orderId + '\'' +
+ ", product='" + product + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/commands/ShipOrderCommand.java b/axon/src/main/java/com/baeldung/axon/coreapi/commands/ShipOrderCommand.java
new file mode 100644
index 0000000000..7312bc1fdb
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/commands/ShipOrderCommand.java
@@ -0,0 +1,43 @@
+package com.baeldung.axon.coreapi.commands;
+
+import java.util.Objects;
+
+import org.axonframework.modelling.command.TargetAggregateIdentifier;
+
+public class ShipOrderCommand {
+
+ @TargetAggregateIdentifier
+ private final String orderId;
+
+ public ShipOrderCommand(String orderId) {
+ this.orderId = orderId;
+ }
+
+ public String getOrderId() {
+ return orderId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(orderId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final ShipOrderCommand other = (ShipOrderCommand) obj;
+ return Objects.equals(this.orderId, other.orderId);
+ }
+
+ @Override
+ public String toString() {
+ return "ShipOrderCommand{" +
+ "orderId='" + orderId + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderConfirmedEvent.java b/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderConfirmedEvent.java
new file mode 100644
index 0000000000..d2b7d58435
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderConfirmedEvent.java
@@ -0,0 +1,40 @@
+package com.baeldung.axon.coreapi.events;
+
+import java.util.Objects;
+
+public class OrderConfirmedEvent {
+
+ private final String orderId;
+
+ public OrderConfirmedEvent(String orderId) {
+ this.orderId = orderId;
+ }
+
+ public String getOrderId() {
+ return orderId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(orderId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final OrderConfirmedEvent other = (OrderConfirmedEvent) obj;
+ return Objects.equals(this.orderId, other.orderId);
+ }
+
+ @Override
+ public String toString() {
+ return "OrderConfirmedEvent{" +
+ "orderId='" + orderId + '\'' +
+ '}';
+ }
+}
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderPlacedEvent.java b/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderPlacedEvent.java
new file mode 100644
index 0000000000..06de4c5f9f
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderPlacedEvent.java
@@ -0,0 +1,48 @@
+package com.baeldung.axon.coreapi.events;
+
+import java.util.Objects;
+
+public class OrderPlacedEvent {
+
+ private final String orderId;
+ private final String product;
+
+ public OrderPlacedEvent(String orderId, String product) {
+ this.orderId = orderId;
+ this.product = product;
+ }
+
+ public String getOrderId() {
+ return orderId;
+ }
+
+ public String getProduct() {
+ return product;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(orderId, product);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final OrderPlacedEvent other = (OrderPlacedEvent) obj;
+ return Objects.equals(this.orderId, other.orderId)
+ && Objects.equals(this.product, other.product);
+ }
+
+ @Override
+ public String toString() {
+ return "OrderPlacedEvent{" +
+ "orderId='" + orderId + '\'' +
+ ", product='" + product + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderShippedEvent.java b/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderShippedEvent.java
new file mode 100644
index 0000000000..76aa684629
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/events/OrderShippedEvent.java
@@ -0,0 +1,40 @@
+package com.baeldung.axon.coreapi.events;
+
+import java.util.Objects;
+
+public class OrderShippedEvent {
+
+ private final String orderId;
+
+ public OrderShippedEvent(String orderId) {
+ this.orderId = orderId;
+ }
+
+ public String getOrderId() {
+ return orderId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(orderId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final OrderShippedEvent other = (OrderShippedEvent) obj;
+ return Objects.equals(this.orderId, other.orderId);
+ }
+
+ @Override
+ public String toString() {
+ return "OrderShippedEvent{" +
+ "orderId='" + orderId + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/queries/FindAllOrderedProductsQuery.java b/axon/src/main/java/com/baeldung/axon/coreapi/queries/FindAllOrderedProductsQuery.java
new file mode 100644
index 0000000000..9d6ca2cfb2
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/queries/FindAllOrderedProductsQuery.java
@@ -0,0 +1,5 @@
+package com.baeldung.axon.coreapi.queries;
+
+public class FindAllOrderedProductsQuery {
+
+}
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/queries/OrderStatus.java b/axon/src/main/java/com/baeldung/axon/coreapi/queries/OrderStatus.java
new file mode 100644
index 0000000000..d215c5fc32
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/queries/OrderStatus.java
@@ -0,0 +1,7 @@
+package com.baeldung.axon.coreapi.queries;
+
+public enum OrderStatus {
+
+ PLACED, CONFIRMED, SHIPPED
+
+}
diff --git a/axon/src/main/java/com/baeldung/axon/coreapi/queries/OrderedProduct.java b/axon/src/main/java/com/baeldung/axon/coreapi/queries/OrderedProduct.java
new file mode 100644
index 0000000000..d847bb2a98
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/coreapi/queries/OrderedProduct.java
@@ -0,0 +1,64 @@
+package com.baeldung.axon.coreapi.queries;
+
+import java.util.Objects;
+
+public class OrderedProduct {
+
+ private final String orderId;
+ private final String product;
+ private OrderStatus orderStatus;
+
+ public OrderedProduct(String orderId, String product) {
+ this.orderId = orderId;
+ this.product = product;
+ orderStatus = OrderStatus.PLACED;
+ }
+
+ public String getOrderId() {
+ return orderId;
+ }
+
+ public String getProduct() {
+ return product;
+ }
+
+ public OrderStatus getOrderStatus() {
+ return orderStatus;
+ }
+
+ public void setOrderConfirmed() {
+ this.orderStatus = OrderStatus.CONFIRMED;
+ }
+
+ public void setOrderShipped() {
+ this.orderStatus = OrderStatus.SHIPPED;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(orderId, product, orderStatus);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final OrderedProduct other = (OrderedProduct) obj;
+ return Objects.equals(this.orderId, other.orderId)
+ && Objects.equals(this.product, other.product)
+ && Objects.equals(this.orderStatus, other.orderStatus);
+ }
+
+ @Override
+ public String toString() {
+ return "OrderedProduct{" +
+ "orderId='" + orderId + '\'' +
+ ", product='" + product + '\'' +
+ ", orderStatus=" + orderStatus +
+ '}';
+ }
+}
diff --git a/axon/src/main/java/com/baeldung/axon/eventhandlers/MessagesEventHandler.java b/axon/src/main/java/com/baeldung/axon/eventhandlers/MessagesEventHandler.java
deleted file mode 100644
index 3e51e19c4e..0000000000
--- a/axon/src/main/java/com/baeldung/axon/eventhandlers/MessagesEventHandler.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.baeldung.axon.eventhandlers;
-
-import com.baeldung.axon.events.MessageReadEvent;
-import com.baeldung.axon.events.MessageCreatedEvent;
-import org.axonframework.eventhandling.EventHandler;
-
-
-public class MessagesEventHandler {
-
- @EventHandler
- public void handle(MessageCreatedEvent event) {
- System.out.println("Message received: " + event.getText() + " (" + event.getId() + ")");
- }
-
- @EventHandler
- public void handle(MessageReadEvent event) {
- System.out.println("Message read: " + event.getId());
- }
-}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/events/MessageCreatedEvent.java b/axon/src/main/java/com/baeldung/axon/events/MessageCreatedEvent.java
deleted file mode 100644
index 3c9aac5ed8..0000000000
--- a/axon/src/main/java/com/baeldung/axon/events/MessageCreatedEvent.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.baeldung.axon.events;
-
-public class MessageCreatedEvent {
-
- private final String id;
- private final String text;
-
- public MessageCreatedEvent(String id, String text) {
- this.id = id;
- this.text = text;
- }
-
- public String getId() {
- return id;
- }
-
- public String getText() {
- return text;
- }
-}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/events/MessageReadEvent.java b/axon/src/main/java/com/baeldung/axon/events/MessageReadEvent.java
deleted file mode 100644
index 57bfc8e19e..0000000000
--- a/axon/src/main/java/com/baeldung/axon/events/MessageReadEvent.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.baeldung.axon.events;
-
-public class MessageReadEvent {
-
- private final String id;
-
- public MessageReadEvent(String id) {
- this.id = id;
- }
-
- public String getId() {
- return id;
- }
-}
\ No newline at end of file
diff --git a/axon/src/main/java/com/baeldung/axon/gui/OrderRestEndpoint.java b/axon/src/main/java/com/baeldung/axon/gui/OrderRestEndpoint.java
new file mode 100644
index 0000000000..a9f34cc691
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/gui/OrderRestEndpoint.java
@@ -0,0 +1,52 @@
+package com.baeldung.axon.gui;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.axonframework.commandhandling.gateway.CommandGateway;
+import org.axonframework.messaging.responsetypes.ResponseTypes;
+import org.axonframework.queryhandling.QueryGateway;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.axon.coreapi.commands.ConfirmOrderCommand;
+import com.baeldung.axon.coreapi.commands.PlaceOrderCommand;
+import com.baeldung.axon.coreapi.commands.ShipOrderCommand;
+import com.baeldung.axon.coreapi.queries.FindAllOrderedProductsQuery;
+import com.baeldung.axon.coreapi.queries.OrderedProduct;
+
+@RestController
+public class OrderRestEndpoint {
+
+ private final CommandGateway commandGateway;
+ private final QueryGateway queryGateway;
+
+ public OrderRestEndpoint(CommandGateway commandGateway, QueryGateway queryGateway) {
+ this.commandGateway = commandGateway;
+ this.queryGateway = queryGateway;
+ }
+
+ @PostMapping("/ship-order")
+ public void shipOrder() {
+ String orderId = UUID.randomUUID().toString();
+ commandGateway.send(new PlaceOrderCommand(orderId, "Deluxe Chair"));
+ commandGateway.send(new ConfirmOrderCommand(orderId));
+ commandGateway.send(new ShipOrderCommand(orderId));
+ }
+
+ @PostMapping("/ship-unconfirmed-order")
+ public void shipUnconfirmedOrder() {
+ String orderId = UUID.randomUUID().toString();
+ commandGateway.send(new PlaceOrderCommand(orderId, "Deluxe Chair"));
+ // This throws an exception, as an Order cannot be shipped if it has not been confirmed yet.
+ commandGateway.send(new ShipOrderCommand(orderId));
+ }
+
+ @GetMapping("/all-orders")
+ public List findAllOrderedProducts() {
+ return queryGateway.query(new FindAllOrderedProductsQuery(), ResponseTypes.multipleInstancesOf(OrderedProduct.class))
+ .join();
+ }
+
+}
diff --git a/axon/src/main/java/com/baeldung/axon/querymodel/OrderedProductsEventHandler.java b/axon/src/main/java/com/baeldung/axon/querymodel/OrderedProductsEventHandler.java
new file mode 100644
index 0000000000..d4cf3d999b
--- /dev/null
+++ b/axon/src/main/java/com/baeldung/axon/querymodel/OrderedProductsEventHandler.java
@@ -0,0 +1,50 @@
+package com.baeldung.axon.querymodel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.axonframework.eventhandling.EventHandler;
+import org.axonframework.queryhandling.QueryHandler;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.axon.coreapi.events.OrderConfirmedEvent;
+import com.baeldung.axon.coreapi.events.OrderPlacedEvent;
+import com.baeldung.axon.coreapi.events.OrderShippedEvent;
+import com.baeldung.axon.coreapi.queries.FindAllOrderedProductsQuery;
+import com.baeldung.axon.coreapi.queries.OrderedProduct;
+
+@Service
+public class OrderedProductsEventHandler {
+
+ private final Map orderedProducts = new HashMap<>();
+
+ @EventHandler
+ public void on(OrderPlacedEvent event) {
+ String orderId = event.getOrderId();
+ orderedProducts.put(orderId, new OrderedProduct(orderId, event.getProduct()));
+ }
+
+ @EventHandler
+ public void on(OrderConfirmedEvent event) {
+ orderedProducts.computeIfPresent(event.getOrderId(), (orderId, orderedProduct) -> {
+ orderedProduct.setOrderConfirmed();
+ return orderedProduct;
+ });
+ }
+
+ @EventHandler
+ public void on(OrderShippedEvent event) {
+ orderedProducts.computeIfPresent(event.getOrderId(), (orderId, orderedProduct) -> {
+ orderedProduct.setOrderShipped();
+ return orderedProduct;
+ });
+ }
+
+ @QueryHandler
+ public List handle(FindAllOrderedProductsQuery query) {
+ return new ArrayList<>(orderedProducts.values());
+ }
+
+}
\ No newline at end of file
diff --git a/axon/src/main/resources/order-api.http b/axon/src/main/resources/order-api.http
new file mode 100644
index 0000000000..a3c69c72bc
--- /dev/null
+++ b/axon/src/main/resources/order-api.http
@@ -0,0 +1,11 @@
+POST http://localhost:8080/ship-order
+
+###
+
+POST http://localhost:8080/ship-unconfirmed-order
+
+###
+
+GET http://localhost:8080/all-orders
+
+###
diff --git a/axon/src/test/java/com/baeldung/axon/MessagesAggregateIntegrationTest.java b/axon/src/test/java/com/baeldung/axon/MessagesAggregateIntegrationTest.java
deleted file mode 100644
index ad099d2c2b..0000000000
--- a/axon/src/test/java/com/baeldung/axon/MessagesAggregateIntegrationTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.baeldung.axon;
-
-import com.baeldung.axon.aggregates.MessagesAggregate;
-import com.baeldung.axon.commands.CreateMessageCommand;
-import com.baeldung.axon.commands.MarkReadMessageCommand;
-import com.baeldung.axon.events.MessageCreatedEvent;
-import com.baeldung.axon.events.MessageReadEvent;
-import org.axonframework.test.aggregate.AggregateTestFixture;
-import org.axonframework.test.aggregate.FixtureConfiguration;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.UUID;
-
-public class MessagesAggregateIntegrationTest {
-
- private FixtureConfiguration fixture;
-
- @Before
- public void setUp() throws Exception {
- fixture = new AggregateTestFixture(MessagesAggregate.class);
-
- }
-
- @Test
- public void giveAggregateRoot_whenCreateMessageCommand_thenShouldProduceMessageCreatedEvent() throws Exception {
- String eventText = "Hello, how is your day?";
- String id = UUID.randomUUID().toString();
- fixture.given()
- .when(new CreateMessageCommand(id, eventText))
- .expectEvents(new MessageCreatedEvent(id, eventText));
- }
-
- @Test
- public void givenMessageCreatedEvent_whenReadMessageCommand_thenShouldProduceMessageReadEvent() throws Exception {
- String id = UUID.randomUUID().toString();
-
- fixture.given(new MessageCreatedEvent(id, "Hello :-)"))
- .when(new MarkReadMessageCommand(id))
- .expectEvents(new MessageReadEvent(id));
- }
-}
\ No newline at end of file
diff --git a/axon/src/test/java/com/baeldung/axon/commandmodel/OrderAggregateUnitTest.java b/axon/src/test/java/com/baeldung/axon/commandmodel/OrderAggregateUnitTest.java
new file mode 100644
index 0000000000..9beedbaa19
--- /dev/null
+++ b/axon/src/test/java/com/baeldung/axon/commandmodel/OrderAggregateUnitTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.axon.commandmodel;
+
+import java.util.UUID;
+
+import org.axonframework.test.aggregate.AggregateTestFixture;
+import org.axonframework.test.aggregate.FixtureConfiguration;
+import org.junit.*;
+
+import com.baeldung.axon.coreapi.commands.ConfirmOrderCommand;
+import com.baeldung.axon.coreapi.commands.PlaceOrderCommand;
+import com.baeldung.axon.coreapi.commands.ShipOrderCommand;
+import com.baeldung.axon.coreapi.events.OrderConfirmedEvent;
+import com.baeldung.axon.coreapi.events.OrderPlacedEvent;
+import com.baeldung.axon.coreapi.events.OrderShippedEvent;
+
+public class OrderAggregateUnitTest {
+
+ private FixtureConfiguration fixture;
+
+ @Before
+ public void setUp() {
+ fixture = new AggregateTestFixture<>(OrderAggregate.class);
+ }
+
+ @Test
+ public void giveNoPriorActivity_whenPlaceOrderCommand_thenShouldPublishOrderPlacedEvent() {
+ String orderId = UUID.randomUUID().toString();
+ String product = "Deluxe Chair";
+ fixture.givenNoPriorActivity()
+ .when(new PlaceOrderCommand(orderId, product))
+ .expectEvents(new OrderPlacedEvent(orderId, product));
+ }
+
+ @Test
+ public void givenOrderPlacedEvent_whenConfirmOrderCommand_thenShouldPublishOrderConfirmedEvent() {
+ String orderId = UUID.randomUUID().toString();
+ String product = "Deluxe Chair";
+ fixture.given(new OrderPlacedEvent(orderId, product))
+ .when(new ConfirmOrderCommand(orderId))
+ .expectEvents(new OrderConfirmedEvent(orderId));
+ }
+
+ @Test
+ public void givenOrderPlacedEvent_whenShipOrderCommand_thenShouldThrowIllegalStateException() {
+ String orderId = UUID.randomUUID().toString();
+ String product = "Deluxe Chair";
+ fixture.given(new OrderPlacedEvent(orderId, product))
+ .when(new ShipOrderCommand(orderId))
+ .expectException(IllegalStateException.class);
+ }
+
+ @Test
+ public void givenOrderPlacedEventAndOrderConfirmedEvent_whenShipOrderCommand_thenShouldPublishOrderShippedEvent() {
+ String orderId = UUID.randomUUID().toString();
+ String product = "Deluxe Chair";
+ fixture.given(new OrderPlacedEvent(orderId, product), new OrderConfirmedEvent(orderId))
+ .when(new ShipOrderCommand(orderId))
+ .expectEvents(new OrderShippedEvent(orderId));
+ }
+
+}
\ No newline at end of file
diff --git a/azure/pom.xml b/azure/pom.xml
index 555efeef70..e2a05796d6 100644
--- a/azure/pom.xml
+++ b/azure/pom.xml
@@ -5,9 +5,9 @@
com.baeldungazure0.1
- warazureDemo project for Spring Boot on Azure
+ warparent-boot-2
diff --git a/blade/README.md b/blade/README.md
new file mode 100644
index 0000000000..d823de775f
--- /dev/null
+++ b/blade/README.md
@@ -0,0 +1,5 @@
+### Relevant Articles:
+
+- [Blade - A Complete GuideBook](http://www.baeldung.com/blade)
+
+Run Integration Tests with `mvn integration-test`
\ No newline at end of file
diff --git a/blade/pom.xml b/blade/pom.xml
new file mode 100644
index 0000000000..37615bed01
--- /dev/null
+++ b/blade/pom.xml
@@ -0,0 +1,205 @@
+
+
+ 4.0.0
+
+ com.baeldung
+ blade
+ 1.0.0-SNAPSHOT
+ blade
+
+
+
+
+
+
+
+
+
+
+
+ com.bladejava
+ blade-mvc
+ ${blade-mvc.version}
+
+
+
+ org.webjars
+ bootstrap
+ ${bootstrap.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ ${assertj-core.version}
+ test
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpclient.version}
+ test
+
+
+ org.apache.httpcomponents
+ httpmime
+ ${httpmime.version}
+ test
+
+
+ org.apache.httpcomponents
+ httpcore
+ ${httpcore.version}
+ test
+
+
+
+ sample-blade-app
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ 3
+ true
+
+ **/*LiveTest.java
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+ **/*LiveTest.java
+
+
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+ com.bazaarvoice.maven.plugins
+ process-exec-maven-plugin
+ ${process-exec-maven-plugin.version}
+
+
+
+ blade-process
+ pre-integration-test
+
+ start
+
+
+ Blade
+ false
+
+ java
+ -jar
+ sample-blade-app.jar
+
+
+
+
+
+
+ stop-all
+ post-integration-test
+
+ stop-all
+
+
+
+
+
+
+
+ maven-assembly-plugin
+ 3.1.0
+
+ ${project.build.finalName}
+ false
+
+
+ com.baeldung.blade.sample.App
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ UTF-8
+
+
+
+
+
+
+ 1.8
+ 1.8
+ 2.0.14.RELEASE
+ 4.2.1
+ 3.8.1
+ 1.18.4
+ 4.12
+ 4.5.6
+ 4.5.6
+ 4.4.10
+ 3.11.1
+ 3.0.0-M3
+ 0.7
+ 2.21.0
+ 3.7.0
+
+
diff --git a/blade/src/main/java/com/baeldung/blade/sample/App.java b/blade/src/main/java/com/baeldung/blade/sample/App.java
new file mode 100644
index 0000000000..f3f3d4aebd
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/App.java
@@ -0,0 +1,38 @@
+package com.baeldung.blade.sample;
+
+import com.baeldung.blade.sample.interceptors.BaeldungMiddleware;
+import com.blade.Blade;
+import com.blade.event.EventType;
+import com.blade.mvc.WebContext;
+import com.blade.mvc.http.Session;
+
+public class App {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(App.class);
+
+ public static void main(String[] args) {
+
+ Blade.of()
+ .get("/", ctx -> ctx.render("index.html"))
+ .get("/basic-route-example", ctx -> ctx.text("GET called"))
+ .post("/basic-route-example", ctx -> ctx.text("POST called"))
+ .put("/basic-route-example", ctx -> ctx.text("PUT called"))
+ .delete("/basic-route-example", ctx -> ctx.text("DELETE called"))
+ .addStatics("/custom-static")
+ // .showFileList(true)
+ .enableCors(true)
+ .before("/user/*", ctx -> log.info("[NarrowedHook] Before '/user/*', URL called: " + ctx.uri()))
+ .on(EventType.SERVER_STARTED, e -> {
+ String version = WebContext.blade()
+ .env("app.version")
+ .orElse("N/D");
+ log.info("[Event::serverStarted] Loading 'app.version' from configuration, value: " + version);
+ })
+ .on(EventType.SESSION_CREATED, e -> {
+ Session session = (Session) e.attribute("session");
+ session.attribute("mySessionValue", "Baeldung");
+ })
+ .use(new BaeldungMiddleware())
+ .start(App.class, args);
+ }
+}
diff --git a/blade/src/main/java/com/baeldung/blade/sample/AttributesExampleController.java b/blade/src/main/java/com/baeldung/blade/sample/AttributesExampleController.java
new file mode 100644
index 0000000000..339ba701f7
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/AttributesExampleController.java
@@ -0,0 +1,37 @@
+package com.baeldung.blade.sample;
+
+import com.blade.mvc.annotation.GetRoute;
+import com.blade.mvc.annotation.Path;
+import com.blade.mvc.http.Request;
+import com.blade.mvc.http.Response;
+import com.blade.mvc.http.Session;
+
+@Path
+public class AttributesExampleController {
+
+ public final static String REQUEST_VALUE = "Some Request value";
+ public final static String SESSION_VALUE = "1337";
+ public final static String HEADER = "Some Header";
+
+ @GetRoute("/request-attribute-example")
+ public void getRequestAttribute(Request request, Response response) {
+ request.attribute("request-val", REQUEST_VALUE);
+ String requestVal = request.attribute("request-val");
+ response.text(requestVal);
+ }
+
+ @GetRoute("/session-attribute-example")
+ public void getSessionAttribute(Request request, Response response) {
+ Session session = request.session();
+ session.attribute("session-val", SESSION_VALUE);
+ String sessionVal = session.attribute("session-val");
+ response.text(sessionVal);
+ }
+
+ @GetRoute("/header-example")
+ public void getHeader(Request request, Response response) {
+ String headerVal = request.header("a-header", HEADER);
+ response.header("a-header", headerVal);
+ }
+
+}
diff --git a/blade/src/main/java/com/baeldung/blade/sample/LogExampleController.java b/blade/src/main/java/com/baeldung/blade/sample/LogExampleController.java
new file mode 100644
index 0000000000..f0c22c70dd
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/LogExampleController.java
@@ -0,0 +1,22 @@
+package com.baeldung.blade.sample;
+
+import com.blade.mvc.annotation.Path;
+import com.blade.mvc.annotation.Route;
+import com.blade.mvc.http.Response;
+
+@Path
+public class LogExampleController {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleController.class);
+
+ @Route(value = "/test-logs")
+ public void testLogs(Response response) {
+ log.trace("This is a TRACE Message");
+ log.debug("This is a DEBUG Message");
+ log.info("This is an INFO Message");
+ log.warn("This is a WARN Message");
+ log.error("This is an ERROR Message");
+ response.text("Check in ./logs");
+ }
+
+}
diff --git a/blade/src/main/java/com/baeldung/blade/sample/ParameterInjectionExampleController.java b/blade/src/main/java/com/baeldung/blade/sample/ParameterInjectionExampleController.java
new file mode 100644
index 0000000000..bc28244022
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/ParameterInjectionExampleController.java
@@ -0,0 +1,71 @@
+package com.baeldung.blade.sample;
+
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+
+import com.baeldung.blade.sample.vo.User;
+import com.blade.mvc.annotation.CookieParam;
+import com.blade.mvc.annotation.GetRoute;
+import com.blade.mvc.annotation.HeaderParam;
+import com.blade.mvc.annotation.JSON;
+import com.blade.mvc.annotation.MultipartParam;
+import com.blade.mvc.annotation.Param;
+import com.blade.mvc.annotation.Path;
+import com.blade.mvc.annotation.PathParam;
+import com.blade.mvc.annotation.PostRoute;
+import com.blade.mvc.http.Response;
+import com.blade.mvc.multipart.FileItem;
+import com.blade.mvc.ui.RestResponse;
+
+@Path
+public class ParameterInjectionExampleController {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ParameterInjectionExampleController.class);
+
+ @GetRoute("/params/form")
+ public void formParam(@Param String name, Response response) {
+ log.info("name: " + name);
+ response.text(name);
+ }
+
+ @GetRoute("/params/path/:uid")
+ public void restfulParam(@PathParam Integer uid, Response response) {
+ log.info("uid: " + uid);
+ response.text(String.valueOf(uid));
+ }
+
+ @PostRoute("/params-file") // DO NOT USE A SLASH WITHIN THE ROUTE OR IT WILL BREAK (?)
+ @JSON
+ public RestResponse> fileParam(@MultipartParam FileItem fileItem) throws Exception {
+ try {
+ byte[] fileContent = fileItem.getData();
+
+ log.debug("Saving the uploaded file");
+ java.nio.file.Path tempFile = Files.createTempFile("baeldung_tempfiles", ".tmp");
+ Files.write(tempFile, fileContent, StandardOpenOption.WRITE);
+
+ return RestResponse.ok();
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ return RestResponse.fail(e.getMessage());
+ }
+ }
+
+ @GetRoute("/params/header")
+ public void headerParam(@HeaderParam String customheader, Response response) {
+ log.info("Custom header: " + customheader);
+ response.text(customheader);
+ }
+
+ @GetRoute("/params/cookie")
+ public void cookieParam(@CookieParam(defaultValue = "default value") String myCookie, Response response) {
+ log.info("myCookie: " + myCookie);
+ response.text(myCookie);
+ }
+
+ @PostRoute("/params/vo")
+ public void voParam(@Param User user, Response response) {
+ log.info("user as voParam: " + user.toString());
+ response.html(user.toString() + "
Back");
+ }
+}
\ No newline at end of file
diff --git a/blade/src/main/java/com/baeldung/blade/sample/RouteExampleController.java b/blade/src/main/java/com/baeldung/blade/sample/RouteExampleController.java
new file mode 100644
index 0000000000..7ba2a270a9
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/RouteExampleController.java
@@ -0,0 +1,78 @@
+package com.baeldung.blade.sample;
+
+import com.baeldung.blade.sample.configuration.BaeldungException;
+import com.blade.mvc.WebContext;
+import com.blade.mvc.annotation.DeleteRoute;
+import com.blade.mvc.annotation.GetRoute;
+import com.blade.mvc.annotation.Path;
+import com.blade.mvc.annotation.PostRoute;
+import com.blade.mvc.annotation.PutRoute;
+import com.blade.mvc.annotation.Route;
+import com.blade.mvc.http.HttpMethod;
+import com.blade.mvc.http.Request;
+import com.blade.mvc.http.Response;
+
+@Path
+public class RouteExampleController {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RouteExampleController.class);
+
+ @GetRoute("/route-example")
+ public String get() {
+ return "get.html";
+ }
+
+ @PostRoute("/route-example")
+ public String post() {
+ return "post.html";
+ }
+
+ @PutRoute("/route-example")
+ public String put() {
+ return "put.html";
+ }
+
+ @DeleteRoute("/route-example")
+ public String delete() {
+ return "delete.html";
+ }
+
+ @Route(value = "/another-route-example", method = HttpMethod.GET)
+ public String anotherGet() {
+ return "get.html";
+ }
+
+ @Route(value = "/allmatch-route-example")
+ public String allmatch() {
+ return "allmatch.html";
+ }
+
+ @Route(value = "/triggerInternalServerError")
+ public void triggerInternalServerError() {
+ int x = 1 / 0;
+ }
+
+ @Route(value = "/triggerBaeldungException")
+ public void triggerBaeldungException() throws BaeldungException {
+ throw new BaeldungException("Foobar Exception to threat differently");
+ }
+
+ @Route(value = "/user/foo")
+ public void urlCoveredByNarrowedWebhook(Response response) {
+ response.text("Check out for the WebHook covering '/user/*' in the logs");
+ }
+
+ @GetRoute("/load-configuration-in-a-route")
+ public void loadConfigurationInARoute(Response response) {
+ String authors = WebContext.blade()
+ .env("app.authors", "Unknown authors");
+ log.info("[/load-configuration-in-a-route] Loading 'app.authors' from configuration, value: " + authors);
+ response.render("index.html");
+ }
+
+ @GetRoute("/template-output-test")
+ public void templateOutputTest(Request request, Response response) {
+ request.attribute("name", "Blade");
+ response.render("template-output-test.html");
+ }
+}
diff --git a/blade/src/main/java/com/baeldung/blade/sample/configuration/BaeldungException.java b/blade/src/main/java/com/baeldung/blade/sample/configuration/BaeldungException.java
new file mode 100644
index 0000000000..01a030b7e7
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/configuration/BaeldungException.java
@@ -0,0 +1,9 @@
+package com.baeldung.blade.sample.configuration;
+
+public class BaeldungException extends RuntimeException {
+
+ public BaeldungException(String message) {
+ super(message);
+ }
+
+}
\ No newline at end of file
diff --git a/blade/src/main/java/com/baeldung/blade/sample/configuration/GlobalExceptionHandler.java b/blade/src/main/java/com/baeldung/blade/sample/configuration/GlobalExceptionHandler.java
new file mode 100644
index 0000000000..ab7b81c0dc
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/configuration/GlobalExceptionHandler.java
@@ -0,0 +1,25 @@
+package com.baeldung.blade.sample.configuration;
+
+import com.blade.ioc.annotation.Bean;
+import com.blade.mvc.WebContext;
+import com.blade.mvc.handler.DefaultExceptionHandler;
+
+@Bean
+public class GlobalExceptionHandler extends DefaultExceptionHandler {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+ @Override
+ public void handle(Exception e) {
+ if (e instanceof BaeldungException) {
+ Exception baeldungException = (BaeldungException) e;
+ String msg = baeldungException.getMessage();
+ log.error("[GlobalExceptionHandler] Intercepted an exception to threat with additional logic. Error message: " + msg);
+ WebContext.response()
+ .render("index.html");
+
+ } else {
+ super.handle(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/blade/src/main/java/com/baeldung/blade/sample/configuration/LoadConfig.java b/blade/src/main/java/com/baeldung/blade/sample/configuration/LoadConfig.java
new file mode 100644
index 0000000000..0f1aab1b52
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/configuration/LoadConfig.java
@@ -0,0 +1,23 @@
+package com.baeldung.blade.sample.configuration;
+
+import com.blade.Blade;
+import com.blade.ioc.annotation.Bean;
+import com.blade.loader.BladeLoader;
+import com.blade.mvc.WebContext;
+
+@Bean
+public class LoadConfig implements BladeLoader {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoadConfig.class);
+
+ @Override
+ public void load(Blade blade) {
+ String version = WebContext.blade()
+ .env("app.version")
+ .orElse("N/D");
+ String authors = WebContext.blade()
+ .env("app.authors", "Unknown authors");
+
+ log.info("[LoadConfig] loaded 'app.version' (" + version + ") and 'app.authors' (" + authors + ") in a configuration bean");
+ }
+}
\ No newline at end of file
diff --git a/blade/src/main/java/com/baeldung/blade/sample/configuration/ScheduleExample.java b/blade/src/main/java/com/baeldung/blade/sample/configuration/ScheduleExample.java
new file mode 100644
index 0000000000..c170975818
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/configuration/ScheduleExample.java
@@ -0,0 +1,15 @@
+package com.baeldung.blade.sample.configuration;
+
+import com.blade.ioc.annotation.Bean;
+import com.blade.task.annotation.Schedule;
+
+@Bean
+public class ScheduleExample {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ScheduleExample.class);
+
+ @Schedule(name = "baeldungTask", cron = "0 */1 * * * ?")
+ public void runScheduledTask() {
+ log.info("[ScheduleExample] This is a scheduled Task running once per minute.");
+ }
+}
diff --git a/blade/src/main/java/com/baeldung/blade/sample/interceptors/BaeldungHook.java b/blade/src/main/java/com/baeldung/blade/sample/interceptors/BaeldungHook.java
new file mode 100644
index 0000000000..4d0d178b0d
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/interceptors/BaeldungHook.java
@@ -0,0 +1,17 @@
+package com.baeldung.blade.sample.interceptors;
+
+import com.blade.ioc.annotation.Bean;
+import com.blade.mvc.RouteContext;
+import com.blade.mvc.hook.WebHook;
+
+@Bean
+public class BaeldungHook implements WebHook {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BaeldungHook.class);
+
+ @Override
+ public boolean before(RouteContext ctx) {
+ log.info("[BaeldungHook] called before Route method");
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/blade/src/main/java/com/baeldung/blade/sample/interceptors/BaeldungMiddleware.java b/blade/src/main/java/com/baeldung/blade/sample/interceptors/BaeldungMiddleware.java
new file mode 100644
index 0000000000..3342cd8b01
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/interceptors/BaeldungMiddleware.java
@@ -0,0 +1,15 @@
+package com.baeldung.blade.sample.interceptors;
+
+import com.blade.mvc.RouteContext;
+import com.blade.mvc.hook.WebHook;
+
+public class BaeldungMiddleware implements WebHook {
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BaeldungMiddleware.class);
+
+ @Override
+ public boolean before(RouteContext context) {
+ log.info("[BaeldungMiddleware] called before Route method and other WebHooks");
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/blade/src/main/java/com/baeldung/blade/sample/vo/User.java b/blade/src/main/java/com/baeldung/blade/sample/vo/User.java
new file mode 100644
index 0000000000..b493dc3663
--- /dev/null
+++ b/blade/src/main/java/com/baeldung/blade/sample/vo/User.java
@@ -0,0 +1,16 @@
+package com.baeldung.blade.sample.vo;
+
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class User {
+ @Getter @Setter private String name;
+ @Getter @Setter private String site;
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilder.toString(this);
+ }
+}
\ No newline at end of file
diff --git a/blade/src/main/resources/application.properties b/blade/src/main/resources/application.properties
new file mode 100644
index 0000000000..ebf365406a
--- /dev/null
+++ b/blade/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+mvc.statics.show-list=true
+mvc.view.404=my-404.html
+mvc.view.500=my-500.html
+app.version=0.0.1
+app.authors=Andrea Ligios
diff --git a/blade/src/main/resources/custom-static/icon.png b/blade/src/main/resources/custom-static/icon.png
new file mode 100644
index 0000000000..59af395afc
Binary files /dev/null and b/blade/src/main/resources/custom-static/icon.png differ
diff --git a/blade/src/main/resources/favicon.ico b/blade/src/main/resources/favicon.ico
new file mode 100644
index 0000000000..ca63a6a890
Binary files /dev/null and b/blade/src/main/resources/favicon.ico differ
diff --git a/blade/src/main/resources/static/app.css b/blade/src/main/resources/static/app.css
new file mode 100644
index 0000000000..9fff13d9b6
--- /dev/null
+++ b/blade/src/main/resources/static/app.css
@@ -0,0 +1 @@
+/* App CSS */
\ No newline at end of file
diff --git a/blade/src/main/resources/static/app.js b/blade/src/main/resources/static/app.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/blade/src/main/resources/static/file-upload.html b/blade/src/main/resources/static/file-upload.html
new file mode 100644
index 0000000000..b805be81b1
--- /dev/null
+++ b/blade/src/main/resources/static/file-upload.html
@@ -0,0 +1,43 @@
+
+
+
+
+Title
+
+
+
+
+
+
File Upload and download test
+
+
+
+ Back
+
+
+
+
\ No newline at end of file
diff --git a/blade/src/main/resources/static/user-post.html b/blade/src/main/resources/static/user-post.html
new file mode 100644
index 0000000000..ccfc4e8d0b
--- /dev/null
+++ b/blade/src/main/resources/static/user-post.html
@@ -0,0 +1,25 @@
+
+
+
+
+Title
+
+
+
+
+
+
User POJO post test
+
+
+
+
+
+ Back
+
+
+
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/allmatch.html b/blade/src/main/resources/templates/allmatch.html
new file mode 100644
index 0000000000..7a4bfa070f
--- /dev/null
+++ b/blade/src/main/resources/templates/allmatch.html
@@ -0,0 +1 @@
+ALLMATCH called
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/delete.html b/blade/src/main/resources/templates/delete.html
new file mode 100644
index 0000000000..1acb4b0b62
--- /dev/null
+++ b/blade/src/main/resources/templates/delete.html
@@ -0,0 +1 @@
+DELETE called
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/get.html b/blade/src/main/resources/templates/get.html
new file mode 100644
index 0000000000..2c37aa1058
--- /dev/null
+++ b/blade/src/main/resources/templates/get.html
@@ -0,0 +1 @@
+GET called
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/index.html b/blade/src/main/resources/templates/index.html
new file mode 100644
index 0000000000..6b7c2e77ad
--- /dev/null
+++ b/blade/src/main/resources/templates/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+Baeldung Blade App • Written by Andrea Ligios
+
+
+
+
Baeldung Blade App - Showcase
+
+
Manual tests
+
The following are tests which are not covered by integration tests, but that can be run manually in order to check the functionality, either in the browser or in the logs, depending on the case.
+
+
+
+
+
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/my-404.html b/blade/src/main/resources/templates/my-404.html
new file mode 100644
index 0000000000..0fa694f241
--- /dev/null
+++ b/blade/src/main/resources/templates/my-404.html
@@ -0,0 +1,10 @@
+
+
+
+
+ 404 Not found
+
+
+ Custom Error 404 Page
+
+
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/my-500.html b/blade/src/main/resources/templates/my-500.html
new file mode 100644
index 0000000000..cc8438bfd6
--- /dev/null
+++ b/blade/src/main/resources/templates/my-500.html
@@ -0,0 +1,12 @@
+
+
+
+
+ 500 Internal Server Error
+
+
+
Custom Error 500 Page
+
The following error occurred: "${message}"
+
${stackTrace}
+
+
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/post.html b/blade/src/main/resources/templates/post.html
new file mode 100644
index 0000000000..b7a8a931cd
--- /dev/null
+++ b/blade/src/main/resources/templates/post.html
@@ -0,0 +1 @@
+POST called
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/put.html b/blade/src/main/resources/templates/put.html
new file mode 100644
index 0000000000..bdbe6d3285
--- /dev/null
+++ b/blade/src/main/resources/templates/put.html
@@ -0,0 +1 @@
+PUT called
\ No newline at end of file
diff --git a/blade/src/main/resources/templates/template-output-test.html b/blade/src/main/resources/templates/template-output-test.html
new file mode 100644
index 0000000000..233b12fb88
--- /dev/null
+++ b/blade/src/main/resources/templates/template-output-test.html
@@ -0,0 +1 @@
+