Merge branch 'eugenp:master' into master
This commit is contained in:
commit
67f5f92133
@ -12,4 +12,6 @@
|
||||
- [Count the Number of Unique Digits in an Integer using Java](https://www.baeldung.com/java-int-count-unique-digits)
|
||||
- [Generate Juggler Sequence in Java](https://www.baeldung.com/java-generate-juggler-sequence)
|
||||
- [Finding the Parent of a Node in a Binary Search Tree with Java](https://www.baeldung.com/java-find-parent-node-binary-search-tree)
|
||||
- [Check if a Number Is a Happy Number in Java](https://www.baeldung.com/java-happy-sad-number-test)
|
||||
- [Find the Largest Number Possible After Removing k Digits of a Number](https://www.baeldung.com/java-find-largest-number-remove-k-digits)
|
||||
- More articles: [[<-- prev]](/algorithms-miscellaneous-6)
|
||||
|
@ -0,0 +1,82 @@
|
||||
package com.baeldung.algorithms.happynumber;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class HappyNumberDecider {
|
||||
|
||||
public static boolean isHappyNumber(int n) {
|
||||
Set<Integer> checkedNumbers = new HashSet<>();
|
||||
while (true) {
|
||||
n = sumDigitsSquare(n);
|
||||
if (n == 1) {
|
||||
return true;
|
||||
}
|
||||
if (checkedNumbers.contains(n)) {
|
||||
return false;
|
||||
}
|
||||
checkedNumbers.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isHappyNumberFloyd(int n) {
|
||||
int slow = n;
|
||||
int fast = n;
|
||||
do {
|
||||
slow = sumDigitsSquare(slow);
|
||||
fast = sumDigitsSquare(sumDigitsSquare(fast));
|
||||
} while (slow != fast);
|
||||
|
||||
return slow == 1;
|
||||
}
|
||||
|
||||
private static int sumDigitsSquare(int n) {
|
||||
int squareSum = 0;
|
||||
while (n != 0) {
|
||||
squareSum += (n % 10) * (n % 10);
|
||||
n /= 10;
|
||||
}
|
||||
return squareSum;
|
||||
}
|
||||
}
|
||||
|
||||
public class HappyNumberUnitTest {
|
||||
|
||||
@Test
|
||||
void whenUsingIsHappyNumber_thenGetTheExpectedResult() {
|
||||
assertTrue(HappyNumberDecider.isHappyNumber(7));
|
||||
assertTrue(HappyNumberDecider.isHappyNumber(10));
|
||||
assertTrue(HappyNumberDecider.isHappyNumber(13));
|
||||
assertTrue(HappyNumberDecider.isHappyNumber(19));
|
||||
assertTrue(HappyNumberDecider.isHappyNumber(23));
|
||||
|
||||
assertFalse(HappyNumberDecider.isHappyNumber(4));
|
||||
assertFalse(HappyNumberDecider.isHappyNumber(6));
|
||||
assertFalse(HappyNumberDecider.isHappyNumber(11));
|
||||
assertFalse(HappyNumberDecider.isHappyNumber(15));
|
||||
assertFalse(HappyNumberDecider.isHappyNumber(20));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenUsingIsHappyNumber2_thenGetTheExpectedResult() {
|
||||
assertTrue(HappyNumberDecider.isHappyNumberFloyd(7));
|
||||
assertTrue(HappyNumberDecider.isHappyNumberFloyd(10));
|
||||
assertTrue(HappyNumberDecider.isHappyNumberFloyd(13));
|
||||
assertTrue(HappyNumberDecider.isHappyNumberFloyd(19));
|
||||
assertTrue(HappyNumberDecider.isHappyNumberFloyd(23));
|
||||
|
||||
assertFalse(HappyNumberDecider.isHappyNumberFloyd(4));
|
||||
assertFalse(HappyNumberDecider.isHappyNumberFloyd(6));
|
||||
assertFalse(HappyNumberDecider.isHappyNumberFloyd(11));
|
||||
assertFalse(HappyNumberDecider.isHappyNumberFloyd(15));
|
||||
assertFalse(HappyNumberDecider.isHappyNumberFloyd(20));
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -6,4 +6,5 @@ This module contains articles about Apache Kafka.
|
||||
You can build the project from the command line using: *mvn clean install*, or in an IDE.
|
||||
|
||||
### Relevant Articles:
|
||||
- [Commit Offsets in Kafka](https://www.baeldung.com/kafka-commit-offsets)
|
||||
|
||||
|
@ -87,6 +87,7 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
@ -13,4 +13,5 @@
|
||||
- [Format LocalDate to ISO 8601 With T and Z](https://www.baeldung.com/java-format-localdate-iso-8601-t-z)
|
||||
- [Check if Two Date Ranges Overlap](https://www.baeldung.com/java-check-two-date-ranges-overlap)
|
||||
- [Difference between ZoneOffset.UTC and ZoneId.of(“UTC”)](https://www.baeldung.com/java-zoneoffset-utc-zoneid-of)
|
||||
- [Check if a Given Time Lies Between Two Times Regardless of Date](https://www.baeldung.com/java-check-between-two-times)
|
||||
- [[<-- Prev]](/core-java-modules/core-java-datetime-java8-1)
|
||||
|
@ -2,3 +2,4 @@
|
||||
- [Find the Middle Element of an Array in Java](https://www.baeldung.com/java-array-middle-item)
|
||||
- [Find the Equilibrium Indexes of an Array in Java](https://www.baeldung.com/java-equilibrium-index-array)
|
||||
- [Moves Zeros to the End of an Array in Java](https://www.baeldung.com/java-array-sort-move-zeros-end)
|
||||
- [Finding the Majority Element of an Array in Java](https://www.baeldung.com/java-array-find-majority-element)
|
||||
|
@ -4,3 +4,4 @@ This module contains articles about the Java ArrayList collection
|
||||
|
||||
### Relevant Articles:
|
||||
- [Create an ArrayList with Multiple Object Types](https://www.baeldung.com/java-arraylist-multiple-object-types)
|
||||
- [Finding the Peak Elements of a List](https://www.baeldung.com/java-list-find-peak)
|
||||
|
@ -0,0 +1,49 @@
|
||||
package com.baeldung.addtoimmutablelist;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class AddElementsToImmutableListUnitTest {
|
||||
|
||||
public static <T> List<T> appendAnElement(List<T> immutableList, T element) {
|
||||
List<T> tmpList = new ArrayList<>(immutableList);
|
||||
tmpList.add(element);
|
||||
return Collections.unmodifiableList(tmpList);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> List<T> appendElements(List<T> immutableList, T... elements) {
|
||||
List<T> tmpList = new ArrayList<>(immutableList);
|
||||
tmpList.addAll(Arrays.asList(elements));
|
||||
return Collections.unmodifiableList(tmpList);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenCallingAppendAnElement_thenGetExpectedResult() {
|
||||
List<String> myList = List.of("A", "B", "C", "D", "E");
|
||||
List<String> expected = List.of("A", "B", "C", "D", "E", "F");
|
||||
List<String> result = appendAnElement(myList, "F");
|
||||
assertThat(result).isEqualTo(expected)
|
||||
.isUnmodifiable();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenCallingAppendElements_thenGetExpectedResult() {
|
||||
List<String> myList = List.of("A", "B", "C", "D", "E");
|
||||
List<String> expected1 = List.of("A", "B", "C", "D", "E", "F");
|
||||
List<String> result1 = appendElements(myList, "F");
|
||||
assertThat(result1).isEqualTo(expected1)
|
||||
.isUnmodifiable();
|
||||
|
||||
List<String> expected2 = List.of("A", "B", "C", "D", "E", "F", "G", "H", "I");
|
||||
List<String> result2 = appendElements(myList, "F", "G", "H", "I");
|
||||
assertThat(result2).isEqualTo(expected2)
|
||||
.isUnmodifiable();
|
||||
}
|
||||
}
|
@ -13,4 +13,5 @@ This module contains articles about Java collections
|
||||
- [Guide to the Java Queue Interface](https://www.baeldung.com/java-queue)
|
||||
- [An Introduction to Synchronized Java Collections](https://www.baeldung.com/java-synchronized-collections)
|
||||
- [Convert an Array of Primitives to a List](https://www.baeldung.com/java-primitive-array-to-list)
|
||||
- [Adding Elements to a Collection During Iteration](https://www.baeldung.com/java-add-elements-collection)
|
||||
- More articles: [[next -->]](/core-java-modules/core-java-collections-2)
|
||||
|
@ -4,3 +4,4 @@
|
||||
- [Working with Exceptions in Java CompletableFuture](https://www.baeldung.com/java-exceptions-completablefuture)
|
||||
- [CountDownLatch vs. Semaphore](https://www.baeldung.com/java-countdownlatch-vs-semaphore)
|
||||
- [Callbacks in ListenableFuture and CompletableFuture](https://www.baeldung.com/java-callbacks-listenablefuture-completablefuture)
|
||||
- [Guide to ExecutorService vs. CompletableFuture](https://www.baeldung.com/java-executorservice-vs-completablefuture)
|
||||
|
@ -5,3 +5,4 @@ This module contains articles about date operations in Java.
|
||||
- [Calculate Number of Weekdays Between Two Dates in Java](https://www.baeldung.com/java-count-weekdays-between-two-dates)
|
||||
- [Convert Long to Date in Java](https://www.baeldung.com/java-long-date-conversion)
|
||||
- [Convert Date to Unix Timestamp in Java](https://www.baeldung.com/java-convert-date-unix-timestamp)
|
||||
- [Checking if a Date Object Equals Yesterday](https://www.baeldung.com/java-date-check-yesterday)
|
||||
|
@ -0,0 +1,73 @@
|
||||
package com.baeldung.usinggzipInputstream;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
public class Main {
|
||||
static String filePath = Objects.requireNonNull(Main.class.getClassLoader().getResource("myFile.gz")).getFile();
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
// Test readGZipFile method
|
||||
List<String> fileContents = readGZipFile(filePath);
|
||||
System.out.println("Contents of GZIP file:");
|
||||
fileContents.forEach(System.out::println);
|
||||
|
||||
// Test findInZipFile method
|
||||
String searchTerm = "Line 1 content";
|
||||
List<String> foundLines = findInZipFile(filePath, searchTerm);
|
||||
System.out.println("Lines containing '" + searchTerm + "' in GZIP file:");
|
||||
foundLines.forEach(System.out::println);
|
||||
|
||||
|
||||
// Test useContentsOfZipFile method
|
||||
System.out.println("Using contents of GZIP file with consumer:");
|
||||
useContentsOfZipFile(filePath, linesStream -> {
|
||||
linesStream.filter(line -> line.length() > 10).forEach(System.out::println);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static List<String> readGZipFile(String filePath) throws IOException {
|
||||
List<String> lines = new ArrayList<>();
|
||||
try (InputStream inputStream = new FileInputStream(filePath);
|
||||
GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(gzipInputStream);
|
||||
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
lines.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static List<String> findInZipFile(String filePath, String toFind) throws IOException {
|
||||
try (InputStream inputStream = new FileInputStream(filePath);
|
||||
GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(gzipInputStream);
|
||||
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
|
||||
|
||||
return bufferedReader.lines().filter(line -> line.contains(toFind)).collect(toList());
|
||||
}
|
||||
}
|
||||
|
||||
public static void useContentsOfZipFile(String filePath, Consumer<Stream<String>> consumer) throws IOException {
|
||||
try (InputStream inputStream = new FileInputStream(filePath);
|
||||
GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(gzipInputStream);
|
||||
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
|
||||
|
||||
consumer.accept(bufferedReader.lines());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
BIN
core-java-modules/core-java-io-5/src/main/resources/myFile.gz
Normal file
BIN
core-java-modules/core-java-io-5/src/main/resources/myFile.gz
Normal file
Binary file not shown.
@ -0,0 +1,50 @@
|
||||
package com.baeldung.usinggzipInputstream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ReadingGZIPUsingGZIPInputStreamUnitTest {
|
||||
String testFilePath = Objects.requireNonNull(ReadingGZIPUsingGZIPInputStreamUnitTest.class.getClassLoader().getResource("myFile.gz")).getFile();
|
||||
List<String> expectedFilteredLines = Arrays.asList("Line 1 content", "Line 2 content", "Line 3 content");
|
||||
|
||||
@Test
|
||||
void givenGZFile_whenUsingGZIPInputStream_thenReadLines() throws IOException {
|
||||
try (Stream<String> lines = Main.readGZipFile(testFilePath).stream()) {
|
||||
List<String> result = lines
|
||||
.filter(expectedFilteredLines::contains)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertEquals(expectedFilteredLines, result);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenGZFile_whenUsingtestFindInZipFile_thenReadLines() throws IOException {
|
||||
String toFind = "Line 1 content";
|
||||
|
||||
List<String> result = Main.findInZipFile(testFilePath, toFind);
|
||||
|
||||
assertEquals("Line 1 content", result.get(0));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenGZFile_whenUsingContentsOfZipFile_thenReadLines() throws IOException {
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
Main.useContentsOfZipFile(testFilePath, linesStream -> {
|
||||
linesStream.filter(line -> line.length() > 10).forEach(line -> count.incrementAndGet());
|
||||
});
|
||||
|
||||
assertEquals(3, count.get());
|
||||
}
|
||||
}
|
BIN
core-java-modules/core-java-io-5/src/test/resources/myFile.gz
Normal file
BIN
core-java-modules/core-java-io-5/src/test/resources/myFile.gz
Normal file
Binary file not shown.
@ -96,4 +96,5 @@
|
||||
<properties>
|
||||
<junit-jupiter-version>5.9.3</junit-jupiter-version>
|
||||
</properties>
|
||||
</project>
|
||||
|
||||
</project>
|
||||
|
@ -0,0 +1,39 @@
|
||||
package com.baeldung.inputstreamreader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
public class InputStreamReaderUnitTest {
|
||||
@Test
|
||||
public void givenAStringWrittenToAFile_whenReadByInputStreamReader_thenShouldMatchWhenRead(@TempDir Path tempDir) throws IOException {
|
||||
String sampleTxt = "Good day. This is just a test. Good bye.";
|
||||
Path sampleOut = tempDir.resolve("sample-out.txt");
|
||||
List<String> lines = Arrays.asList(sampleTxt);
|
||||
Files.write(sampleOut, lines);
|
||||
String absolutePath = String.valueOf(sampleOut.toAbsolutePath());
|
||||
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(absolutePath), StandardCharsets.UTF_8)) {
|
||||
boolean isMatched = false;
|
||||
int b;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while ((b = reader.read()) != -1) {
|
||||
sb.append((char) b);
|
||||
if (sb.toString().contains(sampleTxt)) {
|
||||
isMatched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertThat(isMatched).isTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
=========
|
||||
|
||||
### Relevant articles:
|
||||
- [Calculate Percentiles in Java](https://www.baeldung.com/java-compute-percentiles)
|
||||
|
@ -0,0 +1,26 @@
|
||||
package com.baeldung.constructorversussettermethod;
|
||||
|
||||
public class Product {
|
||||
private String name;
|
||||
private double price;
|
||||
private String category;
|
||||
|
||||
public Product(String name, double price, String category) {
|
||||
this.name = name;
|
||||
this.price = price;
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package com.baeldung.constructorversussettermethod;
|
||||
|
||||
public class User {
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
if (username.matches("[a-zA-Z0-9_]+")) {
|
||||
this.username = username;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid username format");
|
||||
}
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
if (password.length() >= 8) {
|
||||
this.password = password;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Password must be at least 8 characters long");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package com.baeldung.constructorversussettermethod;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ConstructorsVersusSetterMethodsUnitTest {
|
||||
@Test
|
||||
public void givenNewUser_whenSettingUsername_thenUsernameIsSet() {
|
||||
User user = new User();
|
||||
user.setUsername("john_doe");
|
||||
assertEquals("john_doe", user.getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenNewUser_whenSettingPassword_thenPasswordIsSet() {
|
||||
User user = new User();
|
||||
user.setPassword("strongPassword123");
|
||||
assertEquals("strongPassword123", user.getPassword());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenProductDetails_whenCreatingProductWithConstructor_thenProductHasCorrectAttributes() {
|
||||
Product product = new Product("Smartphone", 599.99, "Electronics");
|
||||
assertEquals("Smartphone", product.getName());
|
||||
assertEquals(599.99, product.getPrice(), 0.001);
|
||||
assertEquals("Electronics", product.getCategory());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.baeldung.statementsbeforesuper;
|
||||
|
||||
class Child extends Parent {
|
||||
Child() {
|
||||
super(); // Or super(10); Correct placements
|
||||
System.out.println("Child constructor");
|
||||
additionalInitialization();
|
||||
// super(); Compilation error: Constructor call must be the first statement in a constructor
|
||||
}
|
||||
|
||||
private void additionalInitialization() {
|
||||
System.out.println("Additional initialization in Child");
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.baeldung.statementsbeforesuper;
|
||||
|
||||
public class Parent {
|
||||
public Parent(int id) {
|
||||
System.out.println("Parametrized Parent constructor");
|
||||
}
|
||||
|
||||
public Parent() {
|
||||
System.out.println("Parent constructor");
|
||||
}
|
||||
}
|
@ -12,3 +12,4 @@ This module contains articles about Java operators
|
||||
- [What Does “––>” Mean in Java?](https://www.baeldung.com/java-minus-minus-greaterthan)
|
||||
- [All the Ways Java Uses the Colon Character](https://www.baeldung.com/java-colon)
|
||||
- [Convert Infix to Postfix Expressions in Java](https://www.baeldung.com/java-convert-infix-to-postfix-expressions)
|
||||
- [Representation of Integers at a Bit Level in Java](https://www.baeldung.com/java-integer-bit-representation)
|
||||
|
@ -6,7 +6,7 @@ import java.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class UrlCheckerUnitTest {
|
||||
public class UrlCheckerIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void givenValidUrl_WhenUsingHEAD_ThenReturn200() throws IOException {
|
||||
@ -18,7 +18,7 @@ public class UrlCheckerUnitTest {
|
||||
@Test
|
||||
public void givenInvalidIUrl_WhenUsingHEAD_ThenReturn404() throws IOException {
|
||||
UrlChecker tester = new UrlChecker();
|
||||
int responseCode = tester.getResponseCodeForURLUsingHead("http://www.example.com/unkownurl");
|
||||
int responseCode = tester.getResponseCodeForURLUsingHead("http://www.example.com/xyz");
|
||||
assertEquals(404, responseCode);
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ public class UrlCheckerUnitTest {
|
||||
@Test
|
||||
public void givenInvalidIUrl_WhenUsingGET_ThenReturn404() throws IOException {
|
||||
UrlChecker tester = new UrlChecker();
|
||||
int responseCode = tester.getResponseCodeForURL("http://www.example.com/unkownurl");
|
||||
int responseCode = tester.getResponseCodeForURL("http://www.example.com/xyz");
|
||||
assertEquals(404, responseCode);
|
||||
}
|
||||
|
@ -8,4 +8,5 @@
|
||||
- [Normalize a URL in Java](https://www.baeldung.com/java-url-normalization)
|
||||
- [Translating Space Characters in URLEncoder](https://www.baeldung.com/java-urlencoder-translate-space-characters)
|
||||
- [Creating a Custom URL Connection](https://www.baeldung.com/java-custom-url-connection)
|
||||
- [[<-- Prev]](/core-java-modules/core-java-networking-3)
|
||||
- [Obtaining the Last Path Segment of a URI in Java](https://www.baeldung.com/java-uri-get-last-path-segment)
|
||||
- [[<-- Prev]](/core-java-modules/core-java-networking-3)
|
||||
|
@ -13,4 +13,5 @@ This module contains articles about numbers in Java.
|
||||
- [Binary Numbers in Java](https://www.baeldung.com/java-binary-numbers)
|
||||
- [Finding the Least Common Multiple in Java](https://www.baeldung.com/java-least-common-multiple)
|
||||
- [Binary Numbers in Java](https://www.baeldung.com/java-binary-numbers)
|
||||
- [RGB Representation as an Integer in Java](https://www.baeldung.com/java-rgb-color-representation)
|
||||
- More articles: [[<-- prev]](../core-java-numbers) [[next -->]](../core-java-numbers-3)
|
||||
|
@ -49,7 +49,7 @@
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.8</version>
|
||||
<version>${jacoco-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@ -73,6 +73,7 @@
|
||||
<source.version>1.8</source.version>
|
||||
<target.version>1.8</target.version>
|
||||
<spring.version>5.3.4</spring.version>
|
||||
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
@ -49,7 +49,7 @@
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.8</version>
|
||||
<version>${jacoco-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@ -73,6 +73,7 @@
|
||||
<source.version>1.8</source.version>
|
||||
<target.version>1.8</target.version>
|
||||
<spring.version>5.3.4</spring.version>
|
||||
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
@ -0,0 +1,143 @@
|
||||
package com.baeldung.streams.mapstreamtomap;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.util.stream.Collectors.flatMapping;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.mapping;
|
||||
import static java.util.stream.Collectors.reducing;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class MapStreamToMapUnitTest {
|
||||
|
||||
Map<String, Integer> playerMap1 = new HashMap<String, Integer>() {{
|
||||
put("Kai", 92);
|
||||
put("Liam", 100);
|
||||
}};
|
||||
Map<String, Integer> playerMap2 = new HashMap<String, Integer>() {{
|
||||
put("Eric", 42);
|
||||
put("Kevin", 77);
|
||||
}};
|
||||
Map<String, Integer> playerMap3 = new HashMap<String, Integer>() {{
|
||||
put("Saajan", 35);
|
||||
}};
|
||||
Map<String, Integer> playerMap4 = new HashMap<String, Integer>() {{
|
||||
put("Kai", 76);
|
||||
}};
|
||||
Map<String, Integer> playerMap5 = new HashMap<String, Integer>() {{
|
||||
put("Kai", null);
|
||||
put("Jerry", null);
|
||||
}};
|
||||
|
||||
@Test
|
||||
void givenMapsStream_whenUsingFlatMapAndToMap_thenMultipleMapsMergedIntoOneMap() {
|
||||
|
||||
Map<String, Integer> expectedMap = new HashMap<String, Integer>() {{
|
||||
put("Saajan", 35);
|
||||
put("Liam", 100);
|
||||
put("Kai", 92);
|
||||
put("Eric", 42);
|
||||
put("Kevin", 77);
|
||||
}};
|
||||
|
||||
Map<String, Integer> mergedMap = Stream.of(playerMap1, playerMap2, playerMap3)
|
||||
.flatMap(map -> map.entrySet()
|
||||
.stream())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
assertEquals(expectedMap, mergedMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenMapsWithDuplicateKeys_whenUsingFlatMapAndToMap_thenMultipleMapsMergedIntoOneMap() {
|
||||
|
||||
Map<String, Integer> expectedMap = new HashMap<String, Integer>() {{
|
||||
put("Saajan", 35);
|
||||
put("Liam", 100);
|
||||
put("Kai", 92); // max of 76 and 92
|
||||
put("Eric", 42);
|
||||
put("Kevin", 77);
|
||||
}};
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> Stream.of(playerMap1, playerMap2, playerMap3, playerMap4)
|
||||
.flatMap(map -> map.entrySet()
|
||||
.stream())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), "Duplicate key Kai (attempted merging values 92 and 76)");
|
||||
|
||||
Map<String, Integer> mergedMap = Stream.of(playerMap1, playerMap2, playerMap3, playerMap4)
|
||||
.flatMap(map -> map.entrySet()
|
||||
.stream())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::max));
|
||||
|
||||
assertEquals(expectedMap, mergedMap);
|
||||
}
|
||||
|
||||
private Integer maxInteger(Integer int1, Integer int2) {
|
||||
if (int1 == null) {
|
||||
return int2;
|
||||
}
|
||||
if (int2 == null) {
|
||||
return int1;
|
||||
}
|
||||
return max(int1, int2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenMapsWithDuplicateKeysAndNullValues_whenUsingFlatMapWithForEach_thenMultipleMapsMergedIntoOneMap() {
|
||||
|
||||
Map<String, Integer> expectedMap = new HashMap<String, Integer>() {{
|
||||
put("Saajan", 35);
|
||||
put("Liam", 100);
|
||||
put("Kai", 92); // max of 92, 76, and null
|
||||
put("Eric", 42);
|
||||
put("Kevin", 77);
|
||||
put("Jerry", null);
|
||||
}};
|
||||
|
||||
assertThrows(NullPointerException.class, () -> Stream.of(playerMap1, playerMap2, playerMap3, playerMap4, playerMap5)
|
||||
.flatMap(map -> map.entrySet()
|
||||
.stream())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::max)));
|
||||
|
||||
Map<String, Integer> mergedMap = new HashMap<>();
|
||||
Stream.of(playerMap1, playerMap2, playerMap3, playerMap4, playerMap5)
|
||||
.flatMap(map -> map.entrySet()
|
||||
.stream())
|
||||
.forEach(entry -> {
|
||||
String k = entry.getKey();
|
||||
Integer v = entry.getValue();
|
||||
if (mergedMap.containsKey(k)) {
|
||||
mergedMap.put(k, maxInteger(mergedMap.get(k), v));
|
||||
} else {
|
||||
mergedMap.put(k, v);
|
||||
}
|
||||
});
|
||||
assertEquals(expectedMap, mergedMap);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenMapsWithDuplicateKeysAndNullValues_whenUsingReduce_thenMultipleMapsMergedIntoOneMap() {
|
||||
|
||||
Map<String, Integer> expectedMap = new HashMap<String, Integer>() {{
|
||||
put("Saajan", 35);
|
||||
put("Liam", 100);
|
||||
put("Kai", 92); // max of 92, 76, and null
|
||||
put("Eric", 42);
|
||||
put("Kevin", 77);
|
||||
put("Jerry", null);
|
||||
}};
|
||||
Map<String, Integer> mergedMap = Stream.of(playerMap1, playerMap2, playerMap3, playerMap4, playerMap5)
|
||||
.flatMap(x -> x.entrySet()
|
||||
.stream())
|
||||
.collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, reducing(null, this::maxInteger))));
|
||||
assertEquals(expectedMap, mergedMap);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.baeldung.UTF8ToISO;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class UTF8ToISOUnitTest {
|
||||
String string = "âabcd";
|
||||
byte[] expectedBytes = new byte[]{(byte) 0xE2, 0x61, 0x62, 0x63, 0x64};
|
||||
|
||||
@Test
|
||||
public void givenUtf8String_whenUsingGetByte_thenIsoBytesShouldBeEqual() {
|
||||
byte[] iso88591bytes = string.getBytes(StandardCharsets.ISO_8859_1);
|
||||
assertArrayEquals(expectedBytes, iso88591bytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenString_whenUsingByteBufferCharBufferConvertToIso_thenBytesShouldBeEqual() {
|
||||
ByteBuffer inputBuffer = ByteBuffer.wrap(string.getBytes(StandardCharsets.UTF_8));
|
||||
CharBuffer data = StandardCharsets.UTF_8.decode(inputBuffer);
|
||||
ByteBuffer outputBuffer = StandardCharsets.ISO_8859_1.encode(data);
|
||||
byte[] outputData = new byte[outputBuffer.remaining()];
|
||||
outputBuffer.get(outputData);
|
||||
assertArrayEquals(expectedBytes, outputData);
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
## Dozer
|
||||
|
||||
This module contains articles about Dozer
|
||||
|
||||
### Relevant Articles:
|
||||
|
||||
- [A Guide to Mapping With Dozer](https://www.baeldung.com/dozer)
|
@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>dozer</artifactId>
|
||||
<name>dozer</name>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.dozer</groupId>
|
||||
<artifactId>dozer</artifactId>
|
||||
<version>${dozer.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<dozer.version>5.5.1</dozer.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
@ -1168,7 +1168,7 @@
|
||||
<maven-war-plugin.version>3.2.2</maven-war-plugin.version>
|
||||
<jib-maven-plugin.version>0.9.11</jib-maven-plugin.version>
|
||||
<frontend-maven-plugin.version>1.7.6</frontend-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.2</jacoco-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
|
||||
<lifecycle-mapping.version>1.0.0</lifecycle-mapping.version>
|
||||
<scala-maven-plugin.version>3.4.2</scala-maven-plugin.version>
|
||||
<sonar-maven-plugin.version>3.5.0.1254</sonar-maven-plugin.version>
|
||||
|
@ -24,7 +24,7 @@
|
||||
<hazelcast-hibernate52.version>1.2</hazelcast-hibernate52.version>
|
||||
<hibernate.version>5.2.8.Final</hibernate.version>
|
||||
<hikaricp.version>5.1.0</hikaricp.version>
|
||||
<jacoco-maven-plugin.version>0.7.9</jacoco-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
|
||||
<java.version>1.8</java.version>
|
||||
<javassist.version>3.21.0-GA</javassist.version>
|
||||
<jcache.version>1.0.0</jcache.version>
|
||||
|
@ -24,7 +24,7 @@
|
||||
<hazelcast-hibernate52.version>1.2</hazelcast-hibernate52.version>
|
||||
<hibernate.version>5.2.8.Final</hibernate.version>
|
||||
<hikaricp.version>5.1.0</hikaricp.version>
|
||||
<jacoco-maven-plugin.version>0.7.9</jacoco-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
|
||||
<javassist.version>3.21.0-GA</javassist.version>
|
||||
<jcache.version>1.0.0</jcache.version>
|
||||
<jhipster.server.version>1.1.0</jhipster.server.version>
|
||||
|
@ -27,7 +27,7 @@
|
||||
<hazelcast-hibernate52.version>1.2</hazelcast-hibernate52.version>
|
||||
<hibernate.version>5.2.8.Final</hibernate.version>
|
||||
<hikaricp.version>5.1.0</hikaricp.version>
|
||||
<jacoco-maven-plugin.version>0.7.9</jacoco-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
|
||||
<javassist.version>3.21.0-GA</javassist.version>
|
||||
<jcache.version>1.0.0</jcache.version>
|
||||
<jhipster.server.version>1.1.0</jhipster.server.version>
|
||||
|
@ -893,7 +893,7 @@
|
||||
<gatling.version>2.2.3</gatling.version>
|
||||
<hibernate.version>5.2.8.Final</hibernate.version>
|
||||
<hikaricp.version>5.1.0</hikaricp.version>
|
||||
<jacoco-maven-plugin.version>0.7.9</jacoco-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
|
||||
<javassist.version>3.21.0-GA</javassist.version>
|
||||
<jcache.version>1.0.0</jcache.version>
|
||||
<jhipster.server.version>1.1.0</jhipster.server.version>
|
||||
|
@ -0,0 +1,27 @@
|
||||
package com.baeldung.includenullinjson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class Customer {
|
||||
@JsonProperty
|
||||
private final String name;
|
||||
@JsonProperty
|
||||
private final String address;
|
||||
@JsonProperty
|
||||
private final int age;
|
||||
|
||||
public Customer(String name, String address, int age) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Customer{" +
|
||||
"name='" + name + '\'' +
|
||||
", address='" + address + '\'' +
|
||||
", age=" + age +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.baeldung.includenullinjson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class IncludeNullValuesInJsonUnitTest {
|
||||
String expectedJson = "{\"name\":\"John\",\"address\":null,\"age\":25}";
|
||||
Customer obj = new Customer("John", null, 25);
|
||||
|
||||
@Test
|
||||
public void givenObjectWithNullField_whenJacksonUsed_thenIncludesNullValue() throws JsonProcessingException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||
String json = mapper.writeValueAsString(obj);
|
||||
assertEquals(expectedJson, json);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenObjectWithNullField_whenGsonUsed_thenIncludesNullValue() {
|
||||
Gson gson = new GsonBuilder().serializeNulls().create();
|
||||
String json = gson.toJson(obj);
|
||||
assertEquals(expectedJson, json);
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ This module contains articles about jsoup.
|
||||
- [Parsing HTML in Java with Jsoup](https://www.baeldung.com/java-with-jsoup)
|
||||
- [How to Add Proxy Support to Jsoup?](https://www.baeldung.com/java-jsoup-proxy)
|
||||
- [Preserving Line Breaks When Using Jsoup](https://www.baeldung.com/jsoup-line-breaks)
|
||||
- [Parsing HTML Table in Java With Jsoup](https://www.baeldung.com/java-jsoup-parse-html-table)
|
||||
|
||||
### Build the Project
|
||||
|
||||
|
@ -6,4 +6,5 @@ This module contains articles about cli libraries.
|
||||
|
||||
- [Create a Java Command Line Program with Picocli](https://www.baeldung.com/java-picocli-create-command-line-program)
|
||||
- [Parsing Command-Line Parameters with JCommander](https://www.baeldung.com/jcommander-parsing-command-line-parameters)
|
||||
- [Parsing Command-Line Parameters with Airline](https://www.baeldung.com/java-airline)
|
||||
- [Parsing Command-Line Parameters with Airline](https://www.baeldung.com/java-airline)
|
||||
- [Intro to the Apache Commons CLI](https://www.baeldung.com/apache-commons-cli)
|
||||
|
@ -12,4 +12,5 @@ This module contains articles about libraries for data processing in Java.
|
||||
- [Introduction to Eclipse Collections](https://www.baeldung.com/eclipse-collections)
|
||||
- [Introduction to Caffeine](https://www.baeldung.com/java-caching-caffeine)
|
||||
- [Guide to Using ModelMapper](https://www.baeldung.com/java-modelmapper)
|
||||
- [A Guide to Mapping With Dozer](https://www.baeldung.com/dozer)
|
||||
- More articles: [[<-- prev]](/../libraries-data-2)
|
||||
|
@ -71,6 +71,17 @@
|
||||
<artifactId>modelmapper</artifactId>
|
||||
<version>${org.modelmapper.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.dozer</groupId>
|
||||
<artifactId>dozer</artifactId>
|
||||
<version>${dozer.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -96,6 +107,7 @@
|
||||
<caffeine.version>3.1.8</caffeine.version>
|
||||
<findbugs.version>3.0.2</findbugs.version>
|
||||
<org.modelmapper.version>3.2.0</org.modelmapper.version>
|
||||
<dozer.version>5.5.1</dozer.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
@ -171,7 +171,7 @@ public class DozerIntegrationTest {
|
||||
configureMapper("dozer_custom_convertor.xml");
|
||||
|
||||
String dateTime = "2007-06-26T21:22:39Z";
|
||||
long timestamp = new Long("1182882159000");
|
||||
long timestamp = Long.parseLong("1182882159000");
|
||||
|
||||
Person3 person = new Person3("Rich", dateTime);
|
||||
Personne3 person0 = mapper.map(person, Personne3.class);
|
||||
@ -182,7 +182,7 @@ public class DozerIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void givenSrcAndDestWithDifferentFieldTypes_whenAbleToCustomConvertBidirectionally_thenCorrect() {
|
||||
long timestamp = new Long("1182882159000");
|
||||
long timestamp = Long.parseLong("1182882159000");
|
||||
Personne3 person = new Personne3("Rich", timestamp);
|
||||
configureMapper("dozer_custom_convertor.xml");
|
||||
|
@ -38,7 +38,7 @@
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
@ -84,4 +84,7 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<maven-resources-plugin.version>3.1.0</maven-resources-plugin.version>
|
||||
</properties>
|
||||
</project>
|
@ -30,6 +30,7 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>${maven-resources-plugin.version}</version>
|
||||
<configuration>
|
||||
<resources combine.children="append">
|
||||
<resource>
|
||||
|
@ -16,6 +16,7 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>${maven-resources-plugin.version}</version>
|
||||
<configuration>
|
||||
<resources combine.self="override">
|
||||
<resource>
|
||||
|
2
messaging-modules/java-redpanda/README.md
Normal file
2
messaging-modules/java-redpanda/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
### Relevant Articles:
|
||||
- [Introduction to Redpanda](https://www.baeldung.com/redpanda)
|
@ -101,6 +101,7 @@
|
||||
<plugin>
|
||||
<groupId>io.micronaut.build</groupId>
|
||||
<artifactId>micronaut-maven-plugin</artifactId>
|
||||
<version>${micronaut-build.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@ -154,6 +155,7 @@
|
||||
<packaging>jar</packaging>
|
||||
<compiler.plugin.version>3.12.1</compiler.plugin.version>
|
||||
<micronaut.runtime>netty</micronaut.runtime>
|
||||
<micronaut-build.version>3.5.4</micronaut-build.version>
|
||||
<shade.plugin.version>3.2.0</shade.plugin.version>
|
||||
</properties>
|
||||
|
||||
|
13
microservices-modules/saga-pattern/README.md
Normal file
13
microservices-modules/saga-pattern/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Saga Pattern Using Orkes Conductor
|
||||
|
||||
This is an example project showing how to build event driven applications using [Conductor](https://github.com/conductor-oss/conductor)
|
||||
|
||||
# Pre-requisites
|
||||
1. Docker
|
||||
2. Running conductor server
|
||||
|
||||
**Start the conductor server**
|
||||
|
||||
```shell
|
||||
docker run --init -p 8080:8080 -p 1234:5000 conductoross/conductor-standalone:3.15.0
|
||||
```
|
34
microservices-modules/saga-pattern/build.gradle
Normal file
34
microservices-modules/saga-pattern/build.gradle
Normal file
@ -0,0 +1,34 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:3.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.2.3'
|
||||
id 'io.freefair.lombok' version '8.6'
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
|
||||
group = 'io.orkes.example'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation platform('org.junit:junit-bom:5.9.1')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'io.orkes.conductor:orkes-conductor-client:2.1.0'
|
||||
|
||||
implementation 'org.xerial:sqlite-jdbc:3.32.3.3'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
2
microservices-modules/saga-pattern/settings.gradle
Normal file
2
microservices-modules/saga-pattern/settings.gradle
Normal file
@ -0,0 +1,2 @@
|
||||
rootProject.name = 'conductor-examples-food-delivery'
|
||||
|
@ -0,0 +1,28 @@
|
||||
package io.orkes.example.saga;
|
||||
|
||||
import io.orkes.example.saga.dao.BaseDAO;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
|
||||
@AllArgsConstructor
|
||||
@SpringBootApplication
|
||||
@ComponentScan(basePackages = {"io.orkes"})
|
||||
public class SagaApplication {
|
||||
|
||||
private static final BaseDAO db = new BaseDAO("jdbc:sqlite:food_delivery.db");
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SagaApplication.class, args);
|
||||
initDB();
|
||||
}
|
||||
|
||||
public static void initDB() {
|
||||
db.createTables("orders");
|
||||
db.createTables("inventory");
|
||||
db.createTables("payments");
|
||||
db.createTables("shipments");
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package io.orkes.example.saga.controller;
|
||||
|
||||
import io.orkes.example.saga.pojos.FoodDeliveryRequest;
|
||||
import io.orkes.example.saga.pojos.OrderRequest;
|
||||
import io.orkes.example.saga.service.WorkflowService;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@RestController
|
||||
public class OrderServiceController {
|
||||
|
||||
private final WorkflowService workflowService;
|
||||
|
||||
@PostMapping(value = "/triggerFoodDeliveryFlow", produces = "application/json")
|
||||
public ResponseEntity<Map<String, Object>> triggerFoodDeliveryFlow(@RequestBody FoodDeliveryRequest foodDeliveryRequest) {
|
||||
return ResponseEntity.ok(workflowService.startFoodDeliveryWorkflow(foodDeliveryRequest));
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
package io.orkes.example.saga.dao;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class BaseDAO {
|
||||
|
||||
private String url;
|
||||
|
||||
public BaseDAO(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
protected Connection connect() {
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = DriverManager.getConnection(this.url);
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
protected Boolean execute(String sql) {
|
||||
try (Connection conn = DriverManager.getConnection(this.url); Statement stmt = conn.createStatement()) {
|
||||
stmt.execute(sql);
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void createTables(String service) {
|
||||
switch (service) {
|
||||
case "orders":
|
||||
createOrdersTable();
|
||||
createOrderDetailsTable();
|
||||
createCustomerTable();
|
||||
break;
|
||||
case "inventory":
|
||||
createRestaurantsTable();
|
||||
break;
|
||||
case "shipments":
|
||||
createDriversTable();
|
||||
createShipmentTable();
|
||||
break;
|
||||
case "payments":
|
||||
createPaymentsTable();
|
||||
break;
|
||||
default:
|
||||
System.out.println("Service name not recognized");
|
||||
}
|
||||
}
|
||||
|
||||
private void createOrdersTable() {
|
||||
if (!tableExists("orders")) {
|
||||
|
||||
String sql = "CREATE TABLE orders (\n"
|
||||
+ " orderId text PRIMARY KEY,\n"
|
||||
+ " customerId integer NOT NULL,\n"
|
||||
+ " restaurantId integer NOT NULL,\n"
|
||||
+ " deliveryAddress text NOT NULL,\n"
|
||||
+ " createdAt TIMESTAMP NOT NULL,\n"
|
||||
+ " status text NOT NULL\n"
|
||||
+ ");";
|
||||
|
||||
execute(sql);
|
||||
}
|
||||
}
|
||||
|
||||
private void createOrderDetailsTable() {
|
||||
if (!tableExists("orders_details")) {
|
||||
String sql = "CREATE TABLE orders_details (\n"
|
||||
+ " orderId text PRIMARY KEY,\n"
|
||||
+ " items text NOT NULL,\n"
|
||||
+ " notes text\n"
|
||||
+ ");";
|
||||
|
||||
execute(sql);
|
||||
}
|
||||
}
|
||||
|
||||
private void createCustomerTable() {
|
||||
if (tableExists("customers")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String sql = "CREATE TABLE customers (\n"
|
||||
+ " id integer PRIMARY KEY AUTOINCREMENT,\n"
|
||||
+ " email text NOT NULL,\n"
|
||||
+ " name text NOT NULL,\n"
|
||||
+ " contact text\n"
|
||||
+ ");";
|
||||
|
||||
if(execute(sql)) {
|
||||
seedCustomers();
|
||||
}
|
||||
}
|
||||
|
||||
private void createRestaurantsTable() {
|
||||
if (!tableExists("restaurants")) {
|
||||
String sql = "CREATE TABLE restaurants (\n"
|
||||
+ " id integer PRIMARY KEY AUTOINCREMENT,\n"
|
||||
+ " name text NOT NULL,\n"
|
||||
+ " address text NOT NULL,\n"
|
||||
+ " contact text NOT NULL\n"
|
||||
+ ");";
|
||||
|
||||
if (execute(sql)) {
|
||||
seedRestaurants();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void createPaymentsTable() {
|
||||
if (tableExists("payments")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String sql = "CREATE TABLE payments (\n"
|
||||
+ " paymentId text PRIMARY KEY,\n"
|
||||
+ " orderId text NOT NULL,\n"
|
||||
+ " amount number NOT NULL,\n"
|
||||
+ " method text,\n"
|
||||
+ " status text,\n"
|
||||
+ " createdAt TIMESTAMP NOT NULL\n"
|
||||
+ ");";
|
||||
|
||||
execute(sql);
|
||||
}
|
||||
|
||||
private void createDriversTable() {
|
||||
if (tableExists("drivers")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String sql = "CREATE TABLE drivers (\n"
|
||||
+ " id integer PRIMARY KEY AUTOINCREMENT,\n"
|
||||
+ " name text NOT NULL,\n"
|
||||
+ " contact text\n"
|
||||
+ ");";
|
||||
|
||||
if(execute(sql)) {
|
||||
seedDrivers();
|
||||
}
|
||||
}
|
||||
private void createShipmentTable() {
|
||||
if (tableExists("shipments")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String sql = "CREATE TABLE shipments (\n"
|
||||
+ " id integer PRIMARY KEY AUTOINCREMENT,\n"
|
||||
+ " orderId text NOT NULL,\n"
|
||||
+ " driverId number NOT NULL,\n"
|
||||
+ " address text NOT NULL,\n"
|
||||
+ " instructions text,\n"
|
||||
+ " status text NOT NULL,\n"
|
||||
+ " createdAt TIMESTAMP NOT NULL\n"
|
||||
+ ");";
|
||||
|
||||
execute(sql);
|
||||
}
|
||||
|
||||
private void seedCustomers() {
|
||||
String[] queries = {
|
||||
"INSERT INTO customers(email, name, contact) VALUES('John Smith','john.smith@example.com','+12126781345');",
|
||||
"INSERT INTO customers(email, name, contact) VALUES('Mike Ross','mike.ross@example.com','+15466711147');",
|
||||
"INSERT INTO customers(email, name, contact) VALUES('Martha Williams','martha.williams@example.com','+12790581941');"
|
||||
};
|
||||
|
||||
for (String query : queries) {
|
||||
execute(query);
|
||||
}
|
||||
}
|
||||
|
||||
private void seedRestaurants() {
|
||||
String[] add = {
|
||||
"5331 Redford Court, Montgomery AL 36116",
|
||||
"43 West 4th Street, New York NY 10024",
|
||||
"1693 Alice Court, Annapolis MD 21401"
|
||||
};
|
||||
String[] queries = {
|
||||
"INSERT INTO restaurants(name, address, contact) VALUES('Mikes','+12121231345','" + add[0] + "');",
|
||||
"INSERT INTO restaurants(name, address, contact) VALUES('Tamarind','+12412311147','" + add[1] + "');",
|
||||
"INSERT INTO restaurants(name, address, contact) VALUES('Thai Place','+14790981941','" + add[2] + "');",
|
||||
};
|
||||
|
||||
for (String query : queries) {
|
||||
execute(query);
|
||||
}
|
||||
}
|
||||
|
||||
private void seedDrivers() {
|
||||
String[] queries = {
|
||||
"INSERT INTO drivers(name,contact) VALUES('Wayne Stevens','+12520711467');",
|
||||
"INSERT INTO drivers(name,contact) VALUES('Jim Willis','+16466281981');",
|
||||
"INSERT INTO drivers(name,contact) VALUES('Steven Carroll','+12612590430');",
|
||||
"INSERT INTO drivers(name,contact) VALUES('Tom Cruise','+18659581430');"
|
||||
};
|
||||
|
||||
for (String query : queries) {
|
||||
execute(query);
|
||||
}
|
||||
}
|
||||
|
||||
boolean tableExists(String tableName) {
|
||||
try {
|
||||
Connection conn = DriverManager.getConnection(this.url);
|
||||
DatabaseMetaData meta = conn.getMetaData();
|
||||
ResultSet resultSet = meta.getTables(null, null, tableName, new String[] {"TABLE"});
|
||||
boolean exists = resultSet.next();
|
||||
conn.close();
|
||||
return exists;
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package io.orkes.example.saga.dao;
|
||||
|
||||
import io.orkes.example.saga.pojos.Order;
|
||||
import io.orkes.example.saga.pojos.Restaurant;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class InventoryDAO extends BaseDAO {
|
||||
|
||||
public InventoryDAO(String url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
public void readRestaurant(int restaurantId, Restaurant restaurant) {
|
||||
String sql = "SELECT name, address, contact FROM restaurants WHERE id = ?";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setInt(1, restaurantId);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
restaurant.setName(rs.getString("name"));
|
||||
restaurant.setAddress(rs.getString("address"));
|
||||
restaurant.setContact(rs.getString("contact"));
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
package io.orkes.example.saga.dao;
|
||||
|
||||
import io.orkes.example.saga.pojos.Customer;
|
||||
import io.orkes.example.saga.pojos.Order;
|
||||
|
||||
import java.sql.*;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Date;
|
||||
|
||||
public class OrdersDAO extends BaseDAO {
|
||||
|
||||
public OrdersDAO(String url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
public String insertOrder(Order order) {
|
||||
Date date = new Date();
|
||||
Timestamp nowAsTS = new Timestamp(date.getTime());
|
||||
|
||||
String itemsStr = String.join("", order.getOrderDetails().getItems().toString());
|
||||
|
||||
String notesStr = null;
|
||||
|
||||
if(!order.getOrderDetails().getNotes().isEmpty()) {
|
||||
notesStr = String.join("", order.getOrderDetails().getNotes().toString());
|
||||
} else {
|
||||
notesStr = "";
|
||||
}
|
||||
|
||||
String sql = "INSERT INTO orders(orderId,customerId,restaurantId,deliveryAddress,createdAt,status) VALUES(?,?,?,?,?,?)";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, order.getOrderId());
|
||||
pstmt.setInt(2, order.getCustomer().getId());
|
||||
pstmt.setInt(3, order.getRestaurantId());
|
||||
pstmt.setString(4, order.getDeliveryAddress());
|
||||
pstmt.setTimestamp(5, nowAsTS);
|
||||
pstmt.setString(6, order.getStatus().name());
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
return e.getMessage();
|
||||
}
|
||||
|
||||
sql = "INSERT INTO orders_details(orderId,items,notes) VALUES(?,?,?)";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, order.getOrderId());
|
||||
pstmt.setString(2, itemsStr);
|
||||
pstmt.setString(3, notesStr);
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
return e.getMessage();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public void updateOrder(Order order) {
|
||||
String sql = "UPDATE orders SET restaurantId=?,deliveryAddress=?,status=? WHERE orderId=?";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setInt(1, order.getRestaurantId());
|
||||
pstmt.setString(2, order.getDeliveryAddress());
|
||||
pstmt.setString(3, order.getStatus().name());
|
||||
pstmt.setString(4, order.getOrderId());
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void readOrder(String orderId, Order order) {
|
||||
String sql = "SELECT orderId, customerId, restaurantId, deliveryAddress, createdAt, status FROM orders WHERE orderId = ?";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, orderId);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
order.setOrderId(rs.getString("orderId"));
|
||||
Customer customer = new Customer();
|
||||
customer.setId(rs.getInt("customerId"));
|
||||
order.setCustomer(customer);
|
||||
order.setRestaurantId(rs.getInt("restaurantId"));
|
||||
order.setDeliveryAddress(rs.getString("deliveryAddress"));
|
||||
order.setCreatedAt(rs.getLong("createdAt"));
|
||||
order.setStatus(Order.Status.valueOf(rs.getString("status")));
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public int insertCustomer(Customer customer) {
|
||||
int id = 0;
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM customers WHERE email = ?")) {
|
||||
pstmt.setString(1, customer.getEmail());
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
id = rs.getInt("id");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
|
||||
if (id > 0) {
|
||||
return id;
|
||||
}
|
||||
|
||||
String sql = "INSERT INTO customers(email,name,contact) VALUES(?,?,?)";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, customer.getEmail());
|
||||
pstmt.setString(2, customer.getName());
|
||||
pstmt.setString(3, customer.getContact());
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM customers WHERE email = ?")) {
|
||||
pstmt.setString(1, customer.getEmail());
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
id = rs.getInt("id");
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package io.orkes.example.saga.dao;
|
||||
|
||||
import io.orkes.example.saga.pojos.Payment;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Date;
|
||||
|
||||
public class PaymentsDAO extends BaseDAO {
|
||||
|
||||
public PaymentsDAO(String url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
public String insertPayment(Payment payment) {
|
||||
Date date = new Date();
|
||||
Timestamp nowAsTS = new Timestamp(date.getTime());
|
||||
|
||||
String sql = "INSERT INTO payments(paymentId, orderId, amount, method, createdAt, status) VALUES(?,?,?,?,?,?);";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, payment.getPaymentId());
|
||||
pstmt.setString(2, payment.getOrderId());
|
||||
pstmt.setDouble(3, payment.getAmount());
|
||||
pstmt.setString(4, payment.getPaymentMethod().toString());
|
||||
pstmt.setTimestamp(5, nowAsTS);
|
||||
pstmt.setString(6, payment.getStatus().name());
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
return e.getMessage();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public void updatePaymentStatus(Payment payment) {
|
||||
String sql = "UPDATE payments SET status=? WHERE paymentId=?;";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, payment.getStatus().name());
|
||||
pstmt.setString(2, payment.getPaymentId());
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void readPayment(String orderId, Payment payment) {
|
||||
String sql = "SELECT paymentId, orderId, amount, method, createdAt, status FROM payments WHERE orderId = ?";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, orderId);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
payment.setPaymentId(rs.getString("paymentId"));
|
||||
payment.setOrderId(rs.getString("orderId"));
|
||||
payment.setAmount(rs.getDouble("amount"));
|
||||
payment.setCreatedAt(rs.getLong("createdAt"));
|
||||
payment.setStatus(Payment.Status.valueOf(rs.getString("status")));
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package io.orkes.example.saga.dao;
|
||||
|
||||
import io.orkes.example.saga.pojos.Driver;
|
||||
import io.orkes.example.saga.pojos.Shipment;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Date;
|
||||
|
||||
public class ShipmentDAO extends BaseDAO {
|
||||
|
||||
public ShipmentDAO(String url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
public boolean insertShipment(Shipment shipment) {
|
||||
Date date = new Date();
|
||||
Timestamp nowAsTS = new Timestamp(date.getTime());
|
||||
|
||||
String sql = "INSERT INTO shipments(orderId,driverId,address,instructions,createdAt,status) VALUES(?,?,?,?,?,?)";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, shipment.getOrderId());
|
||||
pstmt.setInt(2, shipment.getDriverId());
|
||||
pstmt.setString(3, shipment.getDeliveryAddress());
|
||||
pstmt.setString(4, shipment.getDeliveryInstructions());
|
||||
pstmt.setTimestamp(5, nowAsTS);
|
||||
pstmt.setString(6, Shipment.Status.SCHEDULED.name());
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public void cancelShipment(String orderId) {
|
||||
String sql = "UPDATE shipments SET status=? WHERE orderId=?;";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, Shipment.Status.CANCELED.name());
|
||||
pstmt.setString(2, orderId);
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void confirmShipment(String orderId) {
|
||||
String sql = "UPDATE shipments SET status=? WHERE orderId=?;";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setString(1, Shipment.Status.CONFIRMED.name());
|
||||
pstmt.setString(2, orderId);
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void readDriver(int driverId, Driver driver) {
|
||||
String sql = "SELECT name, contact FROM drivers WHERE id = ?";
|
||||
|
||||
try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
||||
pstmt.setInt(1, driverId);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
driver.setId(driverId);
|
||||
driver.setName(rs.getString("name"));
|
||||
driver.setContact(rs.getString("contact"));
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CancelRequest {
|
||||
private String orderId;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Data
|
||||
public class CheckInventoryRequest {
|
||||
private int restaurantId;
|
||||
private ArrayList<FoodItem> items;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Customer {
|
||||
private int id;
|
||||
private String email;
|
||||
private String name;
|
||||
private String contact;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Driver {
|
||||
int id;
|
||||
String name;
|
||||
String contact;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DriverNotificationRequest {
|
||||
int driverId;
|
||||
String dropOff;
|
||||
String pickUp;
|
||||
String orderId;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Data
|
||||
public class FoodDeliveryRequest {
|
||||
private String customerEmail;
|
||||
private String customerName;
|
||||
private String customerContact;
|
||||
private int restaurantId;
|
||||
private ArrayList<Object> foodItems;
|
||||
private ArrayList<String> additionalNotes;
|
||||
private String address;
|
||||
private String deliveryInstructions;
|
||||
private double paymentAmount;
|
||||
private Object paymentMethod;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
public class FoodItem {
|
||||
private String item;
|
||||
private int quantity;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.EnumMap;
|
||||
|
||||
@Data
|
||||
public class Order {
|
||||
|
||||
public enum Status {
|
||||
PENDING,
|
||||
ASSIGNED,
|
||||
CONFIRMED,
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
private String orderId;
|
||||
private Customer customer;
|
||||
private int restaurantId;
|
||||
private String deliveryAddress;
|
||||
private long createdAt;
|
||||
private Status status;
|
||||
private OrderDetails orderDetails;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Data
|
||||
public class OrderDetails {
|
||||
private String orderId;
|
||||
private ArrayList<FoodItem> items;
|
||||
private ArrayList<String> notes;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Data
|
||||
public class OrderRequest {
|
||||
private String OrderRequestId;
|
||||
private String customerEmail;
|
||||
private String customerName;
|
||||
private String customerContact;
|
||||
private int restaurantId;
|
||||
private ArrayList<FoodItem> items;
|
||||
private ArrayList<String> notes;
|
||||
private String deliveryAddress;
|
||||
private String deliveryInstructions;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Payment {
|
||||
public enum Status {
|
||||
PENDING,
|
||||
FAILED,
|
||||
SUCCESSFUL,
|
||||
CANCELED
|
||||
}
|
||||
private String paymentId;
|
||||
private String orderId;
|
||||
private double amount;
|
||||
private PaymentMethod paymentMethod;
|
||||
private Status status;
|
||||
private long createdAt;
|
||||
private String errorMsg;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
public class PaymentDetails {
|
||||
private String number;
|
||||
private String expiry;
|
||||
private int cvv;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
public class PaymentMethod {
|
||||
private String type;
|
||||
private PaymentDetails details;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PaymentRequest {
|
||||
private String orderId;
|
||||
private int customerId;
|
||||
private float amount;
|
||||
private PaymentMethod method;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Restaurant {
|
||||
int id;
|
||||
String name;
|
||||
String address;
|
||||
String contact;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Shipment {
|
||||
public enum Status {
|
||||
SCHEDULED,
|
||||
CONFIRMED,
|
||||
DELIVERED,
|
||||
CANCELED
|
||||
}
|
||||
private int id;
|
||||
private String orderId;
|
||||
private int driverId;
|
||||
private String deliveryAddress;
|
||||
private String deliveryInstructions;
|
||||
private long createdAt;
|
||||
private String status;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package io.orkes.example.saga.pojos;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ShippingRequest {
|
||||
private String orderId;
|
||||
private String deliveryAddress;
|
||||
private String deliveryInstructions;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.orkes.example.saga.service;
|
||||
|
||||
import io.orkes.example.saga.dao.InventoryDAO;
|
||||
import io.orkes.example.saga.pojos.FoodItem;
|
||||
import io.orkes.example.saga.pojos.Restaurant;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
public class InventoryService {
|
||||
|
||||
private static final InventoryDAO inventoryDAO = new InventoryDAO("jdbc:sqlite:food_delivery.db");
|
||||
|
||||
public static boolean checkAvailability(int restaurantId, ArrayList<FoodItem> items) {
|
||||
Restaurant restaurant = new Restaurant();
|
||||
restaurant.setId(restaurantId);
|
||||
restaurant.setName("");
|
||||
inventoryDAO.readRestaurant(restaurantId, restaurant);
|
||||
return !Objects.equals(restaurant.getName(), "");
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package io.orkes.example.saga.service;
|
||||
|
||||
import io.orkes.example.saga.dao.OrdersDAO;
|
||||
import io.orkes.example.saga.pojos.Customer;
|
||||
import io.orkes.example.saga.pojos.Order;
|
||||
import io.orkes.example.saga.pojos.OrderDetails;
|
||||
import io.orkes.example.saga.pojos.OrderRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class OrderService {
|
||||
|
||||
private static final OrdersDAO ORDERS_DAO = new OrdersDAO("jdbc:sqlite:food_delivery.db");
|
||||
|
||||
public static String createOrder(OrderRequest orderRequest) {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
String uuidAsString = uuid.toString();
|
||||
|
||||
Order order = new Order();
|
||||
order.setOrderId(uuidAsString);
|
||||
|
||||
Customer customer = new Customer();
|
||||
customer.setEmail(orderRequest.getCustomerEmail());
|
||||
customer.setName(orderRequest.getCustomerName());
|
||||
customer.setContact(orderRequest.getCustomerContact());
|
||||
customer.setId(ORDERS_DAO.insertCustomer(customer));
|
||||
|
||||
log.info("Upsert customer record in DB with id: {}", customer.getId());
|
||||
|
||||
order.setCustomer(customer);
|
||||
order.setRestaurantId(orderRequest.getRestaurantId());
|
||||
order.setDeliveryAddress(orderRequest.getDeliveryAddress());
|
||||
order.setStatus(Order.Status.PENDING);
|
||||
|
||||
OrderDetails orderDetails = new OrderDetails();
|
||||
orderDetails.setOrderId(uuidAsString);
|
||||
orderDetails.setItems(orderRequest.getItems());
|
||||
orderDetails.setNotes(orderRequest.getNotes());
|
||||
|
||||
order.setOrderDetails(orderDetails);
|
||||
|
||||
String error = ORDERS_DAO.insertOrder(order);
|
||||
|
||||
if (error.isEmpty()) {
|
||||
log.info("Created order with id: {}", order.getOrderId());
|
||||
}
|
||||
else {
|
||||
log.error("Order creation failure: {}", error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return uuidAsString;
|
||||
}
|
||||
|
||||
public static Order getOrder(String orderId) {
|
||||
Order order = new Order();
|
||||
ORDERS_DAO.readOrder(orderId, order);
|
||||
return order;
|
||||
}
|
||||
|
||||
public static void cancelOrder(Order order) {
|
||||
order.setStatus(Order.Status.CANCELLED);
|
||||
log.info("Cancelling order {}", order.getOrderId());
|
||||
ORDERS_DAO.updateOrder(order);
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package io.orkes.example.saga.service;
|
||||
|
||||
import io.orkes.example.saga.dao.PaymentsDAO;
|
||||
import io.orkes.example.saga.pojos.Payment;
|
||||
import io.orkes.example.saga.pojos.PaymentDetails;
|
||||
import io.orkes.example.saga.pojos.PaymentMethod;
|
||||
import io.orkes.example.saga.pojos.PaymentRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
public class PaymentService {
|
||||
|
||||
private static final PaymentsDAO paymentsDAO = new PaymentsDAO("jdbc:sqlite:food_delivery.db");
|
||||
|
||||
public static Payment createPayment(PaymentRequest paymentRequest) {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
String uuidAsString = uuid.toString();
|
||||
|
||||
Payment payment = new Payment();
|
||||
payment.setPaymentId(uuidAsString);
|
||||
payment.setOrderId(paymentRequest.getOrderId());
|
||||
payment.setAmount(paymentRequest.getAmount());
|
||||
payment.setPaymentMethod(paymentRequest.getMethod());
|
||||
payment.setStatus(Payment.Status.PENDING);
|
||||
|
||||
// Check if returned error is non-empty, i.e failure
|
||||
if (!paymentsDAO.insertPayment(payment).isEmpty()) {
|
||||
log.error("Failed to process payment for order {}", paymentRequest.getOrderId());
|
||||
payment.setErrorMsg("Payment creation failure");
|
||||
payment.setStatus(Payment.Status.FAILED);
|
||||
}
|
||||
else {
|
||||
if(makePayment(payment)) {
|
||||
payment.setStatus(Payment.Status.SUCCESSFUL);
|
||||
} else {
|
||||
payment.setStatus(Payment.Status.FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
// Record final status
|
||||
paymentsDAO.updatePaymentStatus(payment);
|
||||
return payment;
|
||||
}
|
||||
|
||||
public static void cancelPayment(String orderId) {
|
||||
// Cancel Payment in DB
|
||||
Payment payment = new Payment();
|
||||
paymentsDAO.readPayment(orderId, payment);
|
||||
payment.setStatus(Payment.Status.CANCELED);
|
||||
paymentsDAO.updatePaymentStatus(payment);
|
||||
}
|
||||
|
||||
private static boolean makePayment(Payment payment) {
|
||||
if (Objects.equals(payment.getPaymentMethod().getType(), "Credit Card")) {
|
||||
PaymentDetails details = payment.getPaymentMethod().getDetails();
|
||||
|
||||
DateFormat dateFormat= new SimpleDateFormat("MM/yyyy");
|
||||
Date expiry = new Date();
|
||||
|
||||
try {
|
||||
expiry = dateFormat.parse(details.getExpiry());
|
||||
} catch (ParseException e) {
|
||||
payment.setErrorMsg("Invalid expiry date:" + details.getExpiry());
|
||||
return false;
|
||||
}
|
||||
|
||||
Date today = new Date();
|
||||
if (today.getTime() > expiry.getTime()) {
|
||||
payment.setErrorMsg("Expired payment method:" + details.getExpiry());
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
// Ideally an async call would be made with a callback
|
||||
// But, we're skipping that and assuming payment went through
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.orkes.example.saga.service;
|
||||
|
||||
import io.orkes.example.saga.dao.ShipmentDAO;
|
||||
import io.orkes.example.saga.pojos.Driver;
|
||||
import io.orkes.example.saga.pojos.Shipment;
|
||||
import io.orkes.example.saga.pojos.ShippingRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Slf4j
|
||||
public class ShipmentService {
|
||||
|
||||
private static final ShipmentDAO shipmentDAO = new ShipmentDAO("jdbc:sqlite:food_delivery.db");
|
||||
|
||||
public static int createShipment(ShippingRequest shippingRequest) {
|
||||
String orderId = shippingRequest.getOrderId();
|
||||
|
||||
Shipment shipment = new Shipment();
|
||||
shipment.setOrderId(orderId);
|
||||
shipment.setDeliveryAddress(shippingRequest.getDeliveryAddress());
|
||||
shipment.setDeliveryInstructions(shippingRequest.getDeliveryInstructions());
|
||||
|
||||
int driverId = findDriver();
|
||||
shipment.setDriverId(driverId);
|
||||
|
||||
if (!shipmentDAO.insertShipment(shipment)) {
|
||||
log.error("Shipment creation for order {} failed.", orderId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Driver driver = new Driver();
|
||||
driver.setName("");
|
||||
shipmentDAO.readDriver(driverId, driver);
|
||||
|
||||
if (driver.getName().isBlank()) {
|
||||
log.error("Shipment creation for order {} failed as driver in the area is not available.", orderId);
|
||||
shipmentDAO.cancelShipment(orderId);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
log.info("Assigned driver {} to order with id: {}", driverId, orderId);
|
||||
shipmentDAO.confirmShipment(orderId);
|
||||
}
|
||||
|
||||
return driverId;
|
||||
}
|
||||
|
||||
public static void cancelDelivery(String orderId) {
|
||||
shipmentDAO.cancelShipment(orderId);
|
||||
}
|
||||
|
||||
private static int findDriver() {
|
||||
Random random = new Random();
|
||||
int driverId = 0;
|
||||
int counter = 0;
|
||||
while (counter < 10) {
|
||||
driverId = random.nextInt(4);
|
||||
if(driverId !=0) break;
|
||||
counter += 1;
|
||||
}
|
||||
return driverId;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package io.orkes.example.saga.service;
|
||||
|
||||
import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest;
|
||||
import io.orkes.conductor.client.WorkflowClient;
|
||||
import io.orkes.example.saga.pojos.FoodDeliveryRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class WorkflowService {
|
||||
|
||||
private final WorkflowClient workflowClient;
|
||||
private final Environment environment;
|
||||
|
||||
public Map<String, Object> startFoodDeliveryWorkflow(FoodDeliveryRequest foodDeliveryRequest) {
|
||||
StartWorkflowRequest request = new StartWorkflowRequest();
|
||||
request.setName("FoodDeliveryWorkflow");
|
||||
request.setVersion(1);
|
||||
request.setCorrelationId("api-triggered");
|
||||
|
||||
String TASK_DOMAIN_PROPERTY = "conductor.worker.all.domain";
|
||||
String domain = environment.getProperty(TASK_DOMAIN_PROPERTY, String.class, "");
|
||||
|
||||
if (!domain.isEmpty()) {
|
||||
Map<String, String> taskToDomain = new HashMap<>();
|
||||
taskToDomain.put("*", domain);
|
||||
request.setTaskToDomain(taskToDomain);
|
||||
}
|
||||
|
||||
Map<String, Object> inputData = new HashMap<>();
|
||||
inputData.put("customerEmail", foodDeliveryRequest.getCustomerEmail());
|
||||
inputData.put("customerName", foodDeliveryRequest.getCustomerName());
|
||||
inputData.put("customerContact", foodDeliveryRequest.getCustomerContact());
|
||||
inputData.put("restaurantId", foodDeliveryRequest.getRestaurantId());
|
||||
inputData.put("foodItems", foodDeliveryRequest.getFoodItems());
|
||||
inputData.put("additionalNotes", foodDeliveryRequest.getAdditionalNotes());
|
||||
inputData.put("address", foodDeliveryRequest.getAddress());
|
||||
inputData.put("deliveryInstructions", foodDeliveryRequest.getDeliveryInstructions());
|
||||
inputData.put("paymentAmount", foodDeliveryRequest.getPaymentAmount());
|
||||
inputData.put("paymentMethod", foodDeliveryRequest.getPaymentMethod());
|
||||
request.setInput(inputData);
|
||||
|
||||
String workflowId = "";
|
||||
try {
|
||||
workflowId = workflowClient.startWorkflow(request);
|
||||
log.info("Workflow id: {}", workflowId);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace(System.out);
|
||||
return Map.of("error", "Order creation failure", "detail", ex.toString());
|
||||
}
|
||||
|
||||
return Map.of("workflowId", workflowId);
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
package io.orkes.example.saga.workers;
|
||||
|
||||
import com.netflix.conductor.common.metadata.tasks.TaskResult;
|
||||
import com.netflix.conductor.sdk.workflow.task.WorkerTask;
|
||||
import io.orkes.example.saga.pojos.*;
|
||||
import io.orkes.example.saga.service.InventoryService;
|
||||
import io.orkes.example.saga.service.OrderService;
|
||||
import io.orkes.example.saga.service.PaymentService;
|
||||
import io.orkes.example.saga.service.ShipmentService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Component
|
||||
@ComponentScan(basePackages = {"io.orkes"})
|
||||
public class ConductorWorkers {
|
||||
/**
|
||||
* Note: Using this setting, up to 5 tasks will run in parallel, with tasks being polled every 200ms
|
||||
*/
|
||||
@WorkerTask(value = "order_food", threadCount = 3, pollingInterval = 300)
|
||||
public TaskResult orderFoodTask(OrderRequest orderRequest) {
|
||||
String orderId = OrderService.createOrder(orderRequest);
|
||||
|
||||
TaskResult result = new TaskResult();
|
||||
Map<String, Object> output = new HashMap<>();
|
||||
|
||||
if(orderId != null) {
|
||||
output.put("orderId", orderId);
|
||||
result.setOutputData(output);
|
||||
result.setStatus(TaskResult.Status.COMPLETED);
|
||||
} else {
|
||||
output.put("orderId", null);
|
||||
result.setStatus(TaskResult.Status.FAILED);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@WorkerTask(value = "check_inventory", threadCount = 2, pollingInterval = 300)
|
||||
public TaskResult checkInventoryTask(CheckInventoryRequest checkInventoryRequest) {
|
||||
int restaurantId = checkInventoryRequest.getRestaurantId();
|
||||
ArrayList<FoodItem> items = checkInventoryRequest.getItems();
|
||||
boolean availability = InventoryService.checkAvailability(restaurantId, items);
|
||||
|
||||
TaskResult result = new TaskResult();
|
||||
|
||||
if (availability) {
|
||||
result.setStatus(TaskResult.Status.COMPLETED);
|
||||
} else {
|
||||
result.setReasonForIncompletion("Restaurant is closed");
|
||||
result.setStatus(TaskResult.Status.FAILED_WITH_TERMINAL_ERROR);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@WorkerTask(value = "make_payment", threadCount = 2, pollingInterval = 300)
|
||||
public TaskResult makePaymentTask(PaymentRequest paymentRequest) {
|
||||
TaskResult result = new TaskResult();
|
||||
|
||||
Payment payment = PaymentService.createPayment(paymentRequest);
|
||||
Map<String, Object> output = new HashMap<>();
|
||||
output.put("orderId", payment.getOrderId());
|
||||
output.put("paymentId", payment.getPaymentId());
|
||||
output.put("paymentStatus", payment.getStatus().name());
|
||||
|
||||
if(payment.getStatus() == Payment.Status.SUCCESSFUL) {
|
||||
result.setStatus(TaskResult.Status.COMPLETED);
|
||||
} else {
|
||||
output.put("error", payment.getErrorMsg());
|
||||
result.setStatus(TaskResult.Status.FAILED_WITH_TERMINAL_ERROR);
|
||||
}
|
||||
|
||||
result.setOutputData(output);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@WorkerTask(value = "ship_food", threadCount = 2, pollingInterval = 300)
|
||||
public TaskResult shipFoodTask(ShippingRequest shippingRequest) {
|
||||
TaskResult result = new TaskResult();
|
||||
Map<String, Object> output = new HashMap<>();
|
||||
int driverId = ShipmentService.createShipment(shippingRequest);
|
||||
if (driverId != 0) {
|
||||
result.setStatus(TaskResult.Status.COMPLETED);
|
||||
} else {
|
||||
result.setStatus(TaskResult.Status.FAILED);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@WorkerTask(value = "notify_driver", threadCount = 2, pollingInterval = 300)
|
||||
public Map<String, Object> checkForDriverNotifications(DriverNotificationRequest driverNotificationRequest) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
return result;
|
||||
}
|
||||
|
||||
@WorkerTask(value = "notify_customer", threadCount = 2, pollingInterval = 300)
|
||||
public Map<String, Object> checkForCustomerNotifications(Order order) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
return result;
|
||||
}
|
||||
//
|
||||
@WorkerTask(value = "cancel_payment", threadCount = 2, pollingInterval = 300)
|
||||
public Map<String, Object> cancelPaymentTask(CancelRequest cancelRequest) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
PaymentService.cancelPayment(cancelRequest.getOrderId());
|
||||
return result;
|
||||
}
|
||||
|
||||
@WorkerTask(value = "cancel_delivery", threadCount = 2, pollingInterval = 300)
|
||||
public Map<String, Object> cancelDeliveryTask(CancelRequest cancelRequest) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
ShipmentService.cancelDelivery(cancelRequest.getOrderId());
|
||||
return result;
|
||||
}
|
||||
|
||||
@WorkerTask(value = "cancel_order", threadCount = 2, pollingInterval = 300)
|
||||
public Map<String, Object> cancelOrderTask(CancelRequest cancelRequest) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
Order order = OrderService.getOrder(cancelRequest.getOrderId());
|
||||
OrderService.cancelOrder(order);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
|
||||
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
|
||||
|
||||
springdoc.swagger-ui.path=/swagger-ui.html
|
||||
|
||||
management.endpoints.enabled-by-default=false
|
||||
management.endpoint.info.enabled=false
|
||||
|
||||
# Local app server port
|
||||
server.port=8081
|
||||
|
||||
# Playground
|
||||
|
||||
# Obtain key and secret by logging into https://play.orkes.io/
|
||||
# and navigating to applications menu, create an application and generate key/secret
|
||||
conductor.server.url=https://play.orkes.io/api
|
||||
conductor.security.client.key-id=<key>
|
||||
conductor.security.client.secret=<secret>
|
||||
|
||||
|
||||
# Task Domain
|
||||
conductor.worker.all.domain=saga
|
||||
conductor.worker.all.pollingInterval=22
|
||||
|
||||
|
||||
# DB Setup
|
||||
spring.jpa.database-platform=com.springboot.sqlite.SQLDialect
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.datasource.url=jdbc:sqlite:food_delivery.db
|
||||
spring.datasource.driver-class-name = org.sqlite.JDBC
|
||||
spring.datasource.username=admin
|
||||
spring.datasource.password=admin
|
@ -35,7 +35,7 @@
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<spring.version>6.1.2</spring.version>
|
||||
<spring.version>6.1.5</spring.version>
|
||||
<spring-cloud.version>2023.0.0</spring-cloud.version>
|
||||
<spring-boot.version>3.2.1</spring-boot.version>
|
||||
</properties>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user